Skip to content
This repository was archived by the owner on Aug 18, 2020. It is now read-only.

Commit 1e61367

Browse files
akegaljKtorZ
authored andcommitted
Akegalj/co 319/swagger account index (#3086)
* [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. ')
1 parent 9d07db6 commit 1e61367

File tree

20 files changed

+818
-582
lines changed

20 files changed

+818
-582
lines changed

wallet-new/cardano-sl-wallet-new.cabal

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ library
4444
Cardano.Wallet.API.V1.Handlers.Addresses
4545
Cardano.Wallet.API.V1.Handlers.Transactions
4646
Cardano.Wallet.API.V1.Handlers.Wallets
47+
Cardano.Wallet.API.V1.Headers
4748
Cardano.Wallet.API.V1.Info
4849
Cardano.Wallet.API.V1.LegacyHandlers
4950
Cardano.Wallet.API.V1.LegacyHandlers.Accounts

wallet-new/integration/TransactionSpecs.hs

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ module TransactionSpecs (transactionSpecs) where
55

66
import Universum
77

8-
import Cardano.Wallet.API.V1.Errors hiding (describe)
98
import Cardano.Wallet.Client.Http
9+
import Control.Concurrent (threadDelay)
1010
import Control.Lens
11-
import qualified Pos.Core as Core
1211
import Test.Hspec
13-
14-
import Control.Concurrent (threadDelay)
1512
import Text.Show.Pretty (ppShow)
13+
14+
import qualified Pos.Core as Core
15+
1616
import Util
1717

1818
{-# ANN module ("HLint: ignore Reduce duplication" :: Text) #-}

wallet-new/integration/WalletSpecs.hs

-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ module WalletSpecs (walletSpecs) where
55

66
import Universum
77

8-
import Cardano.Wallet.API.V1.Errors
9-
(WalletError (WalletAlreadyExists))
108
import Cardano.Wallet.Client.Http
119
import Control.Lens
1210
import Test.Hspec

wallet-new/src/Cardano/Wallet/API/Response.hs

+74-17
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
{-# LANGUAGE DeriveFunctor #-}
22
{-# LANGUAGE DeriveGeneric #-}
3+
{-# LANGUAGE LambdaCase #-}
34
{-# LANGUAGE OverloadedLists #-}
45
module Cardano.Wallet.API.Response (
56
Metadata (..)
67
, ResponseStatus(..)
78
, WalletResponse(..)
9+
, JSONValidationError(..)
810
-- * Generating responses for collections
911
, respondWith
1012
, fromSlice
@@ -18,39 +20,42 @@ module Cardano.Wallet.API.Response (
1820
) where
1921

2022
import Prelude
21-
import Universum (Buildable, decodeUtf8, toText, (<>))
23+
import Universum (Buildable, Exception, Text, decodeUtf8, toText,
24+
(<>))
2225

26+
import Cardano.Wallet.API.Indices (Indexable', IxSet')
27+
import Cardano.Wallet.API.Request (RequestParams (..))
28+
import Cardano.Wallet.API.Request.Filter (FilterOperations (..))
29+
import Cardano.Wallet.API.Request.Pagination (Page (..),
30+
PaginationMetadata (..), PaginationParams (..),
31+
PerPage (..))
32+
import Cardano.Wallet.API.Request.Sort (SortOperations (..))
33+
import Cardano.Wallet.API.Response.Filter.IxSet as FilterBackend
2334
import Cardano.Wallet.API.Response.JSend (ResponseStatus (..))
35+
import Cardano.Wallet.API.Response.Sort.IxSet as SortBackend
36+
import Cardano.Wallet.API.V1.Errors (ToServantError (..))
2437
import Cardano.Wallet.API.V1.Swagger.Example (Example, example)
25-
import Control.Lens
26-
import Data.Aeson
38+
import Control.Lens hiding ((.=))
39+
import Data.Aeson (FromJSON (..), ToJSON (..), eitherDecode, encode,
40+
object, pairs, (.:), (.=))
2741
import Data.Aeson.Encode.Pretty (encodePretty)
2842
import Data.Aeson.TH
43+
import Data.Aeson.Types (Value (..), typeMismatch)
2944
import Data.Swagger as S hiding (Example, example)
3045
import Data.Typeable
3146
import Formatting (bprint, build, (%))
47+
import Generics.SOP.TH (deriveGeneric)
3248
import GHC.Generics (Generic)
49+
import Servant (err400)
3350
import Servant.API.ContentTypes (Accept (..), JSON, MimeRender (..),
3451
MimeUnrender (..), OctetStream)
3552
import Test.QuickCheck
3653

37-
import Cardano.Wallet.API.Indices (Indexable', IxSet')
38-
import Cardano.Wallet.API.Request (RequestParams (..))
39-
import Cardano.Wallet.API.Request.Filter (FilterOperations (..))
40-
import Cardano.Wallet.API.Request.Pagination (Page (..),
41-
PaginationMetadata (..), PaginationParams (..),
42-
PerPage (..))
43-
import Cardano.Wallet.API.Request.Sort (SortOperations (..))
44-
import Cardano.Wallet.API.Response.Filter.IxSet as FilterBackend
45-
import Cardano.Wallet.API.Response.Sort.IxSet as SortBackend
46-
import Cardano.Wallet.API.V1.Errors
47-
(WalletError (JSONValidationFailed))
48-
4954
import qualified Data.Aeson.Options as Serokell
5055
import qualified Data.Char as Char
56+
import qualified Data.HashMap.Strict as HMS
5157
import qualified Formatting.Buildable
5258

53-
5459
-- | Extra information associated with an HTTP response.
5560
data Metadata = Metadata
5661
{ metaPagination :: PaginationMetadata
@@ -166,7 +171,7 @@ respondWith :: (Monad m, Indexable' a)
166171
-> m (WalletResponse [a])
167172
respondWith RequestParams{..} fops sorts generator = do
168173
(theData, paginationMetadata) <- paginate rpPaginationParams . sortData sorts . applyFilters fops <$> generator
169-
return $ WalletResponse {
174+
return WalletResponse {
170175
wrData = theData
171176
, wrStatus = SuccessStatus
172177
, wrMeta = Metadata paginationMetadata
@@ -225,3 +230,55 @@ instance Accept ValidJSON where
225230

226231
instance ToJSON a => MimeRender ValidJSON a where
227232
mimeRender _ = mimeRender (Proxy @ JSON)
233+
234+
235+
--
236+
-- Error from parsing / validating JSON inputs
237+
--
238+
239+
newtype JSONValidationError
240+
= JSONValidationFailed Text
241+
deriving (Generic, Show, Eq)
242+
243+
deriveGeneric ''JSONValidationError
244+
245+
instance ToJSON JSONValidationError where
246+
toEncoding (JSONValidationFailed weValidationError) = pairs $ mconcat
247+
[ "message" .= String "JSONValidationFailed"
248+
, "status" .= String "error"
249+
, "diagnostic" .= object
250+
[ "validationError" .= weValidationError
251+
]
252+
]
253+
254+
instance FromJSON JSONValidationError where
255+
parseJSON (Object o)
256+
| HMS.member "message" o =
257+
case HMS.lookup "message" o of
258+
Just "JSONValidationFailed" ->
259+
JSONValidationFailed <$> ((o .: "diagnostic") >>= (.: "validationError"))
260+
_ ->
261+
fail "Incorrect JSON encoding for JSONValidationError"
262+
263+
| otherwise =
264+
fail "Incorrect JSON encoding for JSONValidationError"
265+
266+
parseJSON invalid =
267+
typeMismatch "JSONValidationError" invalid
268+
269+
instance Exception JSONValidationError
270+
271+
instance Arbitrary JSONValidationError where
272+
arbitrary = oneof
273+
[ pure $ JSONValidationFailed "JSON validation failed."
274+
]
275+
276+
instance Buildable JSONValidationError where
277+
build = \case
278+
JSONValidationFailed _ ->
279+
bprint "Couldn't decode a JSON input."
280+
281+
instance ToServantError JSONValidationError where
282+
declareServantError = \case
283+
JSONValidationFailed _ ->
284+
err400

0 commit comments

Comments
 (0)