morpheus-graphql-app 0.18.0 → 0.19.0
raw patch · 24 files changed
+158/−153 lines, 24 filesdep ~aesondep ~morpheus-graphql-coredep ~morpheus-graphql-testsPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: aeson, morpheus-graphql-core, morpheus-graphql-tests
API changes (from Hackage documentation)
- Data.Morpheus.App: App :: AppData event m VALID -> App event (m :: * -> *)
+ Data.Morpheus.App: App :: AppData event m VALID -> App event (m :: Type -> Type)
- Data.Morpheus.App: AppData :: Config -> RootResolverValue event m -> Schema s -> AppData event (m :: * -> *) s
+ Data.Morpheus.App: AppData :: Config -> RootResolverValue event m -> Schema s -> AppData event (m :: Type -> Type) s
- Data.Morpheus.App: FailApp :: GQLErrors -> App event (m :: * -> *)
+ Data.Morpheus.App: FailApp :: GQLErrors -> App event (m :: Type -> Type)
- Data.Morpheus.App: [appConfig] :: AppData event (m :: * -> *) s -> Config
+ Data.Morpheus.App: [appConfig] :: AppData event (m :: Type -> Type) s -> Config
- Data.Morpheus.App: [appErrors] :: App event (m :: * -> *) -> GQLErrors
+ Data.Morpheus.App: [appErrors] :: App event (m :: Type -> Type) -> GQLErrors
- Data.Morpheus.App: [appResolvers] :: AppData event (m :: * -> *) s -> RootResolverValue event m
+ Data.Morpheus.App: [appResolvers] :: AppData event (m :: Type -> Type) s -> RootResolverValue event m
- Data.Morpheus.App: [appSchema] :: AppData event (m :: * -> *) s -> Schema s
+ Data.Morpheus.App: [appSchema] :: AppData event (m :: Type -> Type) s -> Schema s
- Data.Morpheus.App: [app] :: App event (m :: * -> *) -> AppData event m VALID
+ Data.Morpheus.App: [app] :: App event (m :: Type -> Type) -> AppData event m VALID
- Data.Morpheus.App: data App event (m :: * -> *)
+ Data.Morpheus.App: data App event (m :: Type -> Type)
- Data.Morpheus.App: data AppData event (m :: * -> *) s
+ Data.Morpheus.App: data AppData event (m :: Type -> Type) s
- Data.Morpheus.App.Internal.Resolving: NamedEnumResolver :: TypeName -> NamedResolverResult (m :: * -> *)
+ Data.Morpheus.App.Internal.Resolving: NamedEnumResolver :: TypeName -> NamedResolverResult (m :: Type -> Type)
- Data.Morpheus.App.Internal.Resolving: NamedObjectResolver :: ObjectTypeResolver m -> NamedResolverResult (m :: * -> *)
+ Data.Morpheus.App.Internal.Resolving: NamedObjectResolver :: ObjectTypeResolver m -> NamedResolverResult (m :: Type -> Type)
- Data.Morpheus.App.Internal.Resolving: NamedResolver :: TypeName -> (ValidValue -> m (NamedResolverResult m)) -> NamedResolver (m :: * -> *)
+ Data.Morpheus.App.Internal.Resolving: NamedResolver :: TypeName -> (ValidValue -> m (NamedResolverResult m)) -> NamedResolver (m :: Type -> Type)
- Data.Morpheus.App.Internal.Resolving: NamedUnionResolver :: NamedResolverRef -> NamedResolverResult (m :: * -> *)
+ Data.Morpheus.App.Internal.Resolving: NamedUnionResolver :: NamedResolverRef -> NamedResolverResult (m :: Type -> Type)
- Data.Morpheus.App.Internal.Resolving: [resolverName] :: NamedResolver (m :: * -> *) -> TypeName
+ Data.Morpheus.App.Internal.Resolving: [resolverName] :: NamedResolver (m :: Type -> Type) -> TypeName
- Data.Morpheus.App.Internal.Resolving: [resolver] :: NamedResolver (m :: * -> *) -> ValidValue -> m (NamedResolverResult m)
+ Data.Morpheus.App.Internal.Resolving: [resolver] :: NamedResolver (m :: Type -> Type) -> ValidValue -> m (NamedResolverResult m)
- Data.Morpheus.App.Internal.Resolving: data NamedResolver (m :: * -> *)
+ Data.Morpheus.App.Internal.Resolving: data NamedResolver (m :: Type -> Type)
- Data.Morpheus.App.Internal.Resolving: data NamedResolverResult (m :: * -> *)
+ Data.Morpheus.App.Internal.Resolving: data NamedResolverResult (m :: Type -> Type)
- Data.Morpheus.Types.GQLWrapper: class DecodeWrapper (f :: * -> *)
+ Data.Morpheus.Types.GQLWrapper: class DecodeWrapper (f :: Type -> Type)
- Data.Morpheus.Types.GQLWrapper: class EncodeWrapper (wrapper :: * -> *)
+ Data.Morpheus.Types.GQLWrapper: class EncodeWrapper (wrapper :: Type -> Type)
- Data.Morpheus.Types.GQLWrapper: type family DecodeWrapperConstraint (f :: * -> *) a :: Constraint
+ Data.Morpheus.Types.GQLWrapper: type family DecodeWrapperConstraint (f :: Type -> Type) a :: Constraint
Files
- README.md +18/−15
- changelog.md +2/−0
- morpheus-graphql-app.cabal +13/−15
- src/Data/Morpheus/App.hs +30/−30
- src/Data/Morpheus/App/Internal/Resolving/Resolver.hs +5/−5
- src/Data/Morpheus/App/Internal/Resolving/Types.hs +3/−3
- src/Data/Morpheus/App/Internal/Resolving/Utils.hs +9/−7
- src/Data/Morpheus/Types/GQLWrapper.hs +4/−4
- test/merge/schema/query-subscription-mutation/app-1.gql +7/−0
- test/merge/schema/query-subscription-mutation/app-1.json +8/−0
- test/merge/schema/query-subscription-mutation/app-2.gql +12/−0
- test/merge/schema/query-subscription-mutation/app-2.json +9/−0
- test/merge/schema/query-subscription-mutation/app-ext.gql +0/−12
- test/merge/schema/query-subscription-mutation/app-ext.json +0/−9
- test/merge/schema/query-subscription-mutation/app-main.gql +0/−7
- test/merge/schema/query-subscription-mutation/app-main.json +0/−8
- test/merge/schema/simple-query/app-1.gql +12/−0
- test/merge/schema/simple-query/app-1.json +8/−0
- test/merge/schema/simple-query/app-2.gql +9/−0
- test/merge/schema/simple-query/app-2.json +9/−0
- test/merge/schema/simple-query/app-ext.gql +0/−9
- test/merge/schema/simple-query/app-ext.json +0/−9
- test/merge/schema/simple-query/app-main.gql +0/−12
- test/merge/schema/simple-query/app-main.json +0/−8
README.md view
@@ -4,27 +4,28 @@ ## Build schema-first GraphQL App with dynamic typings -```hs-schema :: Schema VALID-schema =- [dsl|- type Deity {- name: String- power: [String!]- }+###### schema.gql - type Query {- deity(id: ID): Deity- }-|]+```gql+type Deity {+ name: String+ power: [String!]+} +type Query {+ deity(id: ID): Deity+}+```++###### App.hs++```hs deityResolver :: Monad m => NamedResolverFunction QUERY e m-deityResolver "morpheus" =+deityResolver arg = object [ ("name", pure "Morpheus"), ("power", pure $ list [enum "Shapeshifting"]) ]-deityResolver _ = object [] resolver :: Monad m => RootResolverValue e m resolver =@@ -34,5 +35,7 @@ ] api :: ByteString -> IO ByteString-api = runApp (mkApp schema resolver)+api query = do+ schema <- LBS.readFile "./schema.gql" >>= resultOr (fail . show) pure . parseSchema+ runApp (mkApp schema resolver) query ```
changelog.md view
@@ -1,5 +1,7 @@ # Changelog +## 0.19.0 - 21.03.2022+ ## 0.18.0 - 08.11.2021 - Data.Morpheus.App.NamedResolvers (inspired by apollo [named resolvers](https://www.apollographql.com/docs/apollo-server/data/resolvers/#as-this-example-shows)
morpheus-graphql-app.cabal view
@@ -3,11 +3,9 @@ -- This file has been generated from package.yaml by hpack version 0.34.4. -- -- see: https://github.com/sol/hpack------ hash: e84d4f16b2769bb325709eb8d8bb59c050c021ce4f9111621b7ec6e79d22b810 name: morpheus-graphql-app-version: 0.18.0+version: 0.19.0 synopsis: Morpheus GraphQL App description: Build GraphQL APIs with your favourite functional language! category: web, graphql@@ -35,13 +33,13 @@ test/api/validation/fragment/on-type/query.gql test/api/validation/fragment/on-union-type/query.gql test/api/validation/fragment/schema.gql- test/merge/schema/query-subscription-mutation/app-ext.gql- test/merge/schema/query-subscription-mutation/app-main.gql+ test/merge/schema/query-subscription-mutation/app-1.gql+ test/merge/schema/query-subscription-mutation/app-2.gql test/merge/schema/query-subscription-mutation/mutation/query.gql test/merge/schema/query-subscription-mutation/query/query.gql test/merge/schema/query-subscription-mutation/rendering.gql- test/merge/schema/simple-query/app-ext.gql- test/merge/schema/simple-query/app-main.gql+ test/merge/schema/simple-query/app-1.gql+ test/merge/schema/simple-query/app-2.gql test/merge/schema/simple-query/query/query.gql test/merge/schema/simple-query/rendering.gql test/named-resolvers/deities-ext/query.gql@@ -71,12 +69,12 @@ test/api/validation/fragment/on-type/response.json test/api/validation/fragment/on-union-type/response.json test/api/validation/fragment/resolvers.json- test/merge/schema/query-subscription-mutation/app-ext.json- test/merge/schema/query-subscription-mutation/app-main.json+ test/merge/schema/query-subscription-mutation/app-1.json+ test/merge/schema/query-subscription-mutation/app-2.json test/merge/schema/query-subscription-mutation/mutation/response.json test/merge/schema/query-subscription-mutation/query/response.json- test/merge/schema/simple-query/app-ext.json- test/merge/schema/simple-query/app-main.json+ test/merge/schema/simple-query/app-1.json+ test/merge/schema/simple-query/app-2.json test/merge/schema/simple-query/query/response.json test/named-resolvers/deities-ext/response.json test/named-resolvers/deities/response.json@@ -119,13 +117,13 @@ src ghc-options: -Wall build-depends:- aeson >=1.4.4.0 && <=1.6+ aeson >=1.4.4.0 && <3 , base >=4.7 && <5 , bytestring >=0.10.4 && <0.11 , containers >=0.4.2.1 && <0.7 , hashable >=1.0.0 , megaparsec >=7.0.0 && <10.0.0- , morpheus-graphql-core >=0.18.0 && <0.19.0+ , morpheus-graphql-core >=0.19.0 && <0.20.0 , mtl >=2.0 && <=3.0 , relude >=0.3.0 , scientific >=0.3.6.2 && <0.4@@ -155,8 +153,8 @@ , hashable >=1.0.0 , megaparsec >=7.0.0 && <10.0.0 , morpheus-graphql-app- , morpheus-graphql-core >=0.18.0 && <0.19.0- , morpheus-graphql-tests >=0.18.0 && <0.19.0+ , morpheus-graphql-core >=0.19.0 && <0.20.0+ , morpheus-graphql-tests >=0.19.0 && <0.20.0 , mtl >=2.0 && <=3.0 , relude >=0.3.0 , scientific >=0.3.6.2 && <0.4
src/Data/Morpheus/App.hs view
@@ -78,7 +78,7 @@ (App . AppData defaultConfig appResolvers) (validateSchema True defaultConfig appSchema) -data App event (m :: * -> *)+data App event (m :: Type -> Type) = App {app :: AppData event m VALID} | FailApp {appErrors :: GQLErrors} @@ -92,7 +92,7 @@ App {} <> FailApp {appErrors} = FailApp appErrors (App x) <> (App y) = resultOr FailApp App (stitch x y) -data AppData event (m :: * -> *) s = AppData+data AppData event (m :: Type -> Type) s = AppData { appConfig :: Config, appResolvers :: RootResolverValue event m, appSchema :: Schema s@@ -124,34 +124,34 @@ Config -> GQLRequest -> ResponseStream event m ResolverContext-validateReq inputSchema config request = ResultT- $ pure- $ do- validSchema <- validateSchema True config inputSchema- schema <- internalSchema <:> validSchema- operation <- parseRequestWith config validSchema request- pure- ( [],- ResolverContext- { schema,- config,- operation,- currentType =- toAny $- fromMaybe- (AST.query schema)- (rootType (operationType operation) schema),- currentSelection =- Selection- { selectionName = "Root",- selectionArguments = empty,- selectionPosition = operationPosition operation,- selectionAlias = Nothing,- selectionContent = SelectionSet (operationSelection operation),- selectionDirectives = empty- }- }- )+validateReq inputSchema config request = ResultT $+ pure $+ do+ validSchema <- validateSchema True config inputSchema+ schema <- internalSchema <:> validSchema+ operation <- parseRequestWith config validSchema request+ pure+ ( [],+ ResolverContext+ { schema,+ config,+ operation,+ currentType =+ toAny $+ fromMaybe+ (AST.query schema)+ (rootType (operationType operation) schema),+ currentSelection =+ Selection+ { selectionName = "Root",+ selectionArguments = empty,+ selectionPosition = operationPosition operation,+ selectionAlias = Nothing,+ selectionContent = SelectionSet (operationSelection operation),+ selectionDirectives = empty+ }+ }+ ) rootType :: OperationType -> Schema s -> Maybe (AST.TypeDefinition AST.OBJECT s) rootType Query = Just . AST.query
src/Data/Morpheus/App/Internal/Resolving/Resolver.hs view
@@ -172,8 +172,8 @@ local f (ResolverS resM) = ResolverS $ mapReaderT (local f) <$> resM -- | A function to return the internal 'ResolverContext' within a resolver's monad.--- Using the 'ResolverContext' itself is unsafe because it expposes internal structures--- of the AST, but you can use the "Data.Morpheus.Types.SelectionTree" typeclass to manipulate+-- Using the 'ResolverContext' itself is unsafe because it exposes internal structures+-- of the AST, but you can use the "Data.Morpheus.Types.SelectionTree" typeClass to manipulate -- the internal AST with a safe interface. unsafeInternalContext :: (Monad m, LiftOperation o) => Resolver o e m ResolverContext unsafeInternalContext = ask@@ -199,9 +199,9 @@ Resolver QUERY e m (e -> Resolver SUBSCRIPTION e m a) -> SubscriptionField (Resolver SUBSCRIPTION e m a) subscribe ch res =- SubscriptionField ch $- ResolverS $- fromSub <$> runResolverQ res+ SubscriptionField ch+ $ ResolverS+ $ fromSub <$> runResolverQ res where fromSub :: Monad m => (e -> Resolver SUBSCRIPTION e m a) -> ReaderT e (ResolverStateT () m) a fromSub f = join (ReaderT (runResolverS . f))
src/Data/Morpheus/App/Internal/Resolving/Types.hs view
@@ -48,9 +48,9 @@ import GHC.Show (Show (show)) import Relude hiding (show) -type ResolverMap (m :: * -> *) = HashMap TypeName (NamedResolver m)+type ResolverMap (m :: Type -> Type) = HashMap TypeName (NamedResolver m) -data NamedResolver (m :: * -> *) = NamedResolver+data NamedResolver (m :: Type -> Type) = NamedResolver { resolverName :: TypeName, resolver :: ValidValue -> m (NamedResolverResult m) }@@ -72,7 +72,7 @@ } deriving (Show) -data NamedResolverResult (m :: * -> *)+data NamedResolverResult (m :: Type -> Type) = NamedObjectResolver (ObjectTypeResolver m) | NamedUnionResolver NamedResolverRef | NamedEnumResolver TypeName
src/Data/Morpheus/App/Internal/Resolving/Utils.hs view
@@ -21,7 +21,6 @@ import Control.Monad.Except (MonadError (throwError)) import qualified Data.Aeson as A-import qualified Data.HashMap.Lazy as HM import Data.Morpheus.App.Internal.Resolving.Types ( NamedResolverRef (..), ObjectTypeResolver (..),@@ -30,24 +29,27 @@ mkNull, mkObjectMaybe, )-import Data.Morpheus.Internal.Utils (selectOr)+import qualified Data.Morpheus.Internal.Utils as U+import Data.Morpheus.Internal.Utils (selectOr, toAssoc) import Data.Morpheus.Types.Internal.AST- ( GQLError,+ ( FieldName,+ GQLError, ScalarValue (..), TypeName, decodeScientific, internal, packName,+ unpackName, ) import qualified Data.Vector as V import Relude -lookupResJSON :: (MonadError GQLError f, Monad m) => Text -> A.Value -> f (ObjectTypeResolver m)+lookupResJSON :: (MonadError GQLError f, Monad m) => FieldName -> A.Value -> f (ObjectTypeResolver m) lookupResJSON name (A.Object fields) = selectOr mkEmptyObject (requireObject . mkValue)- name+ (unpackName name) fields lookupResJSON _ _ = mkEmptyObject @@ -60,10 +62,10 @@ ResolverValue m mkValue (A.Object v) = mkObjectMaybe- (HM.lookup "__typename" v >>= unpackJSONName)+ (U.lookup "__typename" v >>= unpackJSONName) $ fmap (bimap packName (pure . mkValue))- (HM.toList v)+ (toAssoc v) mkValue (A.Array ls) = mkList (fmap mkValue (V.toList ls)) mkValue A.Null = mkNull mkValue (A.Number x) = ResScalar (decodeScientific x)
src/Data/Morpheus/Types/GQLWrapper.hs view
@@ -29,7 +29,7 @@ import Relude -- | GraphQL Wrapper Serializer-class EncodeWrapper (wrapper :: * -> *) where+class EncodeWrapper (wrapper :: Type -> Type) where encodeWrapper :: (Monad m) => (a -> m (ResolverValue m)) ->@@ -67,12 +67,12 @@ instance EncodeWrapper SubscriptionField where encodeWrapper encode (SubscriptionField _ res) = encode res -type family DecodeWrapperConstraint (f :: * -> *) a :: Constraint where+type family DecodeWrapperConstraint (f :: Type -> Type) a :: Constraint where DecodeWrapperConstraint Set a = (Ord a) DecodeWrapperConstraint f a = () -- | GraphQL Wrapper Deserializer-class DecodeWrapper (f :: * -> *) where+class DecodeWrapper (f :: Type -> Type) where decodeWrapper :: (Monad m, DecodeWrapperConstraint f a) => (ValidValue -> m a) ->@@ -110,7 +110,7 @@ ExceptT GQLError m (Set a) haveSameSize setVal listVal | length setVal == length listVal = pure setVal- | otherwise = ExceptT $ pure $ Left (fromString ("Expected a List without duplicates, found " <> show (length listVal - length listVal) <> " duplicates"))+ | otherwise = ExceptT $ pure $ Left (fromString ("Expected a List without duplicates, found " <> show (length listVal - length setVal) <> " duplicates")) withRefinedList :: Monad m =>
+ test/merge/schema/query-subscription-mutation/app-1.gql view
@@ -0,0 +1,7 @@+type Query {+ name: String+}++type Mutation {+ name(i: Int): String+}
+ test/merge/schema/query-subscription-mutation/app-1.json view
@@ -0,0 +1,8 @@+{+ "query": {+ "name": "query name from app"+ },+ "mutation": {+ "name": "mutation name from app"+ }+}
+ test/merge/schema/query-subscription-mutation/app-2.gql view
@@ -0,0 +1,12 @@+type Query {+ id: ID+ name: String+}++type Mutation {+ id: ID+}++type Subscription {+ name: String+}
+ test/merge/schema/query-subscription-mutation/app-2.json view
@@ -0,0 +1,9 @@+{+ "query": {+ "name": "query name from ext",+ "id": "query id from ext"+ },+ "mutation": {+ "id": "mutation id from ext"+ }+}
− test/merge/schema/query-subscription-mutation/app-ext.gql
@@ -1,12 +0,0 @@-type Query {- id: ID- name: String-}--type Mutation {- id: ID-}--type Subscription {- name: String-}
− test/merge/schema/query-subscription-mutation/app-ext.json
@@ -1,9 +0,0 @@-{- "query": {- "name": "query name from ext",- "id": "query id from ext"- },- "mutation": {- "id": "mutation id from ext"- }-}
− test/merge/schema/query-subscription-mutation/app-main.gql
@@ -1,7 +0,0 @@-type Query {- name: String-}--type Mutation {- name(i: Int): String-}
− test/merge/schema/query-subscription-mutation/app-main.json
@@ -1,8 +0,0 @@-{- "query": {- "name": "query name from app"- },- "mutation": {- "name": "mutation name from app"- }-}
+ test/merge/schema/simple-query/app-1.gql view
@@ -0,0 +1,12 @@+type Type1 {+ name: String+}++type Type2 {+ name: String+ age: Int+}++type Query {+ type: Type2+}
+ test/merge/schema/simple-query/app-1.json view
@@ -0,0 +1,8 @@+{+ "query": {+ "type": {+ "name": "name from app",+ "age": 23+ }+ }+}
+ test/merge/schema/simple-query/app-2.gql view
@@ -0,0 +1,9 @@+type Type2 {+ name: String+ id: ID+}++type Query {+ id: ID+ type: Type2+}
+ test/merge/schema/simple-query/app-2.json view
@@ -0,0 +1,9 @@+{+ "query": {+ "type": {+ "name": "name from ext",+ "id": "id from ext"+ },+ "id": "id from ext"+ }+}
− test/merge/schema/simple-query/app-ext.gql
@@ -1,9 +0,0 @@-type Type2 {- name: String- id: ID-}--type Query {- id: ID- type: Type2-}
− test/merge/schema/simple-query/app-ext.json
@@ -1,9 +0,0 @@-{- "query": {- "type": {- "name": "name from ext",- "id": "id from ext"- },- "id": "id from ext"- }-}
− test/merge/schema/simple-query/app-main.gql
@@ -1,12 +0,0 @@-type Type1 {- name: String-}--type Type2 {- name: String- age: Int-}--type Query {- type: Type2-}
− test/merge/schema/simple-query/app-main.json
@@ -1,8 +0,0 @@-{- "query": {- "type": {- "name": "name from app",- "age": 23- }- }-}