diff --git a/CHANGELOG.md b/CHANGELOG.md index 65df811bb5d..1c075bdeb43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,10 +21,10 @@ - We can force an NTP-check when getting node-info via the API (`?force_ntp_check` query flag) (CO-325) -- The API provides an endpoint to retrieve basic statistics on the UTxO distribution of a wallet +- The API provides an endpoint to retrieve basic statistics on the UTxO distribution of a wallet (`/api/v1/wallets/{walletId}/statistics`). (CO-325) -- cardano-sl exposes a new package `x509` with tooling for defining a PKI infrastructure from +- cardano-sl exposes a new package `x509` with tooling for defining a PKI infrastructure from pure Haskell. This is basically an export of the internals of the tool `cardano-sl-x509-generate` (CO-387) @@ -50,8 +50,8 @@ - **[API BREAKING CHANGE]** The behavior of `/api/v1/addresses/{address}` has been adjusted to reflect more accurately the meaning of ownership regarding addresses. The previous version of this endpoint failed with an HTTP error when the given address was unknown to the wallet. - This was misleading since an address that is unknown to the wallet may still belong to the wallet. To reflect this, - the V1 endpoint does not fail anymore as it used to when an address is not recognised and returns instead a new field + This was misleading since an address that is unknown to the wallet may still belong to the wallet. To reflect this, + the V1 endpoint does not fail anymore as it used to when an address is not recognised and returns instead a new field 'is-ours' which indicates either that an address is ours, or that it is 'not-recognised'. (CBR-401) ### Improvements @@ -62,13 +62,15 @@ - Small refactor of wallet Errors implementation to be more maintainable (CBR-26) -- Content-Type parser is now more lenient and accepts `application/json`, `application/json;charset=utf-8` and +- Content-Type parser is now more lenient and accepts `application/json`, `application/json;charset=utf-8` and no Content-Type at all (defaulting to `application/json`). - The codebase now relies on the package `cryptonite` (instead of `ed25519`) for Ed25519 implementation (CO-325) - **[API BREAKING CHANGE]** Improve diagnostic for `NotEnoughMoney` error (CBR-461) +- When Content-Type's main MIME-type cannot fall back to 'application/json' then UnsupportedMimeTypeError is returned + ### Specifications ### Documentation diff --git a/pkgs/default.nix b/pkgs/default.nix index 0f87066f539..cdecebd0da5 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -18051,6 +18051,7 @@ license = stdenv.lib.licenses.mit; , beam-migrate , beam-sqlite , bifunctors +, binary , bytestring , cardano-crypto , cardano-sl @@ -18176,6 +18177,7 @@ beam-core beam-migrate beam-sqlite bifunctors +binary bytestring cardano-crypto cardano-sl diff --git a/wallet-new/cardano-sl-wallet-new.cabal b/wallet-new/cardano-sl-wallet-new.cabal index 6feb2e2a252..0f69896950a 100755 --- a/wallet-new/cardano-sl-wallet-new.cabal +++ b/wallet-new/cardano-sl-wallet-new.cabal @@ -167,6 +167,7 @@ library ghc-options: -Wall build-depends: base + , binary , acid-state , aeson , aeson-options diff --git a/wallet-new/src/Cardano/Wallet/API/Response.hs b/wallet-new/src/Cardano/Wallet/API/Response.hs index cc150e69547..ddc516364ca 100644 --- a/wallet-new/src/Cardano/Wallet/API/Response.hs +++ b/wallet-new/src/Cardano/Wallet/API/Response.hs @@ -7,6 +7,7 @@ module Cardano.Wallet.API.Response ( , ResponseStatus(..) , WalletResponse(..) , JSONValidationError(..) + , UnsupportedMimeTypeError(..) -- * Generating responses for collections , respondWith , fromSlice @@ -34,7 +35,7 @@ import Formatting (bprint, build, (%)) import qualified Formatting.Buildable import Generics.SOP.TH (deriveGeneric) import GHC.Generics (Generic) -import Servant (err400) +import Servant (err400, err415) import Servant.API.ContentTypes (Accept (..), JSON, MimeRender (..), MimeUnrender (..), OctetStream) import Test.QuickCheck @@ -269,3 +270,36 @@ instance HasDiagnostic JSONValidationError where instance ToServantError JSONValidationError where declareServantError _ = err400 + + +newtype UnsupportedMimeTypeError + = UnsupportedMimeTypePresent Text + deriving (Eq, Show, Generic) + +deriveGeneric ''UnsupportedMimeTypeError + +instance ToJSON UnsupportedMimeTypeError where + toJSON = + jsendErrorGenericToJSON + +instance FromJSON UnsupportedMimeTypeError where + parseJSON = + jsendErrorGenericParseJSON + +instance Exception UnsupportedMimeTypeError + +instance Arbitrary UnsupportedMimeTypeError where + arbitrary = + pure (UnsupportedMimeTypePresent "Delivered MIME-type is not supported.") + +instance Buildable UnsupportedMimeTypeError where + build (UnsupportedMimeTypePresent txt) = + bprint build txt + +instance HasDiagnostic UnsupportedMimeTypeError where + getDiagnosticKey _ = + "mimeContentTypeError" + +instance ToServantError UnsupportedMimeTypeError where + declareServantError _ = + err415 diff --git a/wallet-new/src/Cardano/Wallet/API/V1/Swagger.hs b/wallet-new/src/Cardano/Wallet/API/V1/Swagger.hs index cc2ab0b3785..0edbfdfaaca 100644 --- a/wallet-new/src/Cardano/Wallet/API/V1/Swagger.hs +++ b/wallet-new/src/Cardano/Wallet/API/V1/Swagger.hs @@ -376,6 +376,9 @@ $errors -- 'JSONValidationError' , mkRow fmtErr $ JSONValidationFailed "Expected String, found Null." + -- 'UnsupportedMimeTypeError' + , mkRow fmtErr $ UnsupportedMimeTypePresent "Expected Content-Type's main MIME-type to be 'application/json'." + -- TODO 'MnemonicError' ? ] mkRow fmt err = T.intercalate "|" (fmt err) @@ -860,8 +863,8 @@ curl -X POST \ --cacert ./scripts/tls-files/ca.crt \ --cert ./scripts/tls-files/client.pem \ -d '{ - "walletId": "Ae2tdPwUPE...V3AVTnqGZ4", - "accountIndex": 2147483648 + "walletId": "Ae2tdPwUPE...V3AVTnqGZ4", + "accountIndex": 2147483648 }' ``` diff --git a/wallet-new/src/Cardano/Wallet/Action.hs b/wallet-new/src/Cardano/Wallet/Action.hs index 3e38547604e..7b9198b0bc3 100644 --- a/wallet-new/src/Cardano/Wallet/Action.hs +++ b/wallet-new/src/Cardano/Wallet/Action.hs @@ -29,7 +29,7 @@ import Cardano.Wallet.Server.CLI (NewWalletBackendParams, getFullMigrationFlag, getWalletDbOptions, walletDbPath, walletRebuildDb) import Cardano.Wallet.Server.Middlewares (throttleMiddleware, - withDefaultHeader) + unsupportedMimeTypeMiddleware, withDefaultHeader) import qualified Cardano.Wallet.Server.Plugins as Plugins import Cardano.Wallet.WalletLayer (PassiveWalletLayer) import qualified Cardano.Wallet.WalletLayer.Kernel as WalletLayer.Kernel @@ -88,6 +88,7 @@ actionWithWallet params genesisConfig walletConfig txpConfig ntpConfig nodeParam -- Throttle requests. [ throttleMiddleware (ccThrottle walletConfig) , withDefaultHeader Headers.applicationJson + , unsupportedMimeTypeMiddleware ]) -- The corresponding wallet documention, served as a different diff --git a/wallet-new/src/Cardano/Wallet/Server/Middlewares.hs b/wallet-new/src/Cardano/Wallet/Server/Middlewares.hs index 8814ffe2314..e284bdb38a9 100644 --- a/wallet-new/src/Cardano/Wallet/Server/Middlewares.hs +++ b/wallet-new/src/Cardano/Wallet/Server/Middlewares.hs @@ -7,18 +7,25 @@ module Cardano.Wallet.Server.Middlewares ( withMiddlewares , throttleMiddleware , withDefaultHeader + , unsupportedMimeTypeMiddleware ) where -import Universum +import Universum hiding (toStrict) import Data.Aeson (encode) +import Data.Binary.Builder (fromByteString) +import Data.ByteString.Lazy (toStrict) import qualified Data.List as List import Network.HTTP.Types.Header (Header) import Network.HTTP.Types.Method (methodPatch, methodPost, methodPut) -import Network.Wai (Application, Middleware, ifRequest, - requestHeaders, requestMethod, responseLBS) +import Network.HTTP.Types.Status (status415) +import Network.Wai (Application, Middleware, Response, ifRequest, + modifyResponse, requestHeaders, requestMethod, + responseBuilder, responseLBS, responseStatus) import qualified Network.Wai.Middleware.Throttle as Throttle + +import Cardano.Wallet.API.Response (UnsupportedMimeTypeError (..)) import Cardano.Wallet.API.V1.Headers (applicationJson) import qualified Cardano.Wallet.API.V1.Types as V1 @@ -29,6 +36,23 @@ import Pos.Launcher.Configuration (ThrottleSettings (..)) withMiddlewares :: [Middleware] -> Application -> Application withMiddlewares = flip $ foldr ($) +unsupportedMimeTypeMiddleware :: Middleware +unsupportedMimeTypeMiddleware = + modifyResponse responseModifier + where + responseModifier :: Response -> Response + responseModifier r + | responseStatus r == status415 = + responseBuilder status415 + [ ("Content-Type", "application/json") ] + ( fromByteString . + toStrict . + encode $ + UnsupportedMimeTypePresent "The API expects the Content-Type's main MIME-type to be 'application/json'" + ) + | otherwise = r + + -- | Only apply a @Middleware@ to request with bodies (we don't consider -- "DELETE" as one of them). ifRequestWithBody :: Middleware -> Middleware diff --git a/wallet-new/test/MarshallingSpec.hs b/wallet-new/test/MarshallingSpec.hs index 8311b7e4be5..204868fca08 100644 --- a/wallet-new/test/MarshallingSpec.hs +++ b/wallet-new/test/MarshallingSpec.hs @@ -40,7 +40,8 @@ import Test.Pos.Core.Arbitrary () import Cardano.Wallet.API.Indices import Cardano.Wallet.API.Request.Pagination (Page, PerPage) -import Cardano.Wallet.API.Response (JSONValidationError) +import Cardano.Wallet.API.Response (JSONValidationError, + UnsupportedMimeTypeError) import Cardano.Wallet.API.V1.Migration.Types (Migrate (..), MigrationError) import Cardano.Wallet.API.V1.Types @@ -75,6 +76,7 @@ spec = parallel $ describe "Marshalling & Unmarshalling" $ do aesonRoundtripProp @TransactionStatus Proxy aesonRoundtripProp @WalletError Proxy aesonRoundtripProp @JSONValidationError Proxy + aesonRoundtripProp @UnsupportedMimeTypeError Proxy aesonRoundtripProp @MigrationError Proxy aesonRoundtripProp @WalletId Proxy aesonRoundtripProp @Wallet Proxy diff --git a/wallet-new/test/WalletNewJson.hs b/wallet-new/test/WalletNewJson.hs index eed58024dd1..4d1c86c27bb 100644 --- a/wallet-new/test/WalletNewJson.hs +++ b/wallet-new/test/WalletNewJson.hs @@ -6,7 +6,8 @@ import Universum import Hedgehog (Property) -import Cardano.Wallet.API.Response (JSONValidationError (..)) +import Cardano.Wallet.API.Response (JSONValidationError (..), + UnsupportedMimeTypeError (..)) import Cardano.Wallet.API.V1.Migration.Types (MigrationError (..)) import Cardano.Wallet.API.V1.Swagger.Example (genExample) import Cardano.Wallet.API.V1.Types (ErrNotEnoughMoney (..), V1 (..), @@ -165,3 +166,12 @@ golden_JSONValidationError_JSONValidationFailed = goldenTestJSON (JSONValidationFailed "test") "test/golden/JSONValidationError_JSONValidationFailed" + +------------------------------------------------------------------------------- +-- UnsupportedMimeTypeError +------------------------------------------------------------------------------- +golden_UnsupportedMimeTypeError_UnsupportedMimeTypePresent :: Property +golden_UnsupportedMimeTypeError_UnsupportedMimeTypePresent = + goldenTestJSON + (UnsupportedMimeTypePresent "test") + "test/golden/UnsupportedMimeTypeError_UnsupportedMimeTypePresent" diff --git a/wallet-new/test/golden/UnsupportedMimeTypeError_UnsupportedMimeTypePresent b/wallet-new/test/golden/UnsupportedMimeTypeError_UnsupportedMimeTypePresent new file mode 100644 index 00000000000..44589ee92ef --- /dev/null +++ b/wallet-new/test/golden/UnsupportedMimeTypeError_UnsupportedMimeTypePresent @@ -0,0 +1 @@ +{"status":"error","diagnostic":{"mimeContentTypeError":"test"},"message":"UnsupportedMimeTypePresent"} \ No newline at end of file