packages feed

morpheus-graphql-core 0.14.1 → 0.15.0

raw patch · 55 files changed

+1272/−508 lines, 55 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Data.Morpheus.Core: runApi :: forall event m s. (Monad m, ValidateSchema s) => Schema s -> RootResModel event m -> Config -> GQLRequest -> ResponseStream event m (Value VALID)
- Data.Morpheus.Internal.TH: m_ :: TypeName
- Data.Morpheus.Internal.Utils: capital :: Token -> Token
- Data.Morpheus.Internal.Utils: instance (Text.Megaparsec.Stream.Stream s, GHC.Classes.Ord e, Data.Morpheus.Internal.Utils.Failure Data.Morpheus.Types.Internal.AST.Base.GQLErrors m) => Data.Morpheus.Internal.Utils.Failure Data.Morpheus.Types.Internal.AST.Base.ValidationErrors (Text.Megaparsec.Internal.ParsecT e s m)
- Data.Morpheus.Internal.Utils: nonCapital :: TypeName -> Token
- Data.Morpheus.Types.Internal.AST: insertType :: (Monad m, Failure ValidationErrors m) => TypeDefinition cat s -> UpdateT m (Schema s)
- Data.Morpheus.Types.Internal.AST: isInput :: TypeKind -> Bool
- Data.Morpheus.Types.Internal.AST: toListField :: FieldDefinition cat s -> FieldDefinition cat s
- Data.Morpheus.Types.Internal.AST: type REQURE_IMPLEMENTABLE cat = ELEM cat IMPLEMENTABLE ~ TRUE
- Data.Morpheus.Types.Internal.AST: updateSchema :: (Monad m, Failure ValidationErrors m) => TypeName -> DataFingerprint -> [UpdateT m (Schema s)] -> (a -> TypeDefinition cat s) -> a -> UpdateT m (Schema s)
+ Data.Morpheus.Core: App :: AppData event m VALID -> App event
+ Data.Morpheus.Core: AppData :: Config -> RootResModel event m -> Schema s -> AppData event s
+ Data.Morpheus.Core: FailApp :: GQLErrors -> App event
+ Data.Morpheus.Core: [appConfig] :: AppData event s -> Config
+ Data.Morpheus.Core: [appErrors] :: App event -> GQLErrors
+ Data.Morpheus.Core: [appResolvers] :: AppData event s -> RootResModel event m
+ Data.Morpheus.Core: [appSchema] :: AppData event s -> Schema s
+ Data.Morpheus.Core: [app] :: App event -> AppData event m VALID
+ Data.Morpheus.Core: data App event (m :: * -> *)
+ Data.Morpheus.Core: data AppData event (m :: * -> *) s
+ Data.Morpheus.Core: mkApp :: ValidateSchema s => Schema s -> RootResModel e m -> App e m
+ Data.Morpheus.Core: runApp :: (MapAPI a b, Monad m) => App e m -> a -> m b
+ Data.Morpheus.Core: runAppStream :: Monad m => App event m -> GQLRequest -> ResponseStream event m (Value VALID)
+ Data.Morpheus.Core: withDebugger :: App e m -> App e m
+ Data.Morpheus.Error: nameCollisionError :: TypeName -> ValidationError
+ Data.Morpheus.Internal.TH: instance Data.Morpheus.Internal.TH.ToName GHC.Base.String
+ Data.Morpheus.Internal.Utils: capitalize :: Capitalize a => a -> a
+ Data.Morpheus.Internal.Utils: data ResolutionT a coll m x
+ Data.Morpheus.Internal.Utils: fromListT :: RESOLUTION k a coll m => [a] -> ResolutionT a coll m coll
+ Data.Morpheus.Internal.Utils: instance (GHC.Base.Monad m, Data.Morpheus.Internal.Utils.Failure Data.Morpheus.Types.Internal.AST.Base.ValidationErrors m) => Data.Morpheus.Internal.Utils.Failure Data.Morpheus.Types.Internal.AST.Base.ValidationErrors (Data.Morpheus.Internal.Utils.ResolutionT a coll m)
+ Data.Morpheus.Internal.Utils: instance Control.Monad.Trans.Class.MonadTrans (Data.Morpheus.Internal.Utils.ResolutionT e coll)
+ Data.Morpheus.Internal.Utils: instance Data.Morpheus.Internal.Utils.Capitalize Data.Morpheus.Types.Internal.AST.Base.Token
+ Data.Morpheus.Internal.Utils: instance Data.Morpheus.Internal.Utils.Capitalize Data.Morpheus.Types.Internal.AST.Base.TypeName
+ Data.Morpheus.Internal.Utils: instance Data.Morpheus.Internal.Utils.Capitalize GHC.Base.String
+ Data.Morpheus.Internal.Utils: instance GHC.Base.Applicative m => GHC.Base.Applicative (Data.Morpheus.Internal.Utils.ResolutionT a coll m)
+ Data.Morpheus.Internal.Utils: instance GHC.Base.Functor m => GHC.Base.Functor (Data.Morpheus.Internal.Utils.ResolutionT a coll m)
+ Data.Morpheus.Internal.Utils: instance GHC.Base.Monad m => Control.Monad.Reader.Class.MonadReader (Data.Morpheus.Internal.Utils.Resolution a coll m) (Data.Morpheus.Internal.Utils.ResolutionT a coll m)
+ Data.Morpheus.Internal.Utils: instance GHC.Base.Monad m => GHC.Base.Monad (Data.Morpheus.Internal.Utils.ResolutionT a coll m)
+ Data.Morpheus.Internal.Utils: mergeT :: RESOLUTION k a coll m => coll -> coll -> ResolutionT a coll m coll
+ Data.Morpheus.Internal.Utils: prop :: (b -> b -> m b) -> (a -> b) -> a -> a -> m b
+ Data.Morpheus.Internal.Utils: resolveWith :: Monad m => (a -> a -> m a) -> NonEmpty a -> m a
+ Data.Morpheus.Internal.Utils: runResolutionT :: ResolutionT a coll m b -> ([a] -> coll) -> (NonEmpty a -> m a) -> m b
+ Data.Morpheus.Internal.Utils: stripConstructorNamespace :: TypeName -> String -> String
+ Data.Morpheus.Internal.Utils: stripFieldNamespace :: TypeName -> String -> String
+ Data.Morpheus.Types.Internal.AST: OrdMap :: [k] -> HashMap k a -> OrdMap k a
+ Data.Morpheus.Types.Internal.AST: Value :: Value -> ScalarValue
+ Data.Morpheus.Types.Internal.AST: [mapEntries] :: OrdMap k a -> HashMap k a
+ Data.Morpheus.Types.Internal.AST: [mapKeys] :: OrdMap k a -> [k]
+ Data.Morpheus.Types.Internal.AST: isTypeDefined :: TypeName -> Schema s -> Maybe DataFingerprint
+ Data.Morpheus.Types.Internal.AST: mkField :: Maybe (FieldContent TRUE cat s) -> FieldName -> TypeRef -> FieldDefinition cat s
+ Data.Morpheus.Types.Internal.AST: safeDefineType :: (Monad m, Failure ValidationErrors m) => TypeDefinition cat s -> Schema s -> m (Schema s)
+ Data.Morpheus.Types.Internal.AST: type REQUIRE_IMPLEMENTABLE cat = ELEM cat IMPLEMENTABLE ~ TRUE
- Data.Morpheus.Internal.TH: tyConArgs :: TypeKind -> [TypeName]
+ Data.Morpheus.Internal.TH: tyConArgs :: TypeKind -> [String]
- Data.Morpheus.Types.GQLScalar: scalarValidator :: GQLScalar a => Proxy a -> ScalarDefinition
+ Data.Morpheus.Types.GQLScalar: scalarValidator :: GQLScalar a => f a -> ScalarDefinition
- Data.Morpheus.Types.Internal.AST: TypeRef :: TypeName -> Maybe TypeName -> [TypeWrapper] -> TypeRef
+ Data.Morpheus.Types.Internal.AST: TypeRef :: TypeName -> Maybe String -> [TypeWrapper] -> TypeRef
- Data.Morpheus.Types.Internal.AST: [typeArgs] :: TypeRef -> Maybe TypeName
+ Data.Morpheus.Types.Internal.AST: [typeArgs] :: TypeRef -> Maybe String
- Data.Morpheus.Types.Internal.Resolving: ObjectResModel :: TypeName -> [FieldResModel o e m] -> ObjectResModel o e m
+ Data.Morpheus.Types.Internal.Resolving: ObjectResModel :: TypeName -> HashMap FieldName (Resolver o e m (ResModel o e m)) -> ObjectResModel o e m
- Data.Morpheus.Types.Internal.Resolving: [objectFields] :: ObjectResModel o e m -> [FieldResModel o e m]
+ Data.Morpheus.Types.Internal.Resolving: [objectFields] :: ObjectResModel o e m -> HashMap FieldName (Resolver o e m (ResModel o e m))

Files

README.md view
@@ -43,6 +43,6 @@         ("power", pure $ mkList [mkString "Shapeshifting"])       ] -api :: GQLRequest -> ResponseStream e Identity (Value VALID)-api = runApi schema resolver+api :: ByteString -> IO  ByteString+api = runApp (mkApp schema resolver) ```
changelog.md view
@@ -1,5 +1,49 @@ # Changelog +## 0.15.0 - 12.09.2020++### new features++- `render` renders SchemaDefinition e.g++  ```graphql+  schema {+    query: MyQuery+  }+  ```++- query validator automatically adds `__typename` to interface types++- type : `App`++  ```hs+  api :: a -> m b+  api = runApp (mkApp schema resolvers)+  ```++- `App` supports semigroup(`schema Stitching`):++  if whe have two apps `app1` and `app2` with type `Api EVENT IO` we can merge it as.++  ```hs+  mergedApi :: a -> m b+  mergedApi = runApp (app1 <> app2)+  ```++- `runApp` changed signature to:++  ```hs+  runApp :: Api e m -> a -> m b+  ```++### Breaking Changes++- removed `runApi`.++### Minor Changes++- internal refactoring+ ## 0.14.1 - 16.08.2020  ## 0.14.0 - 15.08.2020@@ -14,12 +58,12 @@   - `defaultConfig`   - `debugConfig` -- for better debuging, internal errors messages will display resolving state:+- for better debugging, internal errors messages will display resolving state:   - `current TypeName`   - `current Selection`   - `OperationDefinition`   - `SchemaDefinition`-- rendering graphql "AST". e.g `render ( slection :: Selection VALID)` will render+- rendering graphql "AST". e.g `render (selection :: Selection VALID)` will render  ```graphql {@@ -29,7 +73,7 @@ } ``` -- quasiqouter `[dsl| <type definitions> |]` generates `Schema VALID`.+- quasiquoter `[dsl| <type definitions> |]` generates `Schema VALID`. - parser supports custom directive definition. e.g  ```graphql@@ -43,9 +87,9 @@   query {     createDeity(       name: """-      powerqwe+      power       bla \n sd-      blu \\ dete+      blu \\ date       """     ) {       name@@ -72,6 +116,7 @@   - `FieldDefinition IN VALID`   - ... - runApi requires argument config+   ```hs     runApi ::       Schema s ->@@ -100,7 +145,7 @@  ## New features -- parser supports implemnets interfaces seperated with empty spaces+- parser supports implements interfaces separated with empty spaces    ```gql   type T implements A , B C & D {
morpheus-graphql-core.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: db805940089c68c82bd542e86200950c50069afe4b4d412a9bca5a4ee8f6f8ce+-- hash: 7aaaf3d4b4dab94a5938e6284d262fadabee300b225a623e93fa0d703192a48f  name:           morpheus-graphql-core-version:        0.14.1+version:        0.15.0 synopsis:       Morpheus GraphQL Core description:    Build GraphQL APIs with your favourite functional language! category:       web, graphql@@ -35,6 +35,15 @@     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/api/app.gql+    test/merge/schema/query-subscription-mutation/api/ext.gql+    test/merge/schema/query-subscription-mutation/expected/ok.gql+    test/merge/schema/query-subscription-mutation/request/mutation/query.gql+    test/merge/schema/query-subscription-mutation/request/query/query.gql+    test/merge/schema/simple-query/api/app.gql+    test/merge/schema/simple-query/api/ext.gql+    test/merge/schema/simple-query/expected/ok.gql+    test/merge/schema/simple-query/request/query/query.gql     test/schema/parsing/directive/ok/simple/schema.gql     test/schema/parsing/directive/ok/sophisticated/schema.gql     test/schema/parsing/schema-definition/fail/dupplicate-field/query-mutation-subscription/schema.gql@@ -84,6 +93,13 @@     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/api/app.json+    test/merge/schema/query-subscription-mutation/api/ext.json+    test/merge/schema/query-subscription-mutation/request/mutation/response.json+    test/merge/schema/query-subscription-mutation/request/query/response.json+    test/merge/schema/simple-query/api/app.json+    test/merge/schema/simple-query/api/ext.json+    test/merge/schema/simple-query/request/query/response.json     test/schema/parsing/directive/ok/simple/response.json     test/schema/parsing/directive/ok/sophisticated/response.json     test/schema/parsing/schema-definition/fail/dupplicate-field/query-mutation-subscription/response.json@@ -166,6 +182,7 @@       Data.Morpheus.Schema.DSL       Data.Morpheus.Schema.Schema       Data.Morpheus.Schema.SchemaAPI+      Data.Morpheus.Types.App       Data.Morpheus.Types.Internal.AST.Base       Data.Morpheus.Types.Internal.AST.DirectiveLocation       Data.Morpheus.Types.Internal.AST.Fields@@ -183,6 +200,7 @@       Data.Morpheus.Types.Internal.Resolving.Event       Data.Morpheus.Types.Internal.Resolving.Resolver       Data.Morpheus.Types.Internal.Resolving.ResolverState+      Data.Morpheus.Types.Internal.Stitching       Data.Morpheus.Types.Internal.Validation       Data.Morpheus.Types.Internal.Validation.Error       Data.Morpheus.Types.Internal.Validation.Internal@@ -222,9 +240,10 @@   type: exitcode-stdio-1.0   main-is: Spec.hs   other-modules:-      Lib       Utils.Api+      Utils.MergeSchema       Utils.Schema+      Utils.Utils       Paths_morpheus_graphql_core   hs-source-dirs:       test
src/Data/Morpheus/Core.hs view
@@ -2,14 +2,12 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE KindSignatures #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-}  module Data.Morpheus.Core-  ( runApi,-    parseDSL,+  ( parseDSL,     parseFullGQLDocument,     parseGQLDocument,     parseTypeSystemDefinition,@@ -24,6 +22,12 @@     VALIDATION_MODE (..),     defaultConfig,     debugConfig,+    App (..),+    AppData (..),+    runApp,+    withDebugger,+    mkApp,+    runAppStream,   ) where @@ -34,7 +38,6 @@   ) import Data.Morpheus.Internal.Utils   ( (<:>),-    empty,   ) import Data.Morpheus.Parser   ( parseRequest,@@ -46,17 +49,17 @@   ( RenderGQL (..),   ) import Data.Morpheus.Schema.Schema (internalSchema)-import Data.Morpheus.Schema.SchemaAPI (withSystemFields)-import Data.Morpheus.Types.IO-  ( GQLRequest (..),+import Data.Morpheus.Types.App+  ( App (..),+    AppData (..),+    mkApp,+    runApp,+    runAppStream,+    withDebugger,   ) import Data.Morpheus.Types.Internal.AST-  ( Operation (..),-    Schema (..),-    Selection (..),-    SelectionContent (..),+  ( Schema,     VALID,-    Value,   ) import Data.Morpheus.Types.Internal.Config   ( Config (..),@@ -66,13 +69,7 @@   ) import Data.Morpheus.Types.Internal.Resolving   ( Eventless,-    ResolverContext (..),-    ResponseStream,-    ResultT (..),-    RootResModel,-    cleanEvents,     resultOr,-    runRootResModel,   ) import Data.Morpheus.Types.SelectionTree (SelectionTree (..)) import Data.Morpheus.Validation.Document.Validation (ValidateSchema (..))@@ -83,48 +80,6 @@   ( toStrict,   ) import Data.Text.Lazy.Encoding (decodeUtf8)--runApi ::-  forall event m s.-  (Monad m, ValidateSchema s) =>-  Schema s ->-  RootResModel event m ->-  Config ->-  GQLRequest ->-  ResponseStream event m (Value VALID)-runApi inputSchema resModel config request = do-  validRequest <- validateReq inputSchema config request-  resovers <- withSystemFields (schema validRequest) resModel-  runRootResModel resovers validRequest--validateReq ::-  ( Monad m,-    ValidateSchema s-  ) =>-  Schema s ->-  Config ->-  GQLRequest ->-  ResponseStream event m ResolverContext-validateReq inputSchema config request = cleanEvents $ ResultT $ pure $ do-  validSchema <- validateSchema True config inputSchema-  schema <- internalSchema <:> validSchema-  operation <- parseRequestWith config schema request-  pure $-    ResolverContext-      { schema,-        config,-        operation,-        currentTypeName = "Root",-        currentSelection =-          Selection-            { selectionName = "Root",-              selectionArguments = empty,-              selectionPosition = operationPosition operation,-              selectionAlias = Nothing,-              selectionContent = SelectionSet (operationSelection operation),-              selectionDirectives = []-            }-      }  parseDSL :: ByteString -> Either String (Schema VALID) parseDSL = resultOr (Left . show) pure . parseGQLDocument
src/Data/Morpheus/Error.hs view
@@ -4,9 +4,11 @@     gqlWarnings,     renderGQLErrors,     deprecatedField,+    nameCollisionError,   ) where +import Data.Morpheus.Error.Schema import Data.Morpheus.Error.Utils import Data.Morpheus.Error.Warning   ( deprecatedField,
src/Data/Morpheus/Internal/TH.hs view
@@ -25,7 +25,6 @@     infoTyVars,     isEnum,     m',-    m_,     mkFieldsE,     nameSpaceField,     nameSpaceType,@@ -55,7 +54,8 @@ import Data.Functor ((<$>)) import Data.Maybe (Maybe (..)) import Data.Morpheus.Internal.Utils-  ( nameSpaceField,+  ( capitalize,+    nameSpaceField,     nameSpaceType,   ) import Data.Morpheus.Types.Internal.AST@@ -79,6 +79,7 @@     (.),     (==),     Bool (..),+    String,     id,     map,     otherwise,@@ -86,9 +87,6 @@     (||),   ) -m_ :: TypeName-m_ = "m"- m' :: Type m' = VarT (mkName "m") @@ -120,12 +118,13 @@     wrappedT (TypeMaybe : xs) = AppT (ConT ''Maybe) $ wrappedT xs     wrappedT [] = decType typeArgs     ---------------------------------------------    decType (Just par) = apply typeConName [toVar par]-    decType _ = toCon typeConName+    decType :: Maybe String -> Type+    decType (Just par) = apply (capitalize typeConName) [toVar par]+    decType _ = toCon (capitalize typeConName) -tyConArgs :: TypeKind -> [TypeName]+tyConArgs :: TypeKind -> [String] tyConArgs kindD-  | isOutputObject kindD || kindD == KindUnion = [m_]+  | isOutputObject kindD || kindD == KindUnion = ["m"]   | otherwise = []  cons :: ToCon a b => [a] -> [b]@@ -137,11 +136,14 @@ class ToName a where   toName :: a -> Name +instance ToName String where+  toName = mkName+ instance ToName Name where   toName = id  instance ToName TypeName where-  toName = mkName . unpack . readTypeName+  toName = mkName . unpack . capitalize . readTypeName  instance ToName FieldName where   toName = mkName . unpack . readName . convertToHaskellName
src/Data/Morpheus/Internal/Utils.hs view
@@ -1,12 +1,16 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE NoImplicitPrelude #-}  module Data.Morpheus.Internal.Utils-  ( capital,-    nonCapital,+  ( capitalize,     nameSpaceField,     nameSpaceType,     capitalTypeName,@@ -34,12 +38,21 @@     traverseCollection,     (<.>),     SemigroupM (..),+    fromListT,+    mergeT,+    runResolutionT,+    ResolutionT,+    prop,+    resolveWith,+    stripFieldNamespace,+    stripConstructorNamespace,   ) where  import Control.Applicative (Applicative (..))-import Control.Monad ((=<<), foldM)-import Control.Monad.Trans.Class (lift)+import Control.Monad ((=<<), (>=>), (>>=), foldM)+import Control.Monad.Reader (MonadReader (..), asks)+import Control.Monad.Trans.Class (MonadTrans (..)) import Control.Monad.Trans.Reader   ( ReaderT (..),   )@@ -47,35 +60,28 @@   ( toLower,     toUpper,   )-import Data.Foldable (null, traverse_)+import Data.Foldable (foldlM, null, traverse_) import Data.Function ((&)) import Data.HashMap.Lazy (HashMap) import qualified Data.HashMap.Lazy as HM import Data.Hashable (Hashable)-import Data.List (find)-import Data.Maybe (isJust, maybe)+import Data.List (drop, find)+import Data.List.NonEmpty (NonEmpty (..))+import Data.Maybe (maybe) import Data.Morpheus.Error.NameCollision (NameCollision (..)) import Data.Morpheus.Types.Internal.AST.Base   ( FieldName,     FieldName (..),-    GQLErrors,     Ref (..),     Token,     TypeName (..),     TypeNameRef (..),     ValidationErrors,-    toGQLError,   ) import Data.Semigroup (Semigroup (..)) import qualified Data.Text as T-  ( concat,-    pack,-    unpack,-  ) import Data.Traversable (traverse) import Instances.TH.Lift ()-import Text.Megaparsec.Internal (ParsecT (..))-import Text.Megaparsec.Stream (Stream) import Prelude   ( ($),     (.),@@ -85,7 +91,6 @@     Functor (..),     Int,     Monad,-    Ord,     String,     const,     fst,@@ -94,29 +99,50 @@     snd,   ) -mapText :: (String -> String) -> Token -> Token-mapText f = T.pack . f . T.unpack+prop :: (b -> b -> m b) -> (a -> b) -> a -> a -> m b+prop f fSel a1 a2 = f (fSel a1) (fSel a2)  nameSpaceType :: [FieldName] -> TypeName -> TypeName-nameSpaceType list (TypeName name) = TypeName . T.concat $ fmap capital (fmap readName list <> [name])+nameSpaceType list (TypeName name) = TypeName . T.concat $ fmap capitalize (fmap readName list <> [name])  nameSpaceField :: TypeName -> FieldName -> FieldName-nameSpaceField nSpace (FieldName name) = FieldName (nonCapital nSpace <> capital name)+nameSpaceField nSpace (FieldName name) = FieldName (nonCapital nSpace <> capitalize name) +dropPrefix :: TypeName -> String -> String+dropPrefix (TypeName name) = drop (length (T.unpack name))++stripConstructorNamespace :: TypeName -> String -> String+stripConstructorNamespace = dropPrefix++stripFieldNamespace :: TypeName -> String -> String+stripFieldNamespace prefix = uncapitalize . dropPrefix prefix++mapText :: (String -> String) -> Token -> Token+mapText f = T.pack . f . T.unpack+ nonCapital :: TypeName -> Token-nonCapital = mapText __nonCapital . readTypeName-  where-    __nonCapital [] = []-    __nonCapital (x : xs) = toLower x : xs+nonCapital = uncapitalize . readTypeName -capital :: Token -> Token-capital = mapText __capital-  where-    __capital [] = []-    __capital (x : xs) = toUpper x : xs+class Capitalize a where+  capitalize :: a -> a+  uncapitalize :: a -> a +instance Capitalize String where+  capitalize [] = []+  capitalize (x : xs) = toUpper x : xs+  uncapitalize [] = []+  uncapitalize (x : xs) = toLower x : xs++instance Capitalize Token where+  capitalize = mapText capitalize+  uncapitalize = mapText uncapitalize++instance Capitalize TypeName where+  capitalize = TypeName . capitalize . readTypeName+  uncapitalize = TypeName . uncapitalize . readTypeName+ capitalTypeName :: FieldName -> TypeName-capitalTypeName = TypeName . capital . readName+capitalTypeName = TypeName . capitalize . readName  --(KEY v ~ k) => class Collection a coll | coll -> a where@@ -203,7 +229,7 @@   fromElems :: (Monad m, Failure ValidationErrors m) => [a] -> m coll  instance (NameCollision a, KeyOf k a) => Listable a (HashMap k a) where-  fromElems = safeFromList+  fromElems xs = runResolutionT (fromListT xs) hmUnsafeFromValues failOnDuplicates   elems = HM.elems  keys :: (KeyOf k a, Listable a coll) => coll -> [k]@@ -217,7 +243,7 @@   merge :: (Monad m, Failure ValidationErrors m) => [Ref] -> a -> a -> m a  instance (NameCollision a, KeyOf k a) => Merge (HashMap k a) where-  merge _ = safeJoin+  merge _ x y = runResolutionT (fromListT $ HM.elems x <> HM.elems y) hmUnsafeFromValues failOnDuplicates  (<:>) :: (Monad m, Merge a, Failure ValidationErrors m) => a -> a -> m a (<:>) = merge []@@ -232,7 +258,7 @@   m a (<.>) = mergeM [] --- Failure: for custome Morpheus GrapHQL errors+-- Failure: for custom Morpheus GrapHQL errors class Applicative f => Failure error (f :: * -> *) where   failure :: error -> f v @@ -242,9 +268,6 @@ instance (Monad m, Failure errors m) => Failure errors (ReaderT ctx m) where   failure = lift . failure -instance (Stream s, Ord e, Failure GQLErrors m) => Failure ValidationErrors (ParsecT e s m) where-  failure x = ParsecT $ \_ _ _ _ _ -> failure (fmap toGQLError x)- mapFst :: (a -> a') -> (a, b) -> (a', b) mapFst f (a, b) = (f a, b) @@ -266,40 +289,87 @@ resolveUpdates :: Monad m => a -> [UpdateT m a] -> m a resolveUpdates a = foldM (&) a . fmap updateTState -safeFromList ::-  ( Failure ValidationErrors m,-    Applicative m,-    NameCollision a,-    Eq k,-    Hashable k,-    KeyOf k a+type RESOLUTION k a coll m =+  ( Monad m,+    KeyOf k a,+    Listable a coll+  )++data Resolution a coll m = Resolution+  { resolveDuplicates :: NonEmpty a -> m a,+    fromNoDuplicates :: [a] -> coll+  }++runResolutionT ::+  ResolutionT a coll m b ->+  ([a] -> coll) ->+  (NonEmpty a -> m a) ->+  m b+runResolutionT (ResolutionT x) fromNoDuplicates resolveDuplicates = runReaderT x Resolution {..}++newtype ResolutionT a coll m x = ResolutionT+  { _runResolutionT :: ReaderT (Resolution a coll m) m x+  }+  deriving+    ( Functor,+      Monad,+      Applicative,+      MonadReader (Resolution a coll m)+    )++instance MonadTrans (ResolutionT e coll) where+  lift = ResolutionT . lift++instance+  ( Monad m,+    Failure ValidationErrors m   ) =>+  Failure ValidationErrors (ResolutionT a coll m)+  where+  failure = lift . failure++resolveDuplicatesM :: Monad m => NonEmpty a -> ResolutionT a coll m a+resolveDuplicatesM xs = asks resolveDuplicates >>= lift . (xs &)++fromNoDuplicatesM :: Monad m => [a] -> ResolutionT a coll m coll+fromNoDuplicatesM xs = asks ((xs &) . fromNoDuplicates)++insertWithList :: (Eq k, Hashable k) => (k, NonEmpty a) -> [(k, NonEmpty a)] -> [(k, NonEmpty a)]+insertWithList (key, value) values+  | key `member` values = fmap replaceBy values+  | otherwise = values <> [(key, value)]+  where+    replaceBy (entryKey, entryValue)+      | key == entryKey = (key, entryValue <> value)+      | otherwise = (entryKey, entryValue)++clusterDuplicates :: (Eq k, Hashable k) => [(k, NonEmpty a)] -> [(k, a)] -> [(k, NonEmpty a)]+clusterDuplicates collected [] = collected+clusterDuplicates coll ((key, value) : xs) = clusterDuplicates (insertWithList (key, value :| []) coll) xs++fromListDuplicates :: (KeyOf k a) => [a] -> [(k, NonEmpty a)]+fromListDuplicates xs = clusterDuplicates [] (fmap toPair xs)++fromListT ::+  RESOLUTION k a coll m =>   [a] ->-  m (HashMap k a)-safeFromList values = safeUnionWith HM.empty (fmap toPair values)+  ResolutionT a coll m coll+fromListT = traverse (resolveDuplicatesM . snd) . fromListDuplicates >=> fromNoDuplicatesM -safeJoin :: (Failure ValidationErrors m, Eq k, Hashable k, Applicative m, NameCollision a) => HashMap k a -> HashMap k a -> m (HashMap k a)-safeJoin hm newls = safeUnionWith hm (HM.toList newls)+mergeT :: RESOLUTION k a coll m => coll -> coll -> ResolutionT a coll m coll+mergeT c1 c2 = traverse (resolveDuplicatesM . snd) (fromListDuplicates (elems c1 <> elems c2)) >>= fromNoDuplicatesM -safeUnionWith ::-  ( Failure ValidationErrors m,-    Applicative m,-    Eq k,-    Hashable k,-    NameCollision a-  ) =>-  HashMap k a ->-  [(k, a)] ->-  m (HashMap k a)-safeUnionWith hm names = case insertNoDups (hm, []) names of-  (res, dupps)-    | null dupps -> pure res-    | otherwise -> failure $ fmap (nameCollision . snd) dupps+resolveWith ::+  Monad m =>+  (a -> a -> m a) ->+  NonEmpty a ->+  m a+resolveWith f (x :| xs) = foldlM f x xs -type NoDupHashMap k a = (HashMap k a, [(k, a)])+hmUnsafeFromValues :: (Eq k, KeyOf k a) => [a] -> HashMap k a+hmUnsafeFromValues = HM.fromList . fmap toPair -insertNoDups :: (Eq k, Hashable k) => NoDupHashMap k a -> [(k, a)] -> NoDupHashMap k a-insertNoDups collected [] = collected-insertNoDups (coll, errors) (pair@(name, value) : xs)-  | isJust (name `HM.lookup` coll) = insertNoDups (coll, errors <> [pair]) xs-  | otherwise = insertNoDups (HM.insert name value coll, errors) xs+failOnDuplicates :: (Failure ValidationErrors m, NameCollision a) => NonEmpty a -> m a+failOnDuplicates (x :| xs)+  | null xs = pure x+  | otherwise = failure $ fmap nameCollision (x : xs)
src/Data/Morpheus/Parsing/Document/TypeSystem.hs view
@@ -270,7 +270,7 @@ -- 3.2 Schema -- SchemaDefinition: --    schema Directives[Const,opt]---      { RootOperationTypeDefinitionlist }+--      { RootOperationTypeDefinition(list) } -- --  RootOperationTypeDefinition: --    OperationType: NamedType
src/Data/Morpheus/Parsing/Internal/Terms.hs view
@@ -32,9 +32,9 @@ where  import Control.Monad ((>=>))-import Data.Functor (($>)) -- MORPHEUS-+import Control.Monad.Trans (lift)+import Data.Functor (($>)) import Data.Morpheus.Internal.Utils   ( Collection,     KeyOf,@@ -241,7 +241,7 @@ collection entry = braces (entry `sepEndBy` ignoredTokens)  setOf :: (Listable a coll, KeyOf k a) => Parser a -> Parser coll-setOf = collection >=> fromElems+setOf = collection >=> lift . fromElems  optionalCollection :: Collection a c => Parser c -> Parser c optionalCollection x = x <|> pure empty@@ -259,7 +259,7 @@       (char '(' *> ignoredTokens)       (char ')' *> ignoredTokens)       (parser `sepBy` ignoredTokens <?> "empty Tuple value!")-      >>= fromElems+      >>= lift . fromElems  uniqTupleOpt :: (Listable a coll, Collection a coll, KeyOf k a) => Parser a -> Parser coll uniqTupleOpt x = uniqTuple x <|> pure empty@@ -319,7 +319,7 @@         (char '[' *> ignoredTokens)         (char ']' *> ignoredTokens)         ( do-            (wrappers, tname) <- unwrapped <|> wrapped+            (wrappers, tName) <- unwrapped <|> wrapped             nonNull' <- parseNonNull-            return ((ListType : nonNull') ++ wrappers, tname)+            return ((ListType : nonNull') ++ wrappers, tName)         )
src/Data/Morpheus/Parsing/Request/Parser.hs view
@@ -3,13 +3,13 @@  module Data.Morpheus.Parsing.Request.Parser (parseGQL) where +--+-- MORPHEUS+import Control.Monad.Trans (lift) import qualified Data.Aeson as Aeson   ( Value (..),   ) import Data.HashMap.Lazy (toList)------ MORPHEUS- import Data.Morpheus.Internal.Utils   ( fromElems,   )@@ -46,7 +46,7 @@ request = label "GQLQuery" $ do   ignoredTokens   operation <- parseOperation-  fragments <- manyTill parseFragmentDefinition eof >>= fromElems+  fragments <- manyTill parseFragmentDefinition eof >>= lift . fromElems   pure GQLQuery {operation, fragments, inputVariables = []}  parseGQL :: GQLRequest -> Eventless GQLQuery
src/Data/Morpheus/Rendering/RenderGQL.hs view
@@ -16,6 +16,7 @@  -- MORPHEUS +import qualified Data.Aeson as A import Data.Foldable (null) import Data.Functor ((<$>)) import Data.Maybe (Maybe, maybe)@@ -25,8 +26,11 @@     intercalate,     pack,   )+import Data.Text.Encoding (decodeUtf8)+import Data.ByteString.Lazy (toStrict) import Prelude   ( (.),+    ($),     Bool (..),     Float,     Int,@@ -58,6 +62,9 @@ instance RenderGQL Bool where   render True = "true"   render False = "false"++instance RenderGQL A.Value where+  render x = decodeUtf8 $ toStrict $ A.encode x  indent :: Rendering indent = "  "
+ src/Data/Morpheus/Types/App.hs view
@@ -0,0 +1,170 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.Types.App+  ( App (..),+    AppData (..),+    withDebugger,+    mkApp,+    runApp,+    runAppStream,+  )+where++import Control.Applicative (Applicative (..))+import Control.Monad (Monad)+import qualified Data.Aeson as A+import qualified Data.ByteString.Lazy.Char8 as LBS+import Data.Functor ((<$>), Functor (..))+import Data.Morpheus.Internal.Utils+  ( (<:>),+    empty,+    failure,+    prop,+  )+import Data.Morpheus.Parser+  ( parseRequestWith,+  )+import Data.Morpheus.Rendering.RenderGQL+  ( RenderGQL (..),+  )+import Data.Morpheus.Schema.Schema (internalSchema)+import Data.Morpheus.Schema.SchemaAPI (withSystemFields)+import Data.Morpheus.Types.IO+  ( GQLRequest (..),+    GQLResponse,+    MapAPI (..),+    renderResponse,+  )+import Data.Morpheus.Types.Internal.AST+  ( GQLErrors,+    Operation (..),+    Schema (..),+    Schema (..),+    Selection (..),+    SelectionContent (..),+    VALID,+    Value,+  )+import Data.Morpheus.Types.Internal.Config+  ( Config (..),+    defaultConfig,+  )+import Data.Morpheus.Types.Internal.Resolving+  ( ResolverContext (..),+    ResponseStream,+    ResultT (..),+    RootResModel,+    cleanEvents,+    resultOr,+    runRootResModel,+  )+import Data.Morpheus.Types.Internal.Stitching (Stitching (..))+import Data.Morpheus.Validation.Document.Validation (ValidateSchema (..))+import Data.Semigroup (Semigroup (..))+import Data.Text (pack)+import Prelude+  ( ($),+    (.),+    Bool (..),+    Maybe (..),+    const,+  )++mkApp :: ValidateSchema s => Schema s -> RootResModel e m -> App e m+mkApp appSchema appResolvers =+  resultOr+    FailApp+    (App . AppData defaultConfig appResolvers)+    (validateSchema True defaultConfig appSchema)++data App event (m :: * -> *)+  = App {app :: AppData event m VALID}+  | FailApp {appErrors :: GQLErrors}++instance RenderGQL (App e m) where+  render App {app} = render app+  render FailApp {appErrors} = pack $ LBS.unpack (A.encode appErrors)++instance Monad m => Semigroup (App e m) where+  (FailApp err1) <> (FailApp err2) = FailApp (err1 <> err2)+  FailApp {appErrors} <> App {} = FailApp appErrors+  App {} <> FailApp {appErrors} = FailApp appErrors+  (App x) <> (App y) = resultOr FailApp App (stitch x y)++data AppData event (m :: * -> *) s = AppData+  { appConfig :: Config,+    appResolvers :: RootResModel event m,+    appSchema :: Schema s+  }++instance RenderGQL (AppData e m s) where+  render = render . appSchema++instance Monad m => Stitching (AppData e m s) where+  stitch x y =+    AppData (appConfig y)+      <$> prop stitch appResolvers x y+      <*> prop stitch appSchema x y++runAppData ::+  (Monad m, ValidateSchema s) =>+  AppData event m s ->+  GQLRequest ->+  ResponseStream event m (Value VALID)+runAppData AppData {appConfig, appSchema, appResolvers} request = do+  validRequest <- validateReq appSchema appConfig request+  resovers <- withSystemFields (schema validRequest) appResolvers+  runRootResModel resovers validRequest++validateReq ::+  ( Monad m,+    ValidateSchema s+  ) =>+  Schema s ->+  Config ->+  GQLRequest ->+  ResponseStream event m ResolverContext+validateReq inputSchema config request = cleanEvents $ ResultT $ pure $ do+  validSchema <- validateSchema True config inputSchema+  schema <- internalSchema <:> validSchema+  operation <- parseRequestWith config schema request+  pure $+    ResolverContext+      { schema,+        config,+        operation,+        currentTypeName = "Root",+        currentSelection =+          Selection+            { selectionName = "Root",+              selectionArguments = empty,+              selectionPosition = operationPosition operation,+              selectionAlias = Nothing,+              selectionContent = SelectionSet (operationSelection operation),+              selectionDirectives = []+            }+      }++stateless ::+  Functor m =>+  ResponseStream event m (Value VALID) ->+  m GQLResponse+stateless = fmap renderResponse . runResultT++runAppStream :: Monad m => App event m -> GQLRequest -> ResponseStream event m (Value VALID)+runAppStream App {app} = runAppData app+runAppStream FailApp {appErrors} = const $ failure appErrors++runApp :: (MapAPI a b, Monad m) => App e m -> a -> m b+runApp app = mapAPI (stateless . runAppStream app)++withDebugger :: App e m -> App e m+withDebugger App {app = AppData {appConfig = Config {..}, ..}} =+  App {app = AppData {appConfig = Config {debug = True, ..}, ..}, ..}+withDebugger x = x
src/Data/Morpheus/Types/GQLScalar.hs view
@@ -22,7 +22,6 @@     Value (..),     replaceValue,   )-import Data.Proxy (Proxy (..)) import Data.Text (Text, unpack) import Prelude   ( ($),@@ -60,7 +59,7 @@   -- | serialization of haskell type into scalar value   serialize :: a -> ScalarValue -  scalarValidator :: Proxy a -> ScalarDefinition+  scalarValidator :: f a -> ScalarDefinition   scalarValidator _ = ScalarDefinition {validateValue = validator}     where       validator value = do
src/Data/Morpheus/Types/Internal/AST.hs view
@@ -78,9 +78,7 @@     initTypeLib,     kindOf,     toNullable,-    toListField,     isObject,-    isInput,     toHSWrappers,     isNullable,     toGQLWrapper,@@ -95,23 +93,21 @@     mkTypeRef,     mkInputUnionFields,     fieldVisibility,-    insertType,     lookupDeprecated,     lookupDeprecatedReason,     lookupWith,-    -- Temaplate Haskell+    -- Template Haskell     hsTypeName,     -- LOCAL     GQLQuery (..),     Variables,     unsafeFromFields,-    OrdMap,+    OrdMap (..),     GQLError (..),     GQLErrors,     ObjectEntry (..),     UnionTag (..),     __inputname,-    updateSchema,     internalFingerprint,     ANY,     IN,@@ -164,11 +160,14 @@     toGQLError,     ELEM,     LEAF,-    REQURE_IMPLEMENTABLE,+    REQUIRE_IMPLEMENTABLE,     ToCategory (..),     FromCategory (..),     possibleTypes,     possibleInterfaceTypes,+    mkField,+    isTypeDefined,+    safeDefineType,   ) where 
src/Data/Morpheus/Types/Internal/AST/Base.hs view
@@ -37,7 +37,6 @@     isOutputObject,     isNotSystemTypeName,     isObject,-    isInput,     sysFields,     hsTypeName,     toOperationType,@@ -233,7 +232,15 @@   deriving     (Generic)   deriving newtype-    (Show, Ord, Eq, IsString, Hashable, Semigroup, FromJSON, ToJSON)+    ( Show,+      Ord,+      Eq,+      IsString,+      Hashable,+      Semigroup,+      FromJSON,+      ToJSON+    )  instance Lift TypeName where   lift = liftString . readTypeName@@ -327,7 +334,7 @@ ------------------------------------------------------------------- data TypeRef = TypeRef   { typeConName :: TypeName,-    typeArgs :: Maybe TypeName,+    typeArgs :: Maybe String,     typeWrappers :: [TypeWrapper]   }   deriving (Show, Eq, Lift)@@ -391,10 +398,6 @@ isObject KindInputObject = True isObject KindInterface = True isObject _ = False--isInput :: TypeKind -> Bool-isInput KindInputObject = True-isInput _ = False  -- TypeWrappers -----------------------------------------------------------------------------------
src/Data/Morpheus/Types/Internal/AST/Fields.hs view
@@ -31,7 +31,6 @@     Directives,     Directive (..),     fieldVisibility,-    toListField,     lookupDeprecated,     lookupDeprecatedReason,     unsafeFromFields,@@ -39,6 +38,7 @@     fieldContentArgs,     mkInputValue,     mkObjectField,+    mkField,   ) where @@ -252,7 +252,7 @@     fieldDirectives :: [Directive s],     fieldContent :: Maybe (FieldContent TRUE cat s)   }-  deriving (Show, Lift)+  deriving (Show, Lift, Eq)  data FieldContent (bool :: Bool) (cat :: TypeCategory) (s :: Stage) where   DefaultInputValue ::@@ -268,6 +268,8 @@ fieldContentArgs (FieldArgs args) = args fieldContentArgs _ = empty +deriving instance Eq (FieldContent bool cat s)+ deriving instance Show (FieldContent bool cat s)  deriving instance Lift (FieldContent bool cat s)@@ -299,23 +301,26 @@ fieldVisibility :: FieldDefinition cat s -> Bool fieldVisibility FieldDefinition {fieldName} = fieldName `notElem` sysFields -createField ::+mkField ::   Maybe (FieldContent TRUE cat s) ->   FieldName ->-  [TypeWrapper] ->-  TypeName ->+  TypeRef ->   FieldDefinition cat s-createField fieldContent fieldName typeWrappers typeConName =+mkField fieldContent fieldName fieldType =   FieldDefinition     { fieldName,       fieldContent,       fieldDescription = Nothing,-      fieldType = TypeRef {typeConName, typeWrappers, typeArgs = Nothing},+      fieldType,       fieldDirectives = []     }  mkInputValue :: FieldName -> [TypeWrapper] -> TypeName -> FieldDefinition cat s-mkInputValue = createField Nothing+mkInputValue fieldName typeWrappers typeConName =+  mkField+    Nothing+    fieldName+    TypeRef {typeWrappers, typeConName, typeArgs = Nothing}  mkObjectField ::   ArgumentsDefinition s ->@@ -323,13 +328,11 @@   [TypeWrapper] ->   TypeName ->   FieldDefinition OUT s-mkObjectField args = createField (Just $ FieldArgs args)--toListField :: FieldDefinition cat s -> FieldDefinition cat s-toListField dataField = dataField {fieldType = listW (fieldType dataField)}-  where-    listW alias@TypeRef {typeWrappers} =-      alias {typeWrappers = TypeList : typeWrappers}+mkObjectField args fieldName typeWrappers typeConName =+  mkField+    (Just $ FieldArgs args)+    fieldName+    TypeRef {typeWrappers, typeConName, typeArgs = Nothing}  -- 3.10 Input Objects: https://spec.graphql.org/June2018/#sec-Input-Objects ---------------------------------------------------------------------------@@ -349,7 +352,7 @@   { argumentsTypename :: Maybe TypeName,     arguments :: OrdMap FieldName (ArgumentDefinition s)   }-  deriving (Show, Lift)+  deriving (Show, Lift, Eq)  instance RenderGQL (ArgumentsDefinition s) where   render ArgumentsDefinition {arguments} = renderArguments (elems arguments)
src/Data/Morpheus/Types/Internal/AST/MergeSet.hs view
@@ -23,8 +23,8 @@ import Control.Applicative (Applicative (..)) import Control.Monad (Monad (..)) import Data.Foldable (Foldable (..))-import Data.Functor ((<$>), Functor (..))-import Data.List ((\\), find)+import Data.Functor (Functor (..))+import Data.List (find) import Data.Maybe (maybe) import Data.Morpheus.Internal.Utils   ( (<:>),@@ -35,6 +35,10 @@     Merge (..),     Selectable (..),     elems,+    fromListT,+    mergeT,+    resolveWith,+    runResolutionT,   ) import Data.Morpheus.Types.Internal.AST.Base   ( Ref,@@ -57,7 +61,6 @@     (.),     Eq (..),     Show,-    flip,     otherwise,   ) @@ -123,7 +126,7 @@   ) =>   Merge (MergeSet VALID a)   where-  merge = safeJoin+  merge path x y = runResolutionT (mergeT x y) MergeSet (resolveWith (resolveConflict path))  instance   ( Listable a (MergeSet VALID a),@@ -133,7 +136,7 @@   ) =>   Listable a (MergeSet VALID a)   where-  fromElems = safeFromList+  fromElems xs = runResolutionT (fromListT xs) MergeSet (resolveWith (resolveConflict []))   elems = unpack  instance Merge (MergeSet RAW a) where@@ -143,28 +146,7 @@   fromElems = pure . MergeSet   elems = unpack -safeFromList :: (Monad m, Eq a, KeyOf k a, Merge a, Failure ValidationErrors m) => [a] -> m (MergeSet opt a)-safeFromList = insertList [] empty--safeJoin :: (Monad m, KeyOf k a, Eq a, Listable a (MergeSet opt a), Merge a, Failure ValidationErrors m) => [Ref] -> MergeSet opt a -> MergeSet opt a -> m (MergeSet opt a)-safeJoin path hm1 hm2 = insertList path hm1 (elems hm2)--insertList :: (Monad m, Eq a, KeyOf k a, Merge a, Failure ValidationErrors m) => [Ref] -> MergeSet opt a -> [a] -> m (MergeSet opt a)-insertList _ smap [] = pure smap-insertList path smap (x : xs) = insert path smap x >>= flip (insertList path) xs--insert :: (Monad m, Eq a, KeyOf k a, Merge a, Failure ValidationErrors m) => [Ref] -> MergeSet opt a -> a -> m (MergeSet opt a)-insert path mSet@(MergeSet ls) currentValue = MergeSet <$> __insert-  where-    __insert =-      selectOr-        (pure $ ls <> [currentValue])-        mergeWith-        (keyOf currentValue)-        mSet-    -------------------    mergeWith oldValue-      | oldValue == currentValue = pure ls-      | otherwise = do-        mergedValue <- merge path oldValue currentValue-        pure $ (ls \\ [oldValue]) <> [mergedValue]+resolveConflict :: (Monad m, Eq a, KeyOf k a, Merge a, Failure ValidationErrors m) => [Ref] -> a -> a -> m a+resolveConflict path oldValue newValue+  | oldValue == newValue = pure oldValue+  | otherwise = merge path oldValue newValue
src/Data/Morpheus/Types/Internal/AST/SafeHashMap.hs view
@@ -15,6 +15,7 @@  module Data.Morpheus.Types.Internal.AST.SafeHashMap   ( SafeHashMap,+    unsafeFromValues,     insert,   ) where@@ -35,6 +36,7 @@     Listable (..),     Merge (..),     Selectable (..),+    toPair,   ) import Data.Morpheus.Types.Internal.AST.Base (ValidationErrors) import Data.Traversable (Traversable (..))@@ -73,6 +75,9 @@ instance (NameCollision a, KeyOf k a, Hashable k) => Listable a (SafeHashMap k a) where   fromElems = fmap SafeHashMap . fromElems   elems = elems . unpackSafeHashMap++unsafeFromValues :: (Eq k, KeyOf k a) => [a] -> SafeHashMap k a+unsafeFromValues = SafeHashMap . HM.fromList . fmap toPair  insert ::   ( NameCollision a,
src/Data/Morpheus/Types/Internal/AST/Selection.hs view
@@ -145,8 +145,8 @@   where   merge path (SelectionSet s1) (SelectionSet s2) = SelectionSet <$> merge path s1 s2   merge path (UnionSelection u1) (UnionSelection u2) = UnionSelection <$> merge path u1 u2-  merge path oldC currC-    | oldC == currC = pure oldC+  merge path oldC currentC+    | oldC == currentC = pure oldC     | otherwise =       failure         [ ValidationError@@ -233,8 +233,8 @@       } = fromMaybe selectionName selectionAlias   keyOf _ = "" -useDufferentAliases :: Message-useDufferentAliases =+useDifferentAliases :: Message+useDifferentAliases =   "Use different aliases on the "     <> "fields to fetch both if this was intentional." @@ -275,11 +275,11 @@                 { validationMessage =                     "" <> msg (selectionName old) <> " and " <> msg (selectionName current)                       <> " are different fields. "-                      <> useDufferentAliases,+                      <> useDifferentAliases,                   validationLocations = [pos1, pos2]                 }         ----------------------        -- allias name is relevant only if they collide by allias like:+        -- alias name is relevant only if they collide by allies like:         --   { user1: user         --     user1: user         --   }@@ -292,14 +292,14 @@           | otherwise =             failure $ mergeConflict currentPath $               ValidationError-                { validationMessage = "they have differing arguments. " <> useDufferentAliases,+                { validationMessage = "they have differing arguments. " <> useDifferentAliases,                   validationLocations = [pos1, pos2]                 }   merge path _ _ =     failure $       mergeConflict         path-        ("INTERNAL: can't merge. " <> msgValidation useDufferentAliases :: ValidationError)+        ("INTERNAL: can't merge. " <> msgValidation useDifferentAliases :: ValidationError)  deriving instance Show (Selection a) 
src/Data/Morpheus/Types/Internal/AST/TypeCategory.hs view
@@ -16,7 +16,7 @@     IMPLEMENTABLE,     fromAny,     toAny,-    REQURE_IMPLEMENTABLE,+    REQUIRE_IMPLEMENTABLE,     ToCategory (..),     FromCategory (..),   )@@ -74,7 +74,7 @@     (setOfKind :: TypeCategory) ::     Bool -type REQURE_IMPLEMENTABLE cat = ELEM cat IMPLEMENTABLE ~ TRUE+type REQUIRE_IMPLEMENTABLE cat = ELEM cat IMPLEMENTABLE ~ TRUE  -- ANY type instance ELEM ANY a = TRUE
src/Data/Morpheus/Types/Internal/AST/TypeSystem.hs view
@@ -36,12 +36,10 @@     createScalarType,     mkInputUnionFields,     initTypeLib,-    insertType,     kindOf,     isEntNode,     lookupWith,     __inputname,-    updateSchema,     UnionMember (..),     mkUnionMember,     RawTypeDefinition (..),@@ -53,6 +51,8 @@     typed,     possibleTypes,     possibleInterfaceTypes,+    isTypeDefined,+    safeDefineType,   ) where @@ -68,7 +68,6 @@ import Data.Morpheus.Error.NameCollision   ( NameCollision (..),   )-import Data.Morpheus.Error.Schema (nameCollisionError) import Data.Morpheus.Internal.Utils   ( (<:>),     Collection (..),@@ -77,14 +76,13 @@     Listable (..),     Merge (..),     Selectable (..),-    UpdateT (..),-    concatUpdates,     elems,-    resolveUpdates,   ) import Data.Morpheus.Rendering.RenderGQL   ( RenderGQL (..),+    Rendering,     newline,+    renderEntry,     renderMembers,     renderObject,   )@@ -157,14 +155,13 @@     Bool (..),     Eq (..),     Show (..),-    const,     flip,     otherwise,   )  type DataEnum s = [DataEnumValue s] --- used for perserving type information from untyped values+-- used for preserving type information from untyped values -- e.g -- unionType :: UnionMember IN VALID -> Typed IN VALID TypeName -- unionType = typed memberName@@ -174,7 +171,7 @@ untyped :: (a -> b) -> Typed c s a -> b untyped f = f . _untyped --- | used for perserving type information from untyped values+-- | used for preserving type information from untyped values -- see function typed newtype Typed (cat :: TypeCategory) (s :: Stage) a = Typed   { _untyped :: a@@ -281,6 +278,12 @@   }   deriving (Show) +instance RenderGQL SchemaDefinition where+  render = renderSchemaDefinition . elems . unSchemaDefinition++renderSchemaDefinition :: RenderGQL a => [a] -> Rendering+renderSchemaDefinition entries = "schema" <> renderObject entries <> newline+ instance Selectable OperationType RootOperationTypeDefinition SchemaDefinition where   selectOr fallback f key SchemaDefinition {unSchemaDefinition} =     selectOr fallback f key unSchemaDefinition@@ -310,6 +313,13 @@ instance KeyOf OperationType RootOperationTypeDefinition where   keyOf = rootOperationType +instance RenderGQL RootOperationTypeDefinition where+  render+    RootOperationTypeDefinition+      { rootOperationType,+        rootOperationTypeDefinitionName+      } = renderEntry rootOperationType rootOperationTypeDefinitionName+ type TypeLib s = SafeHashMap TypeName (TypeDefinition ANY s)  instance Selectable TypeName (TypeDefinition ANY s) (Schema s) where@@ -338,17 +348,17 @@     Maybe (TypeDefinition OBJECT s)   ) ->   f (Schema s)-buildWith otypes (Just query, mutation, subscription) = do-  let types = excludeTypes [Just query, mutation, subscription] otypes+buildWith oTypes (Just query, mutation, subscription) = do+  let types = excludeTypes [Just query, mutation, subscription] oTypes   let schema = (initTypeLib query) {mutation, subscription}   foldM (flip safeDefineType) schema types buildWith _ (Nothing, _, _) = failure ["Query root type must be provided." :: ValidationError]  excludeTypes :: [Maybe (TypeDefinition c1 s)] -> [TypeDefinition c2 s] -> [TypeDefinition c2 s]-excludeTypes excusionTypes = filter ((`notElem` blacklist) . typeName)+excludeTypes exclusionTypes = filter ((`notElem` blacklist) . typeName)   where     blacklist :: [TypeName]-    blacklist = fmap typeName (catMaybes excusionTypes)+    blacklist = fmap typeName (catMaybes exclusionTypes)  withDirectives ::   [DirectiveDefinition s] ->@@ -477,15 +487,15 @@   TypeName ->   Schema s ->   [TypeDefinition ANY s]-possibleInterfaceTypes name schema = mapMaybe (isPosibleInterfaceType name) (elems schema)+possibleInterfaceTypes name schema = mapMaybe (isPossibleInterfaceType name) (elems schema) -isPosibleInterfaceType ::+isPossibleInterfaceType ::   TypeName ->   TypeDefinition c s ->   Maybe (TypeDefinition c s)-isPosibleInterfaceType name typeDef@TypeDefinition {typeName, typeContent = DataObject {objectImplements}}+isPossibleInterfaceType name typeDef@TypeDefinition {typeName, typeContent = DataObject {objectImplements}}   | name `elem` (typeName : objectImplements) = Just typeDef-isPosibleInterfaceType _ _ = Nothing+isPossibleInterfaceType _ _ = Nothing  instance   (FromCategory (TypeContent TRUE) cat cat') =>@@ -641,32 +651,6 @@   types <- insert (toAny datatype) (types lib)   pure lib {types} -insertType ::-  (Monad m, Failure ValidationErrors m) =>-  TypeDefinition cat s ->-  UpdateT m (Schema s)-insertType datatype@TypeDefinition {typeName, typeFingerprint} =-  updateSchema typeName typeFingerprint [] (const datatype) ()--updateSchema ::-  (Monad m, Failure ValidationErrors m) =>-  TypeName ->-  DataFingerprint ->-  [UpdateT m (Schema s)] ->-  (a -> TypeDefinition cat s) ->-  a ->-  UpdateT m (Schema s)-updateSchema name fingerprint stack f x-  | isNotSystemTypeName name = UpdateT $ \lib ->-    case isTypeDefined name lib of-      Nothing -> do-        t <- safeDefineType (f x) lib-        resolveUpdates t stack-      Just fingerprint' | fingerprint' == fingerprint -> pure lib-      -- throw error if 2 different types has same name-      Just _ -> failure [nameCollisionError name]-  | otherwise = concatUpdates stack- lookupWith :: Eq k => (a -> k) -> k -> [a] -> Maybe a lookupWith f key = find ((== key) . f) @@ -721,8 +705,15 @@ --------------------------------------------------------------------------------------------------  instance RenderGQL (Schema s) where-  render schema = intercalate newline (fmap render visibleTypes)+  render schema =+    intercalate newline (fmap render visibleTypes <> [renderSchemaDefinition entries])     where+      entries =+        RootOperationTypeDefinition Query (typeName $ query schema)+          : catMaybes+            [ RootOperationTypeDefinition Mutation . typeName <$> mutation schema,+              RootOperationTypeDefinition Subscription . typeName <$> subscription schema+            ]       visibleTypes = filter (isNotSystemTypeName . typeName) (elems schema)  instance RenderGQL (TypeDefinition a s) where
src/Data/Morpheus/Types/Internal/AST/Value.hs view
@@ -48,7 +48,7 @@ import Data.Either (Either (..)) import Data.Foldable (foldl, foldl1, null) import Data.Functor (fmap)-import qualified Data.HashMap.Strict as M+import qualified Data.HashMap.Lazy as M   ( toList,   ) import Data.Maybe (Maybe (..))@@ -115,6 +115,7 @@   | Float Float   | String Text   | Boolean Bool+  | Value A.Value   deriving (Show, Eq, Generic, Lift)  instance RenderGQL ScalarValue where@@ -122,12 +123,14 @@   render (Float x) = render x   render (String x) = render x   render (Boolean x) = render x+  render (Value x) = render x  instance A.ToJSON ScalarValue where   toJSON (Float x) = A.toJSON x   toJSON (Int x) = A.toJSON x   toJSON (Boolean x) = A.toJSON x   toJSON (String x) = A.toJSON x+  toJSON (Value x) = A.toJSON x  instance A.FromJSON ScalarValue where   parseJSON (A.Bool v) = pure $ Boolean v
src/Data/Morpheus/Types/Internal/Resolving.hs view
@@ -20,7 +20,6 @@     unpackEvents,     ObjectResModel (..),     ResModel (..),-    FieldResModel,     WithOperation,     PushEvents (..),     subscribe,@@ -45,6 +44,7 @@     ResolverState,     liftResolverState,     mkValue,+    FieldResModel,   ) where @@ -52,10 +52,7 @@ import Control.Monad (Monad) import qualified Data.Aeson as A import Data.Functor (fmap)-import qualified Data.HashMap.Strict as HM-  ( lookup,-    toList,-  )+import qualified Data.HashMap.Lazy as HM import Data.Maybe (maybe) import Data.Morpheus.Internal.Utils   ( mapTuple,@@ -126,14 +123,16 @@ mkValue (A.String x) = ResScalar (String x) mkValue (A.Bool x) = ResScalar (Boolean x) +type FieldResModel o e m = (FieldName, Resolver o e m (ResModel o e m))+ mkObject ::   TypeName ->   [(FieldName, Resolver o e m (ResModel o e m))] ->   ResModel o e m-mkObject __typename objectFields =+mkObject __typename fields =   ResObject     ( ObjectResModel         { __typename,-          objectFields+          objectFields = HM.fromList fields         }     )
src/Data/Morpheus/Types/Internal/Resolving/Resolver.hs view
@@ -28,7 +28,6 @@     ResponseStream,     ObjectResModel (..),     ResModel (..),-    FieldResModel,     WithOperation,     ResolverContext (..),     unsafeInternalContext,@@ -52,6 +51,8 @@     mapReaderT,   ) import Data.Functor ((<$>), Functor (..))+import Data.HashMap.Lazy (HashMap)+import qualified Data.HashMap.Lazy as HM import Data.Maybe (Maybe (..), maybe) import Data.Morpheus.Error.Selection (subfieldsNotSelected) import Data.Morpheus.Internal.Utils@@ -77,6 +78,7 @@     OperationType,     OperationType (..),     QUERY,+    Ref,     SUBSCRIPTION,     ScalarValue (..),     Selection (..),@@ -127,7 +129,6 @@     (.),     Eq (..),     Show (..),-    lookup,     otherwise,   ) @@ -259,7 +260,7 @@     $ fromSub <$> runResolverQ res   where     fromSub :: (e -> Resolver SUBSCRIPTION e m a) -> ReaderT e (ResolverStateT () m) a-    fromSub f = join (ReaderT $ \e -> runResolverS (f e))+    fromSub f = join (ReaderT (runResolverS . f))  withArguments ::   (LiftOperation o, Monad m) =>@@ -300,7 +301,7 @@     maybe       (pure gqlNull)       (>>= runDataResolver)-      . lookup selectionName+      . HM.lookup selectionName       . objectFields  resolveObject ::@@ -336,9 +337,9 @@               ResUnion name                 $ pure                 $ ResObject-                $ ObjectResModel name [("enum", pure $ ResScalar $ String $ readTypeName enum)]-        encodeNode ResEnum {} _ =-          failure ("wrong selection on enum value" :: Message)+                $ ObjectResModel name+                $ HM.singleton "enum" (pure $ ResScalar $ String $ readTypeName enum)+        encodeNode ResEnum {} _ = failure ("wrong selection on enum value" :: Message)         -- UNION         encodeNode (ResUnion typename unionRef) (UnionSelection selections) =           unionRef >>= resolveObject currentSelection@@ -388,32 +389,54 @@ subscriptionEvents ctx Nothing _ = failure [resolverFailureMessage ctx "channel Resolver is not defined"]  -- Resolver Models --------------------------------------------------------------------type FieldResModel o e m =-  (FieldName, Resolver o e m (ResModel o e m))  data ObjectResModel o e m = ObjectResModel   { __typename :: TypeName,-    objectFields ::-      [FieldResModel o e m]+    objectFields :: HashMap FieldName (Resolver o e m (ResModel o e m))   }   deriving (Show) -instance Applicative f => SemigroupM f (ObjectResModel o e m) where-  mergeM _ (ObjectResModel tyname x) (ObjectResModel _ y) =-    pure $ ObjectResModel tyname (x <> y)+instance+  ( Monad m,+    Applicative f,+    LiftOperation o+  ) =>+  SemigroupM f (ObjectResModel o e m)+  where+  mergeM path (ObjectResModel tyname x) (ObjectResModel _ y) =+    pure $ ObjectResModel tyname (HM.unionWith (mergeResolver path) x y) +mergeResolver ::+  (Monad m, SemigroupM (ResolverStateT e m) a, LiftOperation o) =>+  [Ref] ->+  Resolver o e m a ->+  Resolver o e m a ->+  Resolver o e m a+mergeResolver path a b = do+  a' <- a+  b >>= packResolver . mergeM path a'+ data ResModel (o :: OperationType) e (m :: * -> *)   = ResNull   | ResScalar ScalarValue   | ResEnum TypeName TypeName-  | ResList [ResModel o e m]   | ResObject (ObjectResModel o e m)+  | ResList [ResModel o e m]   | ResUnion TypeName (Resolver o e m (ResModel o e m))   deriving (Show) -instance (Monad f, Failure InternalError f) => SemigroupM f (ResModel o e m) where-  mergeM p (ResObject x) (ResObject y) =-    ResObject <$> mergeM p x y+instance+  ( Monad f,+    Monad m,+    LiftOperation o,+    Failure InternalError f+  ) =>+  SemigroupM f (ResModel o e m)+  where+  mergeM _ ResNull ResNull = pure ResNull+  mergeM _ ResScalar {} x@ResScalar {} = pure x+  mergeM _ ResEnum {} x@ResEnum {} = pure x+  mergeM p (ResObject x) (ResObject y) = ResObject <$> mergeM p x y   mergeM _ _ _ = failure ("can't merge: incompatible resolvers" :: InternalError)  data RootResModel e m = RootResModel
+ src/Data/Morpheus/Types/Internal/Stitching.hs view
@@ -0,0 +1,147 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.Types.Internal.Stitching+  ( Stitching (..),+  )+where++import Control.Applicative (Applicative (..))+import Control.Monad (Monad (..))+import Data.Functor ((<$>))+import Data.Maybe (Maybe (..))+import Data.Morpheus.Error.NameCollision (NameCollision (..))+import Data.Morpheus.Internal.Utils+  ( Failure (..),+    SemigroupM (..),+    mergeT,+    prop,+    resolveWith,+    runResolutionT,+  )+import Data.Morpheus.Types.Internal.AST+  ( Directive,+    DirectiveDefinition,+    FieldDefinition,+    Fields (..),+    FieldsDefinition,+    Schema (..),+    TRUE,+    TypeContent (..),+    TypeDefinition (..),+    TypeLib,+    ValidationErrors,+  )+import qualified Data.Morpheus.Types.Internal.AST.OrdMap as OM (unsafeFromValues)+import qualified Data.Morpheus.Types.Internal.AST.SafeHashMap as SHM (unsafeFromValues)+import Data.Morpheus.Types.Internal.Resolving (RootResModel)+import qualified Data.Morpheus.Types.Internal.Resolving as R (RootResModel (..))+import Data.Semigroup (Semigroup (..))+import Prelude+  ( ($),+    (.),+    Eq (..),+    otherwise,+  )++equal :: (Eq a, Applicative m, Failure ValidationErrors m) => ValidationErrors -> a -> a -> m a+equal err p1 p2+  | p1 == p2 = pure p2+  | otherwise = failure err++fstM :: Applicative m => a -> a -> m a+fstM x _ = pure x++concatM :: (Applicative m, Semigroup a) => a -> a -> m a+concatM x = pure . (x <>)++class Stitching a where+  stitch :: (Monad m, Failure ValidationErrors m) => a -> a -> m a++instance Stitching a => Stitching (Maybe a) where+  stitch = optional stitch++instance Stitching (Schema s) where+  stitch s1 s2 =+    Schema+      <$> prop stitch types s1 s2+      <*> prop stitchOperation query s1 s2+      <*> prop (optional stitchOperation) mutation s1 s2+      <*> prop (optional stitchOperation) subscription s1 s2+      <*> prop stitch directiveDefinitions s1 s2++instance Stitching (TypeLib s) where+  stitch x y = runResolutionT (mergeT x y) SHM.unsafeFromValues (resolveWith stitch)++instance Stitching [DirectiveDefinition s] where+  stitch = concatM++instance Stitching [Directive s] where+  stitch = concatM++optional :: Applicative f => (t -> t -> f t) -> Maybe t -> Maybe t -> f (Maybe t)+optional _ Nothing y = pure y+optional _ (Just x) Nothing = pure (Just x)+optional f (Just x) (Just y) = Just <$> f x y++stitchOperation ::+  (Monad m, Failure ValidationErrors m) =>+  TypeDefinition c s ->+  TypeDefinition c s ->+  m (TypeDefinition c s)+stitchOperation x y =+  TypeDefinition+    <$> prop fstM typeName x y+    <*> prop fstM typeFingerprint x y+    <*> prop concatM typeDescription x y+    <*> prop stitch typeDirectives x y+    <*> prop stitch typeContent x y++instance Stitching (TypeDefinition cat s) where+  stitch x y =+    TypeDefinition+      <$> prop (equal [nameCollision y]) typeName x y+      <*> prop (equal [nameCollision y]) typeFingerprint x y+      <*> prop concatM typeDescription x y+      <*> prop stitch typeDirectives x y+      <*> prop stitch typeContent x y++instance Stitching (TypeContent TRUE cat s) where+  stitch (DataObject i1 fields1) (DataObject i2 fields2) =+    DataObject (i1 <> i2) <$> stitch fields1 fields2+  stitch _ _ = failure (["Schema Stitching works only for objects"] :: ValidationErrors)++instance Stitching (FieldsDefinition cat s) where+  stitch (Fields x) (Fields y) = Fields <$> runResolutionT (mergeT x y) OM.unsafeFromValues (resolveWith stitch)++instance Stitching (FieldDefinition cat s) where+  stitch old new+    | old == new = pure old+    | otherwise = failure [nameCollision new]++rootProp :: (Monad m, SemigroupM m b) => (a -> m b) -> a -> a -> m b+rootProp f x y = do+  x' <- f x+  y' <- f y+  mergeM [] x' y'++stitchSubscriptions :: Failure ValidationErrors m => Maybe a -> Maybe a -> m (Maybe a)+stitchSubscriptions Just {} Just {} = failure (["can't merge  subscription applications"] :: ValidationErrors)+stitchSubscriptions x Nothing = pure x+stitchSubscriptions Nothing x = pure x++instance Monad m => Stitching (RootResModel e m) where+  stitch x y = do+    channelMap <- stitchSubscriptions (R.channelMap x) (R.channelMap y)+    pure $+      R.RootResModel+        { R.query = rootProp R.query x y,+          R.mutation = rootProp R.mutation x y,+          R.subscription = rootProp R.subscription x y,+          R.channelMap+        }
src/Data/Morpheus/Types/Internal/Validation.hs view
@@ -230,17 +230,17 @@     }   values =     selectOr-      (handeNull fieldContent)+      (handleNull fieldContent)       validateF       fieldName       values     where       -------------------      handeNull ::+      handleNull ::         Maybe (FieldContent TRUE IN s) ->         Validator s ctx validValue-      handeNull (Just (DefaultInputValue value)) = f value-      handeNull Nothing+      handleNull (Just (DefaultInputValue value)) = f value+      handleNull Nothing         | isNullable field = f Null         | otherwise = failSelection       -----------------@@ -288,7 +288,7 @@         )         __inputname         hm-  unionMember <- isPosibeInputUnion tags enum+  unionMember <- isPossibleInputUnion tags enum   case size hm of     1 -> pure (unionMember, Nothing)     2 -> do@@ -304,10 +304,10 @@       pure (unionMember, Just value)     _ -> failure ("input union can have only one variant." :: Message) -isPosibeInputUnion :: [UnionMember IN s] -> Value stage -> Either Message (UnionMember IN s)-isPosibeInputUnion tags (Enum name) =+isPossibleInputUnion :: [UnionMember IN s] -> Value stage -> Either Message (UnionMember IN s)+isPossibleInputUnion tags (Enum name) =   selectBy-    (msg name <> " is not posible union type")+    (msg name <> " is not possible union type")     name     tags-isPosibeInputUnion _ _ = failure $ "\"" <> msg __inputname <> "\" must be Enum"+isPossibleInputUnion _ _ = failure $ "\"" <> msg __inputname <> "\" must be Enum"
src/Data/Morpheus/Validation/Query/UnionSelection.hs view
@@ -3,6 +3,7 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} @@ -13,17 +14,20 @@ where  import Control.Monad ((>=>))-import Data.Maybe (isJust)+import Data.Maybe (fromMaybe, isJust) -- MORPHEUS import Data.Morpheus.Internal.Utils-  ( elems,+  ( (<:>),+    elems,     empty,     fromElems,+    singleton,   ) import Data.Morpheus.Types.Internal.AST   ( DataUnion,     Fragment (..),     IMPLEMENTABLE,+    Position (..),     RAW,     Selection (..),     SelectionContent (..),@@ -138,6 +142,23 @@   where     joinCluster (typeName, fragmets) = UnionTag typeName <$> MS.join (selSet : fragmets) +withTypename ::+  SelectionSet VALID ->+  FragmentValidator s (SelectionSet VALID)+withTypename sel = do+  selectionPosition <- fromMaybe (Position 0 0) <$> asksScope position+  sel+    <:> singleton+      ( Selection+          { selectionName = "__typename",+            selectionAlias = Nothing,+            selectionPosition,+            selectionArguments = empty,+            selectionContent = SelectionField,+            selectionDirectives = empty+          }+      )+ validateInterfaceSelection ::   ResolveFragment s =>   (Fragment RAW -> FragmentValidator s (SelectionSet VALID)) ->@@ -156,7 +177,9 @@     let categories = tagUnionFragments possibleTypes spreads     if null categories       then pure (SelectionSet validSelectionSet)-      else joinClusters validSelectionSet (insertDefault (typeName typeDef) categories)+      else do+        validSelectionSetWithTypename <- withTypename validSelectionSet+        joinClusters validSelectionSetWithTypename (insertDefault (typeName typeDef) categories)  insertDefault :: TypeName -> [(TypeName, [a])] -> [(TypeName, [a])] insertDefault interfaceName categories
− test/Lib.hs
@@ -1,177 +0,0 @@-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Lib-  ( getGQLBody,-    expectedResponse,-    getCases,-    maybeVariables,-    readSource,-    scanSchemaTests,-    FileUrl (..),-    CaseTree (..),-    toString,-    getRequest,-    assertValidSchema,-    getSchema,-    getResolvers,-  )-where--import Control.Applicative ((<|>), pure)-import Control.Monad ((>=>), Monad)-import Data.Aeson (FromJSON, Value (..), decode)-import qualified Data.ByteString.Lazy as L (readFile)-import Data.ByteString.Lazy.Char8 (ByteString)-import Data.Either (Either (..))-import Data.Foldable (foldl)-import Data.Functor ((<$>))-import Data.HashMap.Lazy (lookup)-import Data.Maybe (Maybe (..), fromMaybe, maybe)-import Data.Morpheus.Core (parseGQLDocument)-import Data.Morpheus.Types.IO-  ( GQLRequest (..),-  )-import Data.Morpheus.Types.Internal.AST (FieldName (..), Schema (..), VALID)-import Data.Morpheus.Types.Internal.Resolving-  ( Eventless,-    ResModel,-    RootResModel (..),-    WithOperation,-    mkNull,-    mkValue,-    resultOr,-  )-import Data.Semigroup ((<>))-import Data.Text (Text, unpack)-import qualified Data.Text.Lazy as LT (toStrict)-import Data.Text.Lazy.Encoding (decodeUtf8)-import Data.Traversable (traverse)-import System.Directory (doesDirectoryExist, listDirectory)-import Test.Tasty.HUnit-  ( assertFailure,-  )-import Prelude-  ( ($),-    (.),-    Bool,-    FilePath,-    IO,-    Show,-    and,-    map,-    show,-  )--readSource :: FieldName -> IO ByteString-readSource = L.readFile . path . readName--path :: Text -> FilePath-path name = "test/" <> unpack name--gqlLib :: Text -> FilePath-gqlLib x = path x <> "/query.gql"--resLib :: Text -> FilePath-resLib x = path x <> "/response.json"--data FileUrl = FileUrl-  { filePath :: [FilePath],-    fileName :: FilePath-  }-  deriving (Show)--data CaseTree = CaseTree-  { caseUrl :: FileUrl,-    children :: Either [FilePath] [CaseTree]-  }-  deriving (Show)--prefix :: FileUrl -> FilePath -> FileUrl-prefix FileUrl {..} x =-  FileUrl-    { filePath = fileName : filePath,-      fileName = x-    }--toString :: FileUrl -> FilePath-toString FileUrl {..} = foldl (\y x -> x <> "/" <> y) fileName filePath--scanSchemaTests :: FilePath -> IO CaseTree-scanSchemaTests = deepScan--deepScan :: FilePath -> IO CaseTree-deepScan = shouldScan . FileUrl []-  where-    shouldScan :: FileUrl -> IO CaseTree-    shouldScan caseUrl = do-      children <- prefixed caseUrl-      pure CaseTree {..}-    isDirectory :: FileUrl -> IO Bool-    isDirectory = doesDirectoryExist . toString-    prefixed :: FileUrl -> IO (Either [FilePath] [CaseTree])-    prefixed p = do-      dir <- isDirectory p-      if dir-        then do-          list <- map (prefix p) <$> listDirectory (toString p)-          areDirectories <- traverse isDirectory list-          if and areDirectories-            then Right <$> traverse shouldScan list-            else pure $ Left []-        else pure $ Left []--maybeVariables :: FieldName -> IO (Maybe Value)-maybeVariables (FieldName x) = decode <$> (L.readFile (path x <> "/variables.json") <|> pure "{}")--getGQLBody :: FieldName -> IO ByteString-getGQLBody (FieldName p) = L.readFile (gqlLib p)--getCases :: FromJSON a => FilePath -> IO [a]-getCases dir = fromMaybe [] . decode <$> L.readFile ("test/" <> dir <> "/cases.json")--getSchema :: FieldName -> IO (Eventless (Schema VALID))-getSchema (FieldName x) = parseGQLDocument <$> L.readFile (path x <> "/schema.gql")--assertValidSchema :: FieldName -> IO (Schema VALID)-assertValidSchema = getSchema >=> resultOr failedSchema pure-  where-    failedSchema err = assertFailure ("unexpected schema validation error: \n " <> show err)--expectedResponse :: FieldName -> IO Value-expectedResponse (FieldName p) = fromMaybe Null . decode <$> L.readFile (resLib p)--getRequest :: FieldName -> IO GQLRequest-getRequest p = do-  queryBS <- LT.toStrict . decodeUtf8 <$> getGQLBody p-  variables <- maybeVariables p-  pure $-    GQLRequest-      { operationName = Nothing,-        query = queryBS,-        variables-      }--getResolvers :: Monad m => FieldName -> IO (RootResModel e m)-getResolvers (FieldName p) = do-  res <- fromMaybe Null . decode <$> L.readFile (path p <> "/resolvers.json")-  pure-    RootResModel-      { query = pure (lookupRes "query" res),-        mutation = pure (lookupRes "mutation" res),-        subscription = pure (lookupRes "subscription" res),-        channelMap = Nothing-      }--lookupRes ::-  ( WithOperation o,-    Monad m-  ) =>-  Text ->-  Value ->-  ResModel o e m-lookupRes name (Object fields) = maybe mkNull mkValue (lookup name fields)-lookupRes _ _ = mkNull
test/Spec.hs view
@@ -13,6 +13,7 @@ import Utils.Api   ( apiTest,   )+import qualified Utils.MergeSchema as MergeSchema import Utils.Schema   ( testSchema,   )@@ -24,10 +25,12 @@ main :: IO () main = do   schema <- testSchema+  mergeSchema <- MergeSchema.test   defaultMain $     testGroup       "core tests"       [ schema,+        mergeSchema,         apiTest "api/deity" ["simple", "interface"],         apiTest           "api/validation/fragment"
test/Utils/Api.hs view
@@ -15,7 +15,7 @@ import Data.Functor ((<$>), fmap) import Data.Functor.Identity (Identity (..)) import Data.Maybe (Maybe (..))-import Data.Morpheus.Core (defaultConfig, runApi)+import Data.Morpheus.Core (mkApp, runAppStream) import Data.Morpheus.Types.IO import Data.Morpheus.Types.Internal.AST   ( FieldName (..),@@ -28,12 +28,6 @@   ) import Data.Semigroup ((<>)) import Data.Text (unpack)-import Lib-  ( assertValidSchema,-    expectedResponse,-    getRequest,-    getResolvers,-  ) import Test.Tasty   ( TestTree,     testGroup,@@ -42,6 +36,12 @@   ( assertFailure,     testCase,   )+import Utils.Utils+  ( assertValidSchema,+    expectedResponse,+    getRequest,+    getResolvers,+  ) import Prelude   ( ($),     (==),@@ -72,6 +72,7 @@   schema <- assertValidSchema apiPath   resolvers <- getResolvers apiPath   let fullPath = apiPath <> "/" <> path-  actual <- runApi schema resolvers defaultConfig <$> getRequest fullPath+  let api = mkApp schema resolvers+  actual <- runAppStream api <$> getRequest fullPath   expected <- expectedResponse fullPath   assertion expected actual
+ test/Utils/MergeSchema.hs view
@@ -0,0 +1,130 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Utils.MergeSchema+  ( test,+  )+where++import Control.Applicative (pure)+import Control.Monad ((>>=))+import Control.Monad.Fail (fail)+import Data.Aeson (decode, encode)+import qualified Data.Aeson as A+import qualified Data.ByteString.Lazy as L (readFile)+import qualified Data.ByteString.Lazy.Char8 as LB (unpack)+import Data.Functor ((<$>), fmap)+import Data.Functor.Identity (Identity (..))+import Data.Morpheus.Core+  ( App (..),+    AppData (..),+    mkApp,+    mkApp,+    parseGQLDocument,+    render,+    runAppStream,+  )+import Data.Morpheus.Types.IO+import Data.Morpheus.Types.Internal.AST+  ( FieldName (..),+    Schema,+    VALID,+    Value,+  )+import Data.Morpheus.Types.Internal.Resolving+  ( ResponseStream,+    ResultT (..),+    resultOr,+  )+import Data.Semigroup ((<>))+import Data.Text (unpack)+import Data.Traversable (traverse)+import Test.Tasty+  ( TestTree,+    testGroup,+  )+import Test.Tasty.HUnit+  ( assertFailure,+    testCase,+  )+import Utils.Utils+  ( expectedResponse,+    getRequest,+    getResolver,+  )+import Prelude+  ( ($),+    (.),+    Eq (..),+    IO,+    Maybe (..),+    otherwise,+    show,+  )++readSchema :: FieldName -> IO (Schema VALID)+readSchema (FieldName p) = L.readFile (unpack p) >>= (resultOr (fail . show) pure . parseGQLDocument)++loadApi :: FieldName -> IO (App () Identity)+loadApi url = do+  schema <- readSchema ("test/" <> url <> ".gql")+  resolvers <- getResolver ("test/" <> url <> ".json")+  pure $ mkApp schema resolvers++schemaAssertion :: App () Identity -> Schema VALID -> IO ()+schemaAssertion (App AppData {appSchema}) expectedSchema+  | render expectedSchema == render appSchema = pure ()+  | otherwise =+    assertFailure+      $ unpack+      $ "expected: \n " <> render expectedSchema <> " \n but got: \n " <> render appSchema+schemaAssertion (FailApp gqlerror) _ = assertFailure $ " error: " <> show gqlerror++schemaCase :: (FieldName, [FieldName]) -> IO TestTree+schemaCase (url, files) = do+  schema <- loadApi (url <> "/api/app")+  extension <- loadApi (url <> "/api/ext")+  let api = schema <> extension+  pure $+    testGroup+      (show url)+      [ testCase+          "Schema"+          (readSchema ("test/" <> url <> "/expected/ok.gql") >>= schemaAssertion api),+        testGroup+          "Requests"+          (fmap (testApiRequest api url) files)+      ]++test :: IO TestTree+test =+  testGroup+    "merge schema"+    <$> traverse+      schemaCase+      [ ("merge/schema/simple-query", ["query"]),+        ("merge/schema/query-subscription-mutation", ["query", "mutation"])+      ]++assertion :: A.Value -> ResponseStream e Identity (Value VALID) -> IO ()+assertion expected (ResultT (Identity actual))+  | Just expected == decode actualValue = pure ()+  | otherwise =+    assertFailure $+      LB.unpack+        ("expected: \n\n " <> encode expected <> " \n\n but got: \n\n " <> actualValue)+  where+    actualValue = encode (renderResponse actual)++testApiRequest ::+  App () Identity ->+  FieldName ->+  FieldName ->+  TestTree+testApiRequest api base path = testCase (unpack $ readName path) $ do+  let fullPath = base <> "/request/" <> path+  actual <- runAppStream api <$> getRequest fullPath+  expected <- expectedResponse fullPath+  assertion expected actual
test/Utils/Schema.hs view
@@ -29,14 +29,14 @@ import Data.Semigroup ((<>)) import Data.Text (pack) import GHC.Generics (Generic)-import Lib+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertFailure, testCase)+import Utils.Utils   ( CaseTree (..),     FileUrl (..),     scanSchemaTests,     toString,   )-import Test.Tasty (TestTree, testGroup)-import Test.Tasty.HUnit (assertFailure, testCase) import Prelude   ( ($),     (.),
+ test/Utils/Utils.hs view
@@ -0,0 +1,181 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Utils.Utils+  ( getGQLBody,+    expectedResponse,+    getCases,+    maybeVariables,+    readSource,+    scanSchemaTests,+    FileUrl (..),+    CaseTree (..),+    toString,+    getRequest,+    assertValidSchema,+    getSchema,+    getResolvers,+    getResolver,+  )+where++import Control.Applicative ((<|>), pure)+import Control.Monad ((>=>), Monad)+import Data.Aeson (FromJSON, Value (..), decode)+import qualified Data.ByteString.Lazy as L (readFile)+import Data.ByteString.Lazy.Char8 (ByteString)+import Data.Either (Either (..))+import Data.Foldable (foldl)+import Data.Functor ((<$>))+import Data.HashMap.Lazy (lookup)+import Data.Maybe (Maybe (..), fromMaybe, maybe)+import Data.Morpheus.Core (parseGQLDocument)+import Data.Morpheus.Types.IO+  ( GQLRequest (..),+  )+import Data.Morpheus.Types.Internal.AST (FieldName (..), Schema (..), VALID)+import Data.Morpheus.Types.Internal.Resolving+  ( Eventless,+    ResModel,+    RootResModel (..),+    WithOperation,+    mkNull,+    mkValue,+    resultOr,+  )+import Data.Semigroup ((<>))+import Data.Text (Text, unpack)+import qualified Data.Text.Lazy as LT (toStrict)+import Data.Text.Lazy.Encoding (decodeUtf8)+import Data.Traversable (traverse)+import System.Directory (doesDirectoryExist, listDirectory)+import Test.Tasty.HUnit+  ( assertFailure,+  )+import Prelude+  ( ($),+    (.),+    Bool,+    FilePath,+    IO,+    Show,+    and,+    map,+    show,+  )++readSource :: FieldName -> IO ByteString+readSource = L.readFile . path . readName++path :: Text -> FilePath+path name = "test/" <> unpack name++gqlLib :: Text -> FilePath+gqlLib x = path x <> "/query.gql"++resLib :: Text -> FilePath+resLib x = path x <> "/response.json"++data FileUrl = FileUrl+  { filePath :: [FilePath],+    fileName :: FilePath+  }+  deriving (Show)++data CaseTree = CaseTree+  { caseUrl :: FileUrl,+    children :: Either [FilePath] [CaseTree]+  }+  deriving (Show)++prefix :: FileUrl -> FilePath -> FileUrl+prefix FileUrl {..} x =+  FileUrl+    { filePath = fileName : filePath,+      fileName = x+    }++toString :: FileUrl -> FilePath+toString FileUrl {..} = foldl (\y x -> x <> "/" <> y) fileName filePath++scanSchemaTests :: FilePath -> IO CaseTree+scanSchemaTests = deepScan++deepScan :: FilePath -> IO CaseTree+deepScan = shouldScan . FileUrl []+  where+    shouldScan :: FileUrl -> IO CaseTree+    shouldScan caseUrl = do+      children <- prefixed caseUrl+      pure CaseTree {..}+    isDirectory :: FileUrl -> IO Bool+    isDirectory = doesDirectoryExist . toString+    prefixed :: FileUrl -> IO (Either [FilePath] [CaseTree])+    prefixed p = do+      dir <- isDirectory p+      if dir+        then do+          list <- map (prefix p) <$> listDirectory (toString p)+          areDirectories <- traverse isDirectory list+          if and areDirectories+            then Right <$> traverse shouldScan list+            else pure $ Left []+        else pure $ Left []++maybeVariables :: FieldName -> IO (Maybe Value)+maybeVariables (FieldName x) = decode <$> (L.readFile (path x <> "/variables.json") <|> pure "{}")++getGQLBody :: FieldName -> IO ByteString+getGQLBody (FieldName p) = L.readFile (gqlLib p)++getCases :: FromJSON a => FilePath -> IO [a]+getCases dir = fromMaybe [] . decode <$> L.readFile ("test/" <> dir <> "/cases.json")++getSchema :: FieldName -> IO (Eventless (Schema VALID))+getSchema (FieldName x) = parseGQLDocument <$> L.readFile (path x <> "/schema.gql")++assertValidSchema :: FieldName -> IO (Schema VALID)+assertValidSchema = getSchema >=> resultOr failedSchema pure+  where+    failedSchema err = assertFailure ("unexpected schema validation error: \n " <> show err)++expectedResponse :: FieldName -> IO Value+expectedResponse (FieldName p) = fromMaybe Null . decode <$> L.readFile (resLib p)++getRequest :: FieldName -> IO GQLRequest+getRequest p = do+  queryBS <- LT.toStrict . decodeUtf8 <$> getGQLBody p+  variables <- maybeVariables p+  pure $+    GQLRequest+      { operationName = Nothing,+        query = queryBS,+        variables+      }++getResolvers :: Monad m => FieldName -> IO (RootResModel e m)+getResolvers p = getResolver ("test/" <> p <> "/resolvers.json")++getResolver :: Monad m => FieldName -> IO (RootResModel e m)+getResolver (FieldName p) = do+  res <- fromMaybe Null . decode <$> L.readFile (unpack p)+  pure+    RootResModel+      { query = pure (lookupRes "query" res),+        mutation = pure (lookupRes "mutation" res),+        subscription = pure (lookupRes "subscription" res),+        channelMap = Nothing+      }++lookupRes ::+  ( WithOperation o,+    Monad m+  ) =>+  Text ->+  Value ->+  ResModel o e m+lookupRes name (Object fields) = maybe mkNull mkValue (lookup name fields)+lookupRes _ _ = mkNull
test/api/validation/fragment/on-interface-inline/response.json view
@@ -2,9 +2,18 @@   "data": {     "deity": { "power": ["Shapeshifting"] },     "character": [-      { "name": "Test Deity" },-      { "name": "Test Hero" },-      { "name": "Test Character" }+      {+        "__typename": "Deity",+        "name": "Test Deity"+      },+      {+        "__typename": "Hero",+        "name": "Test Hero"+      },+      {+        "__typename": "Character",+        "name": "Test Character"+      }     ]   } }
test/api/validation/fragment/on-interface-type-casting-inline/response.json view
@@ -2,14 +2,17 @@   "data": {     "character": [       {+        "__typename": "Deity",         "name": "Test Deity",         "power": ["Show Only on Deity"]       },       {+        "__typename": "Hero",         "name": "Test Hero",         "nickname": "tets Hero NickName"       },       {+        "__typename": "Character",         "name": "Test Character"       }     ]
test/api/validation/fragment/on-interface-type-casting/response.json view
@@ -2,14 +2,17 @@   "data": {     "character": [       {+        "__typename": "Deity",         "name": "Test Deity",         "power": ["Show Only on Deity"]       },       {+        "__typename": "Hero",         "name": "Test Hero",         "nickname": "tets Hero NickName"       },       {+        "__typename": "Character",         "name": "Test Character"       }     ]
test/api/validation/fragment/on-interface/response.json view
@@ -2,9 +2,18 @@   "data": {     "deity": { "power": ["Shapeshifting"] },     "character": [-      { "name": "Test Deity" },-      { "name": "Test Hero" },-      { "name": "Test Character" }+      {+        "__typename": "Deity",+        "name": "Test Deity"+      },+      {+        "__typename": "Hero",+        "name": "Test Hero"+      },+      {+        "__typename": "Character",+        "name": "Test Character"+      }     ]   } }
+ test/merge/schema/query-subscription-mutation/api/app.gql view
@@ -0,0 +1,7 @@+type Query {+  name: String+}++type Mutation {+  name(i: Int): String+}
+ test/merge/schema/query-subscription-mutation/api/app.json view
@@ -0,0 +1,8 @@+{+  "query": {+    "name": "query name from app"+  },+  "mutation": {+    "name": "mutation name from app"+  }+}
+ test/merge/schema/query-subscription-mutation/api/ext.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/api/ext.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/expected/ok.gql view
@@ -0,0 +1,13 @@+type Query {+  name: String+  id: ID+}++type Mutation {+  name(i: Int): String+  id: ID+}++type Subscription {+  name: String+}
+ test/merge/schema/query-subscription-mutation/request/mutation/query.gql view
@@ -0,0 +1,4 @@+mutation {+  name+  id+}
+ test/merge/schema/query-subscription-mutation/request/mutation/response.json view
@@ -0,0 +1,6 @@+{+  "data": {+    "name": "mutation name from app",+    "id": "mutation id from ext"+  }+}
+ test/merge/schema/query-subscription-mutation/request/query/query.gql view
@@ -0,0 +1,4 @@+query {+  name+  id+}
+ test/merge/schema/query-subscription-mutation/request/query/response.json view
@@ -0,0 +1,6 @@+{+  "data": {+    "name": "query name from ext",+    "id": "query id from ext"+  }+}
+ test/merge/schema/simple-query/api/app.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/api/app.json view
@@ -0,0 +1,8 @@+{+  "query": {+    "type": {+      "name": "name from app",+      "age": 23+    }+  }+}
+ test/merge/schema/simple-query/api/ext.gql view
@@ -0,0 +1,9 @@+type Type2 {+  name: String+  id: ID+}++type Query {+  id: ID+  type: Type2+}
+ test/merge/schema/simple-query/api/ext.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/expected/ok.gql view
@@ -0,0 +1,14 @@+type Type2 {+  name: String+  age: Int+  id: ID+}++type Type1 {+  name: String+}++type Query {+  type: Type2+  id: ID+}
+ test/merge/schema/simple-query/request/query/query.gql view
@@ -0,0 +1,8 @@+query {+  id+  type {+    id+    name+    age+  }+}
+ test/merge/schema/simple-query/request/query/response.json view
@@ -0,0 +1,10 @@+{+  "data": {+    "id": "id from ext",+    "type": {+      "id": "id from ext",+      "name": "name from ext",+      "age": 23+    }+  }+}
test/schema/parsing/schema-definition/fail/dupplicate-field/query-mutation-subscription/response.json view
@@ -5,7 +5,19 @@       "locations": []     },     {+      "message": "There can Be only One TypeDefinition for schema.\"query\"",+      "locations": []+    },+    {       "message": "There can Be only One TypeDefinition for schema.\"mutation\"",+      "locations": []+    },+    {+      "message": "There can Be only One TypeDefinition for schema.\"mutation\"",+      "locations": []+    },+    {+      "message": "There can Be only One TypeDefinition for schema.\"subscription\"",       "locations": []     },     {
test/schema/parsing/schema-definition/fail/dupplicate-field/query/response.json view
@@ -7,6 +7,10 @@     {       "message": "There can Be only One TypeDefinition for schema.\"query\"",       "locations": []+    },+    {+      "message": "There can Be only One TypeDefinition for schema.\"query\"",+      "locations": []     }   ] }