-
Notifications
You must be signed in to change notification settings - Fork 631
[CBR-306] Akegalj/co 319/swagger account index #3086
[CBR-306] Akegalj/co 319/swagger account index #3086
Conversation
mkAccountIndex :: Word32 -> Either Text AccountIndex | ||
mkAccountIndex index | index >= getAccIndex minBound = Right $ AccountIndex index | ||
| otherwise = Left $ sformat | ||
("mkAccountIndex: Account index should be in range ["%int%".."%int%"]") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we think it make sense to add additional constructor to WalletError
which would capture this failure?
There are other places in this module where I can do this refactoring (possibly as part of https://iohk.myjetbrains.com/youtrack/issue/CBR-26 )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oui :)
IMO, we should always favor explicit and typed error when we can. Especially when the error is a well-identified scenario that is reachable.
@@ -186,17 +186,13 @@ instance (HasSwagger subApi) => HasSwagger (WalletRequestParams :> subApi) where | |||
|
|||
instance ToParamSchema WalletId | |||
|
|||
instance ToSchema Core.Address where | |||
declareNamedSchema = pure . paramSchemaToNamedSchema defaultSchemaOptions | |||
|
|||
instance ToParamSchema Core.Address where |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😶 Why exactly? Is this not used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:/ good catch, looks like a mistake
mkAccountIndex :: Word32 -> Either Text AccountIndex | ||
mkAccountIndex index | index >= getAccIndex minBound = Right $ AccountIndex index | ||
| otherwise = Left $ sformat | ||
("mkAccountIndex: Account index should be in range ["%int%".."%int%"]") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oui :)
IMO, we should always favor explicit and typed error when we can. Especially when the error is a well-identified scenario that is reachable.
|
||
instance FromJSON AccountIndex where | ||
parseJSON = | ||
either (fail . toString) pure . mkAccountIndex <=< parseJSON |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that if we define an explicit WalletError
for mkAccountIndex
, it'd be better to use sformat
instead of toString
to leverage a Buildable
instance and have a proper diagnostic!
deriveSafeBuildable ''AccountIndex | ||
-- Nothing secret to redact for a AccountIndex. | ||
instance BuildableSafeGen AccountIndex where | ||
buildSafeGen _ = bprint build |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm... isn't that a bit too recursive ^^" ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uh, you might be right!
I saw a similar definition for SyncProgress
in the same module
buildSafeGen _ sp = bprint build sp |
BuildableSafeGen
is defined...
EDIT: Good catch. In fact, SyncProgress
will also recurse.
httpApiDataRoundtripProp @(V1 Core.Address) Proxy | ||
httpApiDataRoundtripProp @PerPage Proxy | ||
httpApiDataRoundtripProp @Page Proxy | ||
httpApiDataRoundtripProp @Core.Coin Proxy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👌
Erf :/ ... Not sure introducing a breaking change will be well-advised here. Especially for a JSON-key which can be hard to chase in exchange integrations. Yet, we can perhaps have a backward compatible fix making a slightly hacky parser accepting both |
@@ -688,7 +707,14 @@ instance ToSchema SyncProgress where | |||
deriveSafeBuildable ''SyncProgress | |||
-- Nothing secret to redact for a SyncProgress. | |||
instance BuildableSafeGen SyncProgress where | |||
buildSafeGen _ sp = bprint build sp | |||
buildSafeGen sl SyncProgress {..} = bprint ("{" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
es Util WalletSpecs Data.Text.Buildable Test.QuickCheck> sp :: SyncProgress <- generate arbitrary
es Util WalletSpecs Data.Text.Buildable Test.QuickCheck> sp
SyncProgress {spEstimatedCompletionTime = EstimatedCompletionTime (MeasuredIn 2894754), spThroughput = SyncThroughput (MeasuredIn (SyncThroughput (BlockCount {getBlockCount = 12610705}))), spPercentage = SyncPercentage (MeasuredIn 42)}
es Util WalletSpecs Data.Text.Buildable Test.QuickCheck> build sp
"{ estimatedCompletionTime={ quantity=2894754 unit=milliseconds } throughput={ quantity=12610705 unit=blocksPerSecond } percentage=42% }"
it was recursive before
deriveSafeBuildable ''AccountIndex | ||
-- Nothing secret to redact for a AccountIndex. | ||
instance BuildableSafeGen AccountIndex where | ||
buildSafeGen _ = bprint build . getAccIndex |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed recursive buildable. Thanks for catching that!
es Util WalletSpecs Data.Text.Buildable Test.QuickCheck> let ac = fromRight (error "bla") (mkAccountIndex 2147483649)
es Util WalletSpecs Data.Text.Buildable Test.QuickCheck> ac
AccountIndex {getAccIndex = 2147483649}
es Util WalletSpecs Data.Text.Buildable Test.QuickCheck> build ac
"2147483649"
@KtorZ I've implemented the ADT-error-type; care to re-review? |
LGTM 👍 |
Some second thoughts, @parsonsmatt What's the rational behind a new |
@@ -195,7 +195,7 @@ instance Migrate V0.AccountId (V1.WalletId, V1.AccountIndex) where | |||
(,) | |||
<$> eitherMigrate (V0.aiWId accId) | |||
<*> first | |||
Errors.MigrationFailed | |||
(Errors.MigrationFailed . sformat build) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you think it makes sense to introduce new constructor in WalletError
to capture exact underying issue (instead of wrapping it under MigrationFailed
). I wonder does it makes sense to have this pattern in general.
This would make it possible to have more detailed error handling if it is needed. Now we mask underlying errors under higher level error (which is MigrationFailed
in this case).
I wonder do we have some opinion/rules about this?
problem is with cyclic imports. |
-- | ||
-- Error from parsing / validating JSON inputs | ||
-- | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@KtorZ I see you have made MigrationFailed
an instance of Expcetion
, but JSONValidationError
doesn't have the same instance. Is there a reason for the separation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm. No. That's an oversight 👍
21598f4
to
6161cb7
Compare
@KtorZ Clarity. If I say: mkAccountIndex :: Int -> Either AnyApplicationError AccountIndex then you lose all type-safety around what exceptions you should be throwing, and you lose type-safety in handling the appropriate exceptions. |
d93784b
to
2924c46
Compare
This makes it now possible to import V1/Errors from the V1/Types module and leverage errors from this module. One thing is still unclear to me: Why Errors isn't defined in V1/Types already? There's a circular dependency between V1/Response and V1/Types if we go this way, as well as between V1/Migration and V1/Types. Nevertheless, it would make sense to have three data-types here: - WalletError (defined in V1/Types) - MigrationError (defined in V1/Types) - JSONParsingError (defined in Response) This way, we could remove the conflicting constructor from WalletError and remove the need for an extra module here. It will also makes thing clearer
To realize this, we had to extract JSONValidationFailed and MigrationFailed constructor from WalletError. They're now defined as constructor in different data-types (resp. JSONValidationError and MigrationError).
This is rather ugly and could probably be achieved nicely with a better understanding of the Generics.SOP library. As far as I could tell, there's no easy way to retrieve 'Tag' for single constructor (cf: 'For a datatype with a single constructor we do not need to tag values with their constructor; but for a datatype with multiple constructors we do. ')
2924c46
to
808f00b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@KtorZ @parsonsmatt thanks for jumping in. LGTM
* [CO-319] Fix account index swagger example * [CO-319] Add roundtrip tests * [CO-319] Fix recursive buildable instances * [CO-319] Use strongly typed error * [CO-319] Remove duplication in 'renderAccountIndexError' * [CO-319] Distangle V1/Errors This makes it now possible to import V1/Errors from the V1/Types module and leverage errors from this module. One thing is still unclear to me: Why Errors isn't defined in V1/Types already? There's a circular dependency between V1/Response and V1/Types if we go this way, as well as between V1/Migration and V1/Types. Nevertheless, it would make sense to have three data-types here: - WalletError (defined in V1/Types) - MigrationError (defined in V1/Types) - JSONParsingError (defined in Response) This way, we could remove the conflicting constructor from WalletError and remove the need for an extra module here. It will also makes thing clearer * [CO-319] Make V1/Error part of V1/Types To realize this, we had to extract JSONValidationFailed and MigrationFailed constructor from WalletError. They're now defined as constructor in different data-types (resp. JSONValidationError and MigrationError). * [CO-319] Solve rebase conflicts * [CO-319] Correctly format (jsend) newtype errors This is rather ugly and could probably be achieved nicely with a better understanding of the Generics.SOP library. As far as I could tell, there's no easy way to retrieve 'Tag' for single constructor (cf: 'For a datatype with a single constructor we do not need to tag values with their constructor; but for a datatype with multiple constructors we do. ')
* [CO-319] Fix account index swagger example * [CO-319] Add roundtrip tests * [CO-319] Fix recursive buildable instances * [CO-319] Use strongly typed error * [CO-319] Remove duplication in 'renderAccountIndexError' * [CO-319] Distangle V1/Errors This makes it now possible to import V1/Errors from the V1/Types module and leverage errors from this module. One thing is still unclear to me: Why Errors isn't defined in V1/Types already? There's a circular dependency between V1/Response and V1/Types if we go this way, as well as between V1/Migration and V1/Types. Nevertheless, it would make sense to have three data-types here: - WalletError (defined in V1/Types) - MigrationError (defined in V1/Types) - JSONParsingError (defined in Response) This way, we could remove the conflicting constructor from WalletError and remove the need for an extra module here. It will also makes thing clearer * [CO-319] Make V1/Error part of V1/Types To realize this, we had to extract JSONValidationFailed and MigrationFailed constructor from WalletError. They're now defined as constructor in different data-types (resp. JSONValidationError and MigrationError). * [CO-319] Solve rebase conflicts * [CO-319] Correctly format (jsend) newtype errors This is rather ugly and could probably be achieved nicely with a better understanding of the Generics.SOP library. As far as I could tell, there's no easy way to retrieve 'Tag' for single constructor (cf: 'For a datatype with a single constructor we do not need to tag values with their constructor; but for a datatype with multiple constructors we do. ')
* [CO-319] Fix account index swagger example * [CO-319] Add roundtrip tests * [CO-319] Fix recursive buildable instances * [CO-319] Use strongly typed error * [CO-319] Remove duplication in 'renderAccountIndexError' * [CO-319] Distangle V1/Errors This makes it now possible to import V1/Errors from the V1/Types module and leverage errors from this module. One thing is still unclear to me: Why Errors isn't defined in V1/Types already? There's a circular dependency between V1/Response and V1/Types if we go this way, as well as between V1/Migration and V1/Types. Nevertheless, it would make sense to have three data-types here: - WalletError (defined in V1/Types) - MigrationError (defined in V1/Types) - JSONParsingError (defined in Response) This way, we could remove the conflicting constructor from WalletError and remove the need for an extra module here. It will also makes thing clearer * [CO-319] Make V1/Error part of V1/Types To realize this, we had to extract JSONValidationFailed and MigrationFailed constructor from WalletError. They're now defined as constructor in different data-types (resp. JSONValidationError and MigrationError). * [CO-319] Solve rebase conflicts * [CO-319] Correctly format (jsend) newtype errors This is rather ugly and could probably be achieved nicely with a better understanding of the Generics.SOP library. As far as I could tell, there's no easy way to retrieve 'Tag' for single constructor (cf: 'For a datatype with a single constructor we do not need to tag values with their constructor; but for a datatype with multiple constructors we do. ')
* [CO-319] Fix account index swagger example * [CO-319] Add roundtrip tests * [CO-319] Fix recursive buildable instances * [CO-319] Use strongly typed error * [CO-319] Remove duplication in 'renderAccountIndexError' * [CO-319] Distangle V1/Errors This makes it now possible to import V1/Errors from the V1/Types module and leverage errors from this module. One thing is still unclear to me: Why Errors isn't defined in V1/Types already? There's a circular dependency between V1/Response and V1/Types if we go this way, as well as between V1/Migration and V1/Types. Nevertheless, it would make sense to have three data-types here: - WalletError (defined in V1/Types) - MigrationError (defined in V1/Types) - JSONParsingError (defined in Response) This way, we could remove the conflicting constructor from WalletError and remove the need for an extra module here. It will also makes thing clearer * [CO-319] Make V1/Error part of V1/Types To realize this, we had to extract JSONValidationFailed and MigrationFailed constructor from WalletError. They're now defined as constructor in different data-types (resp. JSONValidationError and MigrationError). * [CO-319] Solve rebase conflicts * [CO-319] Correctly format (jsend) newtype errors This is rather ugly and could probably be achieved nicely with a better understanding of the Generics.SOP library. As far as I could tell, there's no easy way to retrieve 'Tag' for single constructor (cf: 'For a datatype with a single constructor we do not need to tag values with their constructor; but for a datatype with multiple constructors we do. ')
* [CO-319] Fix account index swagger example * [CO-319] Add roundtrip tests * [CO-319] Fix recursive buildable instances * [CO-319] Use strongly typed error * [CO-319] Remove duplication in 'renderAccountIndexError' * [CO-319] Distangle V1/Errors This makes it now possible to import V1/Errors from the V1/Types module and leverage errors from this module. One thing is still unclear to me: Why Errors isn't defined in V1/Types already? There's a circular dependency between V1/Response and V1/Types if we go this way, as well as between V1/Migration and V1/Types. Nevertheless, it would make sense to have three data-types here: - WalletError (defined in V1/Types) - MigrationError (defined in V1/Types) - JSONParsingError (defined in Response) This way, we could remove the conflicting constructor from WalletError and remove the need for an extra module here. It will also makes thing clearer * [CO-319] Make V1/Error part of V1/Types To realize this, we had to extract JSONValidationFailed and MigrationFailed constructor from WalletError. They're now defined as constructor in different data-types (resp. JSONValidationError and MigrationError). * [CO-319] Solve rebase conflicts * [CO-319] Correctly format (jsend) newtype errors This is rather ugly and could probably be achieved nicely with a better understanding of the Generics.SOP library. As far as I could tell, there's no easy way to retrieve 'Tag' for single constructor (cf: 'For a datatype with a single constructor we do not need to tag values with their constructor; but for a datatype with multiple constructors we do. ')
Description
The example accountId from the API doc isn't much relevant and reflects an invalid value
Linked issue
https://iohk.myjetbrains.com/youtrack/issue/CO-319
Type of change
Developer checklist
Testing checklist
QA Steps
Screenshots (if available)
accountIndex
in request body shows correct value rangeaccountIndex
on description on the right picks correct value (not bellow the minimum)accountId
(same thing asaccountIndex
) in request params shows correct value rangeroundtrip tests

@KtorZ I wonder why did we ended up with
accountId
andaccountIndex
json keys for the same thing. Is it still too late to unify it? (it would be a breaking change for exchanges)