Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ADP-291: Document endpoints error codes in the API documentation #2258

Merged
merged 6 commits into from
Nov 2, 2020

Conversation

hasufell
Copy link
Contributor

@hasufell hasufell commented Oct 19, 2020

Issue Number

ADP-291

Implementation approach

This is all best-effort. There are no static checks to ensure we didn't miss anything. There's ongoing work at servant to make this possible: haskell-servant/servant#841
But even that would require a major refactor.

Testing all possible errors is also an option, but seems quite excessive.

The workflow for figuring out the error codes is roughly:

  1. follow the entry point to the the Handler () function e.g.
listTransactions
    :: forall ctx s t k n. (ctx ~ ApiLayer s t k)
    => ctx
...
    -> Handler [ApiTransaction n]
  1. check all liftHandler calls, which have functions with ExceptT as argument, e.g.:
listTransactions
    :: forall ctx s k t.
        ( HasDBLayer s k ctx
        , HasNetworkLayer t ctx
        )
    => ctx
...
    -> ExceptT ErrListTransactions IO [TransactionInfo]
  1. find all the LiftHandler instance, e.g.
instance LiftHandler ErrListTransactions where
    handler = \case
        ErrListTransactionsNoSuchWallet e -> handler e
        ErrListTransactionsStartTimeLaterThanEndTime e -> handler e
        ErrListTransactionsMinWithdrawalWrong ->
            apiError err400 MinWithdrawalWrong
            "The minimum withdrawal value must be at least 1 Lovelace."
        ErrListTransactionsPastHorizonException e -> handler e
  1. follow the recursive handlers to resolve all errors and add client error types to swagger.yaml. The error code is the 3rd ApiErrorCode argument to apiError.
  2. Some errors are implicit, e.g. failed parameter parsing etc. These are also in ApiErrorCode. Also see the special instance instance LiftHandler (Request, ServerError)
  3. repeat until exhaustion

Overview

  • Wallets
  • Addresses
  • Coin Selections
  • Transactions
  • Migrations
  • Stake Pools
  • Utils
  • Network
  • Proxy
  • Settings
  • Byron-specific endpoints

Remarks

  1. I did not double check the byron endpoints. Many of their endpoints share the same types in swagger.yaml, so I assumed they have the same behavior wrt error codes.
  2. This will generally be hard to maintain, since yaml anchors aren't enough to express the overlaps of error codes and group them sensibly. It would need something like dhall to do that.
  3. I removed 405 errors, because the HTTP method is part of the very spec. If you follow the spec, you can't get 405.

@hasufell hasufell self-assigned this Oct 19, 2020
@hasufell hasufell marked this pull request as draft October 19, 2020 15:27
@hasufell hasufell changed the title Document endpoints error codes in the API documentation [WIP] Document endpoints error codes in the API documentation Oct 19, 2020
@hasufell hasufell changed the title [WIP] Document endpoints error codes in the API documentation [WIP] ADP-291: Document endpoints error codes in the API documentation Oct 19, 2020
@hasufell hasufell force-pushed the hasufell/ADP-291/document-api-endpoints branch 3 times, most recently from d2aee15 to aa7650a Compare October 20, 2020 09:40
@hasufell hasufell marked this pull request as ready for review October 20, 2020 09:41
@hasufell hasufell changed the title [WIP] ADP-291: Document endpoints error codes in the API documentation ADP-291: Document endpoints error codes in the API documentation Oct 20, 2020
@hasufell hasufell force-pushed the hasufell/ADP-291/document-api-endpoints branch from aa7650a to ff52b1f Compare October 20, 2020 09:48
lib/core/src/Cardano/Wallet.hs Show resolved Hide resolved
specifications/api/swagger.yaml Show resolved Hide resolved
specifications/api/swagger.yaml Outdated Show resolved Hide resolved
@hasufell hasufell force-pushed the hasufell/ADP-291/document-api-endpoints branch from ff52b1f to 623af75 Compare October 26, 2020 12:20
@hasufell hasufell requested a review from KtorZ October 26, 2020 12:20
@hasufell hasufell force-pushed the hasufell/ADP-291/document-api-endpoints branch from 623af75 to 1203859 Compare October 26, 2020 16:13
@KtorZ KtorZ force-pushed the hasufell/ADP-291/document-api-endpoints branch from 1203859 to 1ddd32b Compare October 26, 2020 17:17
errStr = case res of
Error s -> s
_ -> ""
in counterexample errStr $ res == Success SchemaApiErrorCode
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shows a nice error message:

Cardano.Wallet.Api.Types
  Api Errors
    Every constructor from ApiErrorCode has a corresponding type in the schema FAILED [1]

Failures:

  test/unit/Cardano/Wallet/Api/TypesSpec.hs:937:9:
  1) Cardano.Wallet.Api.Types, Api Errors, Every constructor from ApiErrorCode has a corresponding type in the schema
       Falsifiable (after 1 test):
         Missing ApiErrorCode constructors for: ["RejectedTip","InvalidDelegationDiscovery","NotImplemented","WalletNotResponding","AddressAlreadyExists","UtxoTooSmall","WithdrawalNotWorth","PastHorizon","UnableToAssignInputOutput"]
         Each of these need a corresponding swagger type of the form: x-errConstructorName

  To rerun use: --match "/Cardano.Wallet.Api.Types/Api Errors/Every constructor from ApiErrorCode has a corresponding type in the schema/"

Randomized with seed 1387792531

Julian Ospald and others added 6 commits October 30, 2020 11:12
@hasufell hasufell force-pushed the hasufell/ADP-291/document-api-endpoints branch from c215dce to 7225066 Compare October 30, 2020 10:38
@hasufell
Copy link
Contributor Author

bors r+

iohk-bors bot added a commit that referenced this pull request Oct 30, 2020
2258: ADP-291: Document endpoints error codes in the API documentation r=hasufell a=hasufell

# Issue Number

ADP-291

# Implementation approach

This is all best-effort. There are no static checks to ensure we didn't miss anything. There's ongoing work at servant to make this possible: haskell-servant/servant#841
But even that would require a major refactor.

Testing all possible errors is also an option, but seems quite excessive.

The workflow for figuring out the error codes is roughly:

1. follow the entry point to the the `Handler ()` function e.g.
```hs
listTransactions
    :: forall ctx s t k n. (ctx ~ ApiLayer s t k)
    => ctx
...
    -> Handler [ApiTransaction n]
```
2. check all `liftHandler` calls, which have functions with `ExceptT` as argument, e.g.:

```hs
listTransactions
    :: forall ctx s k t.
        ( HasDBLayer s k ctx
        , HasNetworkLayer t ctx
        )
    => ctx
...
    -> ExceptT ErrListTransactions IO [TransactionInfo]
```

3. find all the LiftHandler instance, e.g.

```hs
instance LiftHandler ErrListTransactions where
    handler = \case
        ErrListTransactionsNoSuchWallet e -> handler e
        ErrListTransactionsStartTimeLaterThanEndTime e -> handler e
        ErrListTransactionsMinWithdrawalWrong ->
            apiError err400 MinWithdrawalWrong
            "The minimum withdrawal value must be at least 1 Lovelace."
        ErrListTransactionsPastHorizonException e -> handler e
```

4. follow the recursive handlers to resolve all errors and add client error types to `swagger.yaml`. The error code is the 3rd `ApiErrorCode` argument to `apiError`.
5. Some errors are implicit, e.g. failed parameter parsing etc. These are also in `ApiErrorCode`. Also see the special instance `instance LiftHandler (Request, ServerError)`
6. repeat until exhaustion

# Overview

- [x] Wallets
- [x] Addresses
- [x] Coin Selections
- [x] Transactions
- [x] Migrations
- [x] Stake Pools
- [x] Utils
- [x] Network
- [x] Proxy
- [x] Settings
- [ ] Byron-specific endpoints

# Remarks

1. I did not double check the byron endpoints. Many of their endpoints share the same types in `swagger.yaml`, so I assumed they have the same behavior wrt error codes.
2. This will generally be hard to maintain, since yaml anchors aren't enough to express the overlaps of error codes and group them sensibly. It would need something like [dhall](https://github.com/dhall-lang/dhall-lang) to do that.
3. I removed 405 errors, because the HTTP method is part of the very spec. If you follow the spec, you can't get 405.

<!-- 
Don't forget to:

 ✓ Self-review your changes to make sure nothing unexpected slipped through
 ✓ Assign yourself to the PR
 ✓ Assign one or several reviewer(s)
 ✓ Once created, link this PR to its corresponding ticket
 ✓ Assign the PR to a corresponding milestone
 ✓ Acknowledge any changes required to the Wiki
-->


Co-authored-by: Julian Ospald <julian.ospald@iohk.io>
Co-authored-by: KtorZ <matthias.benkort@gmail.com>
Copy link
Contributor

@piotr-iohk piotr-iohk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work on documenting error messages. I've left few comments.

properties:
message:
type: string
description: May occur when trying to forget a transaction that is not pending.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

schema:
oneOf:
- <<: *errBadRequest
- <<: *errTransactionIsTooBig
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be only in 403?
Screenshot from 2020-10-30 11-10-06

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

        ErrSelectForPaymentTxTooLarge maxSize maxN  ->
            apiError err400 TransactionIsTooBig $ mconcat
                [ "I am afraid that the transaction you're trying to submit is "
                , "too large! The network allows transactions only as large as "
                , pretty maxSize, "s! As it stands, the current transaction only "
                , "allows me to select up to ", showT maxN, " inputs. Note "
                , "that I am selecting inputs randomly, so retrying *may work* "
                , "provided I end up choosing bigger inputs sufficient to cover "
                , "the transaction cost. Alternatively, try sending to less "
                , "recipients or with smaller metadata."
                ]

it is 400

content:
application/json:
schema:
<<: *errNoSuchWallet
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same 410 and errNoSuchWallet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is 410

        ErrSignPaymentNoSuchWallet e -> (handler e)
            { errHTTPCode = 410
            , errReasonPhrase = errReasonPhrase err410
            }

@@ -1908,19 +2401,12 @@ x-responsesErr404: &responsesErr404
application/json:
schema: *responsesErr

x-responsesErr405: &responsesErr405
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we think it is irrelevant to highlight in API doc? Basically every endpoint may return this...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. The spec includes which method to use. If you follow the spec, 405 will never be returned.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then 406 and 415 should be removed also I guess, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

415 maybe, but the swagger spec doesn't document enough for 406 to not be a concern afais.

content:
application/json:
schema:
<<: *errNoSuchWallet
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same 410 and errNoSuchWallet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

        ErrSignPaymentNoSuchWallet e -> (handler e)
            { errHTTPCode = 410
            , errReasonPhrase = errReasonPhrase err410
            }

content:
application/json:
schema:
<<: *errNoSuchWallet
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't feel right to me. I think we should use 404 on 'no such wallet'. Especially looking at some endpoints where:

Screenshot from 2020-10-30 11-25-22

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR isn't really about refactoring or fixing our error codes though. I just added documentation to the existing error handlers. I changed nothing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK. It revealed those small inconsistencies though (I spotted 410 and 403 only).
Could be fixed in this go if it's not a lot of work, but don't have to of course.

content:
application/json:
schema:
<<: *errNoSuchWallet
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same 410 and errNoSuchWallet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

        ErrSignPaymentNoSuchWallet e -> (handler e)
            { errHTTPCode = 410
            , errReasonPhrase = errReasonPhrase err410
            }

@hasufell
Copy link
Contributor Author

bors r-

@iohk-bors
Copy link
Contributor

iohk-bors bot commented Oct 30, 2020

Canceled.

@hasufell
Copy link
Contributor Author

hasufell commented Nov 2, 2020

bors r+

@iohk-bors
Copy link
Contributor

iohk-bors bot commented Nov 2, 2020

Build succeeded:

@iohk-bors iohk-bors bot merged commit 9c56a88 into master Nov 2, 2020
@iohk-bors iohk-bors bot deleted the hasufell/ADP-291/document-api-endpoints branch November 2, 2020 11:08
iohk-bors bot added a commit that referenced this pull request Nov 5, 2020
2293: Revise API error codes r=piotr-iohk a=piotr-iohk

# Issue Number

ADP-291


# Overview

- f533091
  Revise error codes:  - 404 when wallet not found  - 403 when transaction is too big
  
- 430a31f
  Update swagger
  
- e90b264
  Update integration tests



# Comments

Whie reviewing #2258 I noticed that some error codes are a bit inconsistent, e.g.:
 - 410 or 404 is returned when the wallet is not found.
 - 400 or 403 - when transaction is too big.
 
 This pr is about to revise it and:
 - return 404 on wallet not found (removing 410 altogether)
 - return 403 on transaction too big


Co-authored-by: Piotr Stachyra <piotr.stachyra@iohk.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants