Skip to content

Commit 3a27d5d

Browse files
authored
Add missing instances (Decode, Encode) (#479)
* feat: decode Set * feat: non empty and refined list * feat: encode instance + cleaner imports * fix: issues with type order * fix: issues with hlint * feat: error on duplicate value in sets * feat: update changelog
1 parent 27ea73d commit 3a27d5d

File tree

4 files changed

+75
-25
lines changed

4 files changed

+75
-25
lines changed

changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@
1313
- runPubApp: generalized version of `httpPubApp`
1414
- runSubApp: generalized version of `webSocketsApp`
1515

16+
- New encode and decode instances for `Set`, `NonEmpty`, `Seq` and `Vector`
17+
`Set` and `NonEmpty` throw a graphql error when a duplicate is found (Set)
18+
or when an empty list is sent (NonEmpty).
19+
**Beware**: Right now, all these types are advertised as lists in the introspection query.
20+
This is something we are trying to change by submitting a proposal to the graphql spec.
21+
1622
### Minor Changes
1723

1824
- parser performance optimization

src/Data/Morpheus/Server/Deriving/Decode.hs

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@ module Data.Morpheus.Server.Deriving.Decode
2121
)
2222
where
2323

24-
import Control.Applicative ((<*>), pure)
24+
import Control.Applicative (pure, (<*>))
2525
import Control.Monad ((>>=))
26-
import Data.Functor ((<$>), Functor (..))
26+
import Data.Functor (Functor (..), (<$>))
2727
import Data.List (elem)
28+
import Data.List.NonEmpty (NonEmpty)
29+
import qualified Data.List.NonEmpty as NonEmpty
2830
import Data.Maybe (Maybe (..))
2931
import Data.Morpheus.Internal.Utils
3032
( elems,
@@ -41,19 +43,12 @@ import Data.Morpheus.Server.Deriving.Utils
4143
datatypeNameProxy,
4244
selNameProxy,
4345
)
44-
import Data.Morpheus.Server.Internal.TH.Decode
45-
( decodeFieldWith,
46-
withInputObject,
47-
withInputUnion,
48-
withList,
49-
withMaybe,
50-
withScalar,
51-
)
46+
import Data.Morpheus.Server.Internal.TH.Decode (decodeFieldWith, withInputObject, withInputUnion, withList, withMaybe, withRefinedList, withScalar)
5247
import Data.Morpheus.Server.Types.GQLType
5348
( GQLType
5449
( KIND,
55-
__type,
56-
typeOptions
50+
typeOptions,
51+
__type
5752
),
5853
GQLTypeOptions (..),
5954
TypeData (..),
@@ -79,14 +74,15 @@ import Data.Morpheus.Types.Internal.Resolving
7974
)
8075
import Data.Proxy (Proxy (..))
8176
import Data.Semigroup (Semigroup (..))
77+
import Data.Sequence (Seq)
78+
import qualified Data.Sequence as Seq
79+
import Data.Set (Set)
80+
import qualified Data.Set as Set
81+
import Data.String (IsString (fromString))
82+
import Data.Vector (Vector)
83+
import qualified Data.Vector as Vector
8284
import GHC.Generics
83-
import Prelude
84-
( ($),
85-
(.),
86-
Eq (..),
87-
Ord,
88-
otherwise,
89-
)
85+
import Prelude (Either (Left, Right), Eq (..), Foldable (length), Ord, maybe, otherwise, show, ($), (-), (.))
9086

9187
type DecodeConstraint a =
9288
( Generic a,
@@ -113,6 +109,26 @@ instance Decode a => Decode (Maybe a) where
113109
instance Decode a => Decode [a] where
114110
decode = withList decode
115111

112+
instance Decode a => Decode (NonEmpty a) where
113+
decode = withRefinedList (maybe (Left "Expected a NonEmpty list") Right . NonEmpty.nonEmpty) decode
114+
115+
-- | Should this instance dedupe silently or fail on dupes ?
116+
instance (Ord a, Decode a) => Decode (Set a) where
117+
decode val = do
118+
listVal <- withList (decode @a) val
119+
let setVal = Set.fromList listVal
120+
let setLength = length setVal
121+
let listLength = length setVal
122+
if listLength == setLength
123+
then pure setVal
124+
else failure (fromString ("Expected a List without duplicates, found " <> show (setLength - listLength) <> " duplicates") :: InternalError)
125+
126+
instance (Decode a) => Decode (Seq a) where
127+
decode = fmap Seq.fromList . withList decode
128+
129+
instance (Decode a) => Decode (Vector a) where
130+
decode = fmap Vector.fromList . withList decode
131+
116132
-- | Decode GraphQL type with Specific Kind
117133
class DecodeKind (kind :: GQL_KIND) a where
118134
decodeKind :: Proxy kind -> ValidValue -> ResolverState a

src/Data/Morpheus/Server/Deriving/Encode.hs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import Control.Applicative (Applicative (..))
2323
import Control.Monad (Monad ((>>=)))
2424
import Data.Functor (fmap)
2525
import Data.Functor.Identity (Identity (..))
26+
import Data.List.NonEmpty (NonEmpty)
27+
import qualified Data.List.NonEmpty as NonEmpty
2628
import Data.Map (Map)
2729
import qualified Data.Map as M
2830
( toList,
@@ -91,14 +93,17 @@ import Data.Set (Set)
9193
import qualified Data.Set as S
9294
( toList,
9395
)
96+
import Data.Text (pack)
9497
import Data.Traversable (traverse)
98+
import Data.Vector (Vector)
99+
import qualified Data.Vector as Vector
95100
import GHC.Generics
96101
( Generic (..),
97102
)
98103
import Prelude
99-
( ($),
104+
( otherwise,
105+
($),
100106
(.),
101-
otherwise,
102107
)
103108

104109
newtype ContextValue (kind :: GQL_KIND) a = ContextValue
@@ -123,6 +128,14 @@ instance (Monad m, Encode o e m a, LiftOperation o) => Encode o e m [a] where
123128
instance Encode o e m (Pair k v) => Encode o e m (k, v) where
124129
encode (key, value) = encode (Pair key value)
125130

131+
-- NonEmpty
132+
instance Encode o e m [a] => Encode o e m (NonEmpty a) where
133+
encode = encode . NonEmpty.toList
134+
135+
-- Vector
136+
instance Encode o e m [a] => Encode o e m (Vector a) where
137+
encode = encode . Vector.toList
138+
126139
-- Set
127140
instance Encode o e m [a] => Encode o e m (Set a) where
128141
encode = encode . S.toList
@@ -195,10 +208,10 @@ convertNode
195208
encodeUnion fields =
196209
ResUnion
197210
consName
198-
$ pure
199-
$ mkObject
200-
consName
201-
(fmap toFieldRes fields)
211+
$ pure $
212+
mkObject
213+
consName
214+
(fmap toFieldRes fields)
202215

203216
-- Types & Constrains -------------------------------------------------------
204217
exploreResolvers ::

src/Data/Morpheus/Server/Internal/TH/Decode.hs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module Data.Morpheus.Server.Internal.TH.Decode
88
( withInputObject,
99
withMaybe,
1010
withList,
11+
withRefinedList,
1112
withEnum,
1213
withInputUnion,
1314
decodeFieldWith,
@@ -73,6 +74,20 @@ withList ::
7374
withList decode (List li) = traverse decode li
7475
withList _ isType = failure (typeMismatch "List" isType)
7576

77+
-- | Useful for more restrictive instances of lists (non empty, size indexed etc)
78+
withRefinedList ::
79+
(Failure InternalError m, Monad m) =>
80+
([a] -> Either Message (rList a)) ->
81+
(ValidValue -> m a) ->
82+
ValidValue ->
83+
m (rList a)
84+
withRefinedList refiner decode (List li) = do
85+
listRes <- traverse decode li
86+
case refiner listRes of
87+
Left err -> failure (typeMismatch err (List li))
88+
Right value -> pure value
89+
withRefinedList _ _ isType = failure (typeMismatch "List" isType)
90+
7691
withEnum :: Failure InternalError m => (TypeName -> m a) -> Value VALID -> m a
7792
withEnum decode (Enum value) = decode value
7893
withEnum _ isType = failure (typeMismatch "Enum" isType)

0 commit comments

Comments
 (0)