morpheus-graphql-core 0.13.0 → 0.14.0
raw patch · 166 files changed
+7196/−4053 lines, 166 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Data.Morpheus.Core: decodeIntrospection :: ByteString -> Eventless Schema
- Data.Morpheus.Core: type EventCon event = (Eq (StreamChannel event), Typeable event, GQLChannel event)
- Data.Morpheus.Error: internalError :: Message -> Eventless a
- Data.Morpheus.Error: internalTypeMismatch :: Message -> ValidValue -> Eventless a
- Data.Morpheus.Internal.TH: applyT :: Name -> [Q Type] -> Q Type
- Data.Morpheus.Internal.TH: instanceFunD :: Name -> [TypeName] -> ExpQ -> Q Dec
- Data.Morpheus.Internal.TH: instanceHeadMultiT :: Name -> Q Type -> [Q Type] -> Q Type
- Data.Morpheus.Internal.TH: instanceHeadT :: Name -> TypeName -> [TypeName] -> Q Type
- Data.Morpheus.Internal.TH: instanceProxyFunD :: (Name, ExpQ) -> DecQ
- Data.Morpheus.Internal.TH: mkFieldName :: FieldName -> Name
- Data.Morpheus.Internal.TH: mkTypeName :: TypeName -> Name
- Data.Morpheus.Internal.TH: nameConE :: TypeName -> ExpQ
- Data.Morpheus.Internal.TH: nameConT :: TypeName -> Q Type
- Data.Morpheus.Internal.TH: nameConType :: TypeName -> Type
- Data.Morpheus.Internal.TH: nameLitP :: TypeName -> PatQ
- Data.Morpheus.Internal.TH: nameStringE :: TypeName -> ExpQ
- Data.Morpheus.Internal.TH: nameStringL :: TypeName -> Lit
- Data.Morpheus.Internal.TH: nameVarE :: FieldName -> ExpQ
- Data.Morpheus.Internal.TH: nameVarP :: FieldName -> PatQ
- Data.Morpheus.Internal.TH: nameVarT :: TypeName -> Q Type
- Data.Morpheus.Internal.TH: typeT :: Name -> [TypeName] -> Q Type
- Data.Morpheus.Internal.Utils: instance (Data.Hashable.Class.Hashable k, Data.Morpheus.Internal.Utils.KeyOf v, k Data.Type.Equality.~ Data.Morpheus.Internal.Utils.KEY v) => Data.Morpheus.Internal.Utils.Collection v (Data.HashMap.Base.HashMap k v)
- Data.Morpheus.Internal.Utils: instance (Data.Morpheus.Internal.Utils.KEY a Data.Type.Equality.~ k, GHC.Classes.Eq k, Data.Hashable.Class.Hashable k) => Data.Morpheus.Internal.Utils.Selectable (Data.HashMap.Base.HashMap k a) a
- Data.Morpheus.Internal.Utils: instance (Text.Megaparsec.Stream.Stream s, GHC.Classes.Ord e, Data.Morpheus.Internal.Utils.Failure [a] m) => Data.Morpheus.Internal.Utils.Failure [a] (Text.Megaparsec.Internal.ParsecT e s m)
- Data.Morpheus.Internal.Utils: instance Data.Morpheus.Internal.Utils.KeyOf Data.Morpheus.Types.Internal.AST.Base.Ref
- Data.Morpheus.Internal.Utils: instance Data.Morpheus.Internal.Utils.KeyOf Data.Morpheus.Types.Internal.AST.Base.TypeNameRef
- Data.Morpheus.Internal.Utils: instance Data.Morpheus.Internal.Utils.KeyOf a => Data.Morpheus.Internal.Utils.Selectable [a] a
- Data.Morpheus.Internal.Utils: type KEY a = FieldName;
- Data.Morpheus.Internal.Utils: type family KEY a :: *;
- Data.Morpheus.Internal.Utils: }
- Data.Morpheus.Types.IO: instance Data.Morpheus.Types.IO.MapAPI Data.ByteString.Internal.ByteString
- Data.Morpheus.Types.IO: instance Data.Morpheus.Types.IO.MapAPI Data.ByteString.Lazy.Internal.ByteString
- Data.Morpheus.Types.IO: instance Data.Morpheus.Types.IO.MapAPI Data.Text.Internal.Lazy.Text
- Data.Morpheus.Types.IO: instance Data.Morpheus.Types.IO.MapAPI Data.Text.Internal.Text
- Data.Morpheus.Types.Internal.AST: FULL_VALIDATION :: VALIDATION_MODE
- Data.Morpheus.Types.Internal.AST: WITHOUT_VARIABLES :: VALIDATION_MODE
- Data.Morpheus.Types.Internal.AST: class FromAny a (k :: TypeCategory)
- Data.Morpheus.Types.Internal.AST: class ToAny a
- Data.Morpheus.Types.Internal.AST: createAlias :: TypeName -> TypeRef
- Data.Morpheus.Types.Internal.AST: createArgument :: FieldName -> ([TypeWrapper], TypeName) -> FieldDefinition IN
- Data.Morpheus.Types.Internal.AST: createEnumType :: TypeName -> [TypeName] -> TypeDefinition a
- Data.Morpheus.Types.Internal.AST: createEnumValue :: TypeName -> DataEnumValue
- Data.Morpheus.Types.Internal.AST: createField :: Maybe (FieldContent TRUE cat) -> FieldName -> ([TypeWrapper], TypeName) -> FieldDefinition cat
- Data.Morpheus.Types.Internal.AST: createInputUnionFields :: TypeName -> [UnionMember IN] -> [FieldDefinition IN]
- Data.Morpheus.Types.Internal.AST: createType :: TypeName -> TypeContent TRUE a -> TypeDefinition a
- Data.Morpheus.Types.Internal.AST: createUnionType :: TypeName -> [TypeName] -> TypeDefinition OUT
- Data.Morpheus.Types.Internal.AST: data OrderedMap k a
- Data.Morpheus.Types.Internal.AST: data VALIDATION_MODE
- Data.Morpheus.Types.Internal.AST: defineType :: TypeDefinition cat -> Schema -> Schema
- Data.Morpheus.Types.Internal.AST: isFieldNullable :: FieldDefinition cat -> Bool
- Data.Morpheus.Types.Internal.AST: isNullableWrapper :: [TypeWrapper] -> Bool
- Data.Morpheus.Types.Internal.AST: isSystemTypeName :: TypeName -> Bool
- Data.Morpheus.Types.Internal.AST: mkField :: FieldName -> ([TypeWrapper], TypeName) -> FieldDefinition cat
- Data.Morpheus.Types.Internal.AST: sysTypes :: [TypeName]
- Data.Morpheus.Types.Internal.AST: toNullableField :: FieldDefinition cat -> FieldDefinition cat
- Data.Morpheus.Types.Internal.AST: type RESOLVED = 'RESOLVED
- Data.Morpheus.Types.Internal.AST: type TypeUpdater = LibUpdater Schema
- Data.Morpheus.Types.Internal.Resolving: Context :: Selection VALID -> Schema -> Operation VALID -> TypeName -> Context
- Data.Morpheus.Types.Internal.Resolving: class GQLChannel a where {
- Data.Morpheus.Types.Internal.Resolving: class MapStrategy (from :: OperationType) (to :: OperationType)
- Data.Morpheus.Types.Internal.Resolving: data Context
- Data.Morpheus.Types.Internal.Resolving: liftStateless :: (LiftOperation o, Monad m) => Eventless a -> Resolver o e m a
- Data.Morpheus.Types.Internal.Resolving: mapStrategy :: (MapStrategy from to, Monad m) => Resolver from e m (ResModel from e m) -> Resolver to e m (ResModel to e m)
- Data.Morpheus.Types.Internal.Resolving: resolveUpdates :: Monad m => lib -> [lib -> m lib] -> m lib
- Data.Morpheus.Types.Internal.Resolving: setTypeName :: Monad m => TypeName -> Resolver o e m a -> Resolver o e m a
- Data.Morpheus.Types.Internal.Resolving: streamChannels :: GQLChannel a => a -> [Channel a]
- Data.Morpheus.Types.Internal.Resolving: toResolver :: forall o e m a b. (LiftOperation o, Monad m) => (Arguments VALID -> Eventless a) -> (a -> Resolver o e m b) -> Resolver o e m b
- Data.Morpheus.Types.Internal.Resolving: type LibUpdater lib = lib -> Eventless lib
- Data.Morpheus.Types.Internal.Resolving: type family StreamChannel a :: *;
- Data.Morpheus.Types.Internal.Resolving: unsafeBind :: forall o e m a b. Monad m => Resolver o e m a -> (a -> Resolver o e m b) -> Resolver o e m b
- Data.Morpheus.Types.Internal.Resolving: }
+ Data.Morpheus.Core: Config :: Bool -> VALIDATION_MODE -> Config
+ Data.Morpheus.Core: FULL_VALIDATION :: VALIDATION_MODE
+ Data.Morpheus.Core: WITHOUT_VARIABLES :: VALIDATION_MODE
+ Data.Morpheus.Core: [debug] :: Config -> Bool
+ Data.Morpheus.Core: [validationMode] :: Config -> VALIDATION_MODE
+ Data.Morpheus.Core: data Config
+ Data.Morpheus.Core: data VALIDATION_MODE
+ Data.Morpheus.Core: debugConfig :: Config
+ Data.Morpheus.Core: defaultConfig :: Config
+ Data.Morpheus.Internal.TH: _' :: PatQ
+ Data.Morpheus.Internal.TH: _2' :: PatQ
+ Data.Morpheus.Internal.TH: applyCons :: (ToName con, ToName cons) => con -> [cons] -> Q Type
+ Data.Morpheus.Internal.TH: applyVars :: (ToName con, ToName var, Apply res, ToCon con res, ToVar var res) => con -> [var] -> res
+ Data.Morpheus.Internal.TH: cat' :: Type
+ Data.Morpheus.Internal.TH: class ToName a
+ Data.Morpheus.Internal.TH: decodeObjectE :: (Bool -> Name) -> TypeName -> [FieldDefinition cat s] -> ExpQ
+ Data.Morpheus.Internal.TH: e' :: Type
+ Data.Morpheus.Internal.TH: funDProxy :: [(Name, ExpQ)] -> [DecQ]
+ Data.Morpheus.Internal.TH: funDSimple :: Name -> [PatQ] -> ExpQ -> DecQ
+ Data.Morpheus.Internal.TH: instance Data.Morpheus.Internal.TH.Apply Language.Haskell.TH.Lib.Internal.ExpQ
+ Data.Morpheus.Internal.TH: instance Data.Morpheus.Internal.TH.Apply Language.Haskell.TH.Lib.Internal.TypeQ
+ Data.Morpheus.Internal.TH: instance Data.Morpheus.Internal.TH.Apply Language.Haskell.TH.Syntax.Exp
+ Data.Morpheus.Internal.TH: instance Data.Morpheus.Internal.TH.Apply Language.Haskell.TH.Syntax.Type
+ Data.Morpheus.Internal.TH: instance Data.Morpheus.Internal.TH.ToCon a b => Data.Morpheus.Internal.TH.ToCon a (Language.Haskell.TH.Syntax.Q b)
+ Data.Morpheus.Internal.TH: instance Data.Morpheus.Internal.TH.ToName Data.Morpheus.Types.Internal.AST.Base.FieldName
+ Data.Morpheus.Internal.TH: instance Data.Morpheus.Internal.TH.ToName Data.Morpheus.Types.Internal.AST.Base.TypeName
+ Data.Morpheus.Internal.TH: instance Data.Morpheus.Internal.TH.ToName Language.Haskell.TH.Syntax.Name
+ Data.Morpheus.Internal.TH: instance Data.Morpheus.Internal.TH.ToName a => Data.Morpheus.Internal.TH.ToCon a Language.Haskell.TH.Syntax.Exp
+ Data.Morpheus.Internal.TH: instance Data.Morpheus.Internal.TH.ToName a => Data.Morpheus.Internal.TH.ToCon a Language.Haskell.TH.Syntax.Type
+ Data.Morpheus.Internal.TH: instance Data.Morpheus.Internal.TH.ToName a => Data.Morpheus.Internal.TH.ToVar a Language.Haskell.TH.Syntax.Exp
+ Data.Morpheus.Internal.TH: instance Data.Morpheus.Internal.TH.ToName a => Data.Morpheus.Internal.TH.ToVar a Language.Haskell.TH.Syntax.Pat
+ Data.Morpheus.Internal.TH: instance Data.Morpheus.Internal.TH.ToName a => Data.Morpheus.Internal.TH.ToVar a Language.Haskell.TH.Syntax.Type
+ Data.Morpheus.Internal.TH: instance Data.Morpheus.Internal.TH.ToString Data.Morpheus.Types.Internal.AST.Base.FieldName Language.Haskell.TH.Syntax.Exp
+ Data.Morpheus.Internal.TH: instance Data.Morpheus.Internal.TH.ToString Data.Morpheus.Types.Internal.AST.Base.FieldName Language.Haskell.TH.Syntax.Lit
+ Data.Morpheus.Internal.TH: instance Data.Morpheus.Internal.TH.ToString Data.Morpheus.Types.Internal.AST.Base.TypeName Language.Haskell.TH.Syntax.Exp
+ Data.Morpheus.Internal.TH: instance Data.Morpheus.Internal.TH.ToString Data.Morpheus.Types.Internal.AST.Base.TypeName Language.Haskell.TH.Syntax.Lit
+ Data.Morpheus.Internal.TH: instance Data.Morpheus.Internal.TH.ToString Data.Morpheus.Types.Internal.AST.Base.TypeName Language.Haskell.TH.Syntax.Pat
+ Data.Morpheus.Internal.TH: instance Data.Morpheus.Internal.TH.ToString a b => Data.Morpheus.Internal.TH.ToString a (Language.Haskell.TH.Syntax.Q b)
+ Data.Morpheus.Internal.TH: instance Data.Morpheus.Internal.TH.ToVar a b => Data.Morpheus.Internal.TH.ToVar a (Language.Haskell.TH.Syntax.Q b)
+ Data.Morpheus.Internal.TH: matchWith :: Bool -> (t -> (PatQ, ExpQ)) -> [t] -> ExpQ
+ Data.Morpheus.Internal.TH: mkFieldsE :: Name -> [FieldDefinition cat s] -> Exp
+ Data.Morpheus.Internal.TH: o' :: Type
+ Data.Morpheus.Internal.TH: toCon :: ToCon a b => a -> b
+ Data.Morpheus.Internal.TH: toConE :: ToCon a Exp => a -> ExpQ
+ Data.Morpheus.Internal.TH: toConT :: ToName a => a -> Q Type
+ Data.Morpheus.Internal.TH: toName :: ToName a => a -> Name
+ Data.Morpheus.Internal.TH: toString :: ToString a b => a -> b
+ Data.Morpheus.Internal.TH: toVarE :: ToVar a Exp => a -> ExpQ
+ Data.Morpheus.Internal.TH: toVarT :: ToVar a TypeQ => a -> TypeQ
+ Data.Morpheus.Internal.TH: v' :: ToVar Name a => a
+ Data.Morpheus.Internal.TH: vars :: ToVar a b => [a] -> [b]
+ Data.Morpheus.Internal.Utils: (<.>) :: SemigroupM m a => a -> a -> m a
+ Data.Morpheus.Internal.Utils: UpdateT :: (a -> m a) -> UpdateT m a
+ Data.Morpheus.Internal.Utils: [updateTState] :: UpdateT m a -> a -> m a
+ Data.Morpheus.Internal.Utils: class SemigroupM m a
+ Data.Morpheus.Internal.Utils: concatUpdates :: Monad m => [UpdateT m a] -> UpdateT m a
+ Data.Morpheus.Internal.Utils: failUpdates :: Failure e m => e -> UpdateT m a
+ Data.Morpheus.Internal.Utils: instance (Data.Morpheus.Error.NameCollision.NameCollision a, Data.Morpheus.Internal.Utils.KeyOf k a) => Data.Morpheus.Internal.Utils.Listable a (Data.HashMap.Internal.HashMap k a)
+ Data.Morpheus.Internal.Utils: instance (Data.Morpheus.Error.NameCollision.NameCollision a, Data.Morpheus.Internal.Utils.KeyOf k a) => Data.Morpheus.Internal.Utils.Merge (Data.HashMap.Internal.HashMap k a)
+ Data.Morpheus.Internal.Utils: instance (GHC.Base.Monad m, Data.Morpheus.Internal.Utils.Failure errors m) => Data.Morpheus.Internal.Utils.Failure errors (Control.Monad.Trans.Reader.ReaderT ctx m)
+ Data.Morpheus.Internal.Utils: instance (GHC.Classes.Eq k, Data.Hashable.Class.Hashable k) => Data.Morpheus.Internal.Utils.KeyOf k (k, a)
+ 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: instance Data.Morpheus.Internal.Utils.KeyOf Data.Morpheus.Types.Internal.AST.Base.FieldName Data.Morpheus.Types.Internal.AST.Base.Ref
+ Data.Morpheus.Internal.Utils: instance Data.Morpheus.Internal.Utils.KeyOf Data.Morpheus.Types.Internal.AST.Base.TypeName Data.Morpheus.Types.Internal.AST.Base.TypeNameRef
+ Data.Morpheus.Internal.Utils: instance Data.Morpheus.Internal.Utils.KeyOf k a => Data.Morpheus.Internal.Utils.Selectable k a (Data.HashMap.Internal.HashMap k a)
+ Data.Morpheus.Internal.Utils: instance Data.Morpheus.Internal.Utils.KeyOf k a => Data.Morpheus.Internal.Utils.Selectable k a [a]
+ Data.Morpheus.Internal.Utils: instance Data.Morpheus.Internal.Utils.KeyOf k v => Data.Morpheus.Internal.Utils.Collection v (Data.HashMap.Internal.HashMap k v)
+ Data.Morpheus.Internal.Utils: mergeM :: SemigroupM m a => [Ref] -> a -> a -> m a
+ Data.Morpheus.Internal.Utils: newtype UpdateT m a
+ Data.Morpheus.Internal.Utils: ordTraverse :: (Monad f, KeyOf k b, Listable a (t a), Listable b (t b), Failure ValidationErrors f) => (a -> f b) -> t a -> f (t b)
+ Data.Morpheus.Internal.Utils: ordTraverse_ :: (Monad f, Listable a (t a)) => (a -> f b) -> t a -> f ()
+ Data.Morpheus.Internal.Utils: resolveUpdates :: Monad m => a -> [UpdateT m a] -> m a
+ Data.Morpheus.Internal.Utils: traverseCollection :: (Monad f, KeyOf k b, Listable a (t a), Listable b (t' b), Failure ValidationErrors f) => (a -> f b) -> t a -> f (t' b)
+ Data.Morpheus.Types.ID: instance GHC.Classes.Eq Data.Morpheus.Types.ID.ID
+ Data.Morpheus.Types.IO: instance Data.Morpheus.Types.IO.MapAPI Data.ByteString.Internal.ByteString Data.ByteString.Internal.ByteString
+ Data.Morpheus.Types.IO: instance Data.Morpheus.Types.IO.MapAPI Data.ByteString.Lazy.Internal.ByteString Data.ByteString.Lazy.Internal.ByteString
+ Data.Morpheus.Types.IO: instance Data.Morpheus.Types.IO.MapAPI Data.Morpheus.Types.IO.GQLRequest Data.Morpheus.Types.IO.GQLResponse
+ Data.Morpheus.Types.IO: instance Data.Morpheus.Types.IO.MapAPI Data.Text.Internal.Lazy.Text Data.Text.Internal.Lazy.Text
+ Data.Morpheus.Types.IO: instance Data.Morpheus.Types.IO.MapAPI Data.Text.Internal.Text Data.Text.Internal.Text
+ Data.Morpheus.Types.Internal.AST: InternalError :: Text -> InternalError
+ Data.Morpheus.Types.Internal.AST: RawDirectiveDefinition :: DirectiveDefinition CONST -> RawTypeDefinition
+ Data.Morpheus.Types.Internal.AST: RawSchemaDefinition :: SchemaDefinition -> RawTypeDefinition
+ Data.Morpheus.Types.Internal.AST: RawTypeDefinition :: TypeDefinition ANY CONST -> RawTypeDefinition
+ Data.Morpheus.Types.Internal.AST: RootOperationTypeDefinition :: OperationType -> TypeName -> RootOperationTypeDefinition
+ Data.Morpheus.Types.Internal.AST: SchemaDefinition :: Directives CONST -> OrdMap OperationType RootOperationTypeDefinition -> SchemaDefinition
+ Data.Morpheus.Types.Internal.AST: Typed :: a -> Typed a
+ Data.Morpheus.Types.Internal.AST: ValidationError :: Message -> [Position] -> ValidationError
+ Data.Morpheus.Types.Internal.AST: [directiveDefinitions] :: Schema -> [DirectiveDefinition s]
+ Data.Morpheus.Types.Internal.AST: [readInternalError] :: InternalError -> Text
+ Data.Morpheus.Types.Internal.AST: [rootOperationTypeDefinitionName] :: RootOperationTypeDefinition -> TypeName
+ Data.Morpheus.Types.Internal.AST: [rootOperationType] :: RootOperationTypeDefinition -> OperationType
+ Data.Morpheus.Types.Internal.AST: [schemaDirectives] :: SchemaDefinition -> Directives CONST
+ Data.Morpheus.Types.Internal.AST: [unSchemaDefinition] :: SchemaDefinition -> OrdMap OperationType RootOperationTypeDefinition
+ Data.Morpheus.Types.Internal.AST: [validationLocations] :: ValidationError -> [Position]
+ Data.Morpheus.Types.Internal.AST: [validationMessage] :: ValidationError -> Message
+ Data.Morpheus.Types.Internal.AST: buildSchema :: (Monad m, Failure ValidationErrors m) => (Maybe SchemaDefinition, [TypeDefinition ANY s], [DirectiveDefinition s]) -> m (Schema s)
+ Data.Morpheus.Types.Internal.AST: class FromCategory a (k :: TypeCategory) (k' :: TypeCategory)
+ Data.Morpheus.Types.Internal.AST: class ToCategory a (k :: TypeCategory) (k' :: TypeCategory)
+ Data.Morpheus.Types.Internal.AST: data OrdMap k a
+ Data.Morpheus.Types.Internal.AST: data RawTypeDefinition
+ Data.Morpheus.Types.Internal.AST: data RootOperationTypeDefinition
+ Data.Morpheus.Types.Internal.AST: data SchemaDefinition
+ Data.Morpheus.Types.Internal.AST: data ValidationError
+ Data.Morpheus.Types.Internal.AST: fromCategory :: FromCategory a k k' => a k (s :: Stage) -> Maybe (a k' s)
+ Data.Morpheus.Types.Internal.AST: isNotSystemTypeName :: TypeName -> Bool
+ Data.Morpheus.Types.Internal.AST: mkEnumContent :: ELEM LEAF a ~ TRUE => [TypeName] -> TypeContent TRUE a s
+ Data.Morpheus.Types.Internal.AST: mkInputUnionFields :: TypeName -> [UnionMember IN s] -> FieldsDefinition IN s
+ Data.Morpheus.Types.Internal.AST: mkInputValue :: FieldName -> [TypeWrapper] -> TypeName -> FieldDefinition cat s
+ Data.Morpheus.Types.Internal.AST: mkType :: TypeName -> TypeContent TRUE a s -> TypeDefinition a s
+ Data.Morpheus.Types.Internal.AST: mkTypeRef :: TypeName -> TypeRef
+ Data.Morpheus.Types.Internal.AST: mkUnionContent :: [TypeName] -> TypeContent TRUE OUT s
+ Data.Morpheus.Types.Internal.AST: msgInternal :: Msg a => a -> InternalError
+ Data.Morpheus.Types.Internal.AST: msgValidation :: Msg a => a -> ValidationError
+ Data.Morpheus.Types.Internal.AST: newtype InternalError
+ Data.Morpheus.Types.Internal.AST: newtype Typed (cat :: TypeCategory) (s :: Stage) a
+ Data.Morpheus.Types.Internal.AST: possibleInterfaceTypes :: TypeName -> Schema s -> [TypeDefinition ANY s]
+ Data.Morpheus.Types.Internal.AST: possibleTypes :: TypeDefinition a s -> Schema s' -> [TypeName]
+ Data.Morpheus.Types.Internal.AST: toCategory :: ToCategory a k k' => a k (s :: Stage) -> a k' s
+ Data.Morpheus.Types.Internal.AST: toGQLError :: ValidationError -> GQLError
+ Data.Morpheus.Types.Internal.AST: toNullable :: Nullable a => a -> a
+ Data.Morpheus.Types.Internal.AST: type CONST = 'CONST
+ Data.Morpheus.Types.Internal.AST: type IMPLEMENTABLE = 'IMPLEMENTABLE
+ Data.Morpheus.Types.Internal.AST: type LEAF = 'LEAF
+ Data.Morpheus.Types.Internal.AST: type OBJECT = 'OBJECT
+ Data.Morpheus.Types.Internal.AST: type REQURE_IMPLEMENTABLE cat = ELEM cat IMPLEMENTABLE ~ TRUE
+ Data.Morpheus.Types.Internal.AST: type UnionSelection (s :: Stage) = MergeSet s UnionTag
+ Data.Morpheus.Types.Internal.AST: type ValidationErrors = [ValidationError]
+ Data.Morpheus.Types.Internal.AST: type family ELEM (elemKind :: TypeCategory) (setOfKind :: TypeCategory) :: Bool
+ Data.Morpheus.Types.Internal.AST: typed :: (a c s -> b) -> a c s -> Typed c s b
+ Data.Morpheus.Types.Internal.AST: untyped :: (a -> b) -> Typed c s a -> b
+ Data.Morpheus.Types.Internal.AST: withPosition :: Maybe Position -> ValidationError -> ValidationError
+ Data.Morpheus.Types.Internal.Resolving: ResolverContext :: Selection VALID -> Schema VALID -> Operation VALID -> TypeName -> Config -> ResolverContext
+ Data.Morpheus.Types.Internal.Resolving: [Channel] :: a -> Channel (Event a c)
+ Data.Morpheus.Types.Internal.Resolving: [SubscriptionField] :: {channel :: forall e m v. a ~ Resolver SUBSCRIPTION e m v => Channel e, unSubscribe :: a} -> SubscriptionField a
+ Data.Morpheus.Types.Internal.Resolving: [channelMap] :: RootResModel e m -> Maybe (Selection VALID -> ResolverState (Channel e))
+ Data.Morpheus.Types.Internal.Resolving: [config] :: ResolverContext -> Config
+ Data.Morpheus.Types.Internal.Resolving: data Channel (event :: *)
+ Data.Morpheus.Types.Internal.Resolving: data ResolverContext
+ Data.Morpheus.Types.Internal.Resolving: data SubscriptionField (a :: *)
+ Data.Morpheus.Types.Internal.Resolving: eventChannels :: Event e c -> [Channel (Event e c)]
+ Data.Morpheus.Types.Internal.Resolving: getArguments :: (LiftOperation o, Monad m) => Resolver o e m (Arguments VALID)
+ Data.Morpheus.Types.Internal.Resolving: liftResolverState :: (LiftOperation o, Monad m) => ResolverState a -> Resolver o e m a
+ Data.Morpheus.Types.Internal.Resolving: mkValue :: (LiftOperation o, Monad m) => Value -> ResModel o e m
+ Data.Morpheus.Types.Internal.Resolving: type ResolverState = ResolverStateT () Identity
+ Data.Morpheus.Types.SelectionTree: class SelectionTree nodeType
+ Data.Morpheus.Types.SelectionTree: getChildrenList :: SelectionTree nodeType => nodeType -> [nodeType]
+ Data.Morpheus.Types.SelectionTree: getName :: (SelectionTree nodeType, IsString name) => nodeType -> name
+ Data.Morpheus.Types.SelectionTree: instance Data.Morpheus.Types.SelectionTree.SelectionTree (Data.Morpheus.Types.Internal.AST.Selection.Selection Data.Morpheus.Types.Internal.AST.Stage.VALID)
+ Data.Morpheus.Types.SelectionTree: isLeaf :: SelectionTree nodeType => nodeType -> Bool
- Data.Morpheus.Core: getName :: SelectionTree nodeType => nodeType -> FieldName
+ Data.Morpheus.Core: getName :: (SelectionTree nodeType, IsString name) => nodeType -> name
- Data.Morpheus.Core: parseDSL :: ByteString -> Either String Schema
+ Data.Morpheus.Core: parseDSL :: ByteString -> Either String (Schema VALID)
- Data.Morpheus.Core: parseFullGQLDocument :: ByteString -> Eventless Schema
+ Data.Morpheus.Core: parseFullGQLDocument :: ByteString -> Eventless (Schema VALID)
- Data.Morpheus.Core: parseGQLDocument :: ByteString -> Eventless Schema
+ Data.Morpheus.Core: parseGQLDocument :: ByteString -> Eventless (Schema VALID)
- Data.Morpheus.Core: parseRequestWith :: Schema -> GQLRequest -> Eventless (Operation VALID)
+ Data.Morpheus.Core: parseRequestWith :: Config -> Schema VALID -> GQLRequest -> Eventless (Operation VALID)
- Data.Morpheus.Core: parseTypeDefinitions :: Text -> Eventless [TypeDefinition ANY]
+ Data.Morpheus.Core: parseTypeDefinitions :: Text -> Eventless [TypeDefinition ANY CONST]
- Data.Morpheus.Core: parseTypeSystemDefinition :: Text -> Eventless Schema
+ Data.Morpheus.Core: parseTypeSystemDefinition :: Text -> Eventless (Schema VALID)
- Data.Morpheus.Core: runApi :: forall event m. Monad m => Schema -> RootResModel event m -> GQLRequest -> ResponseStream event m (Value VALID)
+ 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.Core: validateRequest :: Schema -> VALIDATION_MODE -> GQLQuery -> Eventless (Operation VALID)
+ Data.Morpheus.Core: validateRequest :: Config -> Schema VALID -> GQLQuery -> Eventless (Operation VALID)
- Data.Morpheus.Core: validateSchema :: Schema -> Eventless Schema
+ Data.Morpheus.Core: validateSchema :: ValidateSchema s => Bool -> Config -> Schema s -> Eventless (Schema VALID)
- Data.Morpheus.Internal.TH: apply :: Name -> [Q Exp] -> Q Exp
+ Data.Morpheus.Internal.TH: apply :: (Apply a, ToCon i a) => i -> [a] -> a
- Data.Morpheus.Internal.TH: declareTypeRef :: Bool -> TypeRef -> Type
+ Data.Morpheus.Internal.TH: declareTypeRef :: TypeRef -> Type
- Data.Morpheus.Internal.TH: destructRecord :: TypeName -> [FieldName] -> PatQ
+ Data.Morpheus.Internal.TH: destructRecord :: TypeName -> [FieldDefinition cat s] -> PatQ
- Data.Morpheus.Internal.TH: isEnum :: [ConsD cat] -> Bool
+ Data.Morpheus.Internal.TH: isEnum :: [ConsD cat s] -> Bool
- Data.Morpheus.Internal.Utils: (<:>) :: (Monad m, Merge a, Failure GQLErrors m) => a -> a -> m a
+ Data.Morpheus.Internal.Utils: (<:>) :: (Monad m, Merge a, Failure ValidationErrors m) => a -> a -> m a
- Data.Morpheus.Internal.Utils: class Eq (KEY a) => KeyOf a where {
+ Data.Morpheus.Internal.Utils: class (Eq k, Hashable k) => KeyOf k a | a -> k
- Data.Morpheus.Internal.Utils: class Selectable c a | c -> a
+ Data.Morpheus.Internal.Utils: class Selectable k a c | c -> a
- Data.Morpheus.Internal.Utils: fromElems :: (Listable a coll, KeyOf a, Monad m, Failure GQLErrors m) => [a] -> m coll
+ Data.Morpheus.Internal.Utils: fromElems :: (Listable a coll, Monad m, Failure ValidationErrors m) => [a] -> m coll
- Data.Morpheus.Internal.Utils: keyOf :: KeyOf a => a -> KEY a
+ Data.Morpheus.Internal.Utils: keyOf :: KeyOf k a => a -> k
- Data.Morpheus.Internal.Utils: keys :: (KeyOf a, Listable a coll) => coll -> [KEY a]
+ Data.Morpheus.Internal.Utils: keys :: (KeyOf k a, Listable a coll) => coll -> [k]
- Data.Morpheus.Internal.Utils: member :: forall a c. Selectable c a => KEY a -> c -> Bool
+ Data.Morpheus.Internal.Utils: member :: Selectable k a c => k -> c -> Bool
- Data.Morpheus.Internal.Utils: merge :: (Merge a, Monad m, Failure GQLErrors m) => [Ref] -> a -> a -> m a
+ Data.Morpheus.Internal.Utils: merge :: (Merge a, Monad m, Failure ValidationErrors m) => [Ref] -> a -> a -> m a
- Data.Morpheus.Internal.Utils: selectBy :: (Failure e m, Selectable c a, Monad m) => e -> KEY a -> c -> m a
+ Data.Morpheus.Internal.Utils: selectBy :: (Failure e m, Selectable k a c, Monad m) => e -> k -> c -> m a
- Data.Morpheus.Internal.Utils: selectOr :: Selectable c a => d -> (a -> d) -> KEY a -> c -> d
+ Data.Morpheus.Internal.Utils: selectOr :: Selectable k a c => d -> (a -> d) -> k -> c -> d
- Data.Morpheus.Internal.Utils: toPair :: KeyOf a => a -> (KEY a, a)
+ Data.Morpheus.Internal.Utils: toPair :: KeyOf k a => a -> (k, a)
- Data.Morpheus.Types.IO: class MapAPI a
+ Data.Morpheus.Types.IO: class MapAPI a b
- Data.Morpheus.Types.IO: mapAPI :: (MapAPI a, Applicative m) => (GQLRequest -> m GQLResponse) -> a -> m a
+ Data.Morpheus.Types.IO: mapAPI :: (MapAPI a b, Applicative m) => (GQLRequest -> m GQLResponse) -> a -> m b
- Data.Morpheus.Types.Internal.AST: Argument :: FieldName -> Value valid -> Position -> Argument
+ Data.Morpheus.Types.Internal.AST: Argument :: FieldName -> Position -> Value valid -> Argument
- Data.Morpheus.Types.Internal.AST: ArgumentsDefinition :: Maybe TypeName -> OrderedMap FieldName ArgumentDefinition -> ArgumentsDefinition
+ Data.Morpheus.Types.Internal.AST: ArgumentsDefinition :: Maybe TypeName -> OrdMap FieldName (ArgumentDefinition s) -> ArgumentsDefinition s
- Data.Morpheus.Types.Internal.AST: ConsD :: TypeName -> [FieldDefinition cat] -> ConsD cat
+ Data.Morpheus.Types.Internal.AST: ConsD :: TypeName -> [FieldDefinition cat s] -> ConsD cat s
- Data.Morpheus.Types.Internal.AST: DataEnumValue :: TypeName -> Maybe Description -> [Directive VALID] -> DataEnumValue
+ Data.Morpheus.Types.Internal.AST: DataEnumValue :: TypeName -> Maybe Description -> [Directive s] -> DataEnumValue s
- Data.Morpheus.Types.Internal.AST: DirectiveDefinition :: FieldName -> Maybe Description -> [DirectiveLocation] -> ArgumentsDefinition -> DirectiveDefinition
+ Data.Morpheus.Types.Internal.AST: DirectiveDefinition :: FieldName -> Maybe Description -> [DirectiveLocation] -> ArgumentsDefinition s -> DirectiveDefinition s
- Data.Morpheus.Types.Internal.AST: FieldDefinition :: FieldName -> Maybe Description -> TypeRef -> Maybe (FieldContent TRUE cat) -> [Directive VALID] -> FieldDefinition
+ Data.Morpheus.Types.Internal.AST: FieldDefinition :: FieldName -> Maybe Description -> TypeRef -> [Directive s] -> Maybe (FieldContent TRUE cat s) -> FieldDefinition
- Data.Morpheus.Types.Internal.AST: Fields :: OrderedMap FieldName def -> Fields def
+ Data.Morpheus.Types.Internal.AST: Fields :: OrdMap FieldName def -> Fields def
- Data.Morpheus.Types.Internal.AST: Fragment :: FieldName -> TypeName -> Position -> SelectionSet RAW -> Directives RAW -> Fragment
+ Data.Morpheus.Types.Internal.AST: Fragment :: FieldName -> TypeName -> Position -> SelectionSet stage -> Directives stage -> Fragment
- Data.Morpheus.Types.Internal.AST: GQLQuery :: Fragments -> Operation RAW -> [(FieldName, ResolvedValue)] -> GQLQuery
+ Data.Morpheus.Types.Internal.AST: GQLQuery :: Fragments RAW -> Operation RAW -> [(FieldName, ResolvedValue)] -> GQLQuery
- Data.Morpheus.Types.Internal.AST: ScalarDefinition :: (ValidValue -> Either Token ValidValue) -> ScalarDefinition
+ Data.Morpheus.Types.Internal.AST: ScalarDefinition :: (Value VALID -> Either Token (Value VALID)) -> ScalarDefinition
- Data.Morpheus.Types.Internal.AST: Schema :: TypeLib -> TypeDefinition 'Out -> Maybe (TypeDefinition 'Out) -> Maybe (TypeDefinition 'Out) -> Schema
+ Data.Morpheus.Types.Internal.AST: Schema :: TypeLib s -> TypeDefinition OBJECT s -> Maybe (TypeDefinition OBJECT s) -> Maybe (TypeDefinition OBJECT s) -> [DirectiveDefinition s] -> Schema
- Data.Morpheus.Types.Internal.AST: TypeDefinition :: TypeName -> DataFingerprint -> Maybe Description -> Directives VALID -> TypeContent TRUE a -> TypeDefinition
+ Data.Morpheus.Types.Internal.AST: TypeDefinition :: TypeName -> DataFingerprint -> Maybe Description -> Directives s -> TypeContent TRUE a s -> TypeDefinition
- Data.Morpheus.Types.Internal.AST: Variable :: FieldName -> TypeRef -> Position -> VariableContent (VAR stage) -> Variable
+ Data.Morpheus.Types.Internal.AST: Variable :: FieldName -> TypeRef -> Position -> VariableContent (CONST_OR_VALID stage) -> Variable
- Data.Morpheus.Types.Internal.AST: [$sel:fragments:GQLQuery] :: GQLQuery -> Fragments
+ Data.Morpheus.Types.Internal.AST: [$sel:fragments:GQLQuery] :: GQLQuery -> Fragments RAW
- Data.Morpheus.Types.Internal.AST: [DataEnum] :: {enumMembers :: DataEnum} -> TypeContent TRUE a
+ Data.Morpheus.Types.Internal.AST: [DataEnum] :: {enumMembers :: DataEnum s} -> TypeContent (ELEM LEAF a) a s
- Data.Morpheus.Types.Internal.AST: [DataInputObject] :: {inputObjectFields :: FieldsDefinition IN} -> TypeContent (IsSelected a IN) a
+ Data.Morpheus.Types.Internal.AST: [DataInputObject] :: {inputObjectFields :: FieldsDefinition IN s} -> TypeContent (ELEM IN a) a s
- Data.Morpheus.Types.Internal.AST: [DataInputUnion] :: {inputUnionMembers :: DataInputUnion} -> TypeContent (IsSelected a IN) a
+ Data.Morpheus.Types.Internal.AST: [DataInputUnion] :: {inputUnionMembers :: DataInputUnion s} -> TypeContent (ELEM IN a) a s
- Data.Morpheus.Types.Internal.AST: [DataInterface] :: {interfaceFields :: FieldsDefinition OUT} -> TypeContent (IsSelected a OUT) a
+ Data.Morpheus.Types.Internal.AST: [DataInterface] :: {interfaceFields :: FieldsDefinition OUT s} -> TypeContent (ELEM IMPLEMENTABLE a) a s
- Data.Morpheus.Types.Internal.AST: [DataObject] :: {objectImplements :: [TypeName], objectFields :: FieldsDefinition OUT} -> TypeContent (IsSelected a OUT) a
+ Data.Morpheus.Types.Internal.AST: [DataObject] :: {objectImplements :: [TypeName], objectFields :: FieldsDefinition OUT s} -> TypeContent (ELEM OBJECT a) a s
- Data.Morpheus.Types.Internal.AST: [DataScalar] :: {dataScalar :: ScalarDefinition} -> TypeContent TRUE a
+ Data.Morpheus.Types.Internal.AST: [DataScalar] :: {dataScalar :: ScalarDefinition} -> TypeContent (ELEM LEAF a) a s
- Data.Morpheus.Types.Internal.AST: [DataUnion] :: {unionMembers :: DataUnion} -> TypeContent (IsSelected a OUT) a
+ Data.Morpheus.Types.Internal.AST: [DataUnion] :: {unionMembers :: DataUnion s} -> TypeContent (ELEM OUT a) a s
- Data.Morpheus.Types.Internal.AST: [DefaultInputValue] :: {defaultInputValue :: Value RESOLVED} -> FieldContent (IsSelected cat IN) cat
+ Data.Morpheus.Types.Internal.AST: [DefaultInputValue] :: {defaultInputValue :: Value s} -> FieldContent (ELEM IN cat) cat s
- Data.Morpheus.Types.Internal.AST: [DefaultValue] :: Maybe ResolvedValue -> VariableContent RESOLVED
+ Data.Morpheus.Types.Internal.AST: [DefaultValue] :: Maybe ResolvedValue -> VariableContent CONST
- Data.Morpheus.Types.Internal.AST: [FieldArgs] :: {fieldArgsDef :: ArgumentsDefinition} -> FieldContent (IsSelected cat OUT) cat
+ Data.Morpheus.Types.Internal.AST: [FieldArgs] :: {fieldArgsDef :: ArgumentsDefinition s} -> FieldContent (ELEM OUT cat) cat s
- Data.Morpheus.Types.Internal.AST: [InlineFragment] :: Fragment -> Selection RAW
+ Data.Morpheus.Types.Internal.AST: [InlineFragment] :: Fragment RAW -> Selection RAW
- Data.Morpheus.Types.Internal.AST: [ResolvedVariable] :: Ref -> Variable VALID -> Value RESOLVED
+ Data.Morpheus.Types.Internal.AST: [ResolvedVariable] :: Ref -> Variable VALID -> Value CONST
- Data.Morpheus.Types.Internal.AST: [argumentsTypename] :: ArgumentsDefinition -> Maybe TypeName
+ Data.Morpheus.Types.Internal.AST: [argumentsTypename] :: ArgumentsDefinition s -> Maybe TypeName
- Data.Morpheus.Types.Internal.AST: [arguments] :: ArgumentsDefinition -> OrderedMap FieldName ArgumentDefinition
+ Data.Morpheus.Types.Internal.AST: [arguments] :: ArgumentsDefinition s -> OrdMap FieldName (ArgumentDefinition s)
- Data.Morpheus.Types.Internal.AST: [cFields] :: ConsD cat -> [FieldDefinition cat]
+ Data.Morpheus.Types.Internal.AST: [cFields] :: ConsD cat s -> [FieldDefinition cat s]
- Data.Morpheus.Types.Internal.AST: [cName] :: ConsD cat -> TypeName
+ Data.Morpheus.Types.Internal.AST: [cName] :: ConsD cat s -> TypeName
- Data.Morpheus.Types.Internal.AST: [directiveDefinitionArgs] :: DirectiveDefinition -> ArgumentsDefinition
+ Data.Morpheus.Types.Internal.AST: [directiveDefinitionArgs] :: DirectiveDefinition s -> ArgumentsDefinition s
- Data.Morpheus.Types.Internal.AST: [directiveDefinitionDescription] :: DirectiveDefinition -> Maybe Description
+ Data.Morpheus.Types.Internal.AST: [directiveDefinitionDescription] :: DirectiveDefinition s -> Maybe Description
- Data.Morpheus.Types.Internal.AST: [directiveDefinitionLocations] :: DirectiveDefinition -> [DirectiveLocation]
+ Data.Morpheus.Types.Internal.AST: [directiveDefinitionLocations] :: DirectiveDefinition s -> [DirectiveLocation]
- Data.Morpheus.Types.Internal.AST: [directiveDefinitionName] :: DirectiveDefinition -> FieldName
+ Data.Morpheus.Types.Internal.AST: [directiveDefinitionName] :: DirectiveDefinition s -> FieldName
- Data.Morpheus.Types.Internal.AST: [enumDescription] :: DataEnumValue -> Maybe Description
+ Data.Morpheus.Types.Internal.AST: [enumDescription] :: DataEnumValue s -> Maybe Description
- Data.Morpheus.Types.Internal.AST: [enumDirectives] :: DataEnumValue -> [Directive VALID]
+ Data.Morpheus.Types.Internal.AST: [enumDirectives] :: DataEnumValue s -> [Directive s]
- Data.Morpheus.Types.Internal.AST: [enumName] :: DataEnumValue -> TypeName
+ Data.Morpheus.Types.Internal.AST: [enumName] :: DataEnumValue s -> TypeName
- Data.Morpheus.Types.Internal.AST: [fieldContent] :: FieldDefinition -> Maybe (FieldContent TRUE cat)
+ Data.Morpheus.Types.Internal.AST: [fieldContent] :: FieldDefinition -> Maybe (FieldContent TRUE cat s)
- Data.Morpheus.Types.Internal.AST: [fieldDirectives] :: FieldDefinition -> [Directive VALID]
+ Data.Morpheus.Types.Internal.AST: [fieldDirectives] :: FieldDefinition -> [Directive s]
- Data.Morpheus.Types.Internal.AST: [fragmentDirectives] :: Fragment -> Directives RAW
+ Data.Morpheus.Types.Internal.AST: [fragmentDirectives] :: Fragment -> Directives stage
- Data.Morpheus.Types.Internal.AST: [fragmentSelection] :: Fragment -> SelectionSet RAW
+ Data.Morpheus.Types.Internal.AST: [fragmentSelection] :: Fragment -> SelectionSet stage
- Data.Morpheus.Types.Internal.AST: [mutation] :: Schema -> Maybe (TypeDefinition 'Out)
+ Data.Morpheus.Types.Internal.AST: [mutation] :: Schema -> Maybe (TypeDefinition OBJECT s)
- Data.Morpheus.Types.Internal.AST: [query] :: Schema -> TypeDefinition 'Out
+ Data.Morpheus.Types.Internal.AST: [query] :: Schema -> TypeDefinition OBJECT s
- Data.Morpheus.Types.Internal.AST: [subscription] :: Schema -> Maybe (TypeDefinition 'Out)
+ Data.Morpheus.Types.Internal.AST: [subscription] :: Schema -> Maybe (TypeDefinition OBJECT s)
- Data.Morpheus.Types.Internal.AST: [typeContent] :: TypeDefinition -> TypeContent TRUE a
+ Data.Morpheus.Types.Internal.AST: [typeContent] :: TypeDefinition -> TypeContent TRUE a s
- Data.Morpheus.Types.Internal.AST: [typeDirectives] :: TypeDefinition -> Directives VALID
+ Data.Morpheus.Types.Internal.AST: [typeDirectives] :: TypeDefinition -> Directives s
- Data.Morpheus.Types.Internal.AST: [types] :: Schema -> TypeLib
+ Data.Morpheus.Types.Internal.AST: [types] :: Schema -> TypeLib s
- Data.Morpheus.Types.Internal.AST: [unFields] :: Fields def -> OrderedMap FieldName def
+ Data.Morpheus.Types.Internal.AST: [unFields] :: Fields def -> OrdMap FieldName def
- Data.Morpheus.Types.Internal.AST: [validateValue] :: ScalarDefinition -> ValidValue -> Either Token ValidValue
+ Data.Morpheus.Types.Internal.AST: [validateValue] :: ScalarDefinition -> Value VALID -> Either Token (Value VALID)
- Data.Morpheus.Types.Internal.AST: [variableValue] :: Variable -> VariableContent (VAR stage)
+ Data.Morpheus.Types.Internal.AST: [variableValue] :: Variable -> VariableContent (CONST_OR_VALID stage)
- Data.Morpheus.Types.Internal.AST: createScalarType :: TypeName -> TypeDefinition a
+ Data.Morpheus.Types.Internal.AST: createScalarType :: ELEM LEAF a ~ TRUE => TypeName -> TypeDefinition a s
- Data.Morpheus.Types.Internal.AST: data ArgumentsDefinition
+ Data.Morpheus.Types.Internal.AST: data ArgumentsDefinition s
- Data.Morpheus.Types.Internal.AST: data ConsD cat
+ Data.Morpheus.Types.Internal.AST: data ConsD cat s
- Data.Morpheus.Types.Internal.AST: data DataEnumValue
+ Data.Morpheus.Types.Internal.AST: data DataEnumValue s
- Data.Morpheus.Types.Internal.AST: data DirectiveDefinition
+ Data.Morpheus.Types.Internal.AST: data DirectiveDefinition s
- Data.Morpheus.Types.Internal.AST: data FieldContent (bool :: Bool) (cat :: TypeCategory)
+ Data.Morpheus.Types.Internal.AST: data FieldContent (bool :: Bool) (cat :: TypeCategory) (s :: Stage)
- Data.Morpheus.Types.Internal.AST: data FieldDefinition (cat :: TypeCategory)
+ Data.Morpheus.Types.Internal.AST: data FieldDefinition (cat :: TypeCategory) (s :: Stage)
- Data.Morpheus.Types.Internal.AST: data Fragment
+ Data.Morpheus.Types.Internal.AST: data Fragment (stage :: Stage)
- Data.Morpheus.Types.Internal.AST: data Schema
+ Data.Morpheus.Types.Internal.AST: data Schema (s :: Stage)
- Data.Morpheus.Types.Internal.AST: data TypeContent (b :: Bool) (a :: TypeCategory)
+ Data.Morpheus.Types.Internal.AST: data TypeContent (b :: Bool) (a :: TypeCategory) (s :: Stage)
- Data.Morpheus.Types.Internal.AST: data TypeDefinition (a :: TypeCategory)
+ Data.Morpheus.Types.Internal.AST: data TypeDefinition (a :: TypeCategory) (s :: Stage)
- Data.Morpheus.Types.Internal.AST: data UnionMember (cat :: TypeCategory)
+ Data.Morpheus.Types.Internal.AST: data UnionMember (cat :: TypeCategory) (s :: Stage)
- Data.Morpheus.Types.Internal.AST: fieldContentArgs :: FieldContent b cat -> OrderedMap FieldName ArgumentDefinition
+ Data.Morpheus.Types.Internal.AST: fieldContentArgs :: FieldContent b cat s -> ArgumentsDefinition s
- Data.Morpheus.Types.Internal.AST: fieldVisibility :: FieldDefinition cat -> Bool
+ Data.Morpheus.Types.Internal.AST: fieldVisibility :: FieldDefinition cat s -> Bool
- Data.Morpheus.Types.Internal.AST: fieldsToArguments :: FieldsDefinition IN -> ArgumentsDefinition
+ Data.Morpheus.Types.Internal.AST: fieldsToArguments :: FieldsDefinition IN s -> ArgumentsDefinition s
- Data.Morpheus.Types.Internal.AST: fromAny :: FromAny a k => a ANY -> Maybe (a k)
+ Data.Morpheus.Types.Internal.AST: fromAny :: FromCategory a ANY k => a ANY (s :: Stage) -> Maybe (a k s)
- Data.Morpheus.Types.Internal.AST: getOperationDataType :: Failure GQLErrors m => Operation a -> Schema -> m (TypeDefinition OUT)
+ Data.Morpheus.Types.Internal.AST: getOperationDataType :: Failure ValidationError m => Operation s -> Schema VALID -> m (TypeDefinition OBJECT VALID)
- Data.Morpheus.Types.Internal.AST: initTypeLib :: TypeDefinition 'Out -> Schema
+ Data.Morpheus.Types.Internal.AST: initTypeLib :: TypeDefinition OBJECT s -> Schema s
- Data.Morpheus.Types.Internal.AST: insertType :: TypeDefinition ANY -> TypeUpdater
+ Data.Morpheus.Types.Internal.AST: insertType :: (Monad m, Failure ValidationErrors m) => TypeDefinition cat s -> UpdateT m (Schema s)
- Data.Morpheus.Types.Internal.AST: isEntNode :: TypeContent TRUE a -> Bool
+ Data.Morpheus.Types.Internal.AST: isEntNode :: TypeContent TRUE a s -> Bool
- Data.Morpheus.Types.Internal.AST: isEnum :: [ConsD cat] -> Bool
+ Data.Morpheus.Types.Internal.AST: isEnum :: [ConsD cat s] -> Bool
- Data.Morpheus.Types.Internal.AST: isNullable :: TypeRef -> Bool
+ Data.Morpheus.Types.Internal.AST: isNullable :: Nullable a => a -> Bool
- Data.Morpheus.Types.Internal.AST: kindOf :: TypeDefinition a -> TypeKind
+ Data.Morpheus.Types.Internal.AST: kindOf :: TypeDefinition a s -> TypeKind
- Data.Morpheus.Types.Internal.AST: lookupDeprecated :: [Directive VALID] -> Maybe (Directive VALID)
+ Data.Morpheus.Types.Internal.AST: lookupDeprecated :: [Directive s] -> Maybe (Directive s)
- Data.Morpheus.Types.Internal.AST: lookupDeprecatedReason :: Directive VALID -> Maybe Description
+ Data.Morpheus.Types.Internal.AST: lookupDeprecatedReason :: Directive s -> Maybe Description
- Data.Morpheus.Types.Internal.AST: mkCons :: TypeName -> FieldsDefinition cat -> ConsD cat
+ Data.Morpheus.Types.Internal.AST: mkCons :: TypeName -> FieldsDefinition cat s -> ConsD cat s
- Data.Morpheus.Types.Internal.AST: mkConsEnum :: DataEnumValue -> ConsD cat
+ Data.Morpheus.Types.Internal.AST: mkConsEnum :: DataEnumValue s -> ConsD cat s
- Data.Morpheus.Types.Internal.AST: mkObjectField :: ArgumentsDefinition -> FieldName -> ([TypeWrapper], TypeName) -> FieldDefinition OUT
+ Data.Morpheus.Types.Internal.AST: mkObjectField :: ArgumentsDefinition s -> FieldName -> [TypeWrapper] -> TypeName -> FieldDefinition OUT s
- Data.Morpheus.Types.Internal.AST: mkUnionMember :: TypeName -> UnionMember cat
+ Data.Morpheus.Types.Internal.AST: mkUnionMember :: TypeName -> UnionMember cat s
- Data.Morpheus.Types.Internal.AST: toAny :: ToAny a => a (k :: TypeCategory) -> a ANY
+ Data.Morpheus.Types.Internal.AST: toAny :: ToCategory a k ANY => a (k :: TypeCategory) (s :: Stage) -> a ANY s
- Data.Morpheus.Types.Internal.AST: toListField :: FieldDefinition cat -> FieldDefinition cat
+ Data.Morpheus.Types.Internal.AST: toListField :: FieldDefinition cat s -> FieldDefinition cat s
- Data.Morpheus.Types.Internal.AST: type ANY = 'Any
+ Data.Morpheus.Types.Internal.AST: type ANY = 'ANY
- Data.Morpheus.Types.Internal.AST: type Arguments s = OrderedMap FieldName (Argument s)
+ Data.Morpheus.Types.Internal.AST: type Arguments (s :: Stage) = OrdMap FieldName (Argument s)
- Data.Morpheus.Types.Internal.AST: type DataEnum = [DataEnumValue]
+ Data.Morpheus.Types.Internal.AST: type DataEnum s = [DataEnumValue s]
- Data.Morpheus.Types.Internal.AST: type DataInputUnion = [UnionMember IN]
+ Data.Morpheus.Types.Internal.AST: type DataInputUnion s = [UnionMember IN s]
- Data.Morpheus.Types.Internal.AST: type DataUnion = [UnionMember OUT]
+ Data.Morpheus.Types.Internal.AST: type DataUnion s = [UnionMember OUT s]
- Data.Morpheus.Types.Internal.AST: type DirectiveDefinitions = [DirectiveDefinition]
+ Data.Morpheus.Types.Internal.AST: type DirectiveDefinitions s = [DirectiveDefinition s]
- Data.Morpheus.Types.Internal.AST: type FieldsDefinition cat = Fields (FieldDefinition cat)
+ Data.Morpheus.Types.Internal.AST: type FieldsDefinition cat s = Fields (FieldDefinition cat s)
- Data.Morpheus.Types.Internal.AST: type Fragments = OrderedMap FieldName Fragment
+ Data.Morpheus.Types.Internal.AST: type Fragments (s :: Stage) = OrdMap FieldName (Fragment s)
- Data.Morpheus.Types.Internal.AST: type IN = 'In
+ Data.Morpheus.Types.Internal.AST: type IN = 'IN
- Data.Morpheus.Types.Internal.AST: type InputFieldsDefinition = Fields InputValueDefinition
+ Data.Morpheus.Types.Internal.AST: type InputFieldsDefinition s = Fields (InputValueDefinition s)
- Data.Morpheus.Types.Internal.AST: type OUT = 'Out
+ Data.Morpheus.Types.Internal.AST: type OUT = 'OUT
- Data.Morpheus.Types.Internal.AST: type Object a = OrderedMap FieldName (ObjectEntry a)
+ Data.Morpheus.Types.Internal.AST: type Object a = OrdMap FieldName (ObjectEntry a)
- Data.Morpheus.Types.Internal.AST: type ResolvedObject = Object RESOLVED
+ Data.Morpheus.Types.Internal.AST: type ResolvedObject = Object CONST
- Data.Morpheus.Types.Internal.AST: type ResolvedValue = Value RESOLVED
+ Data.Morpheus.Types.Internal.AST: type ResolvedValue = Value CONST
- Data.Morpheus.Types.Internal.AST: type TypeLib = HashMap TypeName (TypeDefinition ANY)
+ Data.Morpheus.Types.Internal.AST: type TypeLib s = SafeHashMap TypeName (TypeDefinition ANY s)
- Data.Morpheus.Types.Internal.AST: type VariableDefinitions s = OrderedMap FieldName (Variable s)
+ Data.Morpheus.Types.Internal.AST: type VariableDefinitions s = OrdMap FieldName (Variable s)
- Data.Morpheus.Types.Internal.AST: unsafeFromFields :: [FieldDefinition cat] -> FieldsDefinition cat
+ Data.Morpheus.Types.Internal.AST: unsafeFromFields :: [FieldDefinition cat s] -> FieldsDefinition cat s
- Data.Morpheus.Types.Internal.AST: updateSchema :: TypeName -> DataFingerprint -> [TypeUpdater] -> (a -> TypeDefinition cat) -> a -> TypeUpdater
+ 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.Types.Internal.Resolving: RootResModel :: Eventless (ResModel QUERY e m) -> Eventless (ResModel MUTATION e m) -> Eventless (ResModel SUBSCRIPTION e m) -> RootResModel e m
+ Data.Morpheus.Types.Internal.Resolving: RootResModel :: ResolverState (ResModel QUERY e m) -> ResolverState (ResModel MUTATION e m) -> ResolverState (ResModel SUBSCRIPTION e m) -> Maybe (Selection VALID -> ResolverState (Channel e)) -> RootResModel e m
- Data.Morpheus.Types.Internal.Resolving: [currentSelection] :: Context -> Selection VALID
+ Data.Morpheus.Types.Internal.Resolving: [currentSelection] :: ResolverContext -> Selection VALID
- Data.Morpheus.Types.Internal.Resolving: [currentTypeName] :: Context -> TypeName
+ Data.Morpheus.Types.Internal.Resolving: [currentTypeName] :: ResolverContext -> TypeName
- Data.Morpheus.Types.Internal.Resolving: [mutation] :: RootResModel e m -> Eventless (ResModel MUTATION e m)
+ Data.Morpheus.Types.Internal.Resolving: [mutation] :: RootResModel e m -> ResolverState (ResModel MUTATION e m)
- Data.Morpheus.Types.Internal.Resolving: [operation] :: Context -> Operation VALID
+ Data.Morpheus.Types.Internal.Resolving: [operation] :: ResolverContext -> Operation VALID
- Data.Morpheus.Types.Internal.Resolving: [query] :: RootResModel e m -> Eventless (ResModel QUERY e m)
+ Data.Morpheus.Types.Internal.Resolving: [query] :: RootResModel e m -> ResolverState (ResModel QUERY e m)
- Data.Morpheus.Types.Internal.Resolving: [schema] :: Context -> Schema
+ Data.Morpheus.Types.Internal.Resolving: [schema] :: ResolverContext -> Schema VALID
- Data.Morpheus.Types.Internal.Resolving: [subscription] :: RootResModel e m -> Eventless (ResModel SUBSCRIPTION e m)
+ Data.Morpheus.Types.Internal.Resolving: [subscription] :: RootResModel e m -> ResolverState (ResModel SUBSCRIPTION e m)
- Data.Morpheus.Types.Internal.Resolving: runRootResModel :: Monad m => RootResModel e m -> Context -> ResponseStream e m (Value VALID)
+ Data.Morpheus.Types.Internal.Resolving: runRootResModel :: Monad m => RootResModel e m -> ResolverContext -> ResponseStream e m (Value VALID)
- Data.Morpheus.Types.Internal.Resolving: subscribe :: forall e m a. (PushEvents (Channel e) (ResolverState (Channel e) m), Monad m) => [StreamChannel e] -> Resolver QUERY e m (e -> Resolver QUERY e m a) -> Resolver SUBSCRIPTION e m a
+ Data.Morpheus.Types.Internal.Resolving: subscribe :: forall e channel cont m a. (Monad m, Event channel cont ~ e) => channel -> Resolver QUERY e m (e -> Resolver SUBSCRIPTION e m a) -> SubscriptionField (Resolver SUBSCRIPTION e m a)
- Data.Morpheus.Types.Internal.Resolving: unsafeInternalContext :: (Monad m, LiftOperation o) => Resolver o e m Context
+ Data.Morpheus.Types.Internal.Resolving: unsafeInternalContext :: (Monad m, LiftOperation o) => Resolver o e m ResolverContext
- Data.Morpheus.Types.Internal.Resolving: withArguments :: forall o e m a. (LiftOperation o, Monad m) => (Arguments VALID -> Resolver o e m a) -> Resolver o e m a
+ Data.Morpheus.Types.Internal.Resolving: withArguments :: (LiftOperation o, Monad m) => (Arguments VALID -> Resolver o e m a) -> Resolver o e m a
Files
- README.md +4/−7
- changelog.md +80/−1
- morpheus-graphql-core.cabal +91/−15
- src/Data/Morpheus/Core.hs +57/−48
- src/Data/Morpheus/Error.hs +0/−3
- src/Data/Morpheus/Error/Document/Interface.hs +18/−33
- src/Data/Morpheus/Error/Fragment.hs +12/−9
- src/Data/Morpheus/Error/Input.hs +8/−7
- src/Data/Morpheus/Error/Internal.hs +0/−37
- src/Data/Morpheus/Error/NameCollision.hs +4/−3
- src/Data/Morpheus/Error/Operation.hs +11/−7
- src/Data/Morpheus/Error/Schema.hs +9/−11
- src/Data/Morpheus/Error/Selection.hs +11/−9
- src/Data/Morpheus/Error/Utils.hs +8/−0
- src/Data/Morpheus/Error/Variable.hs +10/−8
- src/Data/Morpheus/Error/Warning.hs +9/−0
- src/Data/Morpheus/Internal/Graph.hs +62/−0
- src/Data/Morpheus/Internal/TH.hs +265/−90
- src/Data/Morpheus/Internal/Utils.hs +167/−27
- src/Data/Morpheus/Parser.hs +20/−11
- src/Data/Morpheus/Parsing/Document/TypeSystem.hs +204/−28
- src/Data/Morpheus/Parsing/Internal/Pattern.hs +74/−13
- src/Data/Morpheus/Parsing/Internal/Terms.hs +142/−113
- src/Data/Morpheus/Parsing/Internal/Value.hs +17/−35
- src/Data/Morpheus/Parsing/JSONSchema/Parse.hs +0/−116
- src/Data/Morpheus/Parsing/JSONSchema/Types.hs +0/−86
- src/Data/Morpheus/Parsing/Request/Operation.hs +4/−18
- src/Data/Morpheus/Parsing/Request/Parser.hs +2/−2
- src/Data/Morpheus/Parsing/Request/Selection.hs +2/−2
- src/Data/Morpheus/QuasiQuoter.hs +2/−2
- src/Data/Morpheus/Rendering/RenderGQL.hs +57/−6
- src/Data/Morpheus/Rendering/RenderIntrospection.hs +94/−139
- src/Data/Morpheus/Schema/DSL.hs +7/−7
- src/Data/Morpheus/Schema/Directives.hs +0/−43
- src/Data/Morpheus/Schema/Schema.hs +35/−49
- src/Data/Morpheus/Schema/SchemaAPI.hs +20/−25
- src/Data/Morpheus/Schema/TypeKind.hs +0/−22
- src/Data/Morpheus/Types/GQLScalar.hs +15/−2
- src/Data/Morpheus/Types/ID.hs +12/−4
- src/Data/Morpheus/Types/IO.hs +19/−7
- src/Data/Morpheus/Types/Internal/AST.hs +45/−24
- src/Data/Morpheus/Types/Internal/AST/Base.hs +169/−56
- src/Data/Morpheus/Types/Internal/AST/DirectiveLocation.hs +6/−1
- src/Data/Morpheus/Types/Internal/AST/Fields.hs +368/−0
- src/Data/Morpheus/Types/Internal/AST/MergeSet.hs +40/−26
- src/Data/Morpheus/Types/Internal/AST/OrdMap.hs +100/−0
- src/Data/Morpheus/Types/Internal/AST/OrderedMap.hs +0/−122
- src/Data/Morpheus/Types/Internal/AST/SafeHashMap.hs +86/−0
- src/Data/Morpheus/Types/Internal/AST/Selection.hs +112/−56
- src/Data/Morpheus/Types/Internal/AST/Stage.hs +31/−0
- src/Data/Morpheus/Types/Internal/AST/TH.hs +20/−9
- src/Data/Morpheus/Types/Internal/AST/TypeCategory.hs +133/−0
- src/Data/Morpheus/Types/Internal/AST/TypeSystem.hs +466/−513
- src/Data/Morpheus/Types/Internal/AST/Value.hs +56/−42
- src/Data/Morpheus/Types/Internal/Config.hs +40/−0
- src/Data/Morpheus/Types/Internal/Resolving.hs +57/−12
- src/Data/Morpheus/Types/Internal/Resolving/Core.hs +14/−64
- src/Data/Morpheus/Types/Internal/Resolving/Event.hs +32/−0
- src/Data/Morpheus/Types/Internal/Resolving/Resolver.hs +175/−295
- src/Data/Morpheus/Types/Internal/Resolving/ResolverState.hs +202/−0
- src/Data/Morpheus/Types/Internal/Validation.hs +110/−201
- src/Data/Morpheus/Types/Internal/Validation/Error.hs +90/−144
- src/Data/Morpheus/Types/Internal/Validation/Internal.hs +184/−0
- src/Data/Morpheus/Types/Internal/Validation/SchemaValidator.hs +20/−45
- src/Data/Morpheus/Types/Internal/Validation/Validator.hs +256/−153
- src/Data/Morpheus/Types/SelectionTree.hs +26/−6
- src/Data/Morpheus/Validation/Document/Validation.hs +257/−100
- src/Data/Morpheus/Validation/Internal/Arguments.hs +179/−0
- src/Data/Morpheus/Validation/Internal/Directive.hs +75/−31
- src/Data/Morpheus/Validation/Internal/Value.hs +204/−223
- src/Data/Morpheus/Validation/Query/Arguments.hs +0/−147
- src/Data/Morpheus/Validation/Query/Fragment.hs +83/−102
- src/Data/Morpheus/Validation/Query/FragmentPreconditions.hs +108/−0
- src/Data/Morpheus/Validation/Query/Selection.hs +147/−115
- src/Data/Morpheus/Validation/Query/UnionSelection.hs +114/−60
- src/Data/Morpheus/Validation/Query/Validation.hs +57/−17
- src/Data/Morpheus/Validation/Query/Variable.hs +38/−17
- test/Lib.hs +100/−18
- test/Schema.hs +0/−93
- test/Spec.hs +25/−136
- test/Utils/Api.hs +77/−0
- test/Utils/Schema.hs +106/−0
- test/api/deity/interface/query.gql +79/−0
- test/api/deity/interface/response.json +67/−0
- test/api/deity/resolvers.json +8/−0
- test/api/deity/schema.gql +20/−0
- test/api/deity/simple/query.gql +6/−0
- test/api/deity/simple/response.json +8/−0
- test/api/interface/query.gql +0/−79
- test/api/interface/response.json +0/−65
- test/api/simple/query.gql +0/−6
- test/api/simple/response.json +0/−6
- test/api/validation/fragment/fail-unknown-field-on-interface/query.gql +16/−0
- test/api/validation/fragment/fail-unknown-field-on-interface/response.json +12/−0
- test/api/validation/fragment/on-interface-fail-without-casting/query.gql +7/−0
- test/api/validation/fragment/on-interface-fail-without-casting/response.json +22/−0
- test/api/validation/fragment/on-interface-inline/query.gql +12/−0
- test/api/validation/fragment/on-interface-inline/response.json +10/−0
- test/api/validation/fragment/on-interface-type-casting-inline/query.gql +13/−0
- test/api/validation/fragment/on-interface-type-casting-inline/response.json +17/−0
- test/api/validation/fragment/on-interface-type-casting/query.gql +19/−0
- test/api/validation/fragment/on-interface-type-casting/response.json +17/−0
- test/api/validation/fragment/on-interface/query.gql +16/−0
- test/api/validation/fragment/on-interface/response.json +10/−0
- test/api/validation/fragment/on-type/query.gql +9/−0
- test/api/validation/fragment/on-type/response.json +7/−0
- test/api/validation/fragment/on-union-type/query.gql +9/−0
- test/api/validation/fragment/on-union-type/response.json +5/−0
- test/api/validation/fragment/resolvers.json +29/−0
- test/api/validation/fragment/schema.gql +25/−0
- test/schema/parsing/directive/ok/simple/response.json +1/−0
- test/schema/parsing/directive/ok/simple/schema.gql +5/−0
- test/schema/parsing/directive/ok/sophisticated/response.json +1/−0
- test/schema/parsing/directive/ok/sophisticated/schema.gql +17/−0
- test/schema/parsing/schema-definition/fail/dupplicate-field/query-mutation-subscription/response.json +20/−0
- test/schema/parsing/schema-definition/fail/dupplicate-field/query-mutation-subscription/schema.gql +21/−0
- test/schema/parsing/schema-definition/fail/dupplicate-field/query/response.json +12/−0
- test/schema/parsing/schema-definition/fail/dupplicate-field/query/schema.gql +9/−0
- test/schema/parsing/schema-definition/fail/dupplicate-schema-definition/response.json +6/−0
- test/schema/parsing/schema-definition/fail/dupplicate-schema-definition/schema.gql +29/−0
- test/schema/parsing/schema-definition/fail/unknown-fields/bla/response.json +8/−0
- test/schema/parsing/schema-definition/fail/unknown-fields/bla/schema.gql +9/−0
- test/schema/parsing/schema-definition/fail/unknown-fields/uppercase/response.json +8/−0
- test/schema/parsing/schema-definition/fail/unknown-fields/uppercase/schema.gql +7/−0
- test/schema/parsing/schema-definition/ok/query-mutation-subscription/response.json +1/−0
- test/schema/parsing/schema-definition/ok/query-mutation-subscription/schema.gql +17/−0
- test/schema/parsing/schema-definition/ok/query-mutation/response.json +1/−0
- test/schema/parsing/schema-definition/ok/query-mutation/schema.gql +12/−0
- test/schema/parsing/schema-definition/ok/query/response.json +1/−0
- test/schema/parsing/schema-definition/ok/query/schema.gql +12/−0
- test/schema/validation/default-value/argument/missing-field/response.json +2/−2
- test/schema/validation/default-value/argument/unexpected-value/response.json +7/−7
- test/schema/validation/default-value/argument/unknown-field/response.json +2/−2
- test/schema/validation/default-value/field/missing-field/response.json +2/−2
- test/schema/validation/default-value/field/unexpected-value/response.json +16/−8
- test/schema/validation/default-value/field/unknown-field/response.json +3/−3
- test/schema/validation/directive/fail/at-invalid-place/response.json +36/−0
- test/schema/validation/directive/fail/at-invalid-place/schema.gql +23/−0
- test/schema/validation/directive/fail/missing-argument/response.json +48/−0
- test/schema/validation/directive/fail/missing-argument/schema.gql +25/−0
- test/schema/validation/directive/fail/sopthisticated/response.json +64/−0
- test/schema/validation/directive/fail/sopthisticated/schema.gql +19/−0
- test/schema/validation/directive/fail/unexpected-value/response.json +36/−0
- test/schema/validation/directive/fail/unexpected-value/schema.gql +26/−0
- test/schema/validation/directive/fail/unknown-directive/response.json +56/−0
- test/schema/validation/directive/fail/unknown-directive/schema.gql +24/−0
- test/schema/validation/directive/ok/deprecated/custom-directive/response.json +1/−0
- test/schema/validation/directive/ok/deprecated/custom-directive/schema.gql +25/−0
- test/schema/validation/directive/ok/deprecated/response.json +1/−0
- test/schema/validation/directive/ok/deprecated/schema.gql +15/−0
- test/schema/validation/schema-definition/fail/non-object-kind/from-schema/response.json +16/−0
- test/schema/validation/schema-definition/fail/non-object-kind/from-schema/schema.gql +19/−0
- test/schema/validation/schema-definition/fail/non-object-kind/without-schema/response.json +16/−0
- test/schema/validation/schema-definition/fail/non-object-kind/without-schema/schema.gql +13/−0
- test/schema/validation/schema-definition/fail/required query/empty/response.json +8/−0
- test/schema/validation/schema-definition/fail/required query/empty/schema.gql +5/−0
- test/schema/validation/schema-definition/fail/required query/no-query/response.json +8/−0
- test/schema/validation/schema-definition/fail/required query/no-query/schema.gql +7/−0
- test/schema/validation/schema-definition/fail/required query/no-schema-no-query/response.json +8/−0
- test/schema/validation/schema-definition/fail/required query/no-schema-no-query/schema.gql +3/−0
- test/schema/validation/schema-definition/fail/required query/schema-with-query/response.json +8/−0
- test/schema/validation/schema-definition/fail/required query/schema-with-query/schema.gql +11/−0
- test/schema/validation/schema-definition/fail/unknown-type/unknown/response.json +16/−0
- test/schema/validation/schema-definition/fail/unknown-type/unknown/schema.gql +9/−0
- test/schema/validation/schema-definition/ok/full/response.json +1/−0
- test/schema/validation/schema-definition/ok/full/schema.gql +17/−0
README.md view
@@ -9,10 +9,9 @@ ## Build GraphQL api with Core ```hs-getSchema :: Monad m => ResponseStream e m Schema-getSchema =- fromList- [dsl|+schema :: Schema VALID+schema =+ [dsl| type Query { deity(name: String): Deity! }@@ -45,7 +44,5 @@ ] api :: GQLRequest -> ResponseStream e Identity (Value VALID)-api request = do- schema <- getSchema- runApi schema resolver request+api = runApi schema resolver ```
changelog.md view
@@ -1,6 +1,85 @@ # Changelog -## 0.13.0 - Unreleased Changes+## 0.14.0 - 15.08.2020++### new features++- query validation supports interfaces+- exposed: `Data.Morpheus.Types.SelectionTree`+- configurable api: `Data.Morpheus.Core` exports++ - `Config`+ - `defaultConfig`+ - `debugConfig`++- for better debuging, internal errors messages will display resolving state:+ - `current TypeName`+ - `current Selection`+ - `OperationDefinition`+ - `SchemaDefinition`+- rendering graphql "AST". e.g `render ( slection :: Selection VALID)` will render++```graphql+{+ user(arg1: 1) {+ name+ }+}+```++- quasiqouter `[dsl| <type definitions> |]` generates `Schema VALID`.+- parser supports custom directive definition. e.g++```graphql+directive @MyDirective on FIELD_DEFINITION | OBJECT+```++- directive Validation for Document (TypeSystem).+- supports of block string values. e.g:++ ```graphql+ query {+ createDeity(+ name: """+ powerqwe+ bla \n sd+ blu \\ dete+ """+ ) {+ name+ }+ }+ ```++- support of `schema`. issue #412++ ```graphql+ schema {+ query: MyQuery+ }+ ```++### Breaking Changes++- `Context' renamed to`ResolverContext'+- removed : `EventCon` from `Data.Morpheus.Core`+- internal refactoring: changed AST.+ Schema AST Types now need parameter `stage = RAW | CONST | VALID`.+ - `Schema VALID`+ - `TypeDefinition VALID`+ - `FieldDefinition IN VALID`+ - ...+- runApi requires argument config+ ```hs+ runApi ::+ Schema s ->+ RootResModel event m ->+ Config ->+ GQLRequest ->+ ResponseStream event m (Value VALID)+ ```++## 0.13.0 - 22.06.2020 ### new features
morpheus-graphql-core.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: b13e1668b9269ab28dfdd66f04b342a5bdbec82e6ba59c98c0467604df7270a6+-- hash: b767cd6b24899f096032a5231c2edb2e7fb1e5d3c29e1a8b775ecbb1ca730ebd name: morpheus-graphql-core-version: 0.13.0+version: 0.14.0 synopsis: Morpheus GraphQL Core description: Build GraphQL APIs with your favourite functional language! category: web, graphql@@ -23,8 +23,28 @@ changelog.md README.md data-files:- test/api/interface/query.gql- test/api/simple/query.gql+ test/api/deity/interface/query.gql+ test/api/deity/schema.gql+ test/api/deity/simple/query.gql+ test/api/validation/fragment/fail-unknown-field-on-interface/query.gql+ test/api/validation/fragment/on-interface-fail-without-casting/query.gql+ test/api/validation/fragment/on-interface-inline/query.gql+ test/api/validation/fragment/on-interface-type-casting-inline/query.gql+ test/api/validation/fragment/on-interface-type-casting/query.gql+ test/api/validation/fragment/on-interface/query.gql+ test/api/validation/fragment/on-type/query.gql+ test/api/validation/fragment/on-union-type/query.gql+ test/api/validation/fragment/schema.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+ test/schema/parsing/schema-definition/fail/dupplicate-field/query/schema.gql+ test/schema/parsing/schema-definition/fail/dupplicate-schema-definition/schema.gql+ test/schema/parsing/schema-definition/fail/unknown-fields/bla/schema.gql+ test/schema/parsing/schema-definition/fail/unknown-fields/uppercase/schema.gql+ test/schema/parsing/schema-definition/ok/query-mutation-subscription/schema.gql+ test/schema/parsing/schema-definition/ok/query-mutation/schema.gql+ test/schema/parsing/schema-definition/ok/query/schema.gql test/schema/validation/default-value/argument/compound-ok/schema.gql test/schema/validation/default-value/argument/missing-field/schema.gql test/schema/validation/default-value/argument/unexpected-value/schema.gql@@ -33,12 +53,47 @@ test/schema/validation/default-value/field/missing-field/schema.gql test/schema/validation/default-value/field/unexpected-value/schema.gql test/schema/validation/default-value/field/unknown-field/schema.gql+ test/schema/validation/directive/fail/at-invalid-place/schema.gql+ test/schema/validation/directive/fail/missing-argument/schema.gql+ test/schema/validation/directive/fail/sopthisticated/schema.gql+ test/schema/validation/directive/fail/unexpected-value/schema.gql+ test/schema/validation/directive/fail/unknown-directive/schema.gql+ test/schema/validation/directive/ok/deprecated/custom-directive/schema.gql+ test/schema/validation/directive/ok/deprecated/schema.gql test/schema/validation/interface/field-args/fail/schema.gql test/schema/validation/interface/field-args/ok/schema.gql test/schema/validation/interface/field-type/fail/schema.gql test/schema/validation/interface/field-type/ok/schema.gql- test/api/interface/response.json- test/api/simple/response.json+ test/schema/validation/schema-definition/fail/non-object-kind/from-schema/schema.gql+ test/schema/validation/schema-definition/fail/non-object-kind/without-schema/schema.gql+ "test/schema/validation/schema-definition/fail/required query/empty/schema.gql"+ "test/schema/validation/schema-definition/fail/required query/no-query/schema.gql"+ "test/schema/validation/schema-definition/fail/required query/no-schema-no-query/schema.gql"+ "test/schema/validation/schema-definition/fail/required query/schema-with-query/schema.gql"+ test/schema/validation/schema-definition/fail/unknown-type/unknown/schema.gql+ test/schema/validation/schema-definition/ok/full/schema.gql+ test/api/deity/interface/response.json+ test/api/deity/resolvers.json+ test/api/deity/simple/response.json+ test/api/validation/fragment/fail-unknown-field-on-interface/response.json+ test/api/validation/fragment/on-interface-fail-without-casting/response.json+ test/api/validation/fragment/on-interface-inline/response.json+ test/api/validation/fragment/on-interface-type-casting-inline/response.json+ test/api/validation/fragment/on-interface-type-casting/response.json+ test/api/validation/fragment/on-interface/response.json+ test/api/validation/fragment/on-type/response.json+ test/api/validation/fragment/on-union-type/response.json+ test/api/validation/fragment/resolvers.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+ test/schema/parsing/schema-definition/fail/dupplicate-field/query/response.json+ test/schema/parsing/schema-definition/fail/dupplicate-schema-definition/response.json+ test/schema/parsing/schema-definition/fail/unknown-fields/bla/response.json+ test/schema/parsing/schema-definition/fail/unknown-fields/uppercase/response.json+ test/schema/parsing/schema-definition/ok/query-mutation-subscription/response.json+ test/schema/parsing/schema-definition/ok/query-mutation/response.json+ test/schema/parsing/schema-definition/ok/query/response.json test/schema/validation/default-value/argument/compound-ok/response.json test/schema/validation/default-value/argument/missing-field/response.json test/schema/validation/default-value/argument/unexpected-value/response.json@@ -47,10 +102,25 @@ test/schema/validation/default-value/field/missing-field/response.json test/schema/validation/default-value/field/unexpected-value/response.json test/schema/validation/default-value/field/unknown-field/response.json+ test/schema/validation/directive/fail/at-invalid-place/response.json+ test/schema/validation/directive/fail/missing-argument/response.json+ test/schema/validation/directive/fail/sopthisticated/response.json+ test/schema/validation/directive/fail/unexpected-value/response.json+ test/schema/validation/directive/fail/unknown-directive/response.json+ test/schema/validation/directive/ok/deprecated/custom-directive/response.json+ test/schema/validation/directive/ok/deprecated/response.json test/schema/validation/interface/field-args/fail/response.json test/schema/validation/interface/field-args/ok/response.json test/schema/validation/interface/field-type/fail/response.json test/schema/validation/interface/field-type/ok/response.json+ test/schema/validation/schema-definition/fail/non-object-kind/from-schema/response.json+ test/schema/validation/schema-definition/fail/non-object-kind/without-schema/response.json+ "test/schema/validation/schema-definition/fail/required query/empty/response.json"+ "test/schema/validation/schema-definition/fail/required query/no-query/response.json"+ "test/schema/validation/schema-definition/fail/required query/no-schema-no-query/response.json"+ "test/schema/validation/schema-definition/fail/required query/schema-with-query/response.json"+ test/schema/validation/schema-definition/fail/unknown-type/unknown/response.json+ test/schema/validation/schema-definition/ok/full/response.json source-repository head type: git@@ -68,11 +138,11 @@ Data.Morpheus.Types.Internal.Resolving Data.Morpheus.Types.GQLScalar Data.Morpheus.Types.ID+ Data.Morpheus.Types.SelectionTree other-modules: Data.Morpheus.Error.Document.Interface Data.Morpheus.Error.Fragment Data.Morpheus.Error.Input- Data.Morpheus.Error.Internal Data.Morpheus.Error.NameCollision Data.Morpheus.Error.Operation Data.Morpheus.Error.Schema@@ -80,6 +150,7 @@ Data.Morpheus.Error.Utils Data.Morpheus.Error.Variable Data.Morpheus.Error.Warning+ Data.Morpheus.Internal.Graph Data.Morpheus.Parser Data.Morpheus.Parsing.Document.TypeSystem Data.Morpheus.Parsing.Internal.Arguments@@ -87,38 +158,42 @@ Data.Morpheus.Parsing.Internal.Pattern Data.Morpheus.Parsing.Internal.Terms Data.Morpheus.Parsing.Internal.Value- Data.Morpheus.Parsing.JSONSchema.Parse- Data.Morpheus.Parsing.JSONSchema.Types Data.Morpheus.Parsing.Request.Operation Data.Morpheus.Parsing.Request.Parser Data.Morpheus.Parsing.Request.Selection Data.Morpheus.Rendering.RenderGQL Data.Morpheus.Rendering.RenderIntrospection- Data.Morpheus.Schema.Directives Data.Morpheus.Schema.DSL Data.Morpheus.Schema.Schema Data.Morpheus.Schema.SchemaAPI- Data.Morpheus.Schema.TypeKind Data.Morpheus.Types.Internal.AST.Base Data.Morpheus.Types.Internal.AST.DirectiveLocation+ Data.Morpheus.Types.Internal.AST.Fields Data.Morpheus.Types.Internal.AST.MergeSet- Data.Morpheus.Types.Internal.AST.OrderedMap+ Data.Morpheus.Types.Internal.AST.OrdMap+ Data.Morpheus.Types.Internal.AST.SafeHashMap Data.Morpheus.Types.Internal.AST.Selection+ Data.Morpheus.Types.Internal.AST.Stage Data.Morpheus.Types.Internal.AST.TH+ Data.Morpheus.Types.Internal.AST.TypeCategory Data.Morpheus.Types.Internal.AST.TypeSystem Data.Morpheus.Types.Internal.AST.Value+ Data.Morpheus.Types.Internal.Config Data.Morpheus.Types.Internal.Resolving.Core+ Data.Morpheus.Types.Internal.Resolving.Event Data.Morpheus.Types.Internal.Resolving.Resolver+ Data.Morpheus.Types.Internal.Resolving.ResolverState Data.Morpheus.Types.Internal.Validation Data.Morpheus.Types.Internal.Validation.Error+ Data.Morpheus.Types.Internal.Validation.Internal Data.Morpheus.Types.Internal.Validation.SchemaValidator Data.Morpheus.Types.Internal.Validation.Validator- Data.Morpheus.Types.SelectionTree Data.Morpheus.Validation.Document.Validation+ Data.Morpheus.Validation.Internal.Arguments Data.Morpheus.Validation.Internal.Directive Data.Morpheus.Validation.Internal.Value- Data.Morpheus.Validation.Query.Arguments Data.Morpheus.Validation.Query.Fragment+ Data.Morpheus.Validation.Query.FragmentPreconditions Data.Morpheus.Validation.Query.Selection Data.Morpheus.Validation.Query.UnionSelection Data.Morpheus.Validation.Query.Validation@@ -148,7 +223,8 @@ main-is: Spec.hs other-modules: Lib- Schema+ Utils.Api+ Utils.Schema Paths_morpheus_graphql_core hs-source-dirs: test
src/Data/Morpheus/Core.hs view
@@ -9,11 +9,9 @@ module Data.Morpheus.Core ( runApi,- EventCon, parseDSL, parseFullGQLDocument, parseGQLDocument,- decodeIntrospection, parseTypeSystemDefinition, parseTypeDefinitions, validateRequest,@@ -22,6 +20,10 @@ parseRequest, RenderGQL (..), SelectionTree (..),+ Config (..),+ VALIDATION_MODE (..),+ defaultConfig,+ debugConfig, ) where @@ -31,7 +33,8 @@ ( ByteString, ) import Data.Morpheus.Internal.Utils- ( empty,+ ( (<:>),+ empty, ) import Data.Morpheus.Parser ( parseRequest,@@ -39,13 +42,10 @@ parseTypeDefinitions, parseTypeSystemDefinition, )-import Data.Morpheus.Parsing.JSONSchema.Parse- ( decodeIntrospection,- ) import Data.Morpheus.Rendering.RenderGQL ( RenderGQL (..), )-import Data.Morpheus.Schema.Schema (withSystemTypes)+import Data.Morpheus.Schema.Schema (internalSchema) import Data.Morpheus.Schema.SchemaAPI (withSystemFields) import Data.Morpheus.Types.IO ( GQLRequest (..),@@ -58,10 +58,15 @@ VALID, Value, )+import Data.Morpheus.Types.Internal.Config+ ( Config (..),+ VALIDATION_MODE (..),+ debugConfig,+ defaultConfig,+ ) import Data.Morpheus.Types.Internal.Resolving- ( Context (..),- Eventless,- GQLChannel (..),+ ( Eventless,+ ResolverContext (..), ResponseStream, ResultT (..), RootResModel,@@ -70,7 +75,7 @@ runRootResModel, ) import Data.Morpheus.Types.SelectionTree (SelectionTree (..))-import Data.Morpheus.Validation.Document.Validation (validateSchema)+import Data.Morpheus.Validation.Document.Validation (ValidateSchema (..)) import Data.Morpheus.Validation.Query.Validation ( validateRequest, )@@ -78,50 +83,54 @@ ( toStrict, ) import Data.Text.Lazy.Encoding (decodeUtf8)-import Data.Typeable (Typeable) -type EventCon event =- (Eq (StreamChannel event), Typeable event, GQLChannel event)- runApi ::- forall event m.- (Monad m) =>- Schema ->+ forall event m s.+ (Monad m, ValidateSchema s) =>+ Schema s -> RootResModel event m ->+ Config -> GQLRequest -> ResponseStream event m (Value VALID)-runApi inputSchema resModel request = do- ctx <- validRequest- model <- withSystemFields (schema ctx) resModel- runRootResModel model ctx- where- validRequest ::- Monad m => ResponseStream event m Context- validRequest = cleanEvents $ ResultT $ pure $ do- validSchema <- validateSchema inputSchema- schema <- withSystemTypes validSchema- operation <- parseRequestWith schema request- pure $- Context- { schema,- operation,- currentTypeName = "Root",- currentSelection =- Selection- { selectionName = "Root",- selectionArguments = empty,- selectionPosition = operationPosition operation,- selectionAlias = Nothing,- selectionContent = SelectionSet (operationSelection operation),- selectionDirectives = []- }- }+runApi inputSchema resModel config request = do+ validRequest <- validateReq inputSchema config request+ resovers <- withSystemFields (schema validRequest) resModel+ runRootResModel resovers validRequest -parseDSL :: ByteString -> Either String Schema+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 -parseGQLDocument :: ByteString -> Eventless Schema+parseGQLDocument :: ByteString -> Eventless (Schema VALID) parseGQLDocument = parseTypeSystemDefinition . LT.toStrict . decodeUtf8 -parseFullGQLDocument :: ByteString -> Eventless Schema-parseFullGQLDocument = parseGQLDocument >=> withSystemTypes+parseFullGQLDocument :: ByteString -> Eventless (Schema VALID)+parseFullGQLDocument = parseGQLDocument >=> (internalSchema <:>)
src/Data/Morpheus/Error.hs view
@@ -1,15 +1,12 @@ module Data.Morpheus.Error ( errorMessage, globalErrorMessage,- internalError,- internalTypeMismatch, gqlWarnings, renderGQLErrors, deprecatedField, ) where -import Data.Morpheus.Error.Internal import Data.Morpheus.Error.Utils import Data.Morpheus.Error.Warning ( deprecatedField,
src/Data/Morpheus/Error/Document/Interface.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Error.Document.Interface ( unknownInterface,@@ -10,14 +11,13 @@ ) where -import Data.Morpheus.Error.Utils (globalErrorMessage)+import Data.Maybe (Maybe (..)) import Data.Morpheus.Types.Internal.AST.Base ( FieldName (..),- GQLError (..),- GQLErrors, TypeName (..), TypeRef,- msg,+ ValidationError,+ msgValidation, ) import Data.Morpheus.Types.Internal.Validation.SchemaValidator ( Field (..),@@ -26,10 +26,8 @@ ) import Data.Semigroup ((<>)) -unknownInterface :: TypeName -> GQLErrors-unknownInterface name = globalErrorMessage message- where- message = "Unknown Interface " <> msg name <> "."+unknownInterface :: TypeName -> ValidationError+unknownInterface name = "Unknown Interface " <> msgValidation name <> "." data ImplementsError = UnexpectedType@@ -45,31 +43,25 @@ } class PartialImplements ctx where- partialImplements :: ctx -> ImplementsError -> GQLErrors+ partialImplements :: ctx -> ImplementsError -> ValidationError instance PartialImplements (Interface, FieldName) where partialImplements (Interface interfaceName typename, fieldname) errorType =- [ GQLError- { message = message,- locations = []- }- ]+ "Interface field "+ <> renderField interfaceName fieldname Nothing+ <> detailedMessage errorType where- message =- "Interface field "- <> renderField interfaceName fieldname Nothing- <> detailedMessage errorType detailedMessage UnexpectedType {expectedType, foundType} = " expects type "- <> msg expectedType+ <> msgValidation expectedType <> " but " <> renderField typename fieldname Nothing <> " is type "- <> msg foundType+ <> msgValidation foundType <> "." detailedMessage Missing = " expected but "- <> msg typename+ <> msgValidation typename <> " does not provide it." -- Interface field TestInterface.name expected but User does not provide it.@@ -77,24 +69,17 @@ instance PartialImplements (Interface, Field) where partialImplements (Interface interfaceName typename, Field fieldname argName) errorType =- [ GQLError- { message = message,- locations = []- }- ]+ "Interface field argument "+ <> renderField interfaceName fieldname (Just argName)+ <> detailedMessage errorType where- --- message =- "Interface field argument "- <> renderField interfaceName fieldname (Just argName)- <> detailedMessage errorType detailedMessage UnexpectedType {expectedType, foundType} = " expects type"- <> msg expectedType+ <> msgValidation expectedType <> " but " <> renderField typename fieldname (Just argName) <> " is type "- <> msg foundType+ <> msgValidation foundType <> "." detailedMessage Missing = " expected but "
src/Data/Morpheus/Error/Fragment.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Error.Fragment ( cannotSpreadWithinItself,@@ -7,18 +8,20 @@ where -- MORPHEUS-import Data.Morpheus.Error.Utils (errorMessage)++import Data.Maybe (Maybe (..))+import Data.Morpheus.Error.Utils (validationErrorMessage) import Data.Morpheus.Types.Internal.AST.Base ( FieldName,- GQLError (..),- GQLErrors, Position, Ref (..), TypeName,+ ValidationError (..), msg, msgSepBy, ) import Data.Semigroup ((<>))+import Prelude (($), fmap, head) {- FRAGMENT:@@ -32,21 +35,21 @@ {...H} -> "Unknown fragment \"H\"." -} -cannotSpreadWithinItself :: [Ref] -> GQLErrors-cannotSpreadWithinItself fragments = [GQLError {message = text, locations = map refPosition fragments}]+cannotSpreadWithinItself :: [Ref] -> ValidationError+cannotSpreadWithinItself fragments = ValidationError text (fmap refPosition fragments) where text = "Cannot spread fragment " <> msg (refName $ head fragments) <> " within itself via "- <> msgSepBy ", " (map refName fragments)+ <> msgSepBy ", " (fmap refName fragments) <> "." -- Fragment type mismatch -> "Fragment \"H\" cannot be spread here as objects of type \"Hobby\" can never be of type \"Experience\"."-cannotBeSpreadOnType :: Maybe FieldName -> TypeName -> Position -> [TypeName] -> GQLErrors+cannotBeSpreadOnType :: Maybe FieldName -> TypeName -> Position -> [TypeName] -> ValidationError cannotBeSpreadOnType key fragmentType position typeMembers =- errorMessage- position+ validationErrorMessage+ (Just position) text where text =
src/Data/Morpheus/Error/Input.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Error.Input ( typeViolation,@@ -6,19 +7,19 @@ where import Data.Morpheus.Types.Internal.AST- ( Message,- ResolvedValue,- TypeRef (..),- msg,+ ( TypeRef (..),+ ValidationError,+ Value,+ msgValidation, ) import Data.Semigroup ((<>)) -typeViolation :: TypeRef -> ResolvedValue -> Message+typeViolation :: TypeRef -> Value s -> ValidationError typeViolation expected found = "Expected type "- <> msg expected+ <> msgValidation expected <> " found "- <> msg found+ <> msgValidation found <> "." {-
− src/Data/Morpheus/Error/Internal.hs
@@ -1,37 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Data.Morpheus.Error.Internal- ( internalTypeMismatch,- internalError,- internalResolvingError,- )-where---- MORPHEUS-import Data.Morpheus.Error.Utils (globalErrorMessage)-import Data.Morpheus.Types.Internal.AST.Base- ( GQLErrors,- Message,- msg,- )-import Data.Morpheus.Types.Internal.AST.Value- ( ValidValue,- )-import Data.Morpheus.Types.Internal.Resolving.Core- ( Eventless,- Failure (..),- )-import Data.Semigroup ((<>))---- GQL:: if no mutation defined -> "Schema is not configured for mutations."--- all kind internal error in development-internalError :: Message -> Eventless a-internalError x = failure $ globalErrorMessage $ "INTERNAL ERROR: " <> x--internalResolvingError :: Message -> GQLErrors-internalResolvingError = globalErrorMessage . ("INTERNAL ERROR:" <>)---- if value is already validated but value has different type-internalTypeMismatch :: Message -> ValidValue -> Eventless a-internalTypeMismatch text jsType =- internalError $ "Type mismatch " <> text <> msg jsType
src/Data/Morpheus/Error/NameCollision.hs view
@@ -1,12 +1,13 @@+{-# LANGUAGE NoImplicitPrelude #-}+ module Data.Morpheus.Error.NameCollision ( NameCollision (..), ) where -import Data.Morpheus.Internal.Utils (KeyOf (..)) import Data.Morpheus.Types.Internal.AST.Base- ( GQLError (..),+ ( ValidationError, ) class NameCollision a where- nameCollision :: KEY a -> a -> GQLError+ nameCollision :: a -> ValidationError
src/Data/Morpheus/Error/Operation.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Error.Operation ( mutationIsNotDefined,@@ -6,16 +7,19 @@ ) where -import Data.Morpheus.Error.Utils (errorMessage) import Data.Morpheus.Types.Internal.AST.Base- ( GQLErrors,- Position,+ ( Position,+ ValidationError (..), ) -mutationIsNotDefined :: Position -> GQLErrors+mutationIsNotDefined :: Position -> ValidationError mutationIsNotDefined position =- errorMessage position "Schema is not configured for mutations."+ ValidationError+ "Schema is not configured for mutations."+ [position] -subscriptionIsNotDefined :: Position -> GQLErrors+subscriptionIsNotDefined :: Position -> ValidationError subscriptionIsNotDefined position =- errorMessage position "Schema is not configured for subscriptions."+ ValidationError+ "Schema is not configured for subscriptions."+ [position]
src/Data/Morpheus/Error/Schema.hs view
@@ -1,27 +1,25 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Error.Schema ( nameCollisionError,- schemaValidationError, ) where -import Data.Morpheus.Error.Utils (globalErrorMessage) import Data.Morpheus.Types.Internal.AST.Base- ( GQLErrors,- Message,- TypeName,- msg,+ ( TypeName,+ ValidationError,+ msgValidation, ) import Data.Semigroup ((<>))+import Prelude (($)) -schemaValidationError :: Message -> GQLErrors-schemaValidationError error' =- globalErrorMessage $ "Schema Validation Error, " <> error'+schemaValidationError :: ValidationError -> ValidationError+schemaValidationError error = "Schema Validation Error, " <> error -nameCollisionError :: TypeName -> GQLErrors+nameCollisionError :: TypeName -> ValidationError nameCollisionError typeName = schemaValidationError $ "Name collision: "- <> msg typeName+ <> msgValidation typeName <> " is used for different dataTypes in two separate modules"
src/Data/Morpheus/Error/Selection.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Error.Selection ( unknownSelectionField,@@ -8,20 +9,21 @@ ) where -import Data.Morpheus.Error.Utils (errorMessage)-import Data.Morpheus.Types.Internal.AST.Base+import Data.Morpheus.Types.Internal.AST ( FieldName,- GQLErrors, Position, Ref (..),+ TypeDefinition (..), TypeName,+ VALID,+ ValidationError (..), msg, ) import Data.Semigroup ((<>)) -- GQL: "Field \"default\" must not have a selection since type \"String!\" has no subfields."-hasNoSubfields :: Ref -> TypeName -> GQLErrors-hasNoSubfields (Ref selectionName position) typeName = errorMessage position text+hasNoSubfields :: Ref -> TypeDefinition s VALID -> ValidationError+hasNoSubfields (Ref selectionName position) TypeDefinition {typeName} = ValidationError text [position] where text = "Field "@@ -30,8 +32,8 @@ <> msg typeName <> " has no subfields." -unknownSelectionField :: TypeName -> Ref -> GQLErrors-unknownSelectionField typeName Ref {refName, refPosition} = errorMessage refPosition text+unknownSelectionField :: TypeName -> Ref -> ValidationError+unknownSelectionField typeName Ref {refName, refPosition} = ValidationError text [refPosition] where text = "Cannot query field " <> msg refName@@ -40,8 +42,8 @@ <> "." -- GQL:: Field \"hobby\" of type \"Hobby!\" must have a selection of subfields. Did you mean \"hobby { ... }\"?-subfieldsNotSelected :: FieldName -> TypeName -> Position -> GQLErrors-subfieldsNotSelected fieldName typeName position = errorMessage position text+subfieldsNotSelected :: FieldName -> TypeName -> Position -> ValidationError+subfieldsNotSelected fieldName typeName position = ValidationError text [position] where text = "Field " <> msg fieldName <> " of type "
src/Data/Morpheus/Error/Utils.hs view
@@ -1,10 +1,12 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Error.Utils ( errorMessage, globalErrorMessage, badRequestError,+ validationErrorMessage, ) where @@ -12,13 +14,19 @@ ( ByteString, pack, )+import Data.Maybe (Maybe, maybeToList) import Data.Morpheus.Types.Internal.AST.Base ( GQLError (..), GQLErrors, Message, Position (..),+ ValidationError (..), ) import Data.Semigroup ((<>))+import Prelude ((.), String)++validationErrorMessage :: Maybe Position -> Message -> ValidationError+validationErrorMessage pos message = ValidationError message (maybeToList pos) errorMessage :: Position -> Message -> GQLErrors errorMessage position message = [GQLError {message, locations = [position]}]
src/Data/Morpheus/Error/Variable.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Error.Variable ( uninitializedVariable,@@ -7,23 +8,24 @@ ) where -import Data.Morpheus.Error.Utils (errorMessage)+import Data.Morpheus.Error.Utils (validationErrorMessage) import Data.Morpheus.Types.Internal.AST- ( GQLErrors,- Ref (..),+ ( Ref (..), TypeRef,+ ValidationError, Variable (..), msg, ) import Data.Semigroup ((<>))+import Prelude (($), Maybe (..)) -- query M ( $v : String ) { a(p:$v) } -> "Variable \"$v\" of type \"String\" used in position expecting type \"LANGUAGE\"."-incompatibleVariableType :: Ref -> Variable s -> TypeRef -> GQLErrors+incompatibleVariableType :: Ref -> Variable s -> TypeRef -> ValidationError incompatibleVariableType (Ref variableName argPosition) Variable {variableType} argumentType =- errorMessage argPosition text+ validationErrorMessage (Just argPosition) text where text = "Variable "@@ -34,10 +36,10 @@ <> msg argumentType <> "." -uninitializedVariable :: Variable s -> GQLErrors+uninitializedVariable :: Variable s -> ValidationError uninitializedVariable Variable {variableName, variableType, variablePosition} =- errorMessage- variablePosition+ validationErrorMessage+ (Just variablePosition) $ "Variable " <> msg ("$" <> variableName) <> " of required type "
src/Data/Morpheus/Error/Warning.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Error.Warning ( renderGQLErrors,@@ -13,6 +14,8 @@ import Data.ByteString.Lazy.Char8 (unpack) import Data.Foldable (traverse_) -- MORPHEUS++import Data.Maybe (Maybe (..), maybe) import Data.Morpheus.Error.Utils (errorMessage) import Data.Morpheus.Types.Internal.AST.Base ( Description,@@ -25,6 +28,12 @@ import Language.Haskell.TH ( Q, reportWarning,+ )+import Prelude+ ( ($),+ (.),+ String,+ pure, ) renderGQLErrors :: GQLErrors -> String
+ src/Data/Morpheus/Internal/Graph.hs view
@@ -0,0 +1,62 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.Internal.Graph+ ( cycleChecking,+ Node,+ Graph,+ Edges,+ )+where++import Control.Applicative (Applicative, pure)+import Data.Foldable (traverse_)+import Data.List (elem, lookup)+import Data.Maybe (Maybe (..))+-- MORPHEUS+import Data.Morpheus.Types.Internal.AST+ ( Ref (..),+ )+import Data.Semigroup ((<>))+import Prelude+ ( otherwise,+ )++type Node = Ref++type Edges = (Ref, [Ref])++type Graph = [Edges]++cycleChecking ::+ Applicative m =>+ ([Ref] -> m ()) ->+ Graph ->+ m ()+cycleChecking fail graph = traverse_ checkNode graph+ where+ checkNode (node, _) = cycleCheckingWith graph node [node] fail++cycleCheckingWith ::+ Applicative m =>+ Graph ->+ Ref ->+ [Ref] ->+ ([Ref] -> m ()) ->+ m ()+cycleCheckingWith graph parentNode history fail =+ case lookup parentNode graph of+ Just node -> traverse_ checkNode node+ Nothing -> pure ()+ where+ checkNode node+ | node `elem` history =+ fail (node : history)+ | otherwise =+ cycleCheckingWith+ graph+ node+ (history <> [node])+ fail
src/Data/Morpheus/Internal/TH.hs view
@@ -1,78 +1,117 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-}+{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TemplateHaskellQuotes #-}-{-# LANGUAGE TypeApplications #-}+-- MORPHEUS+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Internal.TH- ( tyConArgs,+ ( _', apply,- applyT,- typeT,- instanceHeadT,- instanceProxyFunD,- instanceFunD,- instanceHeadMultiT,- destructRecord,- typeInstanceDec,- infoTyVars,+ applyCons,+ applyVars, decArgs,- nameLitP,- nameStringE,- nameStringL,- nameConT,- nameVarE,- nameVarT,- nameConType,- nameConE,- nameVarP,- mkTypeName,- mkFieldName, declareTypeRef,+ destructRecord,+ funDProxy,+ funDSimple,+ infoTyVars,+ isEnum,+ m',+ m_,+ mkFieldsE, nameSpaceField, nameSpaceType,- m_,- m',- isEnum,+ toCon,+ toConE,+ toConT,+ ToName (..),+ toString,+ toVarE,+ toVarT,+ tyConArgs,+ typeInstanceDec,+ v',+ cat',+ _2',+ o',+ e',+ vars,+ decodeObjectE,+ matchWith, ) where --- MORPHEUS+import Control.Applicative ((<*>), pure)+import Control.Monad.Fail (fail)+import Data.Foldable (foldl, foldr1)+import Data.Functor ((<$>))+import Data.Maybe (Maybe (..)) import Data.Morpheus.Internal.Utils ( nameSpaceField, nameSpaceType, ) import Data.Morpheus.Types.Internal.AST- ( FieldName,- TypeKind (..),+ ( FieldDefinition (..),+ FieldName (..), TypeKind (..), TypeName (..), TypeRef (..), TypeWrapper (..), convertToHaskellName, isEnum,+ isNullable, isOutputObject, readName, )-import Data.Morpheus.Types.Internal.Resolving- ( UnSubResolver,- )+import Data.Semigroup ((<>)) import Data.Text (unpack) import Language.Haskell.TH--m' :: Type-m' = VarT $ mkTypeName m_+import Prelude+ ( ($),+ (.),+ (==),+ Bool (..),+ id,+ map,+ otherwise,+ show,+ (||),+ ) m_ :: TypeName m_ = "m" -declareTypeRef :: Bool -> TypeRef -> Type-declareTypeRef isSub TypeRef {typeConName, typeWrappers, typeArgs} =+m' :: Type+m' = VarT (mkName "m")++o' :: Type+o' = VarT (mkName "o")++e' :: Type+e' = VarT (mkName "event")++_' :: PatQ+_' = toVar (mkName "_")++_2' :: PatQ+_2' = toVar (mkName "_2")++v' :: ToVar Name a => a+v' = toVar (mkName "v")++cat' :: Type+cat' = VarT (mkName "cat")++declareTypeRef :: TypeRef -> Type+declareTypeRef TypeRef {typeConName, typeWrappers, typeArgs} = wrappedT typeWrappers where@@ -80,63 +119,133 @@ wrappedT (TypeList : xs) = AppT (ConT ''[]) $ wrappedT xs wrappedT (TypeMaybe : xs) = AppT (ConT ''Maybe) $ wrappedT xs wrappedT [] = decType typeArgs- ------------------------------------------------------- typeName = nameConType typeConName --------------------------------------------- decType _- | isSub =- AppT typeName (AppT (ConT ''UnSubResolver) m')- decType (Just par) = AppT typeName (VarT $ mkTypeName par)- decType _ = typeName+ decType (Just par) = apply typeConName [toVar par]+ decType _ = toCon typeConName tyConArgs :: TypeKind -> [TypeName] tyConArgs kindD | isOutputObject kindD || kindD == KindUnion = [m_] | otherwise = [] -apply :: Name -> [Q Exp] -> Q Exp-apply n = foldl appE (conE n)+cons :: ToCon a b => [a] -> [b]+cons = map toCon -applyT :: Name -> [Q Type] -> Q Type-applyT name = foldl appT (conT name)+vars :: ToVar a b => [a] -> [b]+vars = map toVar -typeT :: Name -> [TypeName] -> Q Type-typeT name li = applyT name (map (varT . mkTypeName) li)+class ToName a where+ toName :: a -> Name -instanceHeadT :: Name -> TypeName -> [TypeName] -> Q Type-instanceHeadT cName iType tArgs = applyT cName [applyT (mkTypeName iType) (map (varT . mkTypeName) tArgs)]+instance ToName Name where+ toName = id -instanceProxyFunD :: (Name, ExpQ) -> DecQ-instanceProxyFunD (name, body) = instanceFunD name ["_"] body+instance ToName TypeName where+ toName = mkName . unpack . readTypeName -instanceFunD :: Name -> [TypeName] -> ExpQ -> Q Dec-instanceFunD name args body = funD name [clause (map (varP . mkTypeName) args) (normalB body) []]+instance ToName FieldName where+ toName = mkName . unpack . readName . convertToHaskellName -instanceHeadMultiT :: Name -> Q Type -> [Q Type] -> Q Type-instanceHeadMultiT className iType li = applyT className (iType : li)+class ToString a b where+ toString :: a -> b --- "User" -> ["name","id"] -> (User name id)-destructRecord :: TypeName -> [FieldName] -> PatQ-destructRecord conName fields = conP (mkTypeName conName) (map (varP . mkFieldName) fields)+instance ToString a b => ToString a (Q b) where+ toString = pure . toString -typeInstanceDec :: Name -> Type -> Type -> Dec+instance ToString TypeName Lit where+ toString = stringL . unpack . readTypeName -nameLitP :: TypeName -> PatQ-nameLitP = litP . nameStringL+instance ToString TypeName Pat where+ toString = LitP . toString -nameStringL :: TypeName -> Lit-nameStringL = stringL . unpack . readTypeName+instance ToString FieldName Lit where+ toString (FieldName x) = stringL (unpack x) -nameStringE :: TypeName -> ExpQ-nameStringE = stringE . (unpack . readTypeName)+instance ToString TypeName Exp where+ toString = LitE . toString -#if MIN_VERSION_template_haskell(2,15,0)--- fix breaking changes-typeInstanceDec typeFamily arg res = TySynInstD (TySynEqn Nothing (AppT (ConT typeFamily) arg) res)-#else+instance ToString FieldName Exp where+ toString = LitE . toString++class ToCon a b where+ toCon :: a -> b++instance ToCon a b => ToCon a (Q b) where+ toCon = pure . toCon++instance (ToName a) => ToCon a Type where+ toCon = ConT . toName++instance (ToName a) => ToCon a Exp where+ toCon = ConE . toName++class ToVar a b where+ toVar :: a -> b++instance ToVar a b => ToVar a (Q b) where+ toVar = pure . toVar++instance (ToName a) => ToVar a Type where+ toVar = VarT . toName++instance (ToName a) => ToVar a Exp where+ toVar = VarE . toName++instance (ToName a) => ToVar a Pat where+ toVar = VarP . toName++class Apply a where+ apply :: ToCon i a => i -> [a] -> a++instance Apply TypeQ where+ apply = foldl appT . toCon++instance Apply Type where+ apply = foldl AppT . toCon++instance Apply Exp where+ apply = foldl AppE . toCon++instance Apply ExpQ where+ apply = foldl appE . toCon++applyVars ::+ ( ToName con,+ ToName var,+ Apply res,+ ToCon con res,+ ToVar var res+ ) =>+ con ->+ [var] ->+ res+applyVars name li = apply name (vars li)++applyCons :: (ToName con, ToName cons) => con -> [cons] -> Q Type+applyCons name li = apply name (cons li)++funDProxy :: [(Name, ExpQ)] -> [DecQ]+funDProxy = map fun+ where+ fun (name, body) = funDSimple name [_'] body++funDSimple :: Name -> [PatQ] -> ExpQ -> DecQ+funDSimple name args body = funD name [clause args (normalB body) []]++-- |+-- input:+-- >>>+-- destructRecord "User" ["name","id"]+-- >>> ---typeInstanceDec typeFamily arg res = TySynInstD typeFamily (TySynEqn [arg] res)-#endif+-- expression:+-- >>>+-- (User name id)+-- >>>+destructRecord :: TypeName -> [FieldDefinition cat s] -> PatQ+destructRecord conName fields = conP (toName conName) (vars names)+ where+ names = map fieldName fields infoTyVars :: Info -> [TyVarBndr] infoTyVars (TyConI x) = decArgs x@@ -148,26 +257,92 @@ decArgs (TySynD _ args _) = args decArgs _ = [] -mkTypeName :: TypeName -> Name-mkTypeName = mkName . unpack . readTypeName+toConT :: ToName a => a -> Q Type+toConT = conT . toName -mkFieldName :: FieldName -> Name-mkFieldName = mkName . unpack . readName . convertToHaskellName+toVarT :: ToVar a TypeQ => a -> TypeQ+toVarT = toVar -nameConT :: TypeName -> Q Type-nameConT = conT . mkTypeName+toVarE :: ToVar a Exp => a -> ExpQ+toVarE = toVar -nameConType :: TypeName -> Type-nameConType = ConT . mkTypeName+toConE :: ToCon a Exp => a -> ExpQ+toConE = toCon -nameVarT :: TypeName -> Q Type-nameVarT = varT . mkTypeName+-- | 'mkFieldsE'+--+-- input :+-- >>>+-- mkFieldsE 'mkValue [FieldDefinition { fieldName = \"field1" ,..} ,..]+-- >>>+--+-- expression :+-- >>>+-- [ mkValue \"field1\" field1,+-- ..+-- ]+-- >>>+mkFieldsE :: Name -> [FieldDefinition cat s] -> Exp+mkFieldsE name = ListE . map (mkEntryWith name) -nameVarE :: FieldName -> ExpQ-nameVarE = varE . mkFieldName+-- input : mkFieldWith 'mkValue (FieldDefinition { fieldName = "field1", ..})+-- expression: mkValue "field1" field1+mkEntryWith ::+ Name ->+ FieldDefinition cat s ->+ Exp+mkEntryWith f FieldDefinition {fieldName} =+ AppE+ (AppE (VarE f) (toString fieldName))+ (toVar fieldName) -nameConE :: TypeName -> ExpQ-nameConE = conE . mkTypeName+decodeObjectE :: (Bool -> Name) -> TypeName -> [FieldDefinition cat s] -> ExpQ+decodeObjectE _ conName [] = appE (varE 'pure) (toCon conName)+decodeObjectE funName conName fields =+ uInfixE+ (toCon conName)+ (varE '(<$>))+ (foldr1 withApplicative $ map (defField funName) fields) -nameVarP :: FieldName -> PatQ-nameVarP = varP . mkFieldName+withApplicative :: ExpQ -> ExpQ -> ExpQ+withApplicative x = uInfixE x (varE '(<*>))++matchWith ::+ Bool ->+ (t -> (PatQ, ExpQ)) ->+ [t] ->+ ExpQ+matchWith isClosed f xs = lamCaseE (map buildMatch xs <> fallback)+ where+ fallback+ | isClosed = []+ | otherwise = [elseCaseEXP]+ buildMatch x = match pat (normalB body) []+ where+ (pat, body) = f x++elseCaseEXP :: MatchQ+elseCaseEXP = match v' body []+ where+ body =+ normalB $+ appE+ (toVarE 'fail)+ ( uInfixE+ (appE (varE 'show) v')+ (varE '(<>))+ (stringE " is Not Valid Union Constructor")+ )++defField :: (Bool -> Name) -> FieldDefinition cat s -> ExpQ+defField f field@FieldDefinition {fieldName} = uInfixE v' (varE $ f (isNullable field)) (toString fieldName)++#if MIN_VERSION_template_haskell(2,15,0)+-- fix breaking changes+typeInstanceDec :: Name -> Type -> Type -> Dec+typeInstanceDec typeFamily arg res = TySynInstD (TySynEqn Nothing (AppT (ConT typeFamily) arg) res)+#else+--+typeInstanceDec :: Name -> Type -> Type -> Dec+typeInstanceDec typeFamily arg res = TySynInstD typeFamily (TySynEqn [arg] res)+#endif
src/Data/Morpheus/Internal/Utils.hs view
@@ -2,9 +2,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE PolyKinds #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Internal.Utils ( capital,@@ -27,17 +25,36 @@ mapFst, mapSnd, mapTuple,+ UpdateT (..),+ resolveUpdates,+ concatUpdates,+ failUpdates,+ ordTraverse,+ ordTraverse_,+ traverseCollection,+ (<.>),+ SemigroupM (..), ) where +import Control.Applicative (Applicative (..))+import Control.Monad ((=<<), foldM)+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.Reader+ ( ReaderT (..),+ ) import Data.Char ( toLower, toUpper, )+import Data.Foldable (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.Morpheus.Error.NameCollision (NameCollision (..)) import Data.Morpheus.Types.Internal.AST.Base ( FieldName, FieldName (..),@@ -46,22 +63,42 @@ Token, TypeName (..), TypeNameRef (..),+ ValidationErrors,+ toGQLError, )-import Data.Semigroup ((<>))+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+ ( ($),+ (.),+ Bool (..),+ Either (..),+ Eq (..),+ Functor (..),+ Int,+ Monad,+ Ord,+ String,+ const,+ fst,+ length,+ otherwise,+ snd,+ ) mapText :: (String -> String) -> Token -> Token mapText f = T.pack . f . T.unpack nameSpaceType :: [FieldName] -> TypeName -> TypeName-nameSpaceType list (TypeName name) = TypeName . T.concat $ map capital (map readName list <> [name])+nameSpaceType list (TypeName name) = TypeName . T.concat $ fmap capital (fmap readName list <> [name]) nameSpaceField :: TypeName -> FieldName -> FieldName nameSpaceField nSpace (FieldName name) = FieldName (nonCapital nSpace <> capital name)@@ -90,61 +127,111 @@ empty = [] singleton x = [x] -instance (Hashable k, KeyOf v, k ~ KEY v) => Collection v (HashMap k v) where+instance KeyOf k v => Collection v (HashMap k v) where empty = HM.empty singleton x = HM.singleton (keyOf x) x -class Selectable c a | c -> a where- selectOr :: d -> (a -> d) -> KEY a -> c -> d+class Selectable k a c | c -> a where+ selectOr :: d -> (a -> d) -> k -> c -> d -instance KeyOf a => Selectable [a] a where+instance KeyOf k a => Selectable k a [a] where selectOr fb f key lib = maybe fb f (find ((key ==) . keyOf) lib) -instance (KEY a ~ k, Eq k, Hashable k) => Selectable (HashMap k a) a where+instance KeyOf k a => Selectable k a (HashMap k a) where selectOr fb f key lib = maybe fb f (HM.lookup key lib) -selectBy :: (Failure e m, Selectable c a, Monad m) => e -> KEY a -> c -> m a+selectBy :: (Failure e m, Selectable k a c, Monad m) => e -> k -> c -> m a selectBy err = selectOr (failure err) pure -member :: forall a c. Selectable c a => KEY a -> c -> Bool+member :: Selectable k a c => k -> c -> Bool member = selectOr False toTrue where toTrue :: a -> Bool toTrue _ = True -class Eq (KEY a) => KeyOf a where- type KEY a :: *- type KEY a = FieldName- keyOf :: a -> KEY a+ordTraverse ::+ ( Monad f,+ KeyOf k b,+ Listable a (t a),+ Listable b (t b),+ Failure ValidationErrors f+ ) =>+ (a -> f b) ->+ t a ->+ f (t b)+ordTraverse = traverseCollection -instance KeyOf Ref where+traverseCollection ::+ ( Monad f,+ KeyOf k b,+ Listable a (t a),+ Listable b (t' b),+ Failure ValidationErrors f+ ) =>+ (a -> f b) ->+ t a ->+ f (t' b)+traverseCollection f a = fromElems =<< traverse f (elems a)++ordTraverse_ ::+ ( Monad f,+ Listable a (t a)+ ) =>+ (a -> f b) ->+ t a ->+ f ()+ordTraverse_ f a = traverse_ f (elems a)++class (Eq k, Hashable k) => KeyOf k a | a -> k where+ keyOf :: a -> k++instance (Eq k, Hashable k) => KeyOf k (k, a) where+ keyOf = fst++instance KeyOf FieldName Ref where keyOf = refName -instance KeyOf TypeNameRef where- type KEY TypeNameRef = TypeName+instance KeyOf TypeName TypeNameRef where keyOf = typeNameRef -toPair :: KeyOf a => a -> (KEY a, a)+toPair :: KeyOf k a => a -> (k, a) toPair x = (keyOf x, x) -- list Like Collections class Listable a coll | coll -> a where elems :: coll -> [a]- fromElems :: (KeyOf a, Monad m, Failure GQLErrors m) => [a] -> m coll+ fromElems :: (Monad m, Failure ValidationErrors m) => [a] -> m coll -keys :: (KeyOf a, Listable a coll) => coll -> [KEY a]-keys = map keyOf . elems+instance (NameCollision a, KeyOf k a) => Listable a (HashMap k a) where+ fromElems = safeFromList+ elems = HM.elems +keys :: (KeyOf k a, Listable a coll) => coll -> [k]+keys = fmap keyOf . elems+ size :: Listable a coll => coll -> Int size = length . elems -- Merge Object with of Failure as an Option class Merge a where- merge :: (Monad m, Failure GQLErrors m) => [Ref] -> a -> a -> m a+ merge :: (Monad m, Failure ValidationErrors m) => [Ref] -> a -> a -> m a -(<:>) :: (Monad m, Merge a, Failure GQLErrors m) => a -> a -> m a+instance (NameCollision a, KeyOf k a) => Merge (HashMap k a) where+ merge _ = safeJoin++(<:>) :: (Monad m, Merge a, Failure ValidationErrors m) => a -> a -> m a (<:>) = merge [] +class SemigroupM m a where+ mergeM :: [Ref] -> a -> a -> m a++(<.>) ::+ (SemigroupM m a) =>+ a ->+ a ->+ m a+(<.>) = mergeM []+ -- Failure: for custome Morpheus GrapHQL errors class Applicative f => Failure error (f :: * -> *) where failure :: error -> f v@@ -152,9 +239,12 @@ instance Failure error (Either error) where failure = Left -instance (Stream s, Ord e, Failure [a] m) => Failure [a] (ParsecT e s m) where- failure x = ParsecT $ \_ _ _ _ _ -> failure x+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) @@ -163,3 +253,53 @@ mapTuple :: (a -> a') -> (b -> b') -> (a, b) -> (a', b') mapTuple f1 f2 (a, b) = (f1 a, f2 b)++-- Helper Functions+newtype UpdateT m a = UpdateT {updateTState :: a -> m a}++failUpdates :: (Failure e m) => e -> UpdateT m a+failUpdates = UpdateT . const . failure++concatUpdates :: Monad m => [UpdateT m a] -> UpdateT m a+concatUpdates x = UpdateT (`resolveUpdates` x)++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+ ) =>+ [a] ->+ m (HashMap k a)+safeFromList values = safeUnionWith HM.empty (fmap toPair values)++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)++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++type NoDupHashMap k a = (HashMap k a, [(k, a)])++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
src/Data/Morpheus/Parser.hs view
@@ -7,28 +7,32 @@ where import Control.Monad ((>=>))-import Data.Morpheus.Internal.Utils- ( fromElems,+import qualified Data.Morpheus.Parsing.Document.TypeSystem as P+ ( parseSchema,+ parseTypeDefinitions, )-import Data.Morpheus.Parsing.Document.TypeSystem (parseSchema) import Data.Morpheus.Parsing.Request.Parser (parseGQL) import Data.Morpheus.Types.IO ( GQLRequest (..), ) import Data.Morpheus.Types.Internal.AST ( ANY,+ CONST, GQLQuery (..), Operation, Schema (..), TypeDefinition (..), VALID,+ )+import Data.Morpheus.Types.Internal.Config+ ( Config (..), VALIDATION_MODE (..), ) import Data.Morpheus.Types.Internal.Resolving ( Eventless, ) import Data.Morpheus.Validation.Document.Validation- ( validatePartialDocument,+ ( validateSchema, ) import Data.Morpheus.Validation.Query.Validation ( validateRequest,@@ -36,17 +40,22 @@ import Data.Text (Text) parseTypeSystemDefinition ::- Text -> Eventless Schema+ Text -> Eventless (Schema VALID) parseTypeSystemDefinition =- parseSchema >=> fromElems+ P.parseSchema+ >=> validateSchema+ True+ Config+ { debug = False,+ validationMode = FULL_VALIDATION+ } parseTypeDefinitions ::- Text -> Eventless [TypeDefinition ANY]-parseTypeDefinitions =- parseSchema >=> validatePartialDocument+ Text -> Eventless [TypeDefinition ANY CONST]+parseTypeDefinitions = P.parseTypeDefinitions parseRequest :: GQLRequest -> Eventless GQLQuery parseRequest = parseGQL -parseRequestWith :: Schema -> GQLRequest -> Eventless (Operation VALID)-parseRequestWith schema = parseRequest >=> validateRequest schema FULL_VALIDATION+parseRequestWith :: Config -> Schema VALID -> GQLRequest -> Eventless (Operation VALID)+parseRequestWith config schema = parseRequest >=> validateRequest config schema
src/Data/Morpheus/Parsing/Document/TypeSystem.hs view
@@ -1,50 +1,77 @@+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Parsing.Document.TypeSystem ( parseSchema,+ parseTypeDefinitions, ) where -- MORPHEUS++import Control.Applicative ((*>), pure)+import Control.Monad ((>=>))+import Data.Foldable (foldr)+import Data.Functor ((<$>), fmap)+import Data.Maybe (Maybe (..))+import Data.Morpheus.Error.NameCollision (NameCollision (..)) import Data.Morpheus.Parsing.Internal.Internal ( Parser, processParser, ) import Data.Morpheus.Parsing.Internal.Pattern- ( enumValueDefinition,+ ( argumentsDefinition,+ enumValueDefinition, fieldsDefinition, inputFieldsDefinition, optionalDirectives,+ parseDirectiveLocation,+ parseOperationType, typeDeclaration, ) import Data.Morpheus.Parsing.Internal.Terms ( collection,+ ignoredTokens, keyword,- operator, optDescription,+ optionalCollection,+ parseName, parseTypeName,- pipeLiteral,+ pipe, sepByAnd,- spaceAndComments,+ setOf,+ symbol, )+import Data.Morpheus.Parsing.Internal.Value+ ( Parse (..),+ ) import Data.Morpheus.Types.Internal.AST ( ANY,+ CONST, DataFingerprint (..), Description,+ DirectiveDefinition (..), IN, OUT,+ RawTypeDefinition (..),+ RootOperationTypeDefinition (..), ScalarDefinition (..),+ Schema,+ SchemaDefinition (..), TypeContent (..), TypeDefinition (..), TypeName,- UnionMember (..),+ Value,+ buildSchema, mkUnionMember, toAny, ) import Data.Morpheus.Types.Internal.Resolving ( Eventless,+ failure, ) import Data.Text (Text) import Text.Megaparsec@@ -52,15 +79,22 @@ eof, label, manyTill,- sepBy1,+ optional, )+import Prelude+ ( ($),+ (.),+ ) -- Scalars : https://graphql.github.io/graphql-spec/June2018/#sec-Scalars -- -- ScalarTypeDefinition: -- Description(opt) scalar Name Directives(Const)(opt) ---scalarTypeDefinition :: Maybe Description -> Parser (TypeDefinition ANY)+scalarTypeDefinition ::+ Parse (Value s) =>+ Maybe Description ->+ Parser (TypeDefinition ANY s) scalarTypeDefinition typeDescription = label "ScalarTypeDefinition" $ do typeName <- typeDeclaration "scalar" typeDirectives <- optionalDirectives@@ -86,7 +120,10 @@ -- FieldDefinition -- Description(opt) Name ArgumentsDefinition(opt) : Type Directives(Const)(opt) ---objectTypeDefinition :: Maybe Description -> Parser (TypeDefinition OUT)+objectTypeDefinition ::+ Parse (Value s) =>+ Maybe Description ->+ Parser (TypeDefinition OUT s) objectTypeDefinition typeDescription = label "ObjectTypeDefinition" $ do typeName <- typeDeclaration "type" objectImplements <- optionalImplementsInterfaces@@ -111,7 +148,10 @@ -- InterfaceTypeDefinition -- Description(opt) interface Name Directives(Const)(opt) FieldsDefinition(opt) ---interfaceTypeDefinition :: Maybe Description -> Parser (TypeDefinition OUT)+interfaceTypeDefinition ::+ Parse (Value s) =>+ Maybe Description ->+ Parser (TypeDefinition OUT s) interfaceTypeDefinition typeDescription = label "InterfaceTypeDefinition" $ do typeName <- typeDeclaration "interface" typeDirectives <- optionalDirectives@@ -131,7 +171,10 @@ -- = |(opt) NamedType -- UnionMemberTypes | NamedType ---unionTypeDefinition :: Maybe Description -> Parser (TypeDefinition OUT)+unionTypeDefinition ::+ Parse (Value s) =>+ Maybe Description ->+ Parser (TypeDefinition OUT s) unionTypeDefinition typeDescription = label "UnionTypeDefinition" $ do typeName <- typeDeclaration "union" typeDirectives <- optionalDirectives@@ -142,7 +185,9 @@ .. } where- unionMemberTypes = operator '=' *> (mkUnionMember <$> parseTypeName) `sepBy1` pipeLiteral+ unionMemberTypes =+ symbol '='+ *> pipe (mkUnionMember <$> parseTypeName) -- Enums : https://graphql.github.io/graphql-spec/June2018/#sec-Enums --@@ -155,7 +200,10 @@ -- EnumValueDefinition -- Description(opt) EnumValue Directives(Const)(opt) ---enumTypeDefinition :: Maybe Description -> Parser (TypeDefinition ANY)+enumTypeDefinition ::+ Parse (Value s) =>+ Maybe Description ->+ Parser (TypeDefinition ANY s) enumTypeDefinition typeDescription = label "EnumTypeDefinition" $ do typeName <- typeDeclaration "enum" typeDirectives <- optionalDirectives@@ -174,7 +222,10 @@ -- InputFieldsDefinition: -- { InputValueDefinition(list) } ---inputObjectTypeDefinition :: Maybe Description -> Parser (TypeDefinition IN)+inputObjectTypeDefinition ::+ Parse (Value s) =>+ Maybe Description ->+ Parser (TypeDefinition IN s) inputObjectTypeDefinition typeDescription = label "InputObjectTypeDefinition" $ do typeName <- typeDeclaration "input"@@ -187,20 +238,145 @@ .. } -parseDataType :: Parser (TypeDefinition ANY)-parseDataType = label "TypeDefinition" $ do- description <- optDescription- -- scalar | enum | input | object | union | interface- (toAny <$> inputObjectTypeDefinition description)- <|> (toAny <$> unionTypeDefinition description)- <|> enumTypeDefinition description- <|> scalarTypeDefinition description- <|> (toAny <$> objectTypeDefinition description)- <|> (toAny <$> interfaceTypeDefinition description)+-- 3.13 DirectiveDefinition+--+-- DirectiveDefinition:+-- Description[opt] directive @ Name ArgumentsDefinition[opt] repeatable[opt] on DirectiveLocations+--+-- DirectiveLocations:+-- DirectiveLocations | DirectiveLocation+-- |[opt] DirectiveLocation -parseSchema :: Text -> Eventless [TypeDefinition ANY]-parseSchema = processParser request+parseDirectiveDefinition ::+ Maybe Description ->+ Parser RawTypeDefinition+parseDirectiveDefinition directiveDefinitionDescription = label "DirectiveDefinition" $ do+ keyword "directive"+ symbol '@'+ directiveDefinitionName <- parseName+ directiveDefinitionArgs <- optionalCollection argumentsDefinition+ _ <- optional (keyword "repeatable")+ keyword "on"+ directiveDefinitionLocations <- pipe parseDirectiveLocation+ pure+ $ RawDirectiveDefinition+ $ DirectiveDefinition+ { directiveDefinitionName,+ directiveDefinitionDescription,+ directiveDefinitionLocations,+ directiveDefinitionArgs+ }++-- 3.2 Schema+-- SchemaDefinition:+-- schema Directives[Const,opt]+-- { RootOperationTypeDefinitionlist }+--+-- RootOperationTypeDefinition:+-- OperationType: NamedType++-- data SchemaDefinition = SchemaDefinition+-- { query :: TypeName,+-- mutation :: Maybe TypeName,+-- subscription :: Maybe TypeName+-- }++parseSchemaDefinition :: Maybe Description -> Parser RawTypeDefinition+parseSchemaDefinition _schemaDescription = label "SchemaDefinition" $ do+ keyword "schema"+ schemaDirectives <- optionalDirectives+ unSchemaDefinition <- setOf parseRootOperationTypeDefinition+ pure+ $ RawSchemaDefinition+ $ SchemaDefinition {schemaDirectives, unSchemaDefinition}++parseRootOperationTypeDefinition :: Parser RootOperationTypeDefinition+parseRootOperationTypeDefinition = do+ operationType <- parseOperationType+ symbol ':'+ RootOperationTypeDefinition operationType <$> parseTypeName++parseTypeSystemUnit ::+ Parser RawTypeDefinition+parseTypeSystemUnit =+ label "TypeDefinition" $+ do+ description <- optDescription+ -- scalar | enum | input | object | union | interface+ types description+ <|> parseSchemaDefinition description+ <|> parseDirectiveDefinition description where- request = label "DocumentTypes" $ do- spaceAndComments- manyTill parseDataType eof+ types description =+ RawTypeDefinition+ <$> ( (toAny <$> inputObjectTypeDefinition description)+ <|> (toAny <$> unionTypeDefinition description)+ <|> enumTypeDefinition description+ <|> scalarTypeDefinition description+ <|> (toAny <$> objectTypeDefinition description)+ <|> (toAny <$> interfaceTypeDefinition description)+ )++typePartition ::+ [RawTypeDefinition] ->+ ( [SchemaDefinition],+ [TypeDefinition ANY CONST],+ [DirectiveDefinition CONST]+ )+typePartition = foldr split ([], [], [])++split ::+ RawTypeDefinition ->+ ( [SchemaDefinition],+ [TypeDefinition ANY CONST],+ [DirectiveDefinition CONST]+ ) ->+ ( [SchemaDefinition],+ [TypeDefinition ANY CONST],+ [DirectiveDefinition CONST]+ )+split (RawSchemaDefinition schema) (schemas, types, dirs) = (schema : schemas, types, dirs)+split (RawTypeDefinition ty) (schemas, types, dirs) = (schemas, ty : types, dirs)+split (RawDirectiveDefinition dir) (schemas, types, dirs) = (schemas, types, dir : dirs)++-- split (RawDirectiveDefinition d)++withSchemaDefinition ::+ ( [SchemaDefinition],+ [TypeDefinition ANY s],+ [DirectiveDefinition CONST]+ ) ->+ Eventless+ (Maybe SchemaDefinition, [TypeDefinition ANY s], [DirectiveDefinition CONST])+withSchemaDefinition ([], t, dirs) = pure (Nothing, t, dirs)+withSchemaDefinition ([x], t, dirs) = pure (Just x, t, dirs)+withSchemaDefinition (_ : xs, _, _) = failure (fmap nameCollision xs)++parseTypeSystemDefinition :: Parser [RawTypeDefinition]+parseTypeSystemDefinition = label "TypeSystemDefinitions" $ do+ ignoredTokens+ manyTill parseTypeSystemUnit eof++typeSystemDefinition ::+ Text ->+ Eventless+ ( Maybe SchemaDefinition,+ [TypeDefinition ANY CONST],+ [DirectiveDefinition CONST]+ )+typeSystemDefinition =+ processParser parseTypeSystemDefinition+ >=> withSchemaDefinition . typePartition++parseTypeDefinitions :: Text -> Eventless [TypeDefinition ANY CONST]+parseTypeDefinitions = fmap snd3 . typeSystemDefinition++snd3 :: (a, b, c) -> b+snd3 (_, x, _) = x++parseSchema ::+ Text ->+ Eventless (Schema CONST)+parseSchema =+ typeSystemDefinition+ >=> buildSchema
src/Data/Morpheus/Parsing/Internal/Pattern.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} module Data.Morpheus.Parsing.Internal.Pattern@@ -8,9 +9,13 @@ optionalDirectives, enumValueDefinition, inputFieldsDefinition,+ parseOperationType,+ argumentsDefinition,+ parseDirectiveLocation, ) where +import Data.Functor (($>)) -- MORPHEUS import Data.Morpheus.Parsing.Internal.Arguments ( maybeArguments,@@ -20,14 +25,14 @@ getLocation, ) import Data.Morpheus.Parsing.Internal.Terms- ( keyword,- litAssignment,- operator,+ ( ignoredTokens,+ keyword, optDescription, parseName, parseType, parseTypeName, setOf,+ symbol, uniqTuple, ) import Data.Morpheus.Parsing.Internal.Value@@ -38,6 +43,7 @@ ( ArgumentsDefinition (..), DataEnumValue (..), Directive (..),+ DirectiveLocation (..), FieldContent (..), FieldDefinition (..), FieldName,@@ -45,21 +51,28 @@ IN, InputFieldsDefinition, OUT,+ OperationType (..), TypeName, Value, )+import Data.Text (pack) import Text.Megaparsec- ( label,+ ( (<|>),+ choice,+ label, many, optional, )+import Text.Megaparsec.Char (string) -- EnumValueDefinition: https://graphql.github.io/graphql-spec/June2018/#EnumValueDefinition -- -- EnumValueDefinition -- Description(opt) EnumValue Directives(Const)(opt) ---enumValueDefinition :: Parser DataEnumValue+enumValueDefinition ::+ Parse (Value s) =>+ Parser (DataEnumValue s) enumValueDefinition = label "EnumValueDefinition" $ do enumDescription <- optDescription enumName <- parseTypeName@@ -71,11 +84,13 @@ -- InputValueDefinition -- Description(opt) Name : Type DefaultValue(opt) Directives (Const)(opt) ---inputValueDefinition :: Parser (FieldDefinition IN)+inputValueDefinition ::+ Parse (Value s) =>+ Parser (FieldDefinition IN s) inputValueDefinition = label "InputValueDefinition" $ do fieldDescription <- optDescription fieldName <- parseName- litAssignment -- ':'+ symbol ':' fieldType <- parseType fieldContent <- optional (DefaultInputValue <$> parseDefaultValue) fieldDirectives <- optionalDirectives@@ -86,7 +101,9 @@ -- ArgumentsDefinition: -- ( InputValueDefinition(list) ) ---argumentsDefinition :: Parser ArgumentsDefinition+argumentsDefinition ::+ Parse (Value s) =>+ Parser (ArgumentsDefinition s) argumentsDefinition = label "ArgumentsDefinition" $ uniqTuple inputValueDefinition@@ -96,18 +113,20 @@ -- FieldsDefinition : -- { FieldDefinition(list) } ---fieldsDefinition :: Parser (FieldsDefinition OUT)+fieldsDefinition ::+ Parse (Value s) =>+ Parser (FieldsDefinition OUT s) fieldsDefinition = label "FieldsDefinition" $ setOf fieldDefinition -- FieldDefinition -- Description(opt) Name ArgumentsDefinition(opt) : Type Directives(Const)(opt) ---fieldDefinition :: Parser (FieldDefinition OUT)+fieldDefinition :: Parse (Value s) => Parser (FieldDefinition OUT s) fieldDefinition = label "FieldDefinition" $ do fieldDescription <- optDescription fieldName <- parseName fieldContent <- optional (FieldArgs <$> argumentsDefinition)- litAssignment -- ':'+ symbol ':' fieldType <- parseType fieldDirectives <- optionalDirectives pure FieldDefinition {..}@@ -116,7 +135,9 @@ -- InputFieldsDefinition: -- { InputValueDefinition(list) } ---inputFieldsDefinition :: Parser InputFieldsDefinition+inputFieldsDefinition ::+ Parse (Value s) =>+ Parser (InputFieldsDefinition s) inputFieldsDefinition = label "InputFieldsDefinition" $ setOf inputValueDefinition -- Directives : https://graphql.github.io/graphql-spec/June2018/#sec-Language.Directives@@ -135,7 +156,7 @@ directive :: Parse (Value s) => Parser (Directive s) directive = label "Directive" $ do directivePosition <- getLocation- operator '@'+ symbol '@' directiveName <- parseName directiveArgs <- maybeArguments pure Directive {..}@@ -149,3 +170,43 @@ typeDeclaration kind = do keyword kind parseTypeName++parseOperationType :: Parser OperationType+parseOperationType = label "OperationType" $ do+ kind <-+ (string "query" $> Query)+ <|> (string "mutation" $> Mutation)+ <|> (string "subscription" $> Subscription)+ ignoredTokens+ return kind++parseDirectiveLocation :: Parser DirectiveLocation+parseDirectiveLocation =+ label+ "DirectiveLocation"+ ( choice $+ toKeyword+ <$> [ FIELD_DEFINITION,+ FRAGMENT_DEFINITION,+ FRAGMENT_SPREAD,+ INLINE_FRAGMENT,+ ARGUMENT_DEFINITION,+ INTERFACE,+ ENUM_VALUE,+ INPUT_OBJECT,+ INPUT_FIELD_DEFINITION,+ SCHEMA,+ SCALAR,+ OBJECT,+ QUERY,+ MUTATION,+ SUBSCRIPTION,+ UNION,+ ENUM,+ FIELD+ ]+ )+ <* ignoredTokens++toKeyword :: Show a => a -> Parser a+toKeyword x = string (pack $ show x) $> x
src/Data/Morpheus/Parsing/Internal/Terms.hs view
@@ -3,12 +3,10 @@ {-# LANGUAGE TupleSections #-} module Data.Morpheus.Parsing.Internal.Terms- ( token,- qualifier,+ ( name, variable,- spaceAndComments,- spaceAndComments1,- pipeLiteral,+ ignoredTokens,+ parseString, ------------- collection, setOf,@@ -19,19 +17,17 @@ parseNonNull, parseAssignment, parseWrappedType,- litEquals,- litAssignment,- parseTuple, parseAlias, sepByAnd, parseName, parseType, keyword,- operator,+ symbol, optDescription,- optionalList,+ optionalCollection, parseNegativeSign, parseTypeName,+ pipe, ) where @@ -40,8 +36,10 @@ -- MORPHEUS import Data.Morpheus.Internal.Utils- ( KeyOf,+ ( Collection,+ KeyOf, Listable (..),+ empty, fromElems, ) import Data.Morpheus.Parsing.Internal.Internal@@ -67,16 +65,16 @@ ( (<?>), (<|>), between,+ choice, label, many, manyTill, optional, sepBy,+ sepBy1, sepEndBy,- skipMany, skipManyTill, try,- try, ) import Text.Megaparsec.Char ( char,@@ -89,57 +87,55 @@ string, ) --- Name : https://graphql.github.io/graphql-spec/June2018/#sec-Names------ Name :: /[_A-Za-z][_0-9A-Za-z]*/---- parseNegativeSign :: Parser Bool-parseNegativeSign = (char '-' $> True <* spaceAndComments) <|> pure False+parseNegativeSign = (char '-' $> True <* ignoredTokens) <|> pure False parseName :: Parser FieldName-parseName = FieldName <$> token+parseName = FieldName <$> name parseTypeName :: Parser TypeName-parseTypeName = TypeName <$> token+parseTypeName = label "TypeName" $ TypeName <$> name keyword :: FieldName -> Parser ()-keyword (FieldName word) = string word *> space1 *> spaceAndComments+keyword (FieldName word) = string word *> space1 *> ignoredTokens -operator :: Char -> Parser ()-operator x = char x *> spaceAndComments+symbol :: Char -> Parser ()+symbol x = char x *> ignoredTokens -- LITERALS braces :: Parser [a] -> Parser [a] braces = between- (char '{' *> spaceAndComments)- (char '}' *> spaceAndComments)--pipeLiteral :: Parser ()-pipeLiteral = char '|' *> spaceAndComments--litEquals :: Parser ()-litEquals = char '=' *> spaceAndComments--litAssignment :: Parser ()-litAssignment = char ':' *> spaceAndComments+ (char '{' *> ignoredTokens)+ (char '}' *> ignoredTokens) -- PRIMITIVE -------------------------------------token :: Parser Token-token = label "token" $ do- firstChar <- letterChar <|> char '_'- restToken <- many $ letterChar <|> char '_' <|> digitChar- spaceAndComments- return $ pack $ firstChar : restToken -qualifier :: Parser (FieldName, Position)-qualifier = label "qualifier" $ do- position <- getLocation- value <- parseName- return (value, position)+-- 2.1.9 Names+-- https://spec.graphql.org/draft/#Name+-- Name ::+-- NameStart NameContinue[list,opt]+--+name :: Parser Token+name = label "Name" $ do+ start <- nameStart+ continue <- nameContinue+ ignoredTokens+ pure $ pack (start : continue) +-- NameStart::+-- Letter+-- _+nameStart :: Parser Char+nameStart = letterChar <|> char '_'++-- NameContinue::+-- Letter+-- Digit+nameContinue :: Parser String+nameContinue = many (letterChar <|> char '_' <|> digitChar)+ -- Variable : https://graphql.github.io/graphql-spec/June2018/#Variable -- -- Variable : $Name@@ -149,37 +145,61 @@ refPosition <- getLocation _ <- char '$' refName <- parseName- spaceAndComments+ ignoredTokens pure $ Ref {refName, refPosition} -spaceAndComments1 :: Parser ()-spaceAndComments1 = space1 *> spaceAndComments- -- Descriptions: https://graphql.github.io/graphql-spec/June2018/#Description -- -- Description: -- StringValue--- TODO: should support """ and "---+parseDescription :: Parser Description+parseDescription = strip <$> parseString+ optDescription :: Parser (Maybe Description) optDescription = optional parseDescription -parseDescription :: Parser Description-parseDescription =- strip . pack <$> (blockDescription <|> singleLine) <* spaceAndComments+parseString :: Parser Token+parseString = blockString <|> singleLineString++blockString :: Parser Token+blockString = stringWith (string "\"\"\"") (printChar <|> newline)++singleLineString :: Parser Token+singleLineString = stringWith (char '"') escapedChar++stringWith :: Parser quote -> Parser Char -> Parser Token+stringWith quote parser =+ pack+ <$> ( quote+ *> manyTill parser quote+ <* ignoredTokens+ )++escapedChar :: Parser Char+escapedChar = label "EscapedChar" $ printChar >>= handleEscape++handleEscape :: Char -> Parser Char+handleEscape '\\' = choice escape+handleEscape x = pure x++escape :: [Parser Char]+escape = map escapeCh escapeOptions where- blockDescription =- blockQuotes- *> manyTill (printChar <|> newline) blockQuotes- <* spaceAndComments- where- blockQuotes = string "\"\"\""- ----------------------------- singleLine =- stringQuote *> manyTill printChar stringQuote <* spaceAndComments- where- stringQuote = char '"'+ escapeCh :: (Char, Char) -> Parser Char+ escapeCh (code, replacement) = char code $> replacement +escapeOptions :: [(Char, Char)]+escapeOptions =+ [ ('b', '\b'),+ ('n', '\n'),+ ('f', '\f'),+ ('r', '\r'),+ ('t', '\t'),+ ('\\', '\\'),+ ('\"', '\"'),+ ('/', '/')+ ]+ -- Ignored Tokens : https://graphql.github.io/graphql-spec/June2018/#sec-Source-Text.Ignored-Tokens -- Ignored: -- UnicodeBOM@@ -187,57 +207,67 @@ -- LineTerminator -- Comment -- Comma--- TODO: implement as in specification-spaceAndComments :: Parser ()-spaceAndComments = ignoredTokens- ignoredTokens :: Parser () ignoredTokens =- label "IgnoredTokens" $ space *> skipMany inlineComment *> space- where- inlineComment = char '#' *> skipManyTill printChar newline *> space+ label "IgnoredTokens" $+ space+ *> many ignored+ *> space +ignored :: Parser ()+ignored =+ label "Ignored" $+ comment+ <|> comma++comment :: Parser ()+comment =+ label "Comment" $+ char '#' *> skipManyTill printChar newline *> space++comma :: Parser ()+comma = label "Comma" $ char ',' *> space+ ------------------------------------------------------------------------ --- COMPLEX sepByAnd :: Parser a -> Parser [a]-sepByAnd entry = entry `sepBy` (optional (char '&') *> spaceAndComments)+sepByAnd entry = entry `sepBy` (optional (symbol '&') *> ignoredTokens) +pipe :: Parser a -> Parser [a]+pipe x = optional (symbol '|') *> (x `sepBy1` symbol '|')+ ----------------------------- collection :: Parser a -> Parser [a]-collection entry = braces (entry `sepEndBy` many (char ',' *> spaceAndComments))+collection entry = braces (entry `sepEndBy` ignoredTokens) -setOf :: (Listable a coll, KeyOf a) => Parser a -> Parser coll+setOf :: (Listable a coll, KeyOf k a) => Parser a -> Parser coll setOf = collection >=> fromElems +optionalCollection :: Collection a c => Parser c -> Parser c+optionalCollection x = x <|> pure empty+ parseNonNull :: Parser [DataTypeWrapper] parseNonNull = do wrapper <- (char '!' $> [NonNullType]) <|> pure []- spaceAndComments+ ignoredTokens return wrapper -optionalList :: Parser [a] -> Parser [a]-optionalList x = x <|> pure []--parseTuple :: Parser a -> Parser [a]-parseTuple parser =+uniqTuple :: (Listable a coll, KeyOf k a) => Parser a -> Parser coll+uniqTuple parser = label "Tuple" $ between- (char '(' *> spaceAndComments)- (char ')' *> spaceAndComments)- ( parser `sepBy` (many (char ',') *> spaceAndComments) <?> "empty Tuple value!"- )--uniqTuple :: (Listable a coll, KeyOf a) => Parser a -> Parser coll-uniqTuple = parseTuple >=> fromElems+ (char '(' *> ignoredTokens)+ (char ')' *> ignoredTokens)+ (parser `sepBy` ignoredTokens <?> "empty Tuple value!")+ >>= fromElems -uniqTupleOpt :: (Listable a coll, KeyOf a) => Parser a -> Parser coll-uniqTupleOpt = optionalList . parseTuple >=> fromElems+uniqTupleOpt :: (Listable a coll, Collection a coll, KeyOf k a) => Parser a -> Parser coll+uniqTupleOpt x = uniqTuple x <|> pure empty parseAssignment :: (Show a, Show b) => Parser a -> Parser b -> Parser (a, b) parseAssignment nameParser valueParser = label "assignment" $ do name' <- nameParser- litAssignment+ symbol ':' value' <- valueParser pure (name', value') @@ -248,8 +278,7 @@ -- parseTypeCondition :: Parser TypeName parseTypeCondition = do- _ <- string "on"- space1+ keyword "on" parseTypeName spreadLiteral :: Parser Position@@ -259,30 +288,13 @@ space return index -parseWrappedType :: Parser ([DataTypeWrapper], TypeName)-parseWrappedType = (unwrapped <|> wrapped) <* spaceAndComments- where- unwrapped :: Parser ([DataTypeWrapper], TypeName)- unwrapped = ([],) <$> parseTypeName <* spaceAndComments- ----------------------------------------------- wrapped :: Parser ([DataTypeWrapper], TypeName)- wrapped =- between- (char '[' *> spaceAndComments)- (char ']' *> spaceAndComments)- ( do- (wrappers, name) <- unwrapped <|> wrapped- nonNull' <- parseNonNull- return ((ListType : nonNull') ++ wrappers, name)- )- -- Field Alias : https://graphql.github.io/graphql-spec/June2018/#sec-Field-Alias -- Alias -- Name: parseAlias :: Parser (Maybe FieldName) parseAlias = try (optional alias) <|> pure Nothing where- alias = label "alias" $ parseName <* char ':' <* spaceAndComments+ alias = label "alias" $ parseName <* char ':' <* ignoredTokens parseType :: Parser TypeRef parseType = do@@ -294,3 +306,20 @@ typeArgs = Nothing, typeWrappers = toHSWrappers $ nonNull ++ wrappers }++parseWrappedType :: Parser ([DataTypeWrapper], TypeName)+parseWrappedType = (unwrapped <|> wrapped) <* ignoredTokens+ where+ unwrapped :: Parser ([DataTypeWrapper], TypeName)+ unwrapped = ([],) <$> parseTypeName <* ignoredTokens+ ----------------------------------------------+ wrapped :: Parser ([DataTypeWrapper], TypeName)+ wrapped =+ between+ (char '[' *> ignoredTokens)+ (char ']' *> ignoredTokens)+ ( do+ (wrappers, tname) <- unwrapped <|> wrapped+ nonNull' <- parseNonNull+ return ((ListType : nonNull') ++ wrappers, tname)+ )
src/Data/Morpheus/Parsing/Internal/Value.hs view
@@ -17,32 +17,29 @@ ( Parser, ) import Data.Morpheus.Parsing.Internal.Terms- ( litEquals,+ ( ignoredTokens, parseAssignment, parseName, parseNegativeSign,+ parseString, parseTypeName, setOf,- spaceAndComments,+ symbol, variable, ) import Data.Morpheus.Types.Internal.AST- ( FieldName,+ ( CONST,+ FieldName, ObjectEntry (..),- OrderedMap,+ OrdMap, RAW,- ResolvedValue, ScalarValue (..),- VALID, Value (..), decodeScientific, )-import Data.Text (pack) import Text.Megaparsec ( (<|>),- anySingleBut, between,- choice, label, many, sepBy,@@ -74,53 +71,38 @@ enumValue :: Parser (Value a) enumValue = do enum <- Enum <$> parseTypeName- spaceAndComments+ ignoredTokens return enum -escaped :: Parser Char-escaped = label "escaped" $ do- x <- anySingleBut '\"'- if x == '\\' then choice (zipWith escapeChar codes replacements) else pure x- where- replacements = ['\b', '\n', '\f', '\r', '\t', '\\', '\"', '/']- codes = ['b', 'n', 'f', 'r', 't', '\\', '\"', '/']- escapeChar code replacement = char code >> return replacement- stringValue :: Parser (Value a)-stringValue =- label "stringValue" $- Scalar . String . pack- <$> between- (char '"')- (char '"')- (many escaped)+stringValue = label "stringValue" $ Scalar . String <$> parseString listValue :: Parser a -> Parser [a] listValue parser = label "ListValue" $ between- (char '[' *> spaceAndComments)- (char ']' *> spaceAndComments)- (parser `sepBy` (many (char ',') *> spaceAndComments))+ (char '[' *> ignoredTokens)+ (char ']' *> ignoredTokens)+ (parser `sepBy` (many (char ',') *> ignoredTokens)) objectEntry :: Parser (Value a) -> Parser (ObjectEntry a) objectEntry parser = label "ObjectEntry" $ do (entryName, entryValue) <- parseAssignment parseName parser pure ObjectEntry {entryName, entryValue} -objectValue :: Parser (Value a) -> Parser (OrderedMap FieldName (ObjectEntry a))+objectValue :: Parser (Value a) -> Parser (OrdMap FieldName (ObjectEntry a)) objectValue = label "ObjectValue" . setOf . objectEntry parsePrimitives :: Parser (Value a) parsePrimitives = valueNull <|> booleanValue <|> valueNumber <|> enumValue <|> stringValue -parseDefaultValue :: Parser ResolvedValue+parseDefaultValue :: Parser (Value s) parseDefaultValue = do- litEquals+ symbol '=' parseV where- parseV :: Parser ResolvedValue+ parseV :: Parser (Value s) parseV = structValue parseV class Parse a where@@ -129,7 +111,7 @@ instance Parse (Value RAW) where parse = (VariableValue <$> variable) <|> structValue parse -instance Parse (Value VALID) where+instance Parse (Value CONST) where parse = structValue parse structValue :: Parser (Value a) -> Parser (Value a)@@ -139,4 +121,4 @@ <|> (Object <$> objectValue parser) <|> (List <$> listValue parser) )- <* spaceAndComments+ <* ignoredTokens
− src/Data/Morpheus/Parsing/JSONSchema/Parse.hs
@@ -1,116 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeOperators #-}--module Data.Morpheus.Parsing.JSONSchema.Parse- ( decodeIntrospection,- )-where--import Data.Aeson-import Data.ByteString.Lazy (ByteString)-import Data.Morpheus.Error.Internal (internalError)-import Data.Morpheus.Internal.Utils- ( fromElems,- )-import Data.Morpheus.Parsing.JSONSchema.Types- ( EnumValue (..),- Field (..),- InputValue (..),- Introspection (..),- Schema (..),- Type (..),- )-import Data.Morpheus.Schema.TypeKind (TypeKind (..))-import Data.Morpheus.Types.IO (JSONResponse (..))-import qualified Data.Morpheus.Types.Internal.AST as AST- ( Schema,- )-import Data.Morpheus.Types.Internal.AST- ( ANY,- ArgumentsDefinition (..),- DataTypeWrapper (..),- FieldDefinition,- IN,- OUT,- TypeContent (..),- TypeDefinition (..),- TypeName,- TypeWrapper,- createArgument,- createEnumType,- createScalarType,- createType,- createUnionType,- mkField,- mkObjectField,- msg,- toAny,- toHSWrappers,- )-import Data.Morpheus.Types.Internal.Resolving- ( Eventless,- )-import Data.Semigroup ((<>))--decodeIntrospection :: ByteString -> Eventless AST.Schema-decodeIntrospection jsonDoc = case jsonSchema of- Left errors -> internalError $ msg errors- Right JSONResponse {responseData = Just Introspection {__schema = Schema {types}}} ->- traverse parse types >>= fromElems . concat- Right res -> internalError (msg $ show res)- where- jsonSchema :: Either String (JSONResponse Introspection)- jsonSchema = eitherDecode jsonDoc--class ParseJSONSchema a b where- parse :: a -> Eventless b--instance ParseJSONSchema Type [TypeDefinition ANY] where- parse Type {name = Just typeName, kind = SCALAR} =- pure [createScalarType typeName]- parse Type {name = Just typeName, kind = ENUM, enumValues = Just enums} =- pure [createEnumType typeName (map enumName enums)]- parse Type {name = Just typeName, kind = UNION, possibleTypes = Just unions} =- case traverse name unions of- Nothing -> internalError "ERROR: GQL ERROR"- Just uni -> pure [toAny $ createUnionType typeName uni]- parse Type {name = Just typeName, kind = INPUT_OBJECT, inputFields = Just iFields} =- do- (fields :: [FieldDefinition IN]) <- traverse parse iFields- fs <- fromElems fields- pure [createType typeName $ DataInputObject fs]- parse Type {name = Just typeName, kind = OBJECT, fields = Just oFields} =- do- (fields :: [FieldDefinition OUT]) <- traverse parse oFields- fs <- fromElems fields- pure [createType typeName $ DataObject [] fs]- parse _ = pure []--instance ParseJSONSchema Field (FieldDefinition OUT) where- parse Field {fieldName, fieldArgs, fieldType} = do- fType <- fieldTypeFromJSON fieldType- args <- traverse genArg fieldArgs >>= fromElems- pure $ mkObjectField (ArgumentsDefinition Nothing args) fieldName fType- where- genArg InputValue {inputName = argName, inputType = argType} =- createArgument argName <$> fieldTypeFromJSON argType--instance ParseJSONSchema InputValue (FieldDefinition IN) where- parse InputValue {inputName, inputType} = mkField inputName <$> fieldTypeFromJSON inputType--fieldTypeFromJSON :: Type -> Eventless ([TypeWrapper], TypeName)-fieldTypeFromJSON = fmap toHs . fieldTypeRec []- where- toHs (w, t) = (toHSWrappers w, t)- fieldTypeRec ::- [DataTypeWrapper] -> Type -> Eventless ([DataTypeWrapper], TypeName)- fieldTypeRec acc Type {kind = LIST, ofType = Just ofType} =- fieldTypeRec (ListType : acc) ofType- fieldTypeRec acc Type {kind = NON_NULL, ofType = Just ofType} =- fieldTypeRec (NonNullType : acc) ofType- fieldTypeRec acc Type {name = Just name} = pure (acc, name)- fieldTypeRec _ x = internalError $ "Unsuported Field" <> msg (show x)
− src/Data/Morpheus/Parsing/JSONSchema/Types.hs
@@ -1,86 +0,0 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}--module Data.Morpheus.Parsing.JSONSchema.Types- ( Introspection (..),- Schema (..),- Type (..),- Field (..),- InputValue (..),- EnumValue (..),- )-where--import Data.Aeson------ MORPHEUS-import Data.Morpheus.Schema.TypeKind (TypeKind)-import Data.Morpheus.Types.Internal.AST- ( FieldName,- TypeName,- )-import GHC.Generics (Generic)---- TYPES FOR DECODING JSON INTROSPECTION----newtype Introspection = Introspection- { __schema :: Schema- }- deriving (Generic, Show, FromJSON)--newtype Schema = Schema- { types :: [Type]- }- deriving (Generic, Show, FromJSON)---- TYPE-data Type = Type- { kind :: TypeKind,- name :: Maybe TypeName,- fields :: Maybe [Field],- interfaces :: Maybe [Type],- possibleTypes :: Maybe [Type],- enumValues :: Maybe [EnumValue],- inputFields :: Maybe [InputValue],- ofType :: Maybe Type- }- deriving (Generic, Show, FromJSON)---- FIELD-data Field = Field- { fieldName :: FieldName,- fieldArgs :: [InputValue],- fieldType :: Type- }- deriving (Show, Generic)--instance FromJSON Field where- parseJSON = withObject "Field" objectParser- where- objectParser o = Field <$> o .: "name" <*> o .: "args" <*> o .: "type"---- INPUT-data InputValue = InputValue- { inputName :: FieldName,- inputType :: Type- }- deriving (Show, Generic)--instance FromJSON InputValue where- parseJSON = withObject "InputValue" objectParser- where- objectParser o = InputValue <$> o .: "name" <*> o .: "type"---- ENUM-newtype EnumValue = EnumValue- { enumName :: TypeName- }- deriving (Generic, Show)--instance FromJSON EnumValue where- parseJSON = withObject "EnumValue" objectParser- where- objectParser o = EnumValue <$> o .: "name"
src/Data/Morpheus/Parsing/Request/Operation.hs view
@@ -6,10 +6,6 @@ ) where -import Data.Functor (($>))------ MORPHEUS- import Data.Morpheus.Internal.Utils ( empty, )@@ -19,12 +15,12 @@ ) import Data.Morpheus.Parsing.Internal.Pattern ( optionalDirectives,+ parseOperationType, ) import Data.Morpheus.Parsing.Internal.Terms- ( operator,- parseName,+ ( parseName, parseType,- spaceAndComments1,+ symbol, uniqTupleOpt, variable, )@@ -48,7 +44,6 @@ label, optional, )-import Text.Megaparsec.Char (string) -- Variables : https://graphql.github.io/graphql-spec/June2018/#VariableDefinition --@@ -58,7 +53,7 @@ variableDefinition :: Parser (Variable RAW) variableDefinition = label "VariableDefinition" $ do (Ref variableName variablePosition) <- variable- operator ':'+ symbol ':' variableType <- parseType variableValue <- DefaultValue <$> optional parseDefaultValue pure Variable {..}@@ -79,15 +74,6 @@ operationDirectives <- optionalDirectives operationSelection <- parseSelectionSet pure Operation {..}--parseOperationType :: Parser OperationType-parseOperationType = label "OperationType" $ do- kind <-- (string "query" $> Query)- <|> (string "mutation" $> Mutation)- <|> (string "subscription" $> Subscription)- spaceAndComments1- return kind parseAnonymousQuery :: Parser (Operation RAW) parseAnonymousQuery = label "AnonymousQuery" $ do
src/Data/Morpheus/Parsing/Request/Parser.hs view
@@ -18,7 +18,7 @@ processParser, ) import Data.Morpheus.Parsing.Internal.Terms- ( spaceAndComments,+ ( ignoredTokens, ) import Data.Morpheus.Parsing.Request.Operation ( parseOperation,@@ -44,7 +44,7 @@ request :: Parser GQLQuery request = label "GQLQuery" $ do- spaceAndComments+ ignoredTokens operation <- parseOperation fragments <- manyTill parseFragmentDefinition eof >>= fromElems pure GQLQuery {operation, fragments, inputVariables = []}
src/Data/Morpheus/Parsing/Request/Selection.hs view
@@ -108,7 +108,7 @@ -- FragmentDefinition: -- fragment FragmentName TypeCondition Directives(opt) SelectionSet ---parseFragmentDefinition :: Parser Fragment+parseFragmentDefinition :: Parser (Fragment RAW) parseFragmentDefinition = label "FragmentDefinition" $ do keyword "fragment" fragmentPosition <- getLocation@@ -125,7 +125,7 @@ fragmentPosition <- spreadLiteral InlineFragment <$> fragmentBody "INLINE_FRAGMENT" fragmentPosition -fragmentBody :: FieldName -> Position -> Parser Fragment+fragmentBody :: FieldName -> Position -> Parser (Fragment RAW) fragmentBody fragmentName fragmentPosition = label "FragmentBody" $ do fragmentType <- parseTypeCondition fragmentDirectives <- optionalDirectives
src/Data/Morpheus/QuasiQuoter.hs view
@@ -19,7 +19,7 @@ ) import Data.Morpheus.Parser ( parseRequest,- parseTypeDefinitions,+ parseTypeSystemDefinition, ) import Data.Morpheus.Types.IO (GQLRequest (..)) import Data.Morpheus.Types.Internal.Resolving@@ -72,7 +72,7 @@ error $ things ++ " are not supported by the GraphQL QuasiQuoter" dslExpression :: Text -> Q Exp-dslExpression doc = case parseTypeDefinitions doc of+dslExpression doc = case parseTypeSystemDefinition doc of Failure errors -> fail (renderGQLErrors errors) Success {result, warnings} -> gqlWarnings warnings >> [|result|]
src/Data/Morpheus/Rendering/RenderGQL.hs view
@@ -1,26 +1,51 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Rendering.RenderGQL ( RenderGQL (..), renderObject,- renderIndent,+ renderMembers,+ newline,+ renderArguments,+ renderEntry,+ space,+ Rendering, ) where -- MORPHEUS++import Data.Foldable (null)+import Data.Functor ((<$>))+import Data.Maybe (Maybe, maybe) import Data.Semigroup ((<>)) import Data.Text ( Text, intercalate, pack, )+import Prelude+ ( (.),+ Bool (..),+ Float,+ Int,+ fmap,+ otherwise,+ show,+ ) type Rendering = Text class RenderGQL a where render :: a -> Rendering +instance+ RenderGQL a =>+ RenderGQL (Maybe a)+ where+ render = maybe "" render+ instance RenderGQL Int where render = pack . show @@ -34,9 +59,35 @@ render True = "true" render False = "false" -renderIndent :: Rendering-renderIndent = " "+indent :: Rendering+indent = " " -renderObject :: (a -> Rendering) -> [a] -> Rendering-renderObject f list =- " { \n " <> intercalate ("\n" <> renderIndent) (map f list) <> "\n}"+space :: Rendering+space = " "++newline :: Rendering+newline = "\n"++indentNewline :: Rendering+indentNewline = newline <> indent++renderAtNewLine :: (RenderGQL a) => [a] -> Rendering+renderAtNewLine elems = indentNewline <> intercalate indentNewline (fmap render elems)++renderObject :: (RenderGQL a) => [a] -> Rendering+renderObject fields = " {" <> renderAtNewLine fields <> "\n}"++renderMembers :: (RenderGQL a) => [a] -> Rendering+renderMembers members = intercalate (space <> "|" <> space) (fmap render members)++renderArguments :: (RenderGQL a) => [a] -> Rendering+renderArguments arguments+ | null arguments = ""+ | otherwise = "(" <> intercalate ", " (render <$> arguments) <> ")"++renderEntry ::+ (RenderGQL name, RenderGQL value) =>+ name ->+ value ->+ Rendering+renderEntry name value = render name <> ": " <> render value
src/Data/Morpheus/Rendering/RenderIntrospection.hs view
@@ -2,13 +2,11 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TupleSections #-}-{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Rendering.RenderIntrospection ( render,@@ -16,20 +14,21 @@ ) where -import Data.Maybe (isJust) -- Morpheus +import Control.Applicative (pure)+import Control.Monad (Monad (..))+import Data.Foldable (foldr)+import Data.Functor ((<$>))+import Data.List (filter)+import Data.Maybe (Maybe (..), isJust, maybe) import Data.Morpheus.Internal.Utils ( Failure, elems, failure,- fromElems, selectBy,- selectOr, ) import qualified Data.Morpheus.Rendering.RenderGQL as GQL (RenderGQL (..))-import Data.Morpheus.Schema.TypeKind (TypeKind (..))-import qualified Data.Morpheus.Types.Internal.AST as AST (TypeKind (..)) import Data.Morpheus.Types.Internal.AST ( ANY, ArgumentsDefinition (..),@@ -47,31 +46,30 @@ IN, Message, OUT,- Object,- ObjectEntry (..), QUERY,- RESOLVED, Schema, TRUE, TypeContent (..), TypeDefinition (..),+ TypeKind (..), TypeName (..), TypeRef (..), UnionMember (..), VALID, Value (..),- createInputUnionFields, fieldVisibility, kindOf, lookupDeprecated, lookupDeprecatedReason,+ mkInputUnionFields, msg,+ possibleInterfaceTypes, toGQLWrapper, ) import Data.Morpheus.Types.Internal.Resolving- ( Context (..),- ResModel,+ ( ResModel, Resolver,+ ResolverContext (..), mkBoolean, mkList, mkNull,@@ -81,8 +79,13 @@ ) import Data.Semigroup ((<>)) import Data.Text (pack)--type Result e m a = Resolver QUERY e m a+import Data.Traversable (traverse)+import Prelude+ ( ($),+ (.),+ Bool,+ show,+ ) class ( Monad m,@@ -91,7 +94,7 @@ ) => WithSchema m where- getSchema :: m Schema+ getSchema :: m (Schema VALID) instance Monad m => WithSchema (Resolver QUERY e m) where getSchema = schema <$> unsafeInternalContext@@ -99,7 +102,7 @@ selectType :: WithSchema m => TypeName ->- m (TypeDefinition ANY)+ m (TypeDefinition ANY VALID) selectType name = getSchema >>= selectBy (" INTERNAL: INTROSPECTION Type not Found: \"" <> msg name <> "\"") name@@ -119,13 +122,20 @@ instance RenderIntrospection Description where render = pure . mkString -instance RenderIntrospection TypeKind where- render = pure . mkString . pack . show- instance RenderIntrospection a => RenderIntrospection [a] where render ls = mkList <$> traverse render ls -instance RenderIntrospection DirectiveDefinition where+instance RenderIntrospection a => RenderIntrospection (Maybe a) where+ render (Just value) = render value+ render Nothing = pure mkNull++instance RenderIntrospection Bool where+ render = pure . mkBoolean++instance RenderIntrospection TypeKind where+ render = pure . mkString . GQL.render++instance RenderIntrospection (DirectiveDefinition VALID) where render DirectiveDefinition { directiveDefinitionName,@@ -145,7 +155,7 @@ instance RenderIntrospection DirectiveLocation where render locations = pure $ mkString (pack $ show locations) -instance RenderIntrospection (TypeDefinition a) where+instance RenderIntrospection (TypeDefinition cat VALID) where render TypeDefinition { typeName,@@ -153,44 +163,66 @@ typeContent } = pure $ renderContent typeContent where- __type :: Monad m => TypeKind -> [(FieldName, Resolver QUERY e m (ResModel QUERY e m))] -> ResModel QUERY e m+ __type ::+ Monad m =>+ TypeKind ->+ [(FieldName, Resolver QUERY e m (ResModel QUERY e m))] ->+ ResModel QUERY e m __type kind = mkType kind typeName typeDescription- renderContent :: Monad m => TypeContent bool a -> ResModel QUERY e m- renderContent DataScalar {} = __type SCALAR []- renderContent (DataEnum enums) = __type ENUM [("enumValues", render enums)]+ renderContent ::+ Monad m =>+ TypeContent bool a VALID ->+ ResModel QUERY e m+ renderContent DataScalar {} = __type KindScalar []+ renderContent (DataEnum enums) = __type KindEnum [("enumValues", render enums)] renderContent (DataInputObject inputFiels) = __type- INPUT_OBJECT+ KindInputObject [("inputFields", render inputFiels)] renderContent DataObject {objectImplements, objectFields} = createObjectType typeName typeDescription objectImplements objectFields renderContent (DataUnion union) = __type- UNION+ KindUnion [("possibleTypes", render union)] renderContent (DataInputUnion members) = __type- INPUT_OBJECT+ KindInputObject [ ( "inputFields", render- $ createInputUnionFields typeName- $ filter visibility members+ ( mkInputUnionFields typeName $+ filter visibility members ::+ FieldsDefinition IN VALID+ ) ) ] renderContent (DataInterface fields) = __type- INTERFACE+ KindInterface [ ("fields", render fields),- ("possibleTypes", interfacePossibleTypes typeName)+ ("possibleTypes", renderPossibleTypes typeName) ] -instance RenderIntrospection (UnionMember OUT) where+instance RenderIntrospection (UnionMember OUT s) where render UnionMember {memberName} = selectType memberName >>= render -instance RenderIntrospection (FieldDefinition cat) => RenderIntrospection (FieldsDefinition cat) where+instance+ RenderIntrospection (FieldDefinition cat s) =>+ RenderIntrospection (FieldsDefinition cat s)+ where render = render . filter fieldVisibility . elems -instance RenderIntrospection (FieldDefinition OUT) where+instance RenderIntrospection (FieldContent TRUE IN VALID) where+ render = render . defaultInputValue++instance RenderIntrospection (Value VALID) where+ render Null = pure mkNull+ render x = pure $ mkString $ GQL.render x++instance+ RenderIntrospection+ (FieldDefinition OUT VALID)+ where render FieldDefinition {..} = pure $ mkObject "__Field"@@ -201,13 +233,13 @@ ] <> renderDeprecated fieldDirectives -instance RenderIntrospection (FieldContent TRUE OUT) where+instance RenderIntrospection (FieldContent TRUE OUT VALID) where render (FieldArgs args) = render args -instance RenderIntrospection ArgumentsDefinition where+instance RenderIntrospection (ArgumentsDefinition VALID) where render ArgumentsDefinition {arguments} = mkList <$> traverse render (elems arguments) -instance RenderIntrospection (FieldDefinition IN) where+instance RenderIntrospection (FieldDefinition IN VALID) where render FieldDefinition {..} = pure $ mkObject@@ -215,10 +247,10 @@ [ renderName fieldName, description fieldDescription, type' fieldType,- defaultValue fieldType (fmap defaultInputValue fieldContent)+ defaultValue fieldContent ] -instance RenderIntrospection DataEnumValue where+instance RenderIntrospection (DataEnumValue VALID) where render DataEnumValue {enumName, enumDescription, enumDirectives} = pure $ mkObject "__Field" $ [ renderName enumName,@@ -228,7 +260,7 @@ instance RenderIntrospection TypeRef where render TypeRef {typeConName, typeWrappers} = do- kind <- lookupKind typeConName+ kind <- kindOf <$> selectType typeConName let currentType = mkType kind typeConName Nothing [] pure $ foldr wrap currentType (toGQLWrapper typeWrappers) where@@ -239,50 +271,30 @@ [ renderKind (wrapperKind wrapper), ("ofType", pure contentType) ]- wrapperKind ListType = LIST- wrapperKind NonNullType = NON_NULL+ wrapperKind ListType = KindList+ wrapperKind NonNullType = KindNonNull -interfacePossibleTypes ::+renderPossibleTypes :: (Monad m) => TypeName -> Resolver QUERY e m (ResModel QUERY e m)-interfacePossibleTypes interfaceName =+renderPossibleTypes name = mkList <$> ( getSchema- >>= sequence- . concatMap implements- . elems+ >>= traverse render . possibleInterfaceTypes name )- where- implements typeDef@TypeDefinition {typeContent = DataObject {objectImplements}, ..}- | interfaceName `elem` objectImplements = [render typeDef]- implements _ = [] renderDeprecated :: (Monad m) =>- Directives VALID ->+ Directives s -> [(FieldName, Resolver QUERY e m (ResModel QUERY e m))] renderDeprecated dirs =- [ ("isDeprecated", pure $ mkBoolean (isJust $ lookupDeprecated dirs)),- ("deprecationReason", opt (pure . mkString) (lookupDeprecated dirs >>= lookupDeprecatedReason))+ [ ("isDeprecated", render (isJust $ lookupDeprecated dirs)),+ ("deprecationReason", render (lookupDeprecated dirs >>= lookupDeprecatedReason)) ] description :: Monad m => Maybe Description -> (FieldName, Resolver QUERY e m (ResModel QUERY e m))-description desc = ("description", opt render desc)--lookupKind :: (Monad m) => TypeName -> Result e m TypeKind-lookupKind = fmap (renderTypeKind . kindOf) . selectType--renderTypeKind :: AST.TypeKind -> TypeKind-renderTypeKind AST.KindScalar = SCALAR-renderTypeKind (AST.KindObject _) = OBJECT-renderTypeKind AST.KindUnion = UNION-renderTypeKind AST.KindInputUnion = INPUT_OBJECT-renderTypeKind AST.KindEnum = ENUM-renderTypeKind AST.KindInputObject = INPUT_OBJECT-renderTypeKind AST.KindList = LIST-renderTypeKind AST.KindNonNull = NON_NULL-renderTypeKind AST.KindInterface = INTERFACE+description = ("description",) . render mkType :: (Monad m, RenderIntrospection name) =>@@ -302,9 +314,14 @@ ) createObjectType ::- Monad m => TypeName -> Maybe Description -> [TypeName] -> FieldsDefinition OUT -> ResModel QUERY e m+ Monad m =>+ TypeName ->+ Maybe Description ->+ [TypeName] ->+ FieldsDefinition OUT VALID ->+ ResModel QUERY e m createObjectType name desc interfaces fields =- mkType OBJECT name desc [("fields", render fields), ("interfaces", mkList <$> traverse implementedInterface interfaces)]+ mkType (KindObject Nothing) name desc [("fields", render fields), ("interfaces", mkList <$> traverse implementedInterface interfaces)] implementedInterface :: (Monad m) =>@@ -317,10 +334,6 @@ renderContent typeDef@TypeDefinition {typeContent = DataInterface {}} = render typeDef renderContent _ = failure ("Type " <> msg name <> " must be an Interface" :: Message) -opt :: Monad m => (a -> Resolver QUERY e m (ResModel QUERY e m)) -> Maybe a -> Resolver QUERY e m (ResModel QUERY e m)-opt f (Just x) = f x-opt _ Nothing = pure mkNull- renderName :: ( RenderIntrospection name, Monad m@@ -333,70 +346,12 @@ renderKind = ("kind",) . render type' :: Monad m => TypeRef -> (FieldName, Resolver QUERY e m (ResModel QUERY e m))-type' ref = ("type", render ref)+type' = ("type",) . render defaultValue :: Monad m =>- TypeRef ->- Maybe (Value RESOLVED) ->+ Maybe (FieldContent TRUE IN VALID) -> ( FieldName, Resolver QUERY e m (ResModel QUERY e m) )-defaultValue- typeRef- value =- ( "defaultValue",- opt- ( fmap- (mkString . GQL.render)- . fulfill typeRef- . Just- )- value- )--fulfill ::- WithSchema m =>- TypeRef ->- Maybe (Value RESOLVED) ->- m (Value RESOLVED)-fulfill TypeRef {typeConName} (Just (Object fields)) =- selectType typeConName- >>= \case- TypeDefinition- { typeContent =- DataInputObject {inputObjectFields}- } ->- Object- <$> ( traverse- (handleField fields)- (elems inputObjectFields)- >>= fromElems- )- _ -> failure (msg typeConName <> "is not must be Object")-fulfill typeRef (Just (List values)) =- List <$> traverse (fulfill typeRef . Just) values-fulfill _ (Just v) = pure v-fulfill _ Nothing = pure Null--handleField ::- WithSchema m =>- Object RESOLVED ->- FieldDefinition IN ->- m (ObjectEntry RESOLVED)-handleField- fields- FieldDefinition- { fieldName,- fieldType,- fieldContent = x- } =- ObjectEntry fieldName- <$> fulfill- fieldType- ( selectOr- (fmap defaultInputValue x)- (Just . entryValue)- fieldName- fields- )+defaultValue = ("defaultValue",) . render
src/Data/Morpheus/Schema/DSL.hs view
@@ -3,6 +3,7 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TemplateHaskell #-} module Data.Morpheus.Schema.DSL (dsl) where@@ -11,11 +12,11 @@ ( gqlWarnings, renderGQLErrors, )-import Data.Morpheus.Parsing.Document.TypeSystem (parseSchema)-import Data.Morpheus.Types.Internal.AST (ANY, TypeDefinition)+import Data.Morpheus.Parsing.Document.TypeSystem+ ( parseSchema,+ ) import Data.Morpheus.Types.Internal.Resolving- ( Eventless,- Result (..),+ ( Result (..), ) import Data.Text ( Text,@@ -37,7 +38,6 @@ error $ things ++ " are not supported by the GraphQL QuasiQuoter" dslExpression :: Text -> Q Exp-dslExpression doc = case (parseSchema doc :: Eventless [TypeDefinition ANY]) of+dslExpression doc = case parseSchema doc of Failure errors -> fail (renderGQLErrors errors)- Success {result, warnings} ->- gqlWarnings warnings >> [|result|]+ Success {result, warnings} -> gqlWarnings warnings >> [|result|]
− src/Data/Morpheus/Schema/Directives.hs
@@ -1,43 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Data.Morpheus.Schema.Directives (defaultDirectives) where--import Data.Morpheus.Internal.Utils- ( singleton,- )-import Data.Morpheus.Types.Internal.AST- ( ArgumentsDefinition (..),- DirectiveDefinition (..),- DirectiveLocation (..),- TypeWrapper (..),- mkField,- )--defaultDirectives :: [DirectiveDefinition]-defaultDirectives =- [ DirectiveDefinition- { directiveDefinitionName = "skip",- directiveDefinitionDescription = Just "Directs the executor to skip this field or fragment when the `if` argument is true.",- directiveDefinitionLocations = [FIELD, FRAGMENT_SPREAD, INLINE_FRAGMENT],- directiveDefinitionArgs = argumentsIf- },- DirectiveDefinition- { directiveDefinitionName = "include",- directiveDefinitionDescription = Just "Directs the executor to include this field or fragment only when the `if` argument is true.",- directiveDefinitionLocations = [FIELD, FRAGMENT_SPREAD, INLINE_FRAGMENT],- directiveDefinitionArgs = argumentsIf- },- DirectiveDefinition- { directiveDefinitionName = "deprecated",- directiveDefinitionDescription = Just "Marks an element of a GraphQL schema as no longer supported.",- directiveDefinitionLocations = [FIELD_DEFINITION, ENUM_VALUE],- directiveDefinitionArgs =- singleton $- mkField- "reason"- ([TypeMaybe], "String")- }- ]--argumentsIf :: ArgumentsDefinition-argumentsIf = singleton $ mkField "if" ([], "Boolean")
src/Data/Morpheus/Schema/Schema.hs view
@@ -3,80 +3,61 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} module Data.Morpheus.Schema.Schema- ( withSystemTypes,- systemTypes,+ ( internalSchema, ) where -- MORPHEUS -import Data.Morpheus.Internal.Utils- ( (<:>),- singleton,- ) import Data.Morpheus.Schema.DSL (dsl) import Data.Morpheus.Types.Internal.AST- ( ANY,- DataFingerprint (..),- FieldsDefinition,- Message,- OUT,+ ( DataFingerprint (..), Schema (..),- TypeContent (..), TypeDefinition (..),- TypeUpdater,- TypeWrapper (..),- createArgument,- insertType, internalFingerprint,- mkField,- mkObjectField,- unsafeFromFields, )-import Data.Morpheus.Types.Internal.Resolving- ( failure,- resolveUpdates,- ) -withSystemTypes :: TypeUpdater-withSystemTypes s@Schema {query = q@TypeDefinition {typeContent = DataObject inter fields}} =- ( do- fs <- fields <:> hiddenFields- pure $ s {query = q {typeContent = DataObject inter fs}}- )- >>= (`resolveUpdates` map (insertType . internalType) systemTypes)-withSystemTypes _ = failure ("Query must be an Object Type" :: Message)--hiddenFields :: FieldsDefinition OUT-hiddenFields =- unsafeFromFields- [ mkObjectField- (singleton (createArgument "name" ([], "String")))- "__type"- ([TypeMaybe], "__Type"),- mkField- "__schema"- ([], "__Schema")- ]+toInternalSchema :: Schema s -> Schema s+toInternalSchema Schema {..} = Schema {types = fmap toInternalType types, ..} -internalType :: TypeDefinition a -> TypeDefinition a-internalType+toInternalType :: TypeDefinition a s -> TypeDefinition a s+toInternalType tyDef@TypeDefinition { typeFingerprint = DataFingerprint name xs } = tyDef {typeFingerprint = internalFingerprint name xs} -systemTypes :: [TypeDefinition ANY]-systemTypes =- [dsl|+internalSchema :: Schema s+internalSchema =+ toInternalSchema+ [dsl| -# default scalars+"""+Directs the executor to skip this field or fragment when the `if` argument is true.+"""+directive @skip(if: Boolean!) + on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT++"""+Directs the executor to include this field or fragment only when the `if` argument is true.+"""+directive @include(if: Boolean!) + on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT++"""+Marks an element of a GraphQL schema as no longer supported.+"""+directive @deprecated(reason: String) + on FIELD_DEFINITION | ENUM_VALUE++ scalar Boolean scalar Int scalar Float@@ -175,6 +156,11 @@ INPUT_OBJECT LIST NON_NULL+}++type Query {+ __type(name: String!): __Type+ __schema: __Schema! } |]
src/Data/Morpheus/Schema/SchemaAPI.hs view
@@ -3,7 +3,6 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE TypeOperators #-} module Data.Morpheus.Schema.SchemaAPI ( withSystemFields,@@ -12,7 +11,7 @@ -- MORPHEUS import Data.Morpheus.Internal.Utils- ( (<:>),+ ( (<.>), elems, empty, selectOr,@@ -21,20 +20,19 @@ ( createObjectType, render, )-import Data.Morpheus.Schema.Directives- ( defaultDirectives,- ) import Data.Morpheus.Schema.Schema ( ) import Data.Morpheus.Types.Internal.AST ( Argument (..),- OUT,+ FieldName,+ OBJECT, QUERY, ScalarValue (..), Schema (..), TypeDefinition (..), TypeName (..),+ VALID, Value (..), ) import Data.Morpheus.Types.Internal.Resolving@@ -49,35 +47,28 @@ ) resolveTypes ::- Monad m => Schema -> Resolver QUERY e m (ResModel QUERY e m)+ Monad m => Schema VALID -> Resolver QUERY e m (ResModel QUERY e m) resolveTypes schema = mkList <$> traverse render (elems schema) renderOperation ::- Monad m => Maybe (TypeDefinition OUT) -> Resolver QUERY e m (ResModel QUERY e m)+ Monad m =>+ Maybe (TypeDefinition OBJECT VALID) ->+ Resolver QUERY e m (ResModel QUERY e m) renderOperation (Just TypeDefinition {typeName}) = pure $ createObjectType typeName Nothing [] empty renderOperation Nothing = pure mkNull findType :: Monad m => TypeName ->- Schema ->+ Schema VALID -> Resolver QUERY e m (ResModel QUERY e m) findType = selectOr (pure mkNull) render -renderDirectives ::- Monad m =>- Resolver QUERY e m (ResModel QUERY e m)-renderDirectives =- mkList- <$> traverse- render- defaultDirectives- schemaResolver :: Monad m =>- Schema ->+ Schema VALID -> Resolver QUERY e m (ResModel QUERY e m)-schemaResolver schema@Schema {query, mutation, subscription} =+schemaResolver schema@Schema {query, mutation, subscription, directiveDefinitions} = pure $ mkObject "__Schema"@@ -85,10 +76,10 @@ ("queryType", renderOperation (Just query)), ("mutationType", renderOperation mutation), ("subscriptionType", renderOperation subscription),- ("directives", renderDirectives)+ ("directives", render directiveDefinitions) ] -schemaAPI :: Monad m => Schema -> ResModel QUERY e m+schemaAPI :: Monad m => Schema VALID -> ResModel QUERY e m schemaAPI schema = mkObject "Root"@@ -96,7 +87,7 @@ ("__schema", schemaResolver schema) ] where- typeResolver = selectOr (pure mkNull) handleArg "name"+ typeResolver = selectOr (pure mkNull) handleArg ("name" :: FieldName) where handleArg Argument@@ -104,10 +95,14 @@ } = findType (TypeName typename) schema handleArg _ = pure mkNull -withSystemFields :: Monad m => Schema -> RootResModel e m -> ResultT e' m (RootResModel e m)+withSystemFields ::+ Monad m =>+ Schema VALID ->+ RootResModel e m ->+ ResultT e' m (RootResModel e m) withSystemFields schema RootResModel {query, ..} = pure $ RootResModel- { query = query >>= (<:> schemaAPI schema),+ { query = query >>= (<.> schemaAPI schema), .. }
− src/Data/Morpheus/Schema/TypeKind.hs
@@ -1,22 +0,0 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE TypeFamilies #-}--module Data.Morpheus.Schema.TypeKind- ( TypeKind (..),- )-where--import Data.Aeson (FromJSON (..))-import GHC.Generics--data TypeKind- = SCALAR- | OBJECT- | INTERFACE- | UNION- | ENUM- | INPUT_OBJECT- | LIST- | NON_NULL- deriving (Eq, Generic, FromJSON, Show)
src/Data/Morpheus/Types/GQLScalar.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Types.GQLScalar ( GQLScalar (..),@@ -11,8 +12,9 @@ ) where -import Control.Monad.Fail (MonadFail)+import Control.Monad.Fail (MonadFail (..)) import qualified Data.Aeson as A+import Data.Either (Either (..), either) import Data.Morpheus.Types.Internal.AST ( ScalarDefinition (..), ScalarValue (..),@@ -22,6 +24,17 @@ ) import Data.Proxy (Proxy (..)) import Data.Text (Text, unpack)+import Prelude+ ( ($),+ (.),+ Bool,+ Float,+ Int,+ Monad,+ fromInteger,+ pure,+ toInteger,+ ) toScalar :: ValidValue -> Either Text ScalarValue toScalar (Scalar x) = pure x@@ -53,7 +66,7 @@ validator value = do scalarValue' <- toScalar value (_ :: a) <- parseValue scalarValue'- return value+ pure value instance GQLScalar Text where parseValue (String x) = pure x
src/Data/Morpheus/Types/ID.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Types.ID ( ID (..),@@ -21,6 +22,13 @@ pack, ) import GHC.Generics (Generic)+import Prelude+ ( ($),+ Either (..),+ Eq,+ Show (..),+ pure,+ ) -- | default GraphQL type, -- parses only 'String' and 'Int' values,@@ -28,12 +36,12 @@ newtype ID = ID { unpackID :: Text }- deriving (Show, Generic)+ deriving (Show, Generic, Eq) instance GQLScalar ID where- parseValue (Int x) = return (ID $ pack $ show x)- parseValue (String x) = return (ID x)- parseValue _ = Left ""+ parseValue (Int x) = pure (ID $ pack $ show x)+ parseValue (String x) = pure (ID x)+ parseValue _ = Left "ID can only be String or number" serialize (ID x) = String x instance ToJSON ID where
src/Data/Morpheus/Types/IO.hs view
@@ -1,8 +1,10 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Types.IO ( GQLRequest (..),@@ -13,6 +15,10 @@ ) where +-- MORPHEUS++import Control.Applicative (Applicative (..))+import Control.Monad.Fail (fail) import Data.Aeson ( (.:?), (.=),@@ -40,10 +46,12 @@ fromStrict, toStrict, )+import Data.Either (Either (..))+import Data.Functor ((<$>), fmap) import qualified Data.HashMap.Lazy as LH ( toList, )--- MORPHEUS+import Data.Maybe (Maybe (..)) import Data.Morpheus.Error.Utils (badRequestError) import Data.Morpheus.Types.Internal.AST ( FieldName,@@ -66,27 +74,31 @@ encodeUtf8, ) import GHC.Generics (Generic)+import Prelude (($), (.), Show, String) decodeNoDup :: Failure String m => LB.ByteString -> m GQLRequest decodeNoDup str = case eitherDecodeWith jsonNoDup ifromJSON str of Left (path, x) -> failure $ formatError path x Right value -> pure value -class MapAPI a where- mapAPI :: Applicative m => (GQLRequest -> m GQLResponse) -> a -> m a+class MapAPI a b where+ mapAPI :: Applicative m => (GQLRequest -> m GQLResponse) -> a -> m b -instance MapAPI LB.ByteString where+instance MapAPI GQLRequest GQLResponse where+ mapAPI f = f++instance MapAPI LB.ByteString LB.ByteString where mapAPI api request = case decodeNoDup request of Left aesonError -> pure $ badRequestError aesonError Right req -> encode <$> api req -instance MapAPI LT.Text where+instance MapAPI LT.Text LT.Text where mapAPI api = fmap decodeUtf8 . mapAPI api . encodeUtf8 -instance MapAPI ByteString where+instance MapAPI ByteString ByteString where mapAPI api = fmap LB.toStrict . mapAPI api . LB.fromStrict -instance MapAPI Text where+instance MapAPI Text Text where mapAPI api = fmap LT.toStrict . mapAPI api . LT.fromStrict renderResponse :: Result e ValidValue -> GQLResponse
src/Data/Morpheus/Types/Internal/AST.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Types.Internal.AST ( -- BASE@@ -12,10 +13,9 @@ FieldName (..), Description, Stage,- RESOLVED,+ CONST, VALID, RAW,- VALIDATION_MODE (..), -- VALUE Value (..), ScalarValue (..),@@ -47,7 +47,6 @@ VariableDefinitions, DefaultValue, getOperationName,- getOperationDataType, -- DSL ScalarDefinition (..), DataEnum,@@ -71,17 +70,14 @@ MUTATION, SUBSCRIPTION, Directive (..),- TypeUpdater, ConsD (..), TypeCategory, DataInputUnion, VariableContent (..), TypeLib, initTypeLib,- defineType,- isFieldNullable, kindOf,- toNullableField,+ toNullable, toListField, isObject, isInput,@@ -91,19 +87,14 @@ isWeaker, isSubscription, isOutputObject,- sysTypes,- isSystemTypeName,+ isNotSystemTypeName, isEntNode,- createField,- createArgument,- createEnumType,+ mkEnumContent, createScalarType,- createType,- createUnionType,- createAlias,- createInputUnionFields,+ mkUnionContent,+ mkTypeRef,+ mkInputUnionFields, fieldVisibility,- createEnumValue, insertType, lookupDeprecated, lookupDeprecatedReason,@@ -113,9 +104,8 @@ -- LOCAL GQLQuery (..), Variables,- isNullableWrapper, unsafeFromFields,- OrderedMap,+ OrdMap, GQLError (..), GQLErrors, ObjectEntry (..),@@ -126,8 +116,10 @@ ANY, IN, OUT,- FromAny (..),- ToAny (..),+ OBJECT,+ IMPLEMENTABLE,+ fromAny,+ toAny, TRUE, FALSE, TypeName (..),@@ -147,30 +139,59 @@ DirectiveLocation (..), FieldContent (..), fieldContentArgs,- mkField,+ mkInputValue,+ mkType, TypeNameTH (..), isOutput, mkObjectField, UnionMember (..), mkUnionMember,+ RawTypeDefinition (..),+ RootOperationTypeDefinition (..),+ UnionSelection,+ SchemaDefinition (..),+ buildSchema,+ InternalError (..),+ ValidationError (..),+ msgInternal,+ getOperationDataType,+ Typed (Typed),+ typed,+ untyped,+ msgValidation,+ withPosition,+ ValidationErrors,+ toGQLError,+ ELEM,+ LEAF,+ REQURE_IMPLEMENTABLE,+ ToCategory (..),+ FromCategory (..),+ possibleTypes,+ possibleInterfaceTypes, ) where import Data.HashMap.Lazy (HashMap) -- Morpheus+ import Data.Morpheus.Types.Internal.AST.Base import Data.Morpheus.Types.Internal.AST.DirectiveLocation (DirectiveLocation (..))-import Data.Morpheus.Types.Internal.AST.OrderedMap+import Data.Morpheus.Types.Internal.AST.Fields+import Data.Morpheus.Types.Internal.AST.OrdMap import Data.Morpheus.Types.Internal.AST.Selection+import Data.Morpheus.Types.Internal.AST.Stage import Data.Morpheus.Types.Internal.AST.TH+import Data.Morpheus.Types.Internal.AST.TypeCategory import Data.Morpheus.Types.Internal.AST.TypeSystem import Data.Morpheus.Types.Internal.AST.Value import Language.Haskell.TH.Syntax (Lift)+import Prelude (Show) type Variables = HashMap FieldName ResolvedValue data GQLQuery = GQLQuery- { fragments :: Fragments,+ { fragments :: Fragments RAW, operation :: Operation RAW, inputVariables :: [(FieldName, ResolvedValue)] }
src/Data/Morpheus/Types/Internal/AST/Base.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-}@@ -8,6 +9,8 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Types.Internal.AST.Base ( Ref (..),@@ -15,13 +18,8 @@ Message (..), FieldName (..), Description,- VALID,- RAW, TypeWrapper (..),- Stage (..),- RESOLVED, TypeRef (..),- VALIDATION_MODE (..), OperationType (..), QUERY, MUTATION,@@ -33,15 +31,13 @@ anonymousRef, toHSWrappers, toGQLWrapper,- sysTypes,- isNullable,+ Nullable (..), isWeaker, isSubscription, isOutputObject,- isSystemTypeName,+ isNotSystemTypeName, isObject, isInput,- isNullableWrapper, sysFields, hsTypeName, toOperationType,@@ -60,6 +56,14 @@ convertToJSONName, convertToHaskellName, isOutput,+ mkTypeRef,+ InternalError (..),+ msgInternal,+ ValidationError (..),+ msgValidation,+ ValidationErrors,+ withPosition,+ toGQLError, ) where @@ -70,16 +74,43 @@ encode, ) import Data.ByteString.Lazy.Char8 (ByteString, unpack)+import Data.Char (toLower) import Data.Hashable (Hashable)+import Data.Maybe (Maybe (..), maybeToList) import Data.Morpheus.Rendering.RenderGQL (RenderGQL (..)) import Data.Semigroup (Semigroup (..))-import Data.String (IsString)+import Data.String (IsString (..)) import Data.Text (Text, intercalate, pack) import qualified Data.Text as T import GHC.Generics (Generic)--- import Instances.TH.Lift ()-import Language.Haskell.TH (stringE)-import Language.Haskell.TH.Syntax (Lift (..))+import Language.Haskell.TH+ ( ExpQ,+ stringE,+ )+import Language.Haskell.TH.Syntax+ ( Lift (..),+ Q,+ TExp,+ unsafeTExpCoerce,+ )+import Prelude+ ( ($),+ (&&),+ (.),+ Bool (..),+ Eq (..),+ Functor (..),+ Int,+ Ord (..),+ Show (..),+ String,+ elem,+ fst,+ id,+ not,+ notElem,+ otherwise,+ ) type TRUE = 'True @@ -96,13 +127,65 @@ (Show, Eq, Ord, IsString, Semigroup, Hashable, FromJSON, ToJSON) instance Lift Message where- lift = stringE . T.unpack . readMessage+ lift = liftString . readMessage +#if MIN_VERSION_template_haskell(2,16,0)+ liftTyped = liftTypedString . readMessage+#endif++newtype InternalError = InternalError+ { readInternalError :: Text+ }+ deriving+ (Generic)+ deriving newtype+ (Show, Eq, Ord, IsString, Semigroup, Hashable, FromJSON, ToJSON)++data ValidationError = ValidationError+ { validationMessage :: Message,+ validationLocations :: [Position]+ }+ deriving (Show)++instance IsString ValidationError where+ fromString = (`ValidationError` []) . msg++instance Semigroup ValidationError where+ ValidationError m1 p1 <> ValidationError m2 p2 =+ ValidationError (m1 <> m2) (p1 <> p2)++withPosition :: Maybe Position -> ValidationError -> ValidationError+withPosition pos (ValidationError m ps) = ValidationError m (ps <> maybeToList pos)++type ValidationErrors = [ValidationError]++toGQLError :: ValidationError -> GQLError+toGQLError (ValidationError m p) = GQLError m p++-- instance Lift InternalError where+-- lift = liftString . readInternalError++-- #if MIN_VERSION_template_haskell(2,16,0)+-- liftTyped = liftTypedString . readInternalError+-- #endif++msgInternal :: (Msg a) => a -> InternalError+msgInternal = InternalError . readMessage . msg++msgValidation :: (Msg a) => a -> ValidationError+msgValidation = (`ValidationError` []) . msg+ class Msg a where msg :: a -> Message msgSepBy :: Text -> [a] -> Message- msgSepBy t = Message . intercalate t . map (readMessage . msg)+ msgSepBy t = Message . intercalate t . fmap (readMessage . msg) +instance Msg Message where+ msg = id++instance Msg InternalError where+ msg = Message . ("Internal Error! " <>) . readInternalError+ instance Msg String where msg = Message . pack @@ -115,6 +198,10 @@ instance Msg Value where msg = msg . encode +class Nullable a where+ isNullable :: a -> Bool+ toNullable :: a -> a+ -- FieldName : lower case names newtype FieldName = FieldName {readName :: Text} deriving@@ -123,8 +210,12 @@ (Show, Ord, Eq, IsString, Hashable, Semigroup, FromJSON, ToJSON) instance Lift FieldName where- lift = stringE . T.unpack . readName+ lift = liftString . readName +#if MIN_VERSION_template_haskell(2,16,0)+ liftTyped = liftTypedString . readName+#endif+ instance Msg FieldName where msg FieldName {readName} = Message $ "\"" <> readName <> "\"" @@ -132,7 +223,7 @@ render = readName intercalateName :: FieldName -> [FieldName] -> FieldName-intercalateName (FieldName x) = FieldName . intercalate x . map readName+intercalateName (FieldName x) = FieldName . intercalate x . fmap readName toFieldName :: TypeName -> FieldName toFieldName = FieldName . readTypeName@@ -145,8 +236,18 @@ (Show, Ord, Eq, IsString, Hashable, Semigroup, FromJSON, ToJSON) instance Lift TypeName where- lift = stringE . T.unpack . readTypeName+ lift = liftString . readTypeName +#if MIN_VERSION_template_haskell(2,16,0)+ liftTyped = liftTypedString . readTypeName+#endif++liftTypedString :: IsString a => Token -> Q (TExp a)+liftTypedString = unsafeTExpCoerce . stringE . T.unpack++liftString :: Token -> ExpQ+liftString = stringE . T.unpack+ instance Msg TypeName where msg TypeName {readTypeName} = Message $ "\"" <> readTypeName <> "\"" @@ -156,8 +257,6 @@ -- Description type Description = Text -data Stage = RAW | RESOLVED | VALID- data Position = Position { line :: Int, column :: Int@@ -177,17 +276,6 @@ type GQLErrors = [GQLError] -type RAW = 'RAW--type RESOLVED = 'RESOLVED--type VALID = 'VALID--data VALIDATION_MODE- = WITHOUT_VARIABLES- | FULL_VALIDATION- deriving (Eq, Show)- data DataFingerprint = DataFingerprint TypeName [String] deriving (Show, Eq, Ord, Lift) internalFingerprint :: TypeName -> [String] -> DataFingerprint@@ -197,8 +285,16 @@ = Query | Subscription | Mutation- deriving (Show, Eq, Lift)+ deriving (Show, Eq, Lift, Generic, Hashable) +instance RenderGQL OperationType where+ render = pack . fmap toLower . show++instance Msg OperationType where+ msg Query = msg ("query" :: TypeName)+ msg Mutation = msg ("mutation" :: TypeName)+ msg Subscription = msg ("subscription" :: TypeName)+ type QUERY = 'Query type MUTATION = 'Mutation@@ -236,9 +332,14 @@ } deriving (Show, Eq, Lift) -isNullable :: TypeRef -> Bool-isNullable TypeRef {typeWrappers = typeWrappers} = isNullableWrapper typeWrappers+mkTypeRef :: TypeName -> TypeRef+mkTypeRef typeConName =+ TypeRef {typeConName, typeWrappers = [], typeArgs = Nothing} +instance Nullable TypeRef where+ isNullable = isNullable . typeWrappers+ toNullable TypeRef {..} = TypeRef {typeWrappers = toNullable typeWrappers, ..}+ instance RenderGQL TypeRef where render TypeRef {typeConName, typeWrappers} = renderWrapped typeConName typeWrappers @@ -259,6 +360,17 @@ | KindInterface deriving (Eq, Show, Lift) +instance RenderGQL TypeKind where+ render KindScalar = "SCALAR"+ render KindObject {} = "OBJECT"+ render KindUnion = "UNION"+ render KindInputUnion = "INPUT_OBJECT"+ render KindEnum = "ENUM"+ render KindInputObject = "INPUT_OBJECT"+ render KindList = "LIST"+ render KindNonNull = "NON_NULL"+ render KindInterface = "INTERFACE"+ isSubscription :: TypeKind -> Bool isSubscription (KindObject (Just Subscription)) = True isSubscription _ = False@@ -296,9 +408,11 @@ | NonNullType deriving (Show, Lift) -isNullableWrapper :: [TypeWrapper] -> Bool-isNullableWrapper (TypeMaybe : _) = True-isNullableWrapper _ = False+instance Nullable [TypeWrapper] where+ isNullable (TypeMaybe : _) = True+ isNullable _ = False+ toNullable (TypeMaybe : xs) = TypeMaybe : xs+ toNullable xs = TypeMaybe : xs isWeaker :: [TypeWrapper] -> [TypeWrapper] -> Bool isWeaker (TypeMaybe : xs1) (TypeMaybe : xs2) = isWeaker xs1 xs2@@ -328,25 +442,24 @@ showGQLWrapper (ListType : xs) = "[" <> showGQLWrapper xs <> "]" showGQLWrapper (NonNullType : xs) = showGQLWrapper xs <> "!" -isSystemTypeName :: TypeName -> Bool-isSystemTypeName = (`elem` sysTypes)--sysTypes :: [TypeName]-sysTypes =- [ "__Schema",- "__Type",- "__Directive",- "__TypeKind",- "__Field",- "__DirectiveLocation",- "__InputValue",- "__EnumValue",- "String",- "Float",- "Int",- "Boolean",- "ID"- ]+isNotSystemTypeName :: TypeName -> Bool+isNotSystemTypeName =+ ( `notElem`+ [ "__Schema",+ "__Type",+ "__Directive",+ "__TypeKind",+ "__Field",+ "__DirectiveLocation",+ "__InputValue",+ "__EnumValue",+ "String",+ "Float",+ "Int",+ "Boolean",+ "ID"+ ]+ ) sysFields :: [FieldName] sysFields = ["__typename", "__schema", "__type"]
src/Data/Morpheus/Types/Internal/AST/DirectiveLocation.hs view
@@ -1,9 +1,14 @@ {-# LANGUAGE DeriveLift #-}+{-# LANGUAGE NoImplicitPrelude #-} -module Data.Morpheus.Types.Internal.AST.DirectiveLocation (DirectiveLocation (..)) where+module Data.Morpheus.Types.Internal.AST.DirectiveLocation+ ( DirectiveLocation (..),+ )+where import Data.Morpheus.Types.Internal.AST.Base (Msg (..)) import Language.Haskell.TH.Syntax (Lift)+import Prelude ((.), Eq, Show (..)) data DirectiveLocation = QUERY
+ src/Data/Morpheus/Types/Internal/AST/Fields.hs view
@@ -0,0 +1,368 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveLift #-}+{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.Types.Internal.AST.Fields+ ( Arguments,+ Argument (..),+ ArgumentDefinition,+ ArgumentsDefinition (..),+ FieldDefinition (..),+ FieldsDefinition,+ Fields (..),+ FieldContent (..),+ InputFieldsDefinition,+ DirectiveDefinitions,+ DirectiveDefinition (..),+ Directives,+ Directive (..),+ fieldVisibility,+ toListField,+ lookupDeprecated,+ lookupDeprecatedReason,+ unsafeFromFields,+ fieldsToArguments,+ fieldContentArgs,+ mkInputValue,+ mkObjectField,+ )+where++-- MORPHEUS++import Data.Foldable (Foldable)+import Data.Functor ((<$>), Functor (..))+import Data.List (find)+import Data.Maybe (Maybe (..))+import Data.Morpheus.Error.NameCollision+ ( NameCollision (..),+ )+import Data.Morpheus.Internal.Utils+ ( Collection (..),+ KeyOf (..),+ Listable (..),+ Merge (..),+ Selectable (..),+ elems,+ )+import Data.Morpheus.Rendering.RenderGQL+ ( RenderGQL (..),+ renderArguments,+ renderEntry,+ renderObject,+ )+import Data.Morpheus.Types.Internal.AST.Base+ ( Description,+ FieldName,+ FieldName (..),+ Msg (..),+ Nullable (..),+ Position,+ TRUE,+ TypeName,+ TypeRef (..),+ TypeWrapper (..),+ ValidationError (..),+ msgValidation,+ sysFields,+ )+import Data.Morpheus.Types.Internal.AST.DirectiveLocation (DirectiveLocation)+import Data.Morpheus.Types.Internal.AST.OrdMap+ ( OrdMap,+ unsafeFromValues,+ )+import Data.Morpheus.Types.Internal.AST.Stage+ ( Stage,+ )+import Data.Morpheus.Types.Internal.AST.TypeCategory+ ( ANY,+ ELEM,+ IN,+ OUT,+ ToCategory (..),+ TypeCategory,+ toAny,+ )+import Data.Morpheus.Types.Internal.AST.Value+ ( ScalarValue (..),+ Value (..),+ )+import Data.Semigroup (Semigroup ((<>)))+import Data.Traversable (Traversable)+import Instances.TH.Lift ()+import Language.Haskell.TH.Syntax (Lift (..))+import Prelude+ ( ($),+ (.),+ Bool (..),+ Eq,+ Show,+ filter,+ notElem,+ )++-- scalar+------------------------------------------------------------------+data Argument (valid :: Stage) = Argument+ { argumentName :: FieldName,+ argumentPosition :: Position,+ argumentValue :: Value valid+ }+ deriving (Show, Eq, Lift)++instance KeyOf FieldName (Argument stage) where+ keyOf = argumentName++instance RenderGQL (Argument s) where+ render Argument {argumentName, argumentValue} =+ renderEntry argumentName argumentValue++instance NameCollision (Argument s) where+ nameCollision Argument {argumentName, argumentPosition} =+ ValidationError+ { validationMessage = "There can Be only One Argument Named " <> msg argumentName,+ validationLocations = [argumentPosition]+ }++type Arguments (s :: Stage) = OrdMap FieldName (Argument s)++-- directive+------------------------------------------------------------------+data Directive (s :: Stage) = Directive+ { directiveName :: FieldName,+ directivePosition :: Position,+ directiveArgs :: Arguments s+ }+ deriving (Show, Lift, Eq)++instance KeyOf FieldName (Directive s) where+ keyOf = directiveName++type Directives s = [Directive s]++data DirectiveDefinition s = DirectiveDefinition+ { directiveDefinitionName :: FieldName,+ directiveDefinitionDescription :: Maybe Description,+ directiveDefinitionLocations :: [DirectiveLocation],+ directiveDefinitionArgs :: ArgumentsDefinition s+ }+ deriving (Show, Lift)++type DirectiveDefinitions s = [DirectiveDefinition s]++instance KeyOf FieldName (DirectiveDefinition s) where+ keyOf = directiveDefinitionName++instance Selectable FieldName (ArgumentDefinition s) (DirectiveDefinition s) where+ selectOr fb f key DirectiveDefinition {directiveDefinitionArgs} =+ selectOr fb f key directiveDefinitionArgs++lookupDeprecated :: [Directive s] -> Maybe (Directive s)+lookupDeprecated = find isDeprecation+ where+ isDeprecation Directive {directiveName = "deprecated"} = True+ isDeprecation _ = False++lookupDeprecatedReason :: Directive s -> Maybe Description+lookupDeprecatedReason Directive {directiveArgs} =+ selectOr+ Nothing+ argumentStringValue+ ("reason" :: FieldName)+ directiveArgs++argumentStringValue :: Argument s -> Maybe Description+argumentStringValue Argument {argumentValue = Null} = Nothing+argumentStringValue Argument {argumentValue = (Scalar (String x))} = Just x+argumentStringValue _ = Just "can't read deprecated Reason Value"++instance ToCategory FieldDefinition a ANY where+ toCategory FieldDefinition {fieldContent, ..} = FieldDefinition {fieldContent = toAny <$> fieldContent, ..}++instance ToCategory (FieldContent TRUE) a ANY where+ toCategory (FieldArgs x) = FieldArgs x+ toCategory (DefaultInputValue x) = DefaultInputValue x++newtype Fields def = Fields+ {unFields :: OrdMap FieldName def}+ deriving+ ( Show,+ Lift,+ Functor,+ Foldable,+ Traversable+ )++deriving instance (KeyOf FieldName def) => Collection def (Fields def)++instance Merge (FieldsDefinition cat s) where+ merge path (Fields x) (Fields y) = Fields <$> merge path x y++instance Selectable FieldName (FieldDefinition cat s) (Fields (FieldDefinition cat s)) where+ selectOr fb f name (Fields lib) = selectOr fb f name lib++unsafeFromFields :: [FieldDefinition cat s] -> FieldsDefinition cat s+unsafeFromFields = Fields . unsafeFromValues++fieldsToArguments :: FieldsDefinition IN s -> ArgumentsDefinition s+fieldsToArguments = ArgumentsDefinition Nothing . unFields++instance (KeyOf FieldName def, NameCollision def) => Listable def (Fields def) where+ fromElems = fmap Fields . fromElems+ elems = elems . unFields++-- 3.6 Objects : https://graphql.github.io/graphql-spec/June2018/#sec-Objects+------------------------------------------------------------------------------+-- ObjectTypeDefinition:+-- Description(opt) type Name ImplementsInterfaces(opt) Directives(Const)(opt) FieldsDefinition(opt)+--+-- ImplementsInterfaces+-- implements &(opt) NamedType+-- ImplementsInterfaces & NamedType+--+-- FieldsDefinition+-- { FieldDefinition(list) }+--+type FieldsDefinition cat s = Fields (FieldDefinition cat s)++-- FieldDefinition+-- Description(opt) Name ArgumentsDefinition(opt) : Type Directives(Const)(opt)+--+-- InputValueDefinition+-- Description(opt) Name: Type DefaultValue(opt) Directives[Const](opt)++data FieldDefinition (cat :: TypeCategory) (s :: Stage) = FieldDefinition+ { fieldName :: FieldName,+ fieldDescription :: Maybe Description,+ fieldType :: TypeRef,+ fieldDirectives :: [Directive s],+ fieldContent :: Maybe (FieldContent TRUE cat s)+ }+ deriving (Show, Lift)++data FieldContent (bool :: Bool) (cat :: TypeCategory) (s :: Stage) where+ DefaultInputValue ::+ { defaultInputValue :: Value s+ } ->+ FieldContent (ELEM IN cat) cat s+ FieldArgs ::+ { fieldArgsDef :: ArgumentsDefinition s+ } ->+ FieldContent (ELEM OUT cat) cat s++fieldContentArgs :: FieldContent b cat s -> ArgumentsDefinition s+fieldContentArgs (FieldArgs args) = args+fieldContentArgs _ = empty++deriving instance Show (FieldContent bool cat s)++deriving instance Lift (FieldContent bool cat s)++instance KeyOf FieldName (FieldDefinition cat s) where+ keyOf = fieldName++instance Selectable FieldName (ArgumentDefinition s) (FieldDefinition OUT s) where+ selectOr fb f key FieldDefinition {fieldContent = Just (FieldArgs args)} = selectOr fb f key args+ selectOr fb _ _ _ = fb++instance NameCollision (FieldDefinition cat s) where+ nameCollision FieldDefinition {fieldName} =+ "There can Be only One field Named " <> msgValidation fieldName++instance RenderGQL (FieldDefinition cat s) where+ render FieldDefinition {fieldName = FieldName name, fieldType, fieldContent = Just (FieldArgs args)} =+ name <> render args <> ": " <> render fieldType+ render FieldDefinition {fieldName, fieldType} =+ renderEntry fieldName fieldType++instance RenderGQL (FieldsDefinition cat s) where+ render = renderObject . filter fieldVisibility . elems++instance Nullable (FieldDefinition cat s) where+ isNullable = isNullable . fieldType+ toNullable field = field {fieldType = toNullable (fieldType field)}++fieldVisibility :: FieldDefinition cat s -> Bool+fieldVisibility FieldDefinition {fieldName} = fieldName `notElem` sysFields++createField ::+ Maybe (FieldContent TRUE cat s) ->+ FieldName ->+ [TypeWrapper] ->+ TypeName ->+ FieldDefinition cat s+createField fieldContent fieldName typeWrappers typeConName =+ FieldDefinition+ { fieldName,+ fieldContent,+ fieldDescription = Nothing,+ fieldType = TypeRef {typeConName, typeWrappers, typeArgs = Nothing},+ fieldDirectives = []+ }++mkInputValue :: FieldName -> [TypeWrapper] -> TypeName -> FieldDefinition cat s+mkInputValue = createField Nothing++mkObjectField ::+ ArgumentsDefinition s ->+ FieldName ->+ [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}++-- 3.10 Input Objects: https://spec.graphql.org/June2018/#sec-Input-Objects+---------------------------------------------------------------------------+--- InputFieldsDefinition+-- { InputValueDefinition(list) }++type InputFieldsDefinition s = Fields (InputValueDefinition s)++type InputValueDefinition = FieldDefinition IN++-- 3.6.1 Field Arguments : https://graphql.github.io/graphql-spec/June2018/#sec-Field-Arguments+-----------------------------------------------------------------------------------------------+-- ArgumentsDefinition:+-- (InputValueDefinition(list))++data ArgumentsDefinition s = ArgumentsDefinition+ { argumentsTypename :: Maybe TypeName,+ arguments :: OrdMap FieldName (ArgumentDefinition s)+ }+ deriving (Show, Lift)++instance RenderGQL (ArgumentsDefinition s) where+ render ArgumentsDefinition {arguments} = renderArguments (elems arguments)++type ArgumentDefinition = FieldDefinition IN++instance Selectable FieldName (ArgumentDefinition s) (ArgumentsDefinition s) where+ selectOr fb f key (ArgumentsDefinition _ args) = selectOr fb f key args++instance Collection (ArgumentDefinition s) (ArgumentsDefinition s) where+ empty = ArgumentsDefinition Nothing empty+ singleton = ArgumentsDefinition Nothing . singleton++instance Listable (ArgumentDefinition s) (ArgumentsDefinition s) where+ elems (ArgumentsDefinition _ args) = elems args+ fromElems args = ArgumentsDefinition Nothing <$> fromElems args
src/Data/Morpheus/Types/Internal/AST/MergeSet.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DeriveLift #-} {-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE FlexibleContexts #-}@@ -9,19 +8,24 @@ {-# LANGUAGE KindSignatures #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Types.Internal.AST.MergeSet ( MergeSet,- toOrderedMap,+ toOrdMap, concatTraverse, join, ) where -import Data.List ((\\), find)-import Data.Maybe (maybe) -- MORPHEUS +import Control.Applicative (Applicative (..))+import Control.Monad (Monad (..))+import Data.Foldable (Foldable (..))+import Data.Functor ((<$>), Functor (..))+import Data.List ((\\), find)+import Data.Maybe (maybe) import Data.Morpheus.Internal.Utils ( (<:>), Collection (..),@@ -33,19 +37,29 @@ elems, ) import Data.Morpheus.Types.Internal.AST.Base- ( FieldName,- GQLErrors,- RAW,- Ref,+ ( Ref,+ ValidationErrors,+ )+import Data.Morpheus.Types.Internal.AST.OrdMap+ ( OrdMap (..),+ )+import qualified Data.Morpheus.Types.Internal.AST.OrdMap as OM+import Data.Morpheus.Types.Internal.AST.Stage+ ( RAW, Stage, VALID, )-import Data.Morpheus.Types.Internal.AST.OrderedMap- ( OrderedMap (..),- )-import qualified Data.Morpheus.Types.Internal.AST.OrderedMap as OM-import Data.Semigroup ((<>))+import Data.Semigroup (Semigroup (..))+import Data.Traversable (Traversable (..)) import Language.Haskell.TH.Syntax (Lift (..))+import Prelude+ ( ($),+ (.),+ Eq (..),+ Show,+ flip,+ otherwise,+ ) -- set with mergeable components newtype MergeSet (dups :: Stage) a = MergeSet@@ -66,9 +80,9 @@ Eq b, Merge a, Merge b,- KeyOf b,+ KeyOf k b, Monad m,- Failure GQLErrors m+ Failure ValidationErrors m ) => (a -> m (MergeSet VALID b)) -> MergeSet RAW a ->@@ -79,10 +93,10 @@ join :: ( Eq a,- KeyOf a,+ KeyOf k a, Merge a, Monad m,- Failure GQLErrors m,+ Failure ValidationErrors m, Listable a (MergeSet opt a), Merge (MergeSet opt a) ) =>@@ -93,16 +107,16 @@ __join acc [] = pure acc __join acc (x : xs) = acc <:> x >>= (`__join` xs) -toOrderedMap :: (KEY a ~ FieldName, KeyOf a) => MergeSet opt a -> OrderedMap FieldName a-toOrderedMap = OM.unsafeFromValues . unpack+toOrdMap :: (KeyOf k a) => MergeSet opt a -> OrdMap k a+toOrdMap = OM.unsafeFromValues . unpack -instance (KeyOf a, k ~ KEY a) => Selectable (MergeSet opt a) a where+instance (KeyOf k a) => Selectable k a (MergeSet opt a) where selectOr fb f key (MergeSet ls) = maybe fb f (find ((key ==) . keyOf) ls) -- must merge files on collision instance- ( KeyOf a,+ ( KeyOf k a, Listable a (MergeSet VALID a), Merge a, Eq a@@ -113,7 +127,7 @@ instance ( Listable a (MergeSet VALID a),- KeyOf a,+ KeyOf k a, Merge a, Eq a ) =>@@ -129,17 +143,17 @@ fromElems = pure . MergeSet elems = unpack -safeFromList :: (Monad m, KeyOf a, Eq a, Merge a, Failure GQLErrors m) => [a] -> m (MergeSet opt a)+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 a, Eq a, Listable a (MergeSet opt a), Merge a, Failure GQLErrors m) => [Ref] -> MergeSet opt a -> MergeSet opt a -> m (MergeSet opt a)+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 a, Merge a, Failure GQLErrors m) => [Ref] -> MergeSet opt a -> [a] -> m (MergeSet opt a)+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 a, Merge a, Failure GQLErrors m) => [Ref] -> MergeSet opt a -> a -> m (MergeSet opt a)+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 =
+ src/Data/Morpheus/Types/Internal/AST/OrdMap.hs view
@@ -0,0 +1,100 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.Types.Internal.AST.OrdMap+ ( OrdMap (..),+ unsafeFromValues,+ )+where++-- MORPHEUS+import Data.Foldable (Foldable (..))+import Data.Functor ((<$>), Functor (..))+import Data.HashMap.Lazy (HashMap)+import qualified Data.HashMap.Lazy as HM+import Data.Hashable (Hashable)+import Data.Maybe (fromMaybe, maybe)+import Data.Morpheus.Error.NameCollision (NameCollision (..))+import Data.Morpheus.Internal.Utils+ ( Collection (..),+ KeyOf (..),+ Listable (..),+ Merge (..),+ Selectable (..),+ toPair,+ )+import Data.Semigroup ((<>))+import Data.Traversable (Traversable (..))+import Language.Haskell.TH.Syntax (Lift (..))+import Prelude+ ( ($),+ (.),+ Eq,+ Show,+ error,+ )++-- OrdMap+data OrdMap k a = OrdMap+ { mapKeys :: [k],+ mapEntries :: HashMap k a+ }+ deriving+ ( Show,+ Eq,+ Functor+ )++instance (Lift a, Lift k, Eq k, Hashable k) => Lift (OrdMap k a) where+ lift (OrdMap names x) = [|OrdMap names (HM.fromList ls)|]+ where+ ls = HM.toList x++#if MIN_VERSION_template_haskell(2,16,0)+ liftTyped (OrdMap names x) = [||OrdMap names (HM.fromList ls)||]+ where+ ls = HM.toList x+#endif++instance (Eq k, Hashable k) => Foldable (OrdMap k) where+ foldMap f = foldMap f . getElements++getElements :: (Eq k, Hashable k) => OrdMap k b -> [b]+getElements OrdMap {mapKeys, mapEntries} = fmap takeValue mapKeys+ where+ takeValue key = fromMaybe (error "TODO: invalid Ordered Map") (key `HM.lookup` mapEntries)++instance (Eq k, Hashable k) => Traversable (OrdMap k) where+ traverse f (OrdMap names values) = OrdMap names <$> traverse f values++instance (KeyOf k a, Hashable k) => Collection a (OrdMap k a) where+ empty = OrdMap [] HM.empty+ singleton x = OrdMap [keyOf x] $ HM.singleton (keyOf x) x++instance (Eq k, Hashable k) => Selectable k a (OrdMap k a) where+ selectOr fb f key OrdMap {mapEntries} = maybe fb f (HM.lookup key mapEntries)++instance (NameCollision a, KeyOf k a) => Merge (OrdMap k a) where+ merge ref (OrdMap k1 x) (OrdMap k2 y) = OrdMap (k1 <> k2) <$> merge ref x y++instance (NameCollision a, KeyOf k a, Hashable k) => Listable a (OrdMap k a) where+ fromElems values = OrdMap (fmap keyOf values) <$> fromElems values+ elems = getElements++unsafeFromValues ::+ ( KeyOf k a,+ Hashable k+ ) =>+ [a] ->+ OrdMap k a+unsafeFromValues x = OrdMap (fmap keyOf x) $ HM.fromList $ fmap toPair x
− src/Data/Morpheus/Types/Internal/AST/OrderedMap.hs
@@ -1,122 +0,0 @@-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE TemplateHaskell #-}--module Data.Morpheus.Types.Internal.AST.OrderedMap- ( OrderedMap (..),- unsafeFromValues,- )-where--import Data.HashMap.Lazy (HashMap)-import qualified Data.HashMap.Lazy as HM-import Data.Hashable (Hashable)-import Data.Maybe (fromMaybe, isJust)--- MORPHEUS-import Data.Morpheus.Error.NameCollision (NameCollision (..))-import Data.Morpheus.Internal.Utils- ( Collection (..),- Failure (..),- KeyOf (..),- Listable (..),- Merge (..),- Selectable (..),- toPair,- )-import Data.Morpheus.Types.Internal.AST.Base- ( GQLErrors,- )-import Data.Semigroup ((<>))-import Language.Haskell.TH.Syntax (Lift (..))---- OrderedMap-data OrderedMap k a = OrderedMap- { mapKeys :: [k],- mapEntries :: HashMap k a- }- deriving (Show, Eq, Functor)--instance (Lift a, Lift k) => Lift (OrderedMap k a) where- lift (OrderedMap names x) = [|OrderedMap names (HM.fromList ls)|]- where- ls = HM.toList x--instance (Eq k, Hashable k) => Foldable (OrderedMap k) where- foldMap f = foldMap f . getElements--getElements :: (Eq k, Hashable k) => OrderedMap k b -> [b]-getElements OrderedMap {mapKeys, mapEntries} = map takeValue mapKeys- where- takeValue key = fromMaybe (error "TODO: invalid Ordered Map") (key `HM.lookup` mapEntries)--instance (Eq k, Hashable k) => Traversable (OrderedMap k) where- traverse f (OrderedMap names values) = OrderedMap names <$> traverse f values--instance (KeyOf a, Hashable k, KEY a ~ k) => Collection a (OrderedMap k a) where- empty = OrderedMap [] HM.empty- singleton x = OrderedMap [keyOf x] $ HM.singleton (keyOf x) x--instance (Eq k, Hashable k, k ~ KEY a) => Selectable (OrderedMap k a) a where- selectOr fb f key OrderedMap {mapEntries} = maybe fb f (HM.lookup key mapEntries)--instance (NameCollision a, Eq k, Hashable k, k ~ KEY a) => Merge (OrderedMap k a) where- merge _ (OrderedMap k1 x) (OrderedMap k2 y) = OrderedMap (k1 <> k2) <$> safeJoin x y--instance (NameCollision a, Eq k, Hashable k, k ~ KEY a) => Listable a (OrderedMap k a) where- fromElems = safeFromList- elems = getElements--safeFromList ::- ( Failure GQLErrors m,- Applicative m,- NameCollision a,- Eq (KEY a),- Hashable (KEY a),- KeyOf a- ) =>- [a] ->- m (OrderedMap (KEY a) a)-safeFromList values = OrderedMap (map keyOf values) <$> safeUnionWith HM.empty (map toPair values)--unsafeFromValues ::- ( KeyOf a,- Eq (KEY a),- Hashable (KEY a)- ) =>- [a] ->- OrderedMap (KEY a) a-unsafeFromValues x = OrderedMap (map keyOf x) $ HM.fromList $ fmap toPair x--safeJoin :: (Failure GQLErrors m, Eq k, Hashable k, KEY a ~ k, Applicative m, NameCollision a) => HashMap k a -> HashMap k a -> m (HashMap k a)-safeJoin hm newls = safeUnionWith hm (HM.toList newls)--safeUnionWith ::- ( Failure GQLErrors m,- Applicative m,- Eq k,- Hashable k,- NameCollision a,- KEY a ~ k- ) =>- 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 $ map (uncurry nameCollision) dupps--type NoDupHashMap k a = (HashMap k a, [(k, a)])--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
+ src/Data/Morpheus/Types/Internal/AST/SafeHashMap.hs view
@@ -0,0 +1,86 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.Types.Internal.AST.SafeHashMap+ ( SafeHashMap,+ insert,+ )+where++-- MORPHEUS+import Control.Monad (Monad)+import Data.Foldable (Foldable (..))+import Data.Functor ((<$>), Functor (..))+import Data.HashMap.Lazy (HashMap)+import qualified Data.HashMap.Lazy as HM+import Data.Hashable (Hashable)+import Data.Morpheus.Error.NameCollision (NameCollision (..))+import Data.Morpheus.Internal.Utils+ ( (<:>),+ Collection (..),+ Failure (..),+ KeyOf (..),+ Listable (..),+ Merge (..),+ Selectable (..),+ )+import Data.Morpheus.Types.Internal.AST.Base (ValidationErrors)+import Data.Traversable (Traversable (..))+import Language.Haskell.TH.Syntax (Lift (..))+import Prelude+ ( (.),+ Eq,+ Show,+ )++newtype SafeHashMap k a = SafeHashMap+ { unpackSafeHashMap :: HashMap k a+ }+ deriving+ ( Show,+ Eq,+ Functor,+ Foldable,+ Traversable+ )+ deriving newtype+ ( Collection a,+ Selectable k a+ )++instance (Lift a, Lift k, Eq k, Hashable k) => Lift (SafeHashMap k a) where+ lift (SafeHashMap x) = let ls = HM.toList x in [|SafeHashMap (HM.fromList ls)|]++#if MIN_VERSION_template_haskell(2,16,0)+ liftTyped (SafeHashMap x) = let ls = HM.toList x in [||SafeHashMap (HM.fromList ls)||]+#endif++instance (NameCollision a, KeyOf k a) => Merge (SafeHashMap k a) where+ merge ref (SafeHashMap x) (SafeHashMap y) = SafeHashMap <$> merge ref x y++instance (NameCollision a, KeyOf k a, Hashable k) => Listable a (SafeHashMap k a) where+ fromElems = fmap SafeHashMap . fromElems+ elems = elems . unpackSafeHashMap++insert ::+ ( NameCollision a,+ KeyOf k a,+ Monad m,+ Failure ValidationErrors m+ ) =>+ a ->+ SafeHashMap k a ->+ m (SafeHashMap k a)+insert x = (<:> singleton x)
src/Data/Morpheus/Types/Internal/AST/Selection.hs view
@@ -3,12 +3,14 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-}+{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Types.Internal.AST.Selection ( Selection (..),@@ -27,9 +29,10 @@ ) where -import Data.Maybe (fromMaybe, isJust)--- MORPHEUS-+import Control.Applicative (pure)+import Data.Foldable (all, foldr)+import Data.Functor ((<$>), fmap)+import Data.Maybe (Maybe (..), fromMaybe, isJust, maybe) import Data.Morpheus.Error.NameCollision ( NameCollision (..), )@@ -41,34 +44,50 @@ ( Failure (..), KeyOf (..), Merge (..),+ elems, )+import Data.Morpheus.Rendering.RenderGQL+ ( RenderGQL (..),+ Rendering,+ renderArguments,+ renderObject,+ space,+ ) import Data.Morpheus.Types.Internal.AST.Base ( FieldName,- GQLError (..),- GQLErrors, Message,+ Msg (..), OperationType (..), Position,- RAW, Ref (..),- Stage, TypeName (..),- VALID,+ ValidationError (..),+ ValidationErrors, intercalateName, msg,+ msgValidation, readName, )+import Data.Morpheus.Types.Internal.AST.Fields+ ( Arguments,+ Directives,+ ) import Data.Morpheus.Types.Internal.AST.MergeSet ( MergeSet, )-import Data.Morpheus.Types.Internal.AST.OrderedMap- ( OrderedMap,+import Data.Morpheus.Types.Internal.AST.OrdMap+ ( OrdMap, )+import Data.Morpheus.Types.Internal.AST.Stage+ ( RAW,+ Stage,+ VALID,+ )+import Data.Morpheus.Types.Internal.AST.TypeCategory+ ( OBJECT,+ ) import Data.Morpheus.Types.Internal.AST.TypeSystem- ( Arguments,- Directives,- OUT,- Schema (..),+ ( Schema (..), TypeDefinition (..), ) import Data.Morpheus.Types.Internal.AST.Value@@ -78,34 +97,48 @@ ) import Data.Semigroup ((<>)) import Language.Haskell.TH.Syntax (Lift (..))+import Prelude+ ( ($),+ (.),+ Eq (..),+ Show (..),+ otherwise,+ ) -data Fragment = Fragment+data Fragment (stage :: Stage) = Fragment { fragmentName :: FieldName, fragmentType :: TypeName, fragmentPosition :: Position,- fragmentSelection :: SelectionSet RAW,- fragmentDirectives :: Directives RAW+ fragmentSelection :: SelectionSet stage,+ fragmentDirectives :: Directives stage } deriving (Show, Eq, Lift) -- ERRORs-instance NameCollision Fragment where- nameCollision _ Fragment {fragmentName, fragmentPosition} =- GQLError- { message = "There can be only one fragment named " <> msg fragmentName <> ".",- locations = [fragmentPosition]- }+instance NameCollision (Fragment s) where+ nameCollision Fragment {fragmentName, fragmentPosition} =+ ValidationError+ ("There can be only one fragment named " <> msg fragmentName <> ".")+ [fragmentPosition] -instance KeyOf Fragment where+instance KeyOf FieldName (Fragment s) where keyOf = fragmentName -type Fragments = OrderedMap FieldName Fragment+type Fragments (s :: Stage) = OrdMap FieldName (Fragment s) data SelectionContent (s :: Stage) where SelectionField :: SelectionContent s SelectionSet :: SelectionSet s -> SelectionContent s UnionSelection :: UnionSelection VALID -> SelectionContent VALID +renderSelectionSet :: SelectionSet VALID -> Rendering+renderSelectionSet = renderObject . elems++instance RenderGQL (SelectionContent VALID) where+ render SelectionField = ""+ render (SelectionSet selSet) = renderSelectionSet selSet+ render (UnionSelection unionSets) = renderObject (elems unionSets)+ instance Merge (SelectionSet s) => Merge (SelectionContent s)@@ -116,9 +149,9 @@ | oldC == currC = pure oldC | otherwise = failure- [ GQLError- { message = msg (intercalateName "." $ map refName path),- locations = map refPosition path+ [ ValidationError+ { validationMessage = msg (intercalateName "." $ fmap refName path),+ validationLocations = fmap refPosition path } ] @@ -134,12 +167,22 @@ } deriving (Show, Eq, Lift) -mergeConflict :: [Ref] -> GQLError -> GQLErrors+instance KeyOf TypeName UnionTag where+ keyOf = unionTagName++instance RenderGQL UnionTag where+ render UnionTag {unionTagName, unionTagSelection} =+ "... on "+ <> render unionTagName+ <> space+ <> renderSelectionSet unionTagSelection++mergeConflict :: [Ref] -> ValidationError -> ValidationErrors mergeConflict [] err = [err] mergeConflict refs@(rootField : xs) err =- [ GQLError- { message = renderSubfields <> message err,- locations = map refPosition refs <> locations err+ [ ValidationError+ { validationMessage = renderSubfields <> validationMessage err,+ validationLocations = fmap refPosition refs <> validationLocations err } ] where@@ -156,10 +199,6 @@ merge path (UnionTag oldTag oldSel) (UnionTag _ currentSel) = UnionTag oldTag <$> merge path oldSel currentSel -instance KeyOf UnionTag where- type KEY UnionTag = TypeName- keyOf = unionTagName- type UnionSelection (s :: Stage) = MergeSet s UnionTag type SelectionSet (s :: Stage) = MergeSet s (Selection s)@@ -174,10 +213,19 @@ selectionDirectives :: Directives s } -> Selection s- InlineFragment :: Fragment -> Selection RAW+ InlineFragment :: Fragment RAW -> Selection RAW Spread :: Directives RAW -> Ref -> Selection RAW -instance KeyOf (Selection s) where+instance RenderGQL (Selection VALID) where+ render+ Selection+ { ..+ } =+ render (fromMaybe selectionName selectionAlias)+ <> renderArguments (elems selectionArguments)+ <> render selectionContent++instance KeyOf FieldName (Selection s) where keyOf Selection { selectionName,@@ -223,12 +271,12 @@ | selectionName old == selectionName current = pure $ selectionName current | otherwise = failure $ mergeConflict path $- GQLError- { message =+ ValidationError+ { validationMessage = "" <> msg (selectionName old) <> " and " <> msg (selectionName current) <> " are different fields. " <> useDufferentAliases,- locations = [pos1, pos2]+ validationLocations = [pos1, pos2] } --------------------- -- allias name is relevant only if they collide by allias like:@@ -243,16 +291,15 @@ | selectionArguments old == selectionArguments current = pure $ selectionArguments current | otherwise = failure $ mergeConflict currentPath $- GQLError- { message = "they have differing arguments. " <> useDufferentAliases,- locations = [pos1, pos2]+ ValidationError+ { validationMessage = "they have differing arguments. " <> useDufferentAliases,+ validationLocations = [pos1, pos2] } merge path _ _ =- failure $ mergeConflict path $- GQLError- { message = "INTERNAL: can't merge. " <> useDufferentAliases,- locations = []- }+ failure $+ mergeConflict+ path+ ("INTERNAL: can't merge. " <> msgValidation useDufferentAliases :: ValidationError) deriving instance Show (Selection a) @@ -272,16 +319,25 @@ } deriving (Show, Lift) +instance RenderGQL (Operation VALID) where+ render+ Operation+ { operationName,+ operationType,+ operationSelection+ } =+ render operationType+ <> space+ <> render operationName+ <> space+ <> renderSelectionSet operationSelection+ getOperationName :: Maybe FieldName -> TypeName getOperationName = maybe "AnonymousOperation" (TypeName . readName) -getOperationDataType :: Failure GQLErrors m => Operation a -> Schema -> m (TypeDefinition OUT)+getOperationDataType :: Failure ValidationError m => Operation s -> Schema VALID -> m (TypeDefinition OBJECT VALID) getOperationDataType Operation {operationType = Query} lib = pure (query lib) getOperationDataType Operation {operationType = Mutation, operationPosition} lib =- case mutation lib of- Just x -> pure x- Nothing -> failure $ mutationIsNotDefined operationPosition+ maybe (failure $ mutationIsNotDefined operationPosition) pure (mutation lib) getOperationDataType Operation {operationType = Subscription, operationPosition} lib =- case subscription lib of- Just x -> pure x- Nothing -> failure $ subscriptionIsNotDefined operationPosition+ maybe (failure $ subscriptionIsNotDefined operationPosition) pure (subscription lib)
+ src/Data/Morpheus/Types/Internal/AST/Stage.hs view
@@ -0,0 +1,31 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.Types.Internal.AST.Stage+ ( CONST,+ VALID,+ RAW,+ Stage (..),+ CONST_OR_VALID,+ )+where++type CONST = 'CONST++type VALID = 'VALID++type RAW = 'RAW++data Stage+ = RAW+ | CONST+ | VALID++type family CONST_OR_VALID (a :: Stage) :: Stage++type instance CONST_OR_VALID RAW = CONST++type instance CONST_OR_VALID CONST = CONST++type instance CONST_OR_VALID VALID = VALID
src/Data/Morpheus/Types/Internal/AST/TH.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Types.Internal.AST.TH ( ConsD (..),@@ -17,13 +18,23 @@ TypeRef (..), hsTypeName, )+import Data.Morpheus.Types.Internal.AST.Fields+ ( FieldDefinition (..),+ FieldsDefinition,+ ) import Data.Morpheus.Types.Internal.AST.TypeSystem ( DataEnumValue (..),- FieldDefinition (..),- FieldsDefinition, )+import Prelude+ ( (.),+ Bool (..),+ Show,+ all,+ fmap,+ null,+ ) -toHSFieldDefinition :: FieldDefinition cat -> FieldDefinition cat+toHSFieldDefinition :: FieldDefinition cat s -> FieldDefinition cat s toHSFieldDefinition field@FieldDefinition {fieldType = tyRef@TypeRef {typeConName}} = field { fieldType = tyRef {typeConName = hsTypeName typeConName}@@ -37,21 +48,21 @@ -- Template Haskell Types -data ConsD cat = ConsD+data ConsD cat s = ConsD { cName :: TypeName,- cFields :: [FieldDefinition cat]+ cFields :: [FieldDefinition cat s] } deriving (Show) -mkCons :: TypeName -> FieldsDefinition cat -> ConsD cat+mkCons :: TypeName -> FieldsDefinition cat s -> ConsD cat s mkCons typename fields = ConsD { cName = hsTypeName typename,- cFields = map toHSFieldDefinition (elems fields)+ cFields = fmap toHSFieldDefinition (elems fields) } -isEnum :: [ConsD cat] -> Bool+isEnum :: [ConsD cat s] -> Bool isEnum = all (null . cFields) -mkConsEnum :: DataEnumValue -> ConsD cat+mkConsEnum :: DataEnumValue s -> ConsD cat s mkConsEnum DataEnumValue {enumName} = ConsD {cName = hsTypeName enumName, cFields = []}
+ src/Data/Morpheus/Types/Internal/AST/TypeCategory.hs view
@@ -0,0 +1,133 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.Types.Internal.AST.TypeCategory+ ( TypeCategory,+ ELEM,+ OUT,+ IN,+ ANY,+ LEAF,+ OBJECT,+ IMPLEMENTABLE,+ fromAny,+ toAny,+ REQURE_IMPLEMENTABLE,+ ToCategory (..),+ FromCategory (..),+ )+where++import Data.Maybe (Maybe (..))+import Data.Morpheus.Types.Internal.AST.Base+ ( FALSE,+ TRUE,+ )+import Data.Morpheus.Types.Internal.AST.Stage (Stage)+import Prelude (Bool (..))++data TypeCategory+ = IN+ | OUT+ | ANY+ | LEAF+ | OBJECT+ | IMPLEMENTABLE++type IN = 'IN++type OUT = 'OUT++type ANY = 'ANY++type OBJECT = 'OBJECT++type IMPLEMENTABLE = 'IMPLEMENTABLE++type LEAF = 'LEAF++toAny ::+ (ToCategory a k ANY) =>+ a (k :: TypeCategory) (s :: Stage) ->+ a ANY s+toAny = toCategory++fromAny ::+ (FromCategory a ANY k) =>+ a ANY (s :: Stage) ->+ Maybe (a k s)+fromAny = fromCategory++class ToCategory a (k :: TypeCategory) (k' :: TypeCategory) where+ toCategory :: a k (s :: Stage) -> a k' s++class FromCategory a (k :: TypeCategory) (k' :: TypeCategory) where+ fromCategory :: a k (s :: Stage) -> Maybe (a k' s)++type family+ ELEM+ (elemKind :: TypeCategory)+ (setOfKind :: TypeCategory) ::+ Bool++type REQURE_IMPLEMENTABLE cat = ELEM cat IMPLEMENTABLE ~ TRUE++-- ANY+type instance ELEM ANY a = TRUE++type instance ELEM a ANY = TRUE++-- LEAF+type instance ELEM LEAF LEAF = TRUE++type instance ELEM LEAF IN = TRUE++type instance ELEM LEAF OUT = TRUE++type instance ELEM LEAF OBJECT = FALSE++type instance ELEM LEAF IMPLEMENTABLE = FALSE++-- IN+type instance ELEM IN IN = TRUE++type instance ELEM IN OUT = FALSE++type instance ELEM IN OBJECT = FALSE++type instance ELEM IN IMPLEMENTABLE = FALSE++-- OUT+type instance ELEM OUT OUT = TRUE++type instance ELEM OUT IN = FALSE++type instance ELEM OUT OBJECT = FALSE++type instance ELEM OUT IMPLEMENTABLE = FALSE++-- IMPLEMENTABLE+type instance ELEM IMPLEMENTABLE IMPLEMENTABLE = TRUE++type instance ELEM IMPLEMENTABLE OUT = TRUE++type instance ELEM IMPLEMENTABLE IN = FALSE++type instance ELEM IMPLEMENTABLE LEAF = FALSE++type instance ELEM IMPLEMENTABLE OBJECT = FALSE++-- OUTPUT_OBJECT+type instance ELEM OBJECT OBJECT = TRUE++type instance ELEM OBJECT IMPLEMENTABLE = TRUE++type instance ELEM OBJECT OUT = TRUE++type instance ELEM OBJECT IN = FALSE++type instance ELEM OBJECT LEAF = FALSE
src/Data/Morpheus/Types/Internal/AST/TypeSystem.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveLift #-} {-# LANGUAGE DeriveTraversable #-}@@ -16,164 +17,195 @@ {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Types.Internal.AST.TypeSystem- ( Arguments,- ScalarDefinition (..),+ ( ScalarDefinition (..), DataEnum,- FieldsDefinition,- ArgumentDefinition, DataUnion,- ArgumentsDefinition (..),- FieldDefinition (..),- InputFieldsDefinition, TypeContent (..), TypeDefinition (..), Schema (..), DataEnumValue (..), TypeLib,- Directive (..),- TypeUpdater, TypeCategory, DataInputUnion,- Argument (..),- Fields (..),- createField,- createArgument,- createEnumType,+ mkEnumContent,+ mkUnionContent,+ mkType, createScalarType,- createType,- createUnionType,- createAlias,- createInputUnionFields,- createEnumValue,- defineType,- isTypeDefined,+ mkInputUnionFields, initTypeLib,- isFieldNullable, insertType,- fieldVisibility, kindOf,- toNullableField,- toListField, isEntNode,- lookupDeprecated,- lookupDeprecatedReason, lookupWith,- unsafeFromFields, __inputname, updateSchema,- OUT,- IN,- ANY,- FromAny (..),- ToAny (..),- DirectiveDefinitions,- DirectiveDefinition (..),- Directives,- fieldsToArguments,- FieldContent (..),- fieldContentArgs,- mkField,- mkObjectField, UnionMember (..), mkUnionMember,+ RawTypeDefinition (..),+ RootOperationTypeDefinition (..),+ SchemaDefinition (..),+ buildSchema,+ Typed (Typed),+ untyped,+ typed,+ possibleTypes,+ possibleInterfaceTypes, ) where -import Data.HashMap.Lazy- ( HashMap,- union,- )-import qualified Data.HashMap.Lazy as HM-import Data.List (find) -- MORPHEUS -import Data.Morpheus.Error (globalErrorMessage)+import Control.Applicative ((<|>), Applicative (..))+import Control.Monad (Monad (..), foldM)+import Data.Either (Either (..))+import Data.Foldable (concatMap)+import Data.Functor ((<$>), fmap)+import Data.List (elem, filter, find, notElem)+import Data.Maybe (Maybe (..), catMaybes, mapMaybe, maybe) import Data.Morpheus.Error.NameCollision ( NameCollision (..), ) import Data.Morpheus.Error.Schema (nameCollisionError) import Data.Morpheus.Internal.Utils- ( Collection (..),+ ( (<:>),+ Collection (..),+ Failure (..), KeyOf (..), Listable (..), Merge (..), Selectable (..),+ UpdateT (..),+ concatUpdates, elems,+ resolveUpdates, ) import Data.Morpheus.Rendering.RenderGQL ( RenderGQL (..),- renderIndent,+ newline,+ renderMembers, renderObject, ) import Data.Morpheus.Types.Internal.AST.Base ( DataFingerprint (..), Description,- FALSE, FieldName, FieldName (..),- GQLError (..), Msg (..),- Position,- RESOLVED,- Stage,+ OperationType (..), TRUE, Token, TypeKind (..), TypeName, TypeRef (..), TypeWrapper (..),- VALID,- isNullable,- isSystemTypeName,+ ValidationError,+ ValidationErrors,+ isNotSystemTypeName,+ mkTypeRef, msg,- sysFields,+ msgValidation, toFieldName, toOperationType, )-import Data.Morpheus.Types.Internal.AST.DirectiveLocation (DirectiveLocation)-import Data.Morpheus.Types.Internal.AST.OrderedMap- ( OrderedMap,- unsafeFromValues,+import Data.Morpheus.Types.Internal.AST.Fields+ ( Directive,+ DirectiveDefinition (..),+ Directives,+ FieldDefinition (..),+ FieldsDefinition,+ unsafeFromFields, )-import Data.Morpheus.Types.Internal.AST.Value- ( ScalarValue (..),- ValidValue,- Value (..),+import Data.Morpheus.Types.Internal.AST.OrdMap+ ( OrdMap, )-import Data.Morpheus.Types.Internal.Resolving.Core- ( Failure (..),- LibUpdater,- resolveUpdates,+import Data.Morpheus.Types.Internal.AST.SafeHashMap+ ( SafeHashMap,+ insert, )+import Data.Morpheus.Types.Internal.AST.Stage+ ( CONST,+ Stage,+ VALID,+ )+import Data.Morpheus.Types.Internal.AST.TypeCategory+ ( ANY,+ ELEM,+ FromCategory (..),+ IMPLEMENTABLE,+ IN,+ LEAF,+ OBJECT,+ OUT,+ ToCategory (..),+ TypeCategory,+ fromAny,+ toAny,+ )+import Data.Morpheus.Types.Internal.AST.Value+ ( Value (..),+ ) import Data.Semigroup (Semigroup (..)) import Data.Text (intercalate) import Instances.TH.Lift () import Language.Haskell.TH.Syntax (Lift (..))+import Prelude+ ( ($),+ (.),+ Bool (..),+ Eq (..),+ Show (..),+ const,+ flip,+ otherwise,+ ) -type DataEnum = [DataEnumValue]+type DataEnum s = [DataEnumValue s] -mkUnionMember :: TypeName -> UnionMember cat+-- used for perserving type information from untyped values+-- e.g+-- unionType :: UnionMember IN VALID -> Typed IN VALID TypeName+-- unionType = typed memberName+typed :: (a c s -> b) -> a c s -> Typed c s b+typed f = Typed . f++untyped :: (a -> b) -> Typed c s a -> b+untyped f = f . _untyped++-- | used for perserving type information from untyped values+-- see function typed+newtype Typed (cat :: TypeCategory) (s :: Stage) a = Typed+ { _untyped :: a+ }++mkUnionMember :: TypeName -> UnionMember cat s mkUnionMember name = UnionMember name True -data UnionMember (cat :: TypeCategory) = UnionMember+data UnionMember (cat :: TypeCategory) (s :: Stage) = UnionMember { memberName :: TypeName, visibility :: Bool } deriving (Show, Lift, Eq) -type DataUnion = [UnionMember OUT]+type DataUnion s = [UnionMember OUT s] -type DataInputUnion = [UnionMember IN]+type DataInputUnion s = [UnionMember IN s] -instance RenderGQL (UnionMember cat) where+instance RenderGQL (UnionMember cat s) where render = render . memberName +instance Msg (UnionMember cat s) where+ msg = msg . memberName++instance KeyOf TypeName (UnionMember cat s) where+ keyOf = memberName+ -- scalar ------------------------------------------------------------------ newtype ScalarDefinition = ScalarDefinition- {validateValue :: ValidValue -> Either Token ValidValue}+ {validateValue :: Value VALID -> Either Token (Value VALID)} instance Show ScalarDefinition where show _ = "ScalarDefinition"@@ -181,79 +213,19 @@ instance Lift ScalarDefinition where lift _ = [|ScalarDefinition pure|] -data Argument (valid :: Stage) = Argument- { argumentName :: FieldName,- argumentValue :: Value valid,- argumentPosition :: Position- }- deriving (Show, Eq, Lift)--instance KeyOf (Argument stage) where- keyOf = argumentName--instance NameCollision (Argument s) where- nameCollision _ Argument {argumentName, argumentPosition} =- GQLError- { message = "There can Be only One Argument Named " <> msg argumentName,- locations = [argumentPosition]- }--type Arguments s = OrderedMap FieldName (Argument s)---- directive--------------------------------------------------------------------data Directive (s :: Stage) = Directive- { directiveName :: FieldName,- directivePosition :: Position,- directiveArgs :: Arguments s- }- deriving (Show, Lift, Eq)--instance KeyOf (Directive s) where- keyOf = directiveName--type Directives s = [Directive s]--data DirectiveDefinition = DirectiveDefinition- { directiveDefinitionName :: FieldName,- directiveDefinitionDescription :: Maybe Description,- directiveDefinitionLocations :: [DirectiveLocation],- directiveDefinitionArgs :: ArgumentsDefinition- }- deriving (Show, Lift)--type DirectiveDefinitions = [DirectiveDefinition]--instance KeyOf DirectiveDefinition where- keyOf = directiveDefinitionName--instance Selectable DirectiveDefinition ArgumentDefinition where- selectOr fb f key DirectiveDefinition {directiveDefinitionArgs} =- selectOr fb f key directiveDefinitionArgs--lookupDeprecated :: [Directive VALID] -> Maybe (Directive VALID)-lookupDeprecated = find isDeprecation- where- isDeprecation Directive {directiveName = "deprecated"} = True- isDeprecation _ = False--lookupDeprecatedReason :: Directive VALID -> Maybe Description-lookupDeprecatedReason Directive {directiveArgs} =- selectOr Nothing (Just . maybeString) "reason" directiveArgs- where- maybeString :: Argument VALID -> Description- maybeString Argument {argumentValue = (Scalar (String x))} = x- maybeString _ = "can't read deprecated Reason Value"+#if MIN_VERSION_template_haskell(2,16,0)+ liftTyped _ = [||ScalarDefinition pure||]+#endif -- ENUM VALUE-data DataEnumValue = DataEnumValue+data DataEnumValue s = DataEnumValue { enumName :: TypeName, enumDescription :: Maybe Description,- enumDirectives :: [Directive VALID]+ enumDirectives :: [Directive s] } deriving (Show, Lift) -instance RenderGQL DataEnumValue where+instance RenderGQL (DataEnumValue s) where render DataEnumValue {enumName} = render enumName -- 3.2 Schema : https://graphql.github.io/graphql-spec/June2018/#sec-Schema@@ -264,47 +236,195 @@ -- RootOperationTypeDefinition : -- OperationType: NamedType -data Schema = Schema- { types :: TypeLib,- query :: TypeDefinition 'Out,- mutation :: Maybe (TypeDefinition 'Out),- subscription :: Maybe (TypeDefinition 'Out)+data Schema (s :: Stage) = Schema+ { types :: TypeLib s,+ query :: TypeDefinition OBJECT s,+ mutation :: Maybe (TypeDefinition OBJECT s),+ subscription :: Maybe (TypeDefinition OBJECT s),+ directiveDefinitions :: [DirectiveDefinition s] }+ deriving (Show, Lift)++instance Merge (Schema s) where+ merge _ s1 s2 =+ Schema+ <$> (types s1 <:> types s2)+ <*> mergeOperation (query s1) (query s2)+ <*> mergeOptional (mutation s1) (mutation s2)+ <*> mergeOptional (subscription s1) (subscription s2)+ <*> pure (directiveDefinitions s1 <> directiveDefinitions s2)++mergeOptional ::+ (Monad m, Failure ValidationErrors m) =>+ Maybe (TypeDefinition OBJECT s) ->+ Maybe (TypeDefinition OBJECT s) ->+ m (Maybe (TypeDefinition OBJECT s))+mergeOptional Nothing y = pure y+mergeOptional (Just x) Nothing = pure (Just x)+mergeOptional (Just x) (Just y) = Just <$> mergeOperation x y++mergeOperation ::+ (Monad m, Failure ValidationErrors m) =>+ TypeDefinition OBJECT s ->+ TypeDefinition OBJECT s ->+ m (TypeDefinition OBJECT s)+mergeOperation+ TypeDefinition {typeContent = DataObject i1 fields1}+ TypeDefinition {typeContent = DataObject i2 fields2, ..} =+ do+ fields <- fields1 <:> fields2+ pure $ TypeDefinition {typeContent = DataObject (i1 <> i2) fields, ..}++data SchemaDefinition = SchemaDefinition+ { schemaDirectives :: Directives CONST,+ unSchemaDefinition :: OrdMap OperationType RootOperationTypeDefinition+ } deriving (Show) -type TypeLib = HashMap TypeName (TypeDefinition ANY)+instance Selectable OperationType RootOperationTypeDefinition SchemaDefinition where+ selectOr fallback f key SchemaDefinition {unSchemaDefinition} =+ selectOr fallback f key unSchemaDefinition -instance Selectable Schema (TypeDefinition ANY) where+instance NameCollision SchemaDefinition where+ nameCollision _ = "There can Be only One SchemaDefinition."++instance KeyOf TypeName SchemaDefinition where+ keyOf _ = "schema"++data RawTypeDefinition+ = RawSchemaDefinition SchemaDefinition+ | RawTypeDefinition (TypeDefinition ANY CONST)+ | RawDirectiveDefinition (DirectiveDefinition CONST)+ deriving (Show)++data RootOperationTypeDefinition = RootOperationTypeDefinition+ { rootOperationType :: OperationType,+ rootOperationTypeDefinitionName :: TypeName+ }+ deriving (Show, Eq)++instance NameCollision RootOperationTypeDefinition where+ nameCollision RootOperationTypeDefinition {rootOperationType} =+ "There can Be only One TypeDefinition for schema." <> msgValidation rootOperationType++instance KeyOf OperationType RootOperationTypeDefinition where+ keyOf = rootOperationType++type TypeLib s = SafeHashMap TypeName (TypeDefinition ANY s)++instance Selectable TypeName (TypeDefinition ANY s) (Schema s) where selectOr fb f name lib = maybe fb f (lookupDataType name lib) -instance Listable (TypeDefinition ANY) Schema where- elems = HM.elems . typeRegister- fromElems types = case popByKey "Query" types of- (Nothing, _) -> failure (globalErrorMessage "INTERNAL: Query Not Defined")- (Just query, lib1) -> do- let (mutation, lib2) = popByKey "Mutation" lib1- let (subscription, lib3) = popByKey "Subscription" lib2- pure $ (foldr defineType (initTypeLib query) lib3) {mutation, subscription}+instance Listable (TypeDefinition ANY s) (Schema s) where+ elems Schema {..} =+ elems types+ <> concatMap fromOperation [Just query, mutation, subscription]+ fromElems types =+ traverse3+ (popByKey types)+ ( RootOperationTypeDefinition Query "Query",+ RootOperationTypeDefinition Mutation "Mutation",+ RootOperationTypeDefinition Subscription "Subscription"+ )+ >>= buildWith types -initTypeLib :: TypeDefinition 'Out -> Schema+buildWith ::+ ( Monad f,+ Failure ValidationErrors f+ ) =>+ [TypeDefinition cat s] ->+ ( Maybe (TypeDefinition OBJECT s),+ Maybe (TypeDefinition OBJECT s),+ Maybe (TypeDefinition OBJECT s)+ ) ->+ f (Schema s)+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)+ where+ blacklist :: [TypeName]+ blacklist = fmap typeName (catMaybes excusionTypes)++withDirectives ::+ [DirectiveDefinition s] ->+ Schema s ->+ Schema s+withDirectives dirs Schema {..} =+ Schema+ { directiveDefinitions = directiveDefinitions <> dirs,+ ..+ }++buildSchema ::+ (Monad m, Failure ValidationErrors m) =>+ ( Maybe SchemaDefinition,+ [TypeDefinition ANY s],+ [DirectiveDefinition s]+ ) ->+ m (Schema s)+buildSchema (Nothing, types, dirs) = withDirectives dirs <$> fromElems types+buildSchema (Just schemaDef, types, dirs) =+ withDirectives+ dirs+ <$> ( traverse3 selectOp (Query, Mutation, Subscription)+ >>= buildWith types+ )+ where+ selectOp op = selectOperation schemaDef op types++traverse3 :: Applicative t => (a -> t b) -> (a, a, a) -> t (b, b, b)+traverse3 f (a1, a2, a3) = (,,) <$> f a1 <*> f a2 <*> f a3++typeReference ::+ (Monad m, Failure ValidationErrors m) =>+ [TypeDefinition ANY s] ->+ RootOperationTypeDefinition ->+ m (Maybe (TypeDefinition OBJECT s))+typeReference types rootOperation =+ popByKey types rootOperation+ >>= maybe+ (failure ["Unknown type " <> msgValidation (rootOperationTypeDefinitionName rootOperation) <> "."])+ (pure . Just)++selectOperation ::+ ( Monad f,+ Failure ValidationErrors f+ ) =>+ SchemaDefinition ->+ OperationType ->+ [TypeDefinition ANY s] ->+ f (Maybe (TypeDefinition OBJECT s))+selectOperation schemaDef operationType lib =+ selectOr (pure Nothing) (typeReference lib) operationType schemaDef++initTypeLib :: TypeDefinition OBJECT s -> Schema s initTypeLib query = Schema { types = empty, query = query, mutation = Nothing,- subscription = Nothing+ subscription = Nothing,+ directiveDefinitions = empty } -typeRegister :: Schema -> TypeLib-typeRegister Schema {types, query, mutation, subscription} =- types- `union` HM.fromList- (concatMap fromOperation [Just query, mutation, subscription])+isType :: TypeName -> TypeDefinition OBJECT s -> Maybe (TypeDefinition ANY s)+isType name x+ | name == typeName x = Just (toAny x)+ | otherwise = Nothing -lookupDataType :: TypeName -> Schema -> Maybe (TypeDefinition ANY)-lookupDataType name = HM.lookup name . typeRegister+lookupDataType :: TypeName -> Schema s -> Maybe (TypeDefinition ANY s)+lookupDataType name Schema {types, query, mutation, subscription} =+ isType name query+ <|> (mutation >>= isType name)+ <|> (subscription >>= isType name)+ <|> selectOr Nothing Just name types -isTypeDefined :: TypeName -> Schema -> Maybe DataFingerprint+isTypeDefined :: TypeName -> Schema s -> Maybe DataFingerprint isTypeDefined name lib = typeFingerprint <$> lookupDataType name lib -- 3.4 Types : https://graphql.github.io/graphql-spec/June2018/#sec-Types@@ -317,123 +437,142 @@ -- EnumTypeDefinition -- InputObjectTypeDefinition -data TypeDefinition (a :: TypeCategory) = TypeDefinition+data TypeDefinition (a :: TypeCategory) (s :: Stage) = TypeDefinition { typeName :: TypeName, typeFingerprint :: DataFingerprint, typeDescription :: Maybe Description,- typeDirectives :: Directives VALID,- typeContent :: TypeContent TRUE a+ typeDirectives :: Directives s,+ typeContent :: TypeContent TRUE a s } deriving (Show, Lift) -instance KeyOf (TypeDefinition a) where- type KEY (TypeDefinition a) = TypeName+instance KeyOf TypeName (TypeDefinition a s) where keyOf = typeName -data TypeCategory = In | Out | Any--type IN = 'In--type OUT = 'Out--type ANY = 'Any--class ToAny a where- toAny :: a (k :: TypeCategory) -> a ANY--instance ToAny TypeDefinition where- toAny TypeDefinition {typeContent, ..} = TypeDefinition {typeContent = toAny typeContent, ..}+instance NameCollision (TypeDefinition cat s) where+ nameCollision x = "There can Be only One TypeDefinition Named " <> msgValidation (typeName x) <> "." -instance ToAny (TypeContent TRUE) where- toAny DataScalar {..} = DataScalar {..}- toAny DataEnum {..} = DataEnum {..}- toAny DataInputObject {..} = DataInputObject {..}- toAny DataInputUnion {..} = DataInputUnion {..}- toAny DataObject {..} = DataObject {..}- toAny DataUnion {..} = DataUnion {..}- toAny DataInterface {..} = DataInterface {..}+instance+ ToCategory (TypeContent TRUE) cat cat' =>+ ToCategory TypeDefinition cat cat'+ where+ toCategory TypeDefinition {typeContent, ..} =+ TypeDefinition+ { typeContent = toCategory typeContent,+ ..+ } -instance ToAny FieldDefinition where- toAny FieldDefinition {fieldContent, ..} = FieldDefinition {fieldContent = toAny <$> fieldContent, ..}+possibleTypes :: TypeDefinition a s -> Schema s' -> [TypeName]+possibleTypes+ TypeDefinition+ { typeName,+ typeContent = DataObject {objectImplements}+ }+ _ = typeName : objectImplements+possibleTypes TypeDefinition {typeName = name, typeContent = DataInterface {}} schema =+ name : fmap typeName (possibleInterfaceTypes name schema)+possibleTypes TypeDefinition {typeName} _ = [typeName] -instance ToAny (FieldContent TRUE) where- toAny (FieldArgs x) = FieldArgs x- toAny (DefaultInputValue x) = DefaultInputValue x+possibleInterfaceTypes ::+ TypeName ->+ Schema s ->+ [TypeDefinition ANY s]+possibleInterfaceTypes name schema = mapMaybe (isPosibleInterfaceType name) (elems schema) -class FromAny a (k :: TypeCategory) where- fromAny :: a ANY -> Maybe (a k)+isPosibleInterfaceType ::+ TypeName ->+ TypeDefinition c s ->+ Maybe (TypeDefinition c s)+isPosibleInterfaceType name typeDef@TypeDefinition {typeName, typeContent = DataObject {objectImplements}}+ | name `elem` (typeName : objectImplements) = Just typeDef+isPosibleInterfaceType _ _ = Nothing -instance (FromAny (TypeContent TRUE) a) => FromAny TypeDefinition a where- fromAny TypeDefinition {typeContent, ..} = bla <$> fromAny typeContent+instance+ (FromCategory (TypeContent TRUE) cat cat') =>+ FromCategory TypeDefinition cat cat'+ where+ fromCategory TypeDefinition {typeContent, ..} = bla <$> fromCategory typeContent where bla x = TypeDefinition {typeContent = x, ..} -instance FromAny (TypeContent TRUE) IN where- fromAny DataScalar {..} = Just DataScalar {..}- fromAny DataEnum {..} = Just DataEnum {..}- fromAny DataInputObject {..} = Just DataInputObject {..}- fromAny DataInputUnion {..} = Just DataInputUnion {..}- fromAny _ = Nothing--instance FromAny (TypeContent TRUE) OUT where- fromAny DataScalar {..} = Just DataScalar {..}- fromAny DataEnum {..} = Just DataEnum {..}- fromAny DataObject {..} = Just DataObject {..}- fromAny DataUnion {..} = Just DataUnion {..}- fromAny DataInterface {..} = Just DataInterface {..}- fromAny _ = Nothing--type family IsSelected (c :: TypeCategory) (a :: TypeCategory) :: Bool--type instance IsSelected ANY a = TRUE--type instance IsSelected OUT OUT = TRUE--type instance IsSelected IN IN = TRUE--type instance IsSelected IN OUT = FALSE--type instance IsSelected OUT IN = FALSE--type instance IsSelected a ANY = TRUE--data TypeContent (b :: Bool) (a :: TypeCategory) where+data+ TypeContent+ (b :: Bool)+ (a :: TypeCategory)+ (s :: Stage)+ where DataScalar :: { dataScalar :: ScalarDefinition } ->- TypeContent TRUE a+ TypeContent (ELEM LEAF a) a s DataEnum ::- { enumMembers :: DataEnum+ { enumMembers :: DataEnum s } ->- TypeContent TRUE a+ TypeContent (ELEM LEAF a) a s DataInputObject ::- { inputObjectFields :: FieldsDefinition IN+ { inputObjectFields :: FieldsDefinition IN s } ->- TypeContent (IsSelected a IN) a+ TypeContent (ELEM IN a) a s DataInputUnion ::- { inputUnionMembers :: DataInputUnion+ { inputUnionMembers :: DataInputUnion s } ->- TypeContent (IsSelected a IN) a+ TypeContent (ELEM IN a) a s DataObject :: { objectImplements :: [TypeName],- objectFields :: FieldsDefinition OUT+ objectFields :: FieldsDefinition OUT s } ->- TypeContent (IsSelected a OUT) a+ TypeContent (ELEM OBJECT a) a s DataUnion ::- { unionMembers :: DataUnion+ { unionMembers :: DataUnion s } ->- TypeContent (IsSelected a OUT) a+ TypeContent (ELEM OUT a) a s DataInterface ::- { interfaceFields :: FieldsDefinition OUT+ { interfaceFields :: FieldsDefinition OUT s } ->- TypeContent (IsSelected a OUT) a+ TypeContent (ELEM IMPLEMENTABLE a) a s -deriving instance Show (TypeContent a b)+deriving instance Show (TypeContent a b s) -deriving instance Lift (TypeContent a b)+deriving instance Lift (TypeContent a b s) -createType :: TypeName -> TypeContent TRUE a -> TypeDefinition a-createType typeName typeContent =+instance ToCategory (TypeContent TRUE) a ANY where+ toCategory DataScalar {..} = DataScalar {..}+ toCategory DataEnum {..} = DataEnum {..}+ toCategory DataInputObject {..} = DataInputObject {..}+ toCategory DataInputUnion {..} = DataInputUnion {..}+ toCategory DataObject {..} = DataObject {..}+ toCategory DataUnion {..} = DataUnion {..}+ toCategory DataInterface {..} = DataInterface {..}++instance ToCategory (TypeContent TRUE) OBJECT IMPLEMENTABLE where+ toCategory DataObject {..} = DataObject {..}++instance FromCategory (TypeContent TRUE) ANY IN where+ fromCategory DataScalar {..} = Just DataScalar {..}+ fromCategory DataEnum {..} = Just DataEnum {..}+ fromCategory DataInputObject {..} = Just DataInputObject {..}+ fromCategory DataInputUnion {..} = Just DataInputUnion {..}+ fromCategory _ = Nothing++instance FromCategory (TypeContent TRUE) ANY OUT where+ fromCategory DataScalar {..} = Just DataScalar {..}+ fromCategory DataEnum {..} = Just DataEnum {..}+ fromCategory DataObject {..} = Just DataObject {..}+ fromCategory DataUnion {..} = Just DataUnion {..}+ fromCategory DataInterface {..} = Just DataInterface {..}+ fromCategory _ = Nothing++instance FromCategory (TypeContent TRUE) ANY OBJECT where+ fromCategory DataObject {..} = Just DataObject {..}+ fromCategory _ = Nothing++instance FromCategory (TypeContent TRUE) ANY IMPLEMENTABLE where+ fromCategory DataObject {..} = Just DataObject {..}+ fromCategory DataInterface {..} = Just DataInterface {..}+ fromCategory _ = Nothing++mkType :: TypeName -> TypeContent TRUE a s -> TypeDefinition a s+mkType typeName typeContent = TypeDefinition { typeName, typeDescription = Nothing,@@ -442,31 +581,29 @@ typeContent } -createScalarType :: TypeName -> TypeDefinition a-createScalarType typeName = createType typeName $ DataScalar (ScalarDefinition pure)+createScalarType :: ELEM LEAF a ~ TRUE => TypeName -> TypeDefinition a s+createScalarType typeName = mkType typeName $ DataScalar (ScalarDefinition pure) -createEnumType :: TypeName -> [TypeName] -> TypeDefinition a-createEnumType typeName typeData = createType typeName (DataEnum enumValues)- where- enumValues = map createEnumValue typeData+mkEnumContent :: ELEM LEAF a ~ TRUE => [TypeName] -> TypeContent TRUE a s+mkEnumContent typeData = DataEnum (fmap mkEnumValue typeData) -createEnumValue :: TypeName -> DataEnumValue-createEnumValue enumName =+mkUnionContent :: [TypeName] -> TypeContent TRUE OUT s+mkUnionContent typeData = DataUnion $ fmap mkUnionMember typeData++mkEnumValue :: TypeName -> DataEnumValue s+mkEnumValue enumName = DataEnumValue { enumName, enumDescription = Nothing, enumDirectives = [] } -createUnionType :: TypeName -> [TypeName] -> TypeDefinition OUT-createUnionType typeName typeData = createType typeName (DataUnion $ map mkUnionMember typeData)--isEntNode :: TypeContent TRUE a -> Bool+isEntNode :: TypeContent TRUE a s -> Bool isEntNode DataScalar {} = True isEntNode DataEnum {} = True isEntNode _ = False -kindOf :: TypeDefinition a -> TypeKind+kindOf :: TypeDefinition a s -> TypeKind kindOf TypeDefinition {typeName, typeContent} = __kind typeContent where __kind DataScalar {} = KindScalar@@ -477,268 +614,95 @@ __kind DataInputUnion {} = KindInputUnion __kind DataInterface {} = KindInterface -fromOperation :: Maybe (TypeDefinition OUT) -> [(TypeName, TypeDefinition ANY)]-fromOperation (Just datatype) = [(typeName datatype, toAny datatype)]+fromOperation :: Maybe (TypeDefinition OBJECT s) -> [TypeDefinition ANY s]+fromOperation (Just datatype) = [toAny datatype] fromOperation Nothing = [] -defineType :: TypeDefinition cat -> Schema -> Schema-defineType dt@TypeDefinition {typeName, typeContent = DataInputUnion enumKeys, typeFingerprint} lib =- lib {types = HM.insert name unionTags (HM.insert typeName (toAny dt) (types lib))}+safeDefineType ::+ ( Monad m,+ Failure ValidationErrors m+ ) =>+ TypeDefinition cat s ->+ Schema s ->+ m (Schema s)+safeDefineType dt@TypeDefinition {typeName, typeContent = DataInputUnion enumKeys, typeFingerprint} lib = do+ types <- insert unionTags (types lib) >>= insert (toAny dt)+ pure lib {types} where- name = typeName <> "Tags" unionTags = TypeDefinition- { typeName = name,+ { typeName = typeName <> "Tags", typeFingerprint, typeDescription = Nothing, typeDirectives = [],- typeContent = DataEnum $ map (createEnumValue . memberName) enumKeys+ typeContent = mkEnumContent (fmap memberName enumKeys) }-defineType datatype lib =- lib {types = HM.insert (typeName datatype) (toAny datatype) (types lib)}+safeDefineType datatype lib = do+ types <- insert (toAny datatype) (types lib)+ pure lib {types} insertType ::- TypeDefinition ANY ->- TypeUpdater-insertType datatype@TypeDefinition {typeName} lib = case isTypeDefined typeName lib of- Nothing -> resolveUpdates (defineType datatype lib) []- Just fingerprint- | fingerprint == typeFingerprint datatype -> return lib- -- throw error if 2 different types has same name- | otherwise -> failure $ nameCollisionError typeName+ (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 ->- [TypeUpdater] ->- (a -> TypeDefinition cat) ->+ [UpdateT m (Schema s)] ->+ (a -> TypeDefinition cat s) -> a ->- TypeUpdater-updateSchema name fingerprint stack f x lib =- case isTypeDefined name lib of- Nothing ->- resolveUpdates- (defineType (f x) lib)- stack- Just fingerprint' | fingerprint' == fingerprint -> return lib- -- throw error if 2 different types has same name- Just _ -> failure $ nameCollisionError name+ 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) --- lookups and removes TypeDefinition from hashmap-popByKey :: TypeName -> [TypeDefinition ANY] -> (Maybe (TypeDefinition OUT), [TypeDefinition ANY])-popByKey name types = case lookupWith typeName name types of+popByKey ::+ (Applicative m, Failure ValidationErrors m) =>+ [TypeDefinition ANY s] ->+ RootOperationTypeDefinition ->+ m (Maybe (TypeDefinition OBJECT s))+popByKey types (RootOperationTypeDefinition opType name) = case lookupWith typeName name types of Just dt@TypeDefinition {typeContent = DataObject {}} ->- (fromAny dt, filter ((/= name) . typeName) types)- _ -> (Nothing, types)--newtype Fields def = Fields- {unFields :: OrderedMap FieldName def}- deriving- ( Show,- Lift,- Functor,- Foldable,- Traversable- )--deriving instance (KEY def ~ FieldName, KeyOf def) => Collection def (Fields def)--instance Merge (FieldsDefinition cat) where- merge path (Fields x) (Fields y) = Fields <$> merge path x y--instance Selectable (Fields (FieldDefinition cat)) (FieldDefinition cat) where- selectOr fb f name (Fields lib) = selectOr fb f name lib--unsafeFromFields :: [FieldDefinition cat] -> FieldsDefinition cat-unsafeFromFields = Fields . unsafeFromValues--fieldsToArguments :: FieldsDefinition IN -> ArgumentsDefinition-fieldsToArguments = ArgumentsDefinition Nothing . unFields--instance (KEY def ~ FieldName, KeyOf def, NameCollision def) => Listable def (Fields def) where- fromElems = fmap Fields . fromElems- elems = elems . unFields---- 3.6 Objects : https://graphql.github.io/graphql-spec/June2018/#sec-Objects---------------------------------------------------------------------------------- ObjectTypeDefinition:--- Description(opt) type Name ImplementsInterfaces(opt) Directives(Const)(opt) FieldsDefinition(opt)------ ImplementsInterfaces--- implements &(opt) NamedType--- ImplementsInterfaces & NamedType------ FieldsDefinition--- { FieldDefinition(list) }----type FieldsDefinition cat = Fields (FieldDefinition cat)---- FieldDefinition--- Description(opt) Name ArgumentsDefinition(opt) : Type Directives(Const)(opt)------ https://spec.graphql.org/June2018/#InputValueDefinition--- InputValueDefinition--- Description(opt) Name: Type DefaultValue(opt) Directives[Const](opt)--data FieldDefinition (cat :: TypeCategory) = FieldDefinition- { fieldName :: FieldName,- fieldDescription :: Maybe Description,- fieldType :: TypeRef,- fieldContent :: Maybe (FieldContent TRUE cat),- fieldDirectives :: [Directive VALID]- }- deriving (Show, Lift)--data FieldContent (bool :: Bool) (cat :: TypeCategory) where- DefaultInputValue ::- { defaultInputValue :: Value RESOLVED- } ->- FieldContent (IsSelected cat IN) cat- FieldArgs ::- { fieldArgsDef :: ArgumentsDefinition- } ->- FieldContent (IsSelected cat OUT) cat--fieldContentArgs :: FieldContent b cat -> OrderedMap FieldName ArgumentDefinition-fieldContentArgs (FieldArgs (ArgumentsDefinition _ argsD)) = argsD-fieldContentArgs _ = empty--deriving instance Show (FieldContent bool cat)--deriving instance Lift (FieldContent bool cat)--instance KeyOf (FieldDefinition cat) where- keyOf = fieldName--instance Selectable (FieldDefinition OUT) ArgumentDefinition where- selectOr fb f key FieldDefinition {fieldContent = Just (FieldArgs args)} = selectOr fb f key args- selectOr fb _ _ _ = fb--instance NameCollision (FieldDefinition cat) where- nameCollision name _ =- GQLError- { message = "There can Be only One field Named " <> msg name,- locations = []- }--instance RenderGQL (FieldDefinition cat) where- render FieldDefinition {fieldName = FieldName name, fieldType, fieldContent = Just (FieldArgs args)} =- name <> render args <> ": " <> render fieldType- render FieldDefinition {fieldName = FieldName name, fieldType} =- name <> ": " <> render fieldType--instance RenderGQL (FieldsDefinition OUT) where- render = renderObject render . ignoreHidden . elems--instance RenderGQL (FieldsDefinition IN) where- render = renderObject render . ignoreHidden . elems--fieldVisibility :: FieldDefinition cat -> Bool-fieldVisibility FieldDefinition {fieldName} = fieldName `notElem` sysFields--isFieldNullable :: FieldDefinition cat -> Bool-isFieldNullable = isNullable . fieldType--createField :: Maybe (FieldContent TRUE cat) -> FieldName -> ([TypeWrapper], TypeName) -> FieldDefinition cat-createField fieldContent fieldName (typeWrappers, typeConName) =- FieldDefinition- { fieldName,- fieldContent,- fieldDescription = Nothing,- fieldType = TypeRef {typeConName, typeWrappers, typeArgs = Nothing},- fieldDirectives = []- }--mkField :: FieldName -> ([TypeWrapper], TypeName) -> FieldDefinition cat-mkField = createField Nothing--mkObjectField :: ArgumentsDefinition -> FieldName -> ([TypeWrapper], TypeName) -> FieldDefinition OUT-mkObjectField args = createField (Just $ FieldArgs args)--toNullableField :: FieldDefinition cat -> FieldDefinition cat-toNullableField dataField- | isNullable (fieldType dataField) = dataField- | otherwise = dataField {fieldType = nullable (fieldType dataField)}- where- nullable alias@TypeRef {typeWrappers} =- alias {typeWrappers = TypeMaybe : typeWrappers}--toListField :: FieldDefinition cat -> FieldDefinition cat-toListField dataField = dataField {fieldType = listW (fieldType dataField)}- where- listW alias@TypeRef {typeWrappers} =- alias {typeWrappers = TypeList : typeWrappers}---- 3.10 Input Objects: https://spec.graphql.org/June2018/#sec-Input-Objects------------------------------------------------------------------------------- InputObjectTypeDefinition--- Description(opt) input Name Directives(const,opt) InputFieldsDefinition(opt)------- InputFieldsDefinition--- { InputValueDefinition(list) }--type InputFieldsDefinition = Fields InputValueDefinition--type InputValueDefinition = FieldDefinition IN---- 3.6.1 Field Arguments : https://graphql.github.io/graphql-spec/June2018/#sec-Field-Arguments--------------------------------------------------------------------------------------------------- ArgumentsDefinition:--- (InputValueDefinition(list))--data ArgumentsDefinition = ArgumentsDefinition- { argumentsTypename :: Maybe TypeName,- arguments :: OrderedMap FieldName ArgumentDefinition- }- deriving (Show, Lift)--instance RenderGQL ArgumentsDefinition where- render ArgumentsDefinition {arguments}- | null arguments =- ""- | otherwise = "(" <> intercalate ", " (map render $ elems arguments) <> ")"--type ArgumentDefinition = FieldDefinition IN--instance Selectable ArgumentsDefinition ArgumentDefinition where- selectOr fb f key (ArgumentsDefinition _ args) = selectOr fb f key args--instance Collection ArgumentDefinition ArgumentsDefinition where- empty = ArgumentsDefinition Nothing empty- singleton = ArgumentsDefinition Nothing . singleton--instance Listable ArgumentDefinition ArgumentsDefinition where- elems (ArgumentsDefinition _ args) = elems args- fromElems args = ArgumentsDefinition Nothing <$> fromElems args--createArgument :: FieldName -> ([TypeWrapper], TypeName) -> FieldDefinition IN-createArgument = mkField---- https://spec.graphql.org/June2018/#InputValueDefinition--- InputValueDefinition--- Description(opt) Name: TypeDefaultValue(opt) Directives[Const](opt)--- TODO: implement inputValue+ pure (fromAny dt)+ Just {} ->+ failure+ [ msgValidation (show opType)+ <> " root type must be Object type if provided, it cannot be "+ <> msgValidation name+ ]+ _ -> pure Nothing __inputname :: FieldName __inputname = "inputname" -createInputUnionFields :: TypeName -> [UnionMember IN] -> [FieldDefinition IN]-createInputUnionFields name members = fieldTag : map unionField members+mkInputUnionFields :: TypeName -> [UnionMember IN s] -> FieldsDefinition IN s+mkInputUnionFields name members = unsafeFromFields $ fieldTag : fmap mkUnionField members where fieldTag = FieldDefinition { fieldName = __inputname, fieldDescription = Nothing, fieldContent = Nothing,- fieldType = createAlias (name <> "Tags"),+ fieldType = mkTypeRef (name <> "Tags"), fieldDirectives = [] } -unionField :: UnionMember IN -> FieldDefinition IN-unionField UnionMember {memberName} =+mkUnionField :: UnionMember IN s -> FieldDefinition IN s+mkUnionField UnionMember {memberName} = FieldDefinition { fieldName = toFieldName memberName, fieldDescription = Nothing,@@ -756,35 +720,24 @@ -- OTHER -------------------------------------------------------------------------------------------------- -createAlias :: TypeName -> TypeRef-createAlias typeConName =- TypeRef {typeConName, typeWrappers = [], typeArgs = Nothing}--type TypeUpdater = LibUpdater Schema--instance RenderGQL Schema where- render schema = intercalate "\n\n" $ map render visibleTypes+instance RenderGQL (Schema s) where+ render schema = intercalate newline (fmap render visibleTypes) where- visibleTypes = filter (not . isSystemTypeName . typeName) (elems schema)+ visibleTypes = filter (isNotSystemTypeName . typeName) (elems schema) -instance RenderGQL (TypeDefinition a) where- render TypeDefinition {typeName, typeContent} = __render typeContent+instance RenderGQL (TypeDefinition a s) where+ render TypeDefinition {typeName, typeContent} = __render typeContent <> newline where __render DataInterface {interfaceFields} = "interface " <> render typeName <> render interfaceFields __render DataScalar {} = "scalar " <> render typeName- __render (DataEnum tags) = "enum " <> render typeName <> renderObject render tags+ __render (DataEnum tags) = "enum " <> render typeName <> renderObject tags __render (DataUnion members) = "union " <> render typeName- <> " =\n "- <> intercalate ("\n" <> renderIndent <> "| ") (map render members)+ <> " = "+ <> renderMembers members __render (DataInputObject fields) = "input " <> render typeName <> render fields- __render (DataInputUnion members) = "input " <> render typeName <> render fieldsDef+ __render (DataInputUnion members) = "input " <> render typeName <> render fields where- fieldsDef = unsafeFromFields fields- fields :: [FieldDefinition IN]- fields = createInputUnionFields typeName members+ fields = mkInputUnionFields typeName members __render DataObject {objectFields} = "type " <> render typeName <> render objectFields--ignoreHidden :: [FieldDefinition cat] -> [FieldDefinition cat]-ignoreHidden = filter fieldVisibility
src/Data/Morpheus/Types/Internal/AST/Value.hs view
@@ -1,14 +1,17 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveLift #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-}+{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Types.Internal.AST.Value ( Value (..),@@ -30,6 +33,10 @@ ) where +-- MORPHEUS++import Control.Applicative (pure)+import Control.Monad.Fail (MonadFail (fail)) import qualified Data.Aeson as A ( (.=), FromJSON (..),@@ -38,10 +45,13 @@ object, pairs, )+import Data.Either (Either (..))+import Data.Foldable (foldl, foldl1, null)+import Data.Functor (fmap) import qualified Data.HashMap.Strict as M ( toList, )--- MORPHEUS+import Data.Maybe (Maybe (..)) import Data.Morpheus.Error.NameCollision ( NameCollision (..), )@@ -54,22 +64,25 @@ import Data.Morpheus.Types.Internal.AST.Base ( FieldName, FieldName (..),- GQLError (..), Msg (..), Position,- RAW,- RESOLVED, Ref (..),- Stage, TypeName (..), TypeRef,- TypeRef (..),- VALID,+ ValidationError (..),+ msgValidation, )-import Data.Morpheus.Types.Internal.AST.OrderedMap- ( OrderedMap,+import Data.Morpheus.Types.Internal.AST.OrdMap+ ( OrdMap, unsafeFromValues, )+import Data.Morpheus.Types.Internal.AST.Stage+ ( CONST,+ CONST_OR_VALID,+ RAW,+ Stage,+ VALID,+ ) import Data.Scientific ( Scientific, floatingOrInteger,@@ -84,6 +97,16 @@ import GHC.Generics (Generic) import Instances.TH.Lift () import Language.Haskell.TH.Syntax (Lift (..))+import Prelude+ ( ($),+ (.),+ Bool,+ Eq,+ Float,+ Int,+ Show (..),+ otherwise,+ ) -- | Primitive Values for GQLScalar: 'Int', 'Float', 'String', 'Boolean'. -- for performance reason type 'Text' represents GraphQl 'String' value@@ -112,22 +135,18 @@ parseJSON (A.String v) = pure $ String v parseJSON notScalar = fail $ "Expected Scalar got :" <> show notScalar -type family VAR (a :: Stage) :: Stage--type instance VAR RAW = RESOLVED--type instance VAR RESOLVED = RESOLVED--type instance VAR VALID = VALID- data VariableContent (stage :: Stage) where- DefaultValue :: Maybe ResolvedValue -> VariableContent RESOLVED+ DefaultValue :: Maybe ResolvedValue -> VariableContent CONST ValidVariableValue :: {validVarContent :: ValidValue} -> VariableContent VALID instance Lift (VariableContent a) where lift (DefaultValue x) = [|DefaultValue x|] lift (ValidVariableValue x) = [|ValidVariableValue x|] +#if MIN_VERSION_template_haskell(2,16,0)+ liftTyped (DefaultValue x) = [||DefaultValue x||]+ liftTyped (ValidVariableValue x) = [||ValidVariableValue x||]+#endif deriving instance Show (VariableContent a) deriving instance Eq (VariableContent a)@@ -136,24 +155,24 @@ { variableName :: FieldName, variableType :: TypeRef, variablePosition :: Position,- variableValue :: VariableContent (VAR stage)+ variableValue :: VariableContent (CONST_OR_VALID stage) } deriving (Show, Eq, Lift) -instance KeyOf (Variable s) where+instance KeyOf FieldName (Variable s) where keyOf = variableName instance NameCollision (Variable s) where- nameCollision _ Variable {variableName, variablePosition} =- GQLError- { message = "There can Be only One Variable Named " <> msg variableName,- locations = [variablePosition]+ nameCollision Variable {variableName, variablePosition} =+ ValidationError+ { validationMessage = "There can Be only One Variable Named " <> msg variableName,+ validationLocations = [variablePosition] } -type VariableDefinitions s = OrderedMap FieldName (Variable s)+type VariableDefinitions s = OrdMap FieldName (Variable s) data Value (stage :: Stage) where- ResolvedVariable :: Ref -> Variable VALID -> Value RESOLVED+ ResolvedVariable :: Ref -> Variable VALID -> Value CONST VariableValue :: Ref -> Value RAW Object :: Object stage -> Value stage List :: [Value stage] -> Value stage@@ -175,39 +194,34 @@ render (ObjectEntry (FieldName name) value) = name <> ": " <> render value instance NameCollision (ObjectEntry s) where- nameCollision _ ObjectEntry {entryName} =- GQLError- { message = "There can Be only One field Named " <> msg entryName,- locations = []- }+ nameCollision ObjectEntry {entryName} =+ "There can Be only One field Named " <> msgValidation entryName :: ValidationError -instance KeyOf (ObjectEntry s) where+instance KeyOf FieldName (ObjectEntry s) where keyOf = entryName -type Object a = OrderedMap FieldName (ObjectEntry a)+type Object a = OrdMap FieldName (ObjectEntry a) type ValidObject = Object VALID type RawObject = Object RAW -type ResolvedObject = Object RESOLVED+type ResolvedObject = Object CONST type RawValue = Value RAW type ValidValue = Value VALID -type ResolvedValue = Value RESOLVED+type ResolvedValue = Value CONST deriving instance Lift (Value a) deriving instance Lift (ObjectEntry a) instance RenderGQL (Value a) where- -- TODO: fix render (ResolvedVariable Ref {refName} _) = "$" <> readName refName render (VariableValue Ref {refName}) = "$" <> readName refName <> " "- -- TODO: fix render Null = "null" render (Enum x) = readTypeName x render (Scalar x) = render x@@ -236,7 +250,7 @@ toJSON (Enum (TypeName x)) = A.String x toJSON (Scalar x) = A.toJSON x toJSON (List x) = A.toJSON x- toJSON (Object fields) = A.object $ map toEntry (elems fields)+ toJSON (Object fields) = A.object $ fmap toEntry (elems fields) where toEntry (ObjectEntry (FieldName name) value) = name A..= A.toJSON value @@ -251,7 +265,7 @@ toEncoding (List x) = A.toEncoding x toEncoding (Object ordmap) | null ordmap = A.toEncoding $ A.object []- | otherwise = A.pairs $ foldl1 (<>) $ map encodeField (elems ordmap)+ | otherwise = A.pairs $ foldl1 (<>) $ fmap encodeField (elems ordmap) where encodeField (ObjectEntry (FieldName key) value) = key A..= value @@ -266,10 +280,10 @@ replaceValue (A.String v) = gqlString v replaceValue (A.Object v) = gqlObject $- map+ fmap (mapTuple FieldName replaceValue) (M.toList v)-replaceValue (A.Array li) = gqlList (map replaceValue (V.toList li))+replaceValue (A.Array li) = gqlList (fmap replaceValue (V.toList li)) replaceValue A.Null = gqlNull instance A.FromJSON (Value a) where@@ -291,6 +305,6 @@ gqlBoolean = Scalar . Boolean gqlString = Scalar . String gqlList = List- gqlObject = Object . unsafeFromValues . map toEntry+ gqlObject = Object . unsafeFromValues . fmap toEntry where toEntry (key, value) = ObjectEntry key value
+ src/Data/Morpheus/Types/Internal/Config.hs view
@@ -0,0 +1,40 @@+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.Types.Internal.Config+ ( Config (..),+ VALIDATION_MODE (..),+ defaultConfig,+ debugConfig,+ )+where++import Prelude+ ( Bool (..),+ Eq,+ Show,+ )++data VALIDATION_MODE+ = WITHOUT_VARIABLES+ | FULL_VALIDATION+ deriving (Eq, Show)++data Config = Config+ { debug :: Bool,+ validationMode :: VALIDATION_MODE+ }+ deriving (Show)++defaultConfig :: Config+defaultConfig =+ Config+ { debug = False,+ validationMode = FULL_VALIDATION+ }++debugConfig :: Config+debugConfig =+ Config+ { debug = True,+ validationMode = FULL_VALIDATION+ }
src/Data/Morpheus/Types/Internal/Resolving.hs view
@@ -1,38 +1,32 @@ {-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Types.Internal.Resolving ( Event (..),- UnSubResolver, Resolver,- MapStrategy (..), LiftOperation, runRootResModel,- toResolver, lift, SubEvent, Eventless, Failure (..),- GQLChannel (..),+ eventChannels, ResponseEvent (..), ResponseStream, cleanEvents, Result (..), ResultT (..), unpackEvents,- LibUpdater,- resolveUpdates,- setTypeName, ObjectResModel (..), ResModel (..), FieldResModel, WithOperation, PushEvents (..), subscribe,- Context (..),+ ResolverContext (..), unsafeInternalContext, RootResModel (..),- unsafeBind,- liftStateless, resultOr, withArguments, -- Dynamic Resolver@@ -45,17 +39,48 @@ mkObject, mkNull, mkString,+ SubscriptionField (..),+ getArguments,+ Channel (..),+ ResolverState,+ liftResolverState,+ mkValue, ) where +import Control.Applicative (pure)+import Control.Monad (Monad)+import qualified Data.Aeson as A+import Data.Functor (fmap)+import qualified Data.HashMap.Strict as HM+ ( lookup,+ toList,+ )+import Data.Maybe (maybe)+import Data.Morpheus.Internal.Utils+ ( mapTuple,+ ) import Data.Morpheus.Types.Internal.AST- ( FieldName,+ ( FieldName (..), ScalarValue (..), Token,- TypeName,+ TypeName (..),+ decodeScientific, ) import Data.Morpheus.Types.Internal.Resolving.Core+import Data.Morpheus.Types.Internal.Resolving.Event import Data.Morpheus.Types.Internal.Resolving.Resolver+import Data.Morpheus.Types.Internal.Resolving.ResolverState+import qualified Data.Vector as V+ ( toList,+ )+import Prelude+ ( ($),+ (.),+ Bool,+ Float,+ Int,+ ) mkString :: Token -> ResModel o e m mkString = ResScalar . String@@ -80,6 +105,26 @@ mkNull :: ResModel o e m mkNull = ResNull++unPackName :: A.Value -> TypeName+unPackName (A.String x) = TypeName x+unPackName _ = "__JSON__"++mkValue ::+ (LiftOperation o, Monad m) =>+ A.Value ->+ ResModel o e m+mkValue (A.Object v) =+ mkObject+ (maybe "__JSON__" unPackName $ HM.lookup "__typename" v)+ $ fmap+ (mapTuple FieldName (pure . mkValue))+ (HM.toList v)+mkValue (A.Array ls) = mkList (fmap mkValue (V.toList ls))+mkValue A.Null = mkNull+mkValue (A.Number x) = ResScalar (decodeScientific x)+mkValue (A.String x) = ResScalar (String x)+mkValue (A.Bool x) = ResScalar (Boolean x) mkObject :: TypeName ->
src/Data/Morpheus/Types/Internal/Resolving/Core.hs view
@@ -1,13 +1,10 @@-{-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PolyKinds #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Types.Internal.Resolving.Core ( Eventless,@@ -15,32 +12,31 @@ Failure (..), ResultT (..), unpackEvents,- LibUpdater,- resolveUpdates, mapEvent, cleanEvents,- Event (..),- Channel (..),- GQLChannel (..), PushEvents (..),- statelessToResultT, resultOr, ) where -import Control.Applicative (liftA2)-import Control.Monad (foldM)+import Control.Applicative (Applicative (..), liftA2)+import Control.Monad (Monad (..)) import Control.Monad.Trans.Class (MonadTrans (..))-import Data.Function ((&))+import Data.Functor ((<$>), Functor (..)) import Data.Morpheus.Internal.Utils ( Failure (..), ) import Data.Morpheus.Types.Internal.AST.Base ( GQLError (..), GQLErrors,- Message,+ ValidationError (..),+ toGQLError, ) import Data.Semigroup ((<>))+import Prelude+ ( ($),+ (.),+ ) type Eventless = Result () @@ -48,29 +44,6 @@ class PushEvents e m where pushEvents :: [e] -> m () --- Channel-newtype Channel event = Channel- { _unChannel :: StreamChannel event- }--instance (Eq (StreamChannel event)) => Eq (Channel event) where- Channel x == Channel y = x == y--class GQLChannel a where- type StreamChannel a :: *- streamChannels :: a -> [Channel a]--instance GQLChannel () where- type StreamChannel () = ()- streamChannels _ = []--instance GQLChannel (Event channel content) where- type StreamChannel (Event channel content) = channel- streamChannels Event {channels} = map Channel channels--data Event e c = Event- {channels :: [e], content :: c}- unpackEvents :: Result event a -> [event] unpackEvents Success {events} = events unpackEvents _ = []@@ -101,13 +74,12 @@ instance Failure [GQLError] (Result ev) where failure = Failure -instance Failure Message (Result e) where- failure text =- Failure [GQLError {message = "INTERNAL: " <> text, locations = []}]- instance PushEvents events (Result events) where pushEvents events = Success {result = (), warnings = [], events} +instance Failure [ValidationError] (Result ev) where+ failure = failure . fmap toGQLError+ resultOr :: (GQLErrors -> a') -> (a -> a') -> Result e a -> a' resultOr _ f (Success x _ _) = f x resultOr f _ (Failure e) = f e@@ -118,15 +90,6 @@ } deriving (Functor) -statelessToResultT ::- Applicative m =>- Eventless a ->- ResultT e m a-statelessToResultT =- cleanEvents- . ResultT- . pure- instance Applicative m => Applicative (ResultT event m) where pure = ResultT . pure . pure ResultT app1 <*> ResultT app2 = ResultT $ liftA2 (<*>) app1 app2@@ -146,16 +109,9 @@ instance MonadTrans (ResultT event) where lift = ResultT . fmap pure --- instance Applicative m => Failure String (ResultT event m) where--- failure x =--- ResultT $ pure $ Failure [GQLError {message = pack x, locations = []}]- instance Monad m => Failure GQLErrors (ResultT event m) where failure = ResultT . pure . failure -instance Applicative m => Failure Message (ResultT event m) where- failure = ResultT . pure . failure- instance Applicative m => PushEvents event (ResultT event m) where pushEvents = ResultT . pure . pushEvents @@ -176,11 +132,5 @@ mapEvent func (ResultT ma) = ResultT $ mapEv <$> ma where mapEv Success {result, warnings, events} =- Success {result, warnings, events = map func events}+ Success {result, warnings, events = fmap func events} mapEv (Failure err) = Failure err---- Helper Functions-type LibUpdater lib = lib -> Eventless lib--resolveUpdates :: Monad m => lib -> [lib -> m lib] -> m lib-resolveUpdates = foldM (&)
+ src/Data/Morpheus/Types/Internal/Resolving/Event.hs view
@@ -0,0 +1,32 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.Types.Internal.Resolving.Event+ ( Event (..),+ Channel (..),+ eventChannels,+ )+where++import Prelude+ ( Eq (..),+ fmap,+ )++-- Channel+data Channel (event :: *) where+ Channel :: a -> Channel (Event a c)++data Event e c = Event+ { channels :: [e],+ content :: c+ }++eventChannels :: Event e c -> [Channel (Event e c)]+eventChannels Event {channels} = fmap Channel channels++instance (Eq ch) => Eq (Channel (Event ch con)) where+ Channel x == Channel y = x == y
src/Data/Morpheus/Types/Internal/Resolving/Resolver.hs view
@@ -6,7 +6,6 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE InstanceSigs #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}@@ -15,47 +14,48 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Types.Internal.Resolving.Resolver ( Event (..),- UnSubResolver, Resolver,- MapStrategy (..), LiftOperation,- unsafeBind,- toResolver, lift, subscribe, SubEvent,- GQLChannel (..), ResponseEvent (..), ResponseStream, ObjectResModel (..), ResModel (..), FieldResModel, WithOperation,- Context (..),+ ResolverContext (..), unsafeInternalContext, runRootResModel,- setTypeName, RootResModel (..),- liftStateless, withArguments,+ getArguments,+ SubscriptionField (..),+ liftResolverState, ) where +import Control.Applicative (Applicative (..))+import Control.Monad (Monad (..), join) import Control.Monad.Fail (MonadFail (..)) import Control.Monad.IO.Class (MonadIO (..))+import Control.Monad.Reader (MonadReader (..), asks) import Control.Monad.Trans.Class (MonadTrans (..))-import Control.Monad.Trans.Reader (ReaderT (..), ask, mapReaderT, withReaderT)-import Data.Maybe (maybe)--- MORPHEUS-import Data.Morpheus.Error.Internal (internalResolvingError)+import Control.Monad.Trans.Reader+ ( ReaderT (..),+ mapReaderT,+ )+import Data.Functor ((<$>), Functor (..))+import Data.Maybe (Maybe (..), maybe) import Data.Morpheus.Error.Selection (subfieldsNotSelected) import Data.Morpheus.Internal.Utils- ( Merge (..),+ ( SemigroupM (..), empty, keyOf, selectOr,@@ -64,60 +64,72 @@ ( GQLResponse, renderResponse, )-import Data.Morpheus.Types.Internal.AST.Base- ( FieldName,- GQLError (..),+import Data.Morpheus.Types.Internal.AST+ ( Arguments,+ FieldName, GQLErrors,+ GQLValue (..),+ InternalError, MUTATION, Message,+ ObjectEntry (..),+ Operation (..), OperationType, OperationType (..), QUERY, SUBSCRIPTION,- TypeName (..),- VALID,- msg,- )-import Data.Morpheus.Types.Internal.AST.MergeSet- ( toOrderedMap,- )-import Data.Morpheus.Types.Internal.AST.Selection- ( Operation (..),+ ScalarValue (..), Selection (..), SelectionContent (..), SelectionSet,+ TypeName (..), UnionSelection, UnionTag (..),- )-import Data.Morpheus.Types.Internal.AST.TypeSystem- ( Arguments,- Schema,- )-import Data.Morpheus.Types.Internal.AST.Value- ( GQLValue (..),- ObjectEntry (..),- ScalarValue (..),+ VALID, ValidValue, Value (..),+ msg,+ toGQLError, )+import Data.Morpheus.Types.Internal.AST.MergeSet+ ( toOrdMap,+ ) import Data.Morpheus.Types.Internal.Resolving.Core- ( Channel (..),- Event (..),- Eventless,+ ( Eventless, Failure (..),- GQLChannel (..), PushEvents (..), Result (..), ResultT (..),- StreamChannel, cleanEvents, mapEvent,- statelessToResultT, )+import Data.Morpheus.Types.Internal.Resolving.Event+ ( Channel (..),+ Event (..),+ )+import Data.Morpheus.Types.Internal.Resolving.ResolverState+ ( ResolverContext (..),+ ResolverState,+ ResolverStateT (..),+ clearStateResolverEvents,+ resolverFailureMessage,+ runResolverState,+ runResolverStateM,+ runResolverStateT,+ toResolverStateT,+ ) import Data.Semigroup- ( (<>),- Semigroup (..),+ ( Semigroup (..), )+import Data.Traversable (traverse)+import Prelude+ ( ($),+ (.),+ Eq (..),+ Show (..),+ lookup,+ otherwise,+ ) type WithOperation (o :: OperationType) = LiftOperation o @@ -129,74 +141,24 @@ type SubEvent event m = Event (Channel event) (event -> m GQLResponse) --- | A datatype to expose 'Schema' and the query's AST information ('Selection', 'Operation').-data Context = Context- { currentSelection :: Selection VALID,- schema :: Schema,- operation :: Operation VALID,- currentTypeName :: TypeName- }- deriving (Show)---- Resolver Internal State-newtype ResolverState event m a = ResolverState- { runResolverState :: ReaderT Context (ResultT event m) a- }- deriving- ( Functor,- Applicative,- Monad- )--instance MonadTrans (ResolverState e) where- lift = ResolverState . lift . lift--instance (Monad m) => Failure Message (ResolverState e m) where- failure message = ResolverState $ do- selection <- currentSelection <$> ask- lift $ failure [resolverFailureMessage selection message]--instance (Monad m) => Failure GQLErrors (ResolverState e m) where- failure = ResolverState . lift . failure--instance (Monad m) => PushEvents e (ResolverState e m) where- pushEvents = ResolverState . lift . pushEvents--mapResolverState ::- ( ReaderT Context (ResultT e m) a ->- ReaderT Context (ResultT e' m') a'- ) ->- ResolverState e m a ->- ResolverState e' m' a'-mapResolverState f (ResolverState x) = ResolverState (f x)--getState :: (Monad m) => ResolverState e m (Selection VALID)-getState = ResolverState $ currentSelection <$> ask--mapState :: (Context -> Context) -> ResolverState e m a -> ResolverState e m a-mapState f = mapResolverState (withReaderT f)---- clear evets and starts new resolver with diferenct type of events but with same value--- use properly. only if you know what you are doing-clearStateResolverEvents :: (Functor m) => ResolverState e m a -> ResolverState e' m a-clearStateResolverEvents = mapResolverState (mapReaderT cleanEvents)--resolverFailureMessage :: Selection VALID -> Message -> GQLError-resolverFailureMessage Selection {selectionName, selectionPosition} message =- GQLError- { message = "Failure on Resolving Field " <> msg selectionName <> ": " <> message,- locations = [selectionPosition]- }+data SubscriptionField (a :: *) where+ SubscriptionField ::+ { channel :: forall e m v. a ~ Resolver SUBSCRIPTION e m v => Channel e,+ unSubscribe :: a+ } ->+ SubscriptionField a -- -- GraphQL Field Resolver -- --------------------------------------------------------------- data Resolver (o :: OperationType) event (m :: * -> *) value where- ResolverQ :: {runResolverQ :: ResolverState () m value} -> Resolver QUERY event m value- ResolverM :: {runResolverM :: ResolverState event m value} -> Resolver MUTATION event m value- ResolverS :: {runResolverS :: ResolverState (Channel event) m (ReaderT event (Resolver QUERY event m) value)} -> Resolver SUBSCRIPTION event m value+ ResolverQ :: {runResolverQ :: ResolverStateT () m value} -> Resolver QUERY event m value+ ResolverM :: {runResolverM :: ResolverStateT event m value} -> Resolver MUTATION event m value+ ResolverS :: {runResolverS :: ResolverStateT () m (SubEventRes event m value)} -> Resolver SUBSCRIPTION event m value +type SubEventRes event m value = ReaderT event (ResolverStateT () m) value+ instance Show (Resolver o e m value) where show ResolverQ {} = "Resolver QUERY e m a" show ResolverM {} = "Resolver MUTATION e m a"@@ -214,12 +176,24 @@ -- Monad instance (Monad m, LiftOperation o) => Monad (Resolver o e m) where return = pure- (>>=) = unsafeBind+ (ResolverQ x) >>= m2 = ResolverQ (x >>= runResolverQ . m2)+ (ResolverM x) >>= m2 = ResolverM (x >>= runResolverM . m2)+ (ResolverS res) >>= m2 = ResolverS (liftSubResolver m2 <$> res) #if __GLASGOW_HASKELL__ < 808 fail = failure . msg # endif +liftSubResolver ::+ (Monad m) =>+ (t -> Resolver SUBSCRIPTION r m a) ->+ ReaderT r (ResolverStateT () m) t ->+ ReaderT r (ResolverStateT () m) a+liftSubResolver m2 readResA = ReaderT $ \e -> do+ a <- runReaderT readResA e+ readResB <- runResolverS (m2 a)+ runReaderT readResB e+ -- MonadIO instance (MonadIO m, LiftOperation o) => MonadIO (Resolver o e m) where liftIO = lift . liftIO@@ -245,137 +219,74 @@ instance (Monad m, Semigroup a, LiftOperation o) => Semigroup (Resolver o e m a) where x <> y = fmap (<>) x <*> y -liftStateless ::- ( LiftOperation o,- Monad m- ) =>- Eventless a ->- Resolver o e m a-liftStateless =- packResolver- . ResolverState- . ReaderT- . const- . statelessToResultT+instance (LiftOperation o, Monad m) => MonadReader ResolverContext (Resolver o e m) where+ ask = packResolver ask+ local f (ResolverQ res) = ResolverQ (local f res)+ local f (ResolverM res) = ResolverM (local f res)+ 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+-- the internal AST with a safe interface.+unsafeInternalContext :: (Monad m, LiftOperation o) => Resolver o e m ResolverContext+unsafeInternalContext = ask++liftResolverState :: (LiftOperation o, Monad m) => ResolverState a -> Resolver o e m a+liftResolverState = packResolver . toResolverStateT+ class LiftOperation (o :: OperationType) where- packResolver :: Monad m => ResolverState e m a -> Resolver o e m a- withResolver :: Monad m => ResolverState e m a -> (a -> Resolver o e m b) -> Resolver o e m b+ packResolver :: Monad m => ResolverStateT e m a -> Resolver o e m a --- packResolver instance LiftOperation QUERY where packResolver = ResolverQ . clearStateResolverEvents- withResolver ctxRes toRes = ResolverQ $ do- v <- clearStateResolverEvents ctxRes- runResolverQ $ toRes v instance LiftOperation MUTATION where packResolver = ResolverM- withResolver ctxRes toRes = ResolverM $ ctxRes >>= runResolverM . toRes instance LiftOperation SUBSCRIPTION where- packResolver = ResolverS . pure . lift . packResolver- withResolver ctxRes toRes = ResolverS $ do- value <- clearStateResolverEvents ctxRes- runResolverS $ toRes value--mapResolverContext :: Monad m => (Context -> Context) -> Resolver o e m a -> Resolver o e m a-mapResolverContext f (ResolverQ res) = ResolverQ (mapState f res)-mapResolverContext f (ResolverM res) = ResolverM (mapState f res)-mapResolverContext f (ResolverS resM) = ResolverS $ do- res <- resM- pure $ ReaderT $ \e -> ResolverQ $ mapState f (runResolverQ (runReaderT res e))--setSelection :: Monad m => Selection VALID -> Resolver o e m a -> Resolver o e m a-setSelection currentSelection =- mapResolverContext (\ctx -> ctx {currentSelection})--setTypeName :: Monad m => TypeName -> Resolver o e m a -> Resolver o e m a-setTypeName currentTypeName =- mapResolverContext (\ctx -> ctx {currentTypeName})---- unsafe variant of >>= , not for public api. user can be confused:--- ignores `channels` on second Subsciption, only returns events from first Subscription monad.--- reason: second monad is waiting for `event` until he does not have some event can't tell which--- channel does it have to listen-unsafeBind ::- forall o e m a b.- Monad m =>- Resolver o e m a ->- (a -> Resolver o e m b) ->- Resolver o e m b-unsafeBind (ResolverQ x) m2 = ResolverQ (x >>= runResolverQ . m2)-unsafeBind (ResolverM x) m2 = ResolverM (x >>= runResolverM . m2)-unsafeBind (ResolverS res) m2 = ResolverS $ do- (readResA :: ReaderT e (Resolver QUERY e m) a) <- res- pure $ ReaderT $ \e -> ResolverQ $ do- let (resA :: Resolver QUERY e m a) = runReaderT readResA e- (valA :: a) <- runResolverQ resA- (readResB :: ReaderT e (Resolver QUERY e m) b) <- clearStateResolverEvents $ runResolverS (m2 valA)- runResolverQ $ runReaderT readResB e+ packResolver = ResolverS . pure . lift . clearStateResolverEvents subscribe ::- forall e m a.- ( PushEvents (Channel e) (ResolverState (Channel e) m),- Monad m- ) =>- [StreamChannel e] ->- Resolver QUERY e m (e -> Resolver QUERY e m a) ->- Resolver SUBSCRIPTION e m a-subscribe ch res = ResolverS $ do- pushEvents (map Channel ch :: [Channel e])- (eventRes :: e -> Resolver QUERY e m a) <- clearStateResolverEvents (runResolverQ res)- pure $ ReaderT eventRes---- | A function to return the internal 'Context' within a resolver's monad.--- Using the 'Context' itself is unsafe because it expposes 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 Context-unsafeInternalContext = packResolver $ ResolverState ask---- Converts Subscription Resolver Type to Query Resolver-type family UnSubResolver (a :: * -> *) :: (* -> *)--type instance UnSubResolver (Resolver SUBSCRIPTION e m) = Resolver QUERY e m+ forall e channel cont m a.+ (Monad m, Event channel cont ~ e) =>+ channel ->+ Resolver QUERY e m (e -> Resolver SUBSCRIPTION e m a) ->+ SubscriptionField (Resolver SUBSCRIPTION e m a)+subscribe ch res =+ SubscriptionField (Channel ch)+ $ ResolverS+ $ fromSub <$> runResolverQ res+ where+ fromSub :: (e -> Resolver SUBSCRIPTION e m a) -> ReaderT e (ResolverStateT () m) a+ fromSub f = join (ReaderT $ \e -> runResolverS (f e)) withArguments ::- forall o e m a. (LiftOperation o, Monad m) => (Arguments VALID -> Resolver o e m a) -> Resolver o e m a-withArguments = withResolver args- where- args :: ResolverState e m (Arguments VALID)- args = selectionArguments <$> getState+withArguments = (getArguments >>=) ------ Selection Processing-toResolver ::- forall o e m a b.+getArguments :: (LiftOperation o, Monad m) =>- (Arguments VALID -> Eventless a) ->- (a -> Resolver o e m b) ->- Resolver o e m b-toResolver toArgs = withResolver args- where- args :: ResolverState e m a- args =- ResultT . pure . toArgs . selectionArguments <$> getState- >>= ResolverState . lift . cleanEvents+ Resolver o e m (Arguments VALID)+getArguments = selectionArguments . currentSelection <$> unsafeInternalContext pickSelection :: TypeName -> UnionSelection VALID -> SelectionSet VALID pickSelection = selectOr empty unionTagSelection withObject :: (LiftOperation o, Monad m) =>+ TypeName -> (SelectionSet VALID -> Resolver o e m value) -> Selection VALID -> Resolver o e m value-withObject f Selection {selectionName, selectionContent, selectionPosition} = checkContent selectionContent+withObject __typename f Selection {selectionName, selectionContent, selectionPosition} = checkContent selectionContent where checkContent (SelectionSet selection) = f selection- checkContent _ = failure (subfieldsNotSelected selectionName "" selectionPosition)+ checkContent (UnionSelection unionSel) =+ f (selectOr empty unionTagSelection __typename unionSel)+ checkContent _ = failure [toGQLError $ subfieldsNotSelected selectionName "" selectionPosition] lookupRes :: (LiftOperation o, Monad m) =>@@ -388,7 +299,7 @@ | otherwise = maybe (pure gqlNull)- (`unsafeBind` runDataResolver)+ (>>= runDataResolver) . lookup selectionName . objectFields @@ -399,30 +310,23 @@ ResModel o e m -> Resolver o e m ValidValue resolveObject selectionSet (ResObject drv@ObjectResModel {__typename}) =- Object . toOrderedMap <$> traverse resolver selectionSet+ Object . toOrdMap <$> traverse resolver selectionSet where resolver :: Selection VALID -> Resolver o e m (ObjectEntry VALID)- resolver sel =- setSelection sel- $ setTypeName __typename- $ ObjectEntry (keyOf sel) <$> lookupRes sel drv-resolveObject _ _ =- failure $ internalResolvingError "expected object as resolver"--toEventResolver :: Monad m => ReaderT event (Resolver QUERY event m) ValidValue -> Context -> event -> m GQLResponse-toEventResolver (ReaderT subRes) sel event = do- value <- runResultT $ runReaderT (runResolverState $ runResolverQ (subRes event)) sel- pure $ renderResponse value+ resolver currentSelection =+ local (\ctx -> ctx {currentSelection, currentTypeName = __typename}) $+ ObjectEntry (keyOf currentSelection) <$> lookupRes currentSelection drv+resolveObject _ _ = packResolver $ failure ("expected object as resolver" :: InternalError) runDataResolver :: (Monad m, LiftOperation o) => ResModel o e m -> Resolver o e m ValidValue-runDataResolver = withResolver getState . __encode+runDataResolver res = asks currentSelection >>= __encode res where __encode obj sel@Selection {selectionContent} = encodeNode obj selectionContent where -- LIST encodeNode (ResList x) _ = List <$> traverse runDataResolver x -- Object ------------------ encodeNode objDrv@ResObject {} _ = withObject (`resolveObject` objDrv) sel+ encodeNode objDrv@(ResObject ObjectResModel {__typename}) _ = withObject __typename (`resolveObject` objDrv) sel -- ENUM encodeNode (ResEnum _ enum) SelectionField = pure $ gqlString $ readTypeName enum encodeNode (ResEnum typename enum) unionSel@UnionSelection {} =@@ -450,23 +354,39 @@ runResolver :: Monad m =>+ Maybe (Selection VALID -> ResolverState (Channel event)) -> Resolver o event m ValidValue ->- Context ->+ ResolverContext -> ResponseStream event m ValidValue-runResolver (ResolverQ resT) sel = cleanEvents $ runReaderT (runResolverState resT) sel-runResolver (ResolverM resT) sel = mapEvent Publish $ runReaderT (runResolverState resT) sel-runResolver (ResolverS resT) sel = ResultT $ do- readResValue <- runResultT $ runReaderT (runResolverState resT) sel- pure $ case readResValue of+runResolver _ (ResolverQ resT) sel = cleanEvents $ runResolverStateT resT sel+runResolver _ (ResolverM resT) sel = mapEvent Publish $ runResolverStateT resT sel+runResolver toChannel (ResolverS resT) ctx = ResultT $ do+ readResValue <- runResolverStateM resT ctx+ pure $ case readResValue >>= subscriptionEvents ctx toChannel . toEventResolver ctx of Failure x -> Failure x- Success {warnings, result, events = channels} -> do- let eventRes = toEventResolver result sel+ Success {warnings, result} -> Success- { events = [Subscribe $ Event channels eventRes],+ { events = [result], warnings, result = gqlNull } +toEventResolver :: Monad m => ResolverContext -> SubEventRes event m ValidValue -> (event -> m GQLResponse)+toEventResolver sel (ReaderT subRes) event = renderResponse <$> runResolverStateM (subRes event) sel++subscriptionEvents ::+ ResolverContext ->+ Maybe (Selection VALID -> ResolverState (Channel e)) ->+ (e -> m GQLResponse) ->+ Eventless (ResponseEvent e m)+subscriptionEvents ctx@ResolverContext {currentSelection} (Just channelGenerator) res =+ runResolverState handle ctx+ where+ handle = do+ channel <- channelGenerator currentSelection+ pure $ Subscribe (Event [channel] res)+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))@@ -478,8 +398,8 @@ } deriving (Show) -instance Merge (ObjectResModel o e m) where- merge _ (ObjectResModel tyname x) (ObjectResModel _ y) =+instance Applicative f => SemigroupM f (ObjectResModel o e m) where+ mergeM _ (ObjectResModel tyname x) (ObjectResModel _ y) = pure $ ObjectResModel tyname (x <> y) data ResModel (o :: OperationType) e (m :: * -> *)@@ -491,86 +411,46 @@ | ResUnion TypeName (Resolver o e m (ResModel o e m)) deriving (Show) -instance Merge (ResModel o e m) where- merge p (ResObject x) (ResObject y) =- ResObject <$> merge p x y- merge _ _ _ =- failure $ internalResolvingError "can't merge: incompatible resolvers"+instance (Monad f, Failure InternalError f) => SemigroupM f (ResModel o e m) where+ mergeM p (ResObject x) (ResObject y) =+ ResObject <$> mergeM p x y+ mergeM _ _ _ = failure ("can't merge: incompatible resolvers" :: InternalError) data RootResModel e m = RootResModel- { query :: Eventless (ResModel QUERY e m),- mutation :: Eventless (ResModel MUTATION e m),- subscription :: Eventless (ResModel SUBSCRIPTION e m)+ { query :: ResolverState (ResModel QUERY e m),+ mutation :: ResolverState (ResModel MUTATION e m),+ subscription :: ResolverState (ResModel SUBSCRIPTION e m),+ channelMap :: Maybe (Selection VALID -> ResolverState (Channel e)) } runRootDataResolver :: (Monad m, LiftOperation o) =>- Eventless (ResModel o e m) ->- Context ->+ Maybe (Selection VALID -> ResolverState (Channel e)) ->+ ResolverState (ResModel o e m) ->+ ResolverContext -> ResponseStream e m (Value VALID) runRootDataResolver+ channels res- ctx@Context {operation = Operation {operationSelection}} =+ ctx@ResolverContext {operation = Operation {operationSelection}} = do- root <- statelessToResultT res- runResolver (resolveObject operationSelection root) ctx+ root <- runResolverStateT (toResolverStateT res) ctx+ runResolver channels (resolveObject operationSelection root) ctx -runRootResModel :: Monad m => RootResModel e m -> Context -> ResponseStream e m (Value VALID)+runRootResModel :: Monad m => RootResModel e m -> ResolverContext -> ResponseStream e m (Value VALID) runRootResModel RootResModel { query, mutation,- subscription+ subscription,+ channelMap }- ctx@Context {operation = Operation {operationType}} =+ ctx@ResolverContext {operation = Operation {operationType}} = selectByOperation operationType where selectByOperation Query =- runRootDataResolver query ctx+ runRootDataResolver channelMap query ctx selectByOperation Mutation =- runRootDataResolver mutation ctx+ runRootDataResolver channelMap mutation ctx selectByOperation Subscription =- runRootDataResolver subscription ctx---- map Resolving strategies-class- MapStrategy- (from :: OperationType)- (to :: OperationType)- where- mapStrategy ::- Monad m =>- Resolver from e m (ResModel from e m) ->- Resolver to e m (ResModel to e m)--instance MapStrategy o o where- mapStrategy = id--instance MapStrategy QUERY SUBSCRIPTION where- mapStrategy = ResolverS . pure . lift . fmap mapDeriving--mapDeriving ::- ( MapStrategy o o',- Monad m- ) =>- ResModel o e m ->- ResModel o' e m-mapDeriving ResNull = ResNull-mapDeriving (ResScalar x) = ResScalar x-mapDeriving (ResEnum typeName enum) = ResEnum typeName enum-mapDeriving (ResList x) = ResList $ map mapDeriving x-mapDeriving (ResObject x) = ResObject (mapObjectDeriving x)-mapDeriving (ResUnion name x) = ResUnion name (mapStrategy x)--mapObjectDeriving ::- ( MapStrategy o o',- Monad m- ) =>- ObjectResModel o e m ->- ObjectResModel o' e m-mapObjectDeriving (ObjectResModel tyname x) =- ObjectResModel tyname $- map (mapEntry mapStrategy) x--mapEntry :: (a -> b) -> (k, a) -> (k, b)-mapEntry f (name, value) = (name, f value)+ runRootDataResolver channelMap subscription ctx
+ src/Data/Morpheus/Types/Internal/Resolving/ResolverState.hs view
@@ -0,0 +1,202 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.Types.Internal.Resolving.ResolverState+ ( ResolverContext (..),+ ResolverStateT (..),+ resolverFailureMessage,+ clearStateResolverEvents,+ ResolverState,+ toResolverStateT,+ runResolverStateT,+ runResolverStateM,+ runResolverState,+ )+where++import Control.Applicative (Applicative (..))+import Control.Monad (Monad (..))+import Control.Monad.Reader (MonadReader (..), asks)+import Control.Monad.Trans.Class (MonadTrans (..))+import Control.Monad.Trans.Reader+ ( ReaderT (..),+ mapReaderT,+ )+import Data.Functor (Functor (..))+import Data.Functor.Identity (Identity (..))+import Data.Morpheus.Rendering.RenderGQL+ ( RenderGQL (..),+ )+import Data.Morpheus.Types.Internal.AST+ ( GQLError (..),+ GQLErrors,+ InternalError,+ Message,+ Operation,+ Schema,+ Selection (..),+ TypeName,+ VALID,+ msg,+ )+import Data.Morpheus.Types.Internal.Config (Config (..))+import Data.Morpheus.Types.Internal.Resolving.Core+ ( Eventless,+ Failure (..),+ PushEvents (..),+ Result,+ ResultT (..),+ cleanEvents,+ )+import Data.Semigroup+ ( Semigroup (..),+ )+import Prelude+ ( ($),+ (.),+ Bool (..),+ Int,+ Show (..),+ id,+ )++data ResolverContext = ResolverContext+ { currentSelection :: Selection VALID,+ schema :: Schema VALID,+ operation :: Operation VALID,+ currentTypeName :: TypeName,+ config :: Config+ }+ deriving (Show)++type ResolverState = ResolverStateT () Identity++runResolverStateT :: ResolverStateT e m a -> ResolverContext -> ResultT e m a+runResolverStateT = runReaderT . _runResolverStateT++runResolverStateM :: ResolverStateT e m a -> ResolverContext -> m (Result e a)+runResolverStateM res = runResultT . runResolverStateT res++runResolverState :: ResolverState a -> ResolverContext -> Eventless a+runResolverState res = runIdentity . runResolverStateM res++-- Resolver Internal State+newtype ResolverStateT event m a = ResolverStateT+ { _runResolverStateT :: ReaderT ResolverContext (ResultT event m) a+ }+ deriving+ ( Functor,+ Applicative,+ Monad,+ MonadReader ResolverContext+ )++instance MonadTrans (ResolverStateT e) where+ lift = ResolverStateT . lift . lift++instance (Monad m) => Failure Message (ResolverStateT e m) where+ failure message = do+ cxt <- asks id+ failure [resolverFailureMessage cxt message]++instance (Monad m) => Failure InternalError (ResolverStateT e m) where+ failure message = do+ ctx <- asks id+ failure [renderInternalResolverError ctx message]++instance (Monad m) => Failure GQLErrors (ResolverStateT e m) where+ failure = ResolverStateT . lift . failure++instance (Monad m) => PushEvents e (ResolverStateT e m) where+ pushEvents = ResolverStateT . lift . pushEvents++mapResolverState ::+ ( ResultT e m a ->+ ResultT e' m' a'+ ) ->+ ResolverStateT e m a ->+ ResolverStateT e' m' a'+mapResolverState f (ResolverStateT x) = ResolverStateT (mapReaderT f x)++toResolverStateT ::+ Applicative m =>+ ResolverState a ->+ ResolverStateT e m a+toResolverStateT = mapResolverState injectResult++injectResult ::+ (Applicative m) =>+ ResultT () Identity a ->+ ResultT e m a+injectResult (ResultT (Identity x)) =+ cleanEvents $ ResultT (pure x)++-- clear evets and starts new resolver with diferenct type of events but with same value+-- use properly. only if you know what you are doing+clearStateResolverEvents :: (Functor m) => ResolverStateT e m a -> ResolverStateT e' m a+clearStateResolverEvents = mapResolverState cleanEvents++resolverFailureMessage :: ResolverContext -> Message -> GQLError+resolverFailureMessage+ ctx@ResolverContext+ { currentSelection =+ Selection {selectionName, selectionPosition}+ }+ message =+ GQLError+ { message =+ "Failure on Resolving Field "+ <> msg selectionName+ <> ": "+ <> message+ <> withInternalContext ctx,+ locations = [selectionPosition]+ }++renderInternalResolverError :: ResolverContext -> InternalError -> GQLError+renderInternalResolverError ctx@ResolverContext {currentSelection} message =+ GQLError+ { message = msg message <> ". " <> renderContext ctx,+ locations = [selectionPosition currentSelection]+ }++withInternalContext :: ResolverContext -> Message+withInternalContext ResolverContext {config = Config {debug = False}} = ""+withInternalContext resCTX = renderContext resCTX++renderContext :: ResolverContext -> Message+renderContext+ ResolverContext+ { currentSelection,+ schema,+ operation,+ currentTypeName+ } =+ renderSection "Current Type" currentTypeName+ <> renderSection "Current Selection" currentSelection+ <> renderSection "OperationDefinition" operation+ <> renderSection "SchemaDefinition" schema++renderSection :: RenderGQL a => Message -> a -> Message+renderSection label content =+ "\n\n" <> label <> ":\n" <> line+ <> "\n\n"+ <> msg (render content)+ <> "\n\n"+ where+ line = stimes (50 :: Int) "-"
src/Data/Morpheus/Types/Internal/Validation.hs view
@@ -1,14 +1,13 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GADTs #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Types.Internal.Validation ( Validator,@@ -18,7 +17,8 @@ InputSource (..), OperationContext (..), runValidator,- askFieldType,+ DirectiveValidator,+ askType, askTypeMember, selectRequired, selectKnown,@@ -28,11 +28,10 @@ withScopeType, withPosition, asks,+ asksScope, selectWithDefaultValue,- askInputFieldType,- askInputMember, startInput,- withInputScope,+ inField, inputMessagePrefix, checkUnused, Prop (..),@@ -51,14 +50,25 @@ askFragments, MonadContext, CurrentSelection (..),+ getOperationType,+ selectType,+ FragmentValidator,+ askInterfaceTypes, ) where +-- MORPHEUS++import Control.Applicative (pure)+import Control.Monad ((>>=)) import Control.Monad.Trans.Reader ( ask, )--- MORPHEUS-+import Data.Either (Either)+import Data.Foldable (null)+import Data.Functor ((<$>), fmap)+import Data.List (filter)+import Data.Maybe (Maybe (..), fromMaybe, maybe) import Data.Morpheus.Internal.Utils ( Failure (..), KeyOf (..),@@ -73,41 +83,45 @@ FieldContent (..), FieldDefinition (..), FieldName,- FieldsDefinition,- GQLErrors, IN, Message,- OUT, Object, ObjectEntry (..),- RESOLVED,+ Position (..), Ref (..),- Schema, TRUE, TypeContent (..), TypeDefinition (..),- TypeName (..),- TypeRef (..),+ TypeName, UnionMember (..),+ ValidationError, Value (..), __inputname, entryValue, fromAny,- isFieldNullable,+ isNullable, msg,+ msgValidation, toFieldName, ) import Data.Morpheus.Types.Internal.Validation.Error- ( InternalError (..),- KindViolation (..),+ ( KindViolation (..), MissingRequired (..), Unknown (..), Unused (..), )+import Data.Morpheus.Types.Internal.Validation.Internal+ ( askInterfaceTypes,+ askType,+ askTypeMember,+ getOperationType,+ ) import Data.Morpheus.Types.Internal.Validation.Validator ( BaseValidator, Constraint (..), CurrentSelection (..),+ DirectiveValidator,+ FragmentValidator, GetWith (..), InputContext, InputSource (..),@@ -122,16 +136,18 @@ SetWith (..), Target (..), Validator (..),+ ValidatorContext (..), askFragments, askSchema, askVariables, asks,+ asksScope,+ inField, inputMessagePrefix, inputValueSource, runValidator, startInput, withDirective,- withInputScope, withPosition, withScope, withScopeType,@@ -139,62 +155,75 @@ import Data.Semigroup ( (<>), )+import Prelude+ ( ($),+ (.),+ not,+ otherwise,+ ) -getUnused :: (KeyOf b, KEY a ~ KEY b, Selectable ca a) => ca -> [b] -> [b]+getUnused :: (KeyOf k b, Selectable k a c) => c -> [b] -> [b] getUnused uses = filter (not . (`member` uses) . keyOf) -failOnUnused :: Unused ctx b => [b] -> Validator ctx ()+failOnUnused :: Unused ctx b => [b] -> Validator s ctx () failOnUnused x- | null x = return ()+ | null x = pure () | otherwise = do- ctx <- Validator ask- failure $ map (unused ctx) x+ ctx <- validatorCTX <$> Validator ask+ failure $ fmap (unused ctx) x -checkUnused :: (KeyOf b, KEY a ~ KEY b, Selectable ca a, Unused ctx b) => ca -> [b] -> Validator ctx ()+checkUnused ::+ ( KeyOf k b,+ Selectable k a ca,+ Unused ctx b+ ) =>+ ca ->+ [b] ->+ Validator s ctx () checkUnused uses = failOnUnused . getUnused uses constraint ::- forall (a :: Target) inp ctx. KindViolation a inp => Constraint (a :: Target) -> inp ->- TypeDefinition ANY ->- Validator ctx (Resolution a)-constraint OBJECT _ TypeDefinition {typeContent = DataObject {objectFields}, typeName} =- pure (typeName, objectFields)+ TypeDefinition ANY s ->+ Validator s ctx (Resolution s a)+constraint IMPLEMENTABLE _ TypeDefinition {typeContent = DataObject {objectFields, ..}, ..} =+ pure TypeDefinition {typeContent = DataObject {objectFields, ..}, ..}+constraint IMPLEMENTABLE _ TypeDefinition {typeContent = DataInterface fields, ..} =+ pure TypeDefinition {typeContent = DataInterface fields, ..} constraint INPUT ctx x = maybe (failure [kindViolation INPUT ctx]) pure (fromAny x) constraint target ctx _ = failure [kindViolation target ctx] selectRequired ::- ( Selectable c value,- MissingRequired c ctx,- KEY Ref ~ KEY value+ ( Selectable FieldName value c,+ MissingRequired c ctx ) => Ref -> c ->- Validator ctx value+ Validator s ctx value selectRequired selector container = do- ctx <- Validator ask+ ValidatorContext {scope, validatorCTX} <- Validator ask selectBy- [missingRequired ctx selector container]+ [missingRequired scope validatorCTX selector container] (keyOf selector) container selectWithDefaultValue ::- forall ctx values value.- ( Selectable values value,+ forall ctx values value s validValue.+ ( Selectable FieldName value values, MissingRequired values ctx,- KEY value ~ FieldName,- GetWith ctx Scope,- MonadContext Validator ctx+ MonadContext (Validator s) s ctx ) =>- (Value RESOLVED -> value) ->- FieldDefinition IN ->+ (Value s -> Validator s ctx validValue) ->+ (value -> Validator s ctx validValue) ->+ FieldDefinition IN s -> values ->- Validator ctx value+ Validator s ctx validValue selectWithDefaultValue f+ validateF field@FieldDefinition { fieldName, fieldContent@@ -202,175 +231,53 @@ values = selectOr (handeNull fieldContent)- pure+ validateF fieldName values where ------------------- handeNull :: Maybe (FieldContent TRUE IN) -> Validator ctx value- handeNull (Just (DefaultInputValue value)) = pure $ f value+ handeNull ::+ Maybe (FieldContent TRUE IN s) ->+ Validator s ctx validValue+ handeNull (Just (DefaultInputValue value)) = f value handeNull Nothing- | isFieldNullable field = pure $ f Null+ | isNullable field = f Null | otherwise = failSelection ----------------- failSelection = do- ctx <- Validator ask- position <- asks position- failure [missingRequired ctx (Ref fieldName position) values]+ ValidatorContext {scope, validatorCTX} <- Validator ask+ position <- asksScope position+ failure [missingRequired scope validatorCTX (Ref fieldName (fromMaybe (Position 0 0) position)) values] +selectType ::+ TypeName ->+ Validator s ctx (TypeDefinition ANY s)+selectType name =+ askSchema >>= selectBy err name+ where+ err = "Unknown Type " <> msgValidation name <> "." :: ValidationError+ selectKnown ::- ( Selectable c a,- Unknown c ctx,- KeyOf sel,- sel ~ UnknownSelector c,- KEY sel ~ KEY a+ ( Selectable k a c,+ Unknown c sel ctx,+ KeyOf k sel ) => sel -> c ->- Validator ctx a+ Validator s ctx a selectKnown selector lib = do- ctx <- Validator ask+ ValidatorContext {scope, validatorCTX} <- Validator ask selectBy- (unknown ctx lib selector)+ (unknown scope validatorCTX lib selector) (keyOf selector) lib -askFieldType ::- FieldDefinition OUT ->- SelectionValidator (TypeDefinition OUT)-askFieldType field@FieldDefinition {fieldType = TypeRef {typeConName}} =- do- schema <- askSchema- anyType <-- selectBy- [internalError field]- typeConName- schema- case fromAny anyType of- Just x -> pure x- Nothing ->- failure $- "Type \"" <> msg (typeName anyType)- <> "\" referenced by OBJECT \""- <> "\" must be an OUTPUT_TYPE."--askTypeMember ::- UnionMember OUT ->- SelectionValidator (TypeName, FieldsDefinition OUT)-askTypeMember UnionMember {memberName} =- askSchema- >>= selectOr notFound pure memberName- >>= constraintOBJECT- where- notFound = do- scopeType <- asks typename- failure $- "Type \""- <> msg memberName- <> "\" referenced by union \""- <> msg scopeType- <> "\" can't found in Schema."- --------------------------------------- constraintOBJECT :: TypeDefinition ANY -> SelectionValidator (TypeName, FieldsDefinition OUT)- constraintOBJECT TypeDefinition {typeName, typeContent} = con typeContent- where- con DataObject {objectFields} = pure (typeName, objectFields)- con _ = do- scopeType <- asks typename- failure $- "Type \"" <> msg typeName- <> "\" referenced by union \""- <> msg scopeType- <> "\" must be an OBJECT."--askInputFieldType ::- ( Failure GQLErrors (m c),- Failure Message (m c),- Monad (m c),- GetWith c Schema,- MonadContext m c- ) =>- FieldDefinition IN ->- m c (TypeDefinition IN)-askInputFieldType field@FieldDefinition {fieldName, fieldType = TypeRef {typeConName}} =- askSchema- >>= selectBy- [internalError field]- typeConName- >>= constraintINPUT- where- constraintINPUT ::- ( Failure Message m,- Monad m- ) =>- TypeDefinition ANY ->- m (TypeDefinition IN)- constraintINPUT x = case (fromAny x :: Maybe (TypeDefinition IN)) of- Just inputType -> pure inputType- Nothing ->- failure $- "Type \""- <> msg (typeName x)- <> "\" referenced by field \""- <> msg fieldName- <> "\" must be an input type."--askInputMember ::- ( GetWith c Schema,- GetWith c Scope,- Failure Message (m c),- Monad (m c),- MonadContext m c- ) =>- TypeName ->- m c (TypeDefinition IN)-askInputMember name =- askSchema- >>= selectOr notFound pure name- >>= constraintINPUT_OBJECT- where- typeInfo tName =- "Type \"" <> msg tName <> "\" referenced by inputUnion "- notFound = do- scopeType <- asks typename- failure $- typeInfo name- <> msg scopeType- <> "\" can't found in Schema."- --------------------------------------- constraintINPUT_OBJECT ::- ( Monad (m c),- GetWith c Scope,- Failure Message (m c),- MonadContext m c- ) =>- TypeDefinition ANY ->- m c (TypeDefinition IN)- constraintINPUT_OBJECT TypeDefinition {typeContent, ..} = con (fromAny typeContent)- where- con ::- ( Monad (m c),- GetWith c Scope,- Failure Message (m c),- MonadContext m c- ) =>- Maybe (TypeContent a IN) ->- m c (TypeDefinition IN)- con (Just content@DataInputObject {}) = pure TypeDefinition {typeContent = content, ..}- con _ = do- scopeType <- asks typename- failure $- typeInfo typeName- <> "\""- <> msg scopeType- <> "\" must be an INPUT_OBJECT."- constraintInputUnion ::- forall stage.- [UnionMember IN] ->+ forall stage schemaStage.+ [UnionMember IN schemaStage] -> Object stage ->- Either Message (TypeName, Maybe (Value stage))+ Either Message (UnionMember IN schemaStage, Maybe (Value stage)) constraintInputUnion tags hm = do (enum :: Value stage) <- entryValue@@ -381,24 +288,26 @@ ) __inputname hm- tyName <- isPosibeInputUnion tags enum+ unionMember <- isPosibeInputUnion tags enum case size hm of- 1 -> pure (tyName, Nothing)+ 1 -> pure (unionMember, Nothing) 2 -> do value <- entryValue <$> selectBy ( "value for Union \""- <> msg tyName+ <> msg unionMember <> "\" was not Provided." )- (toFieldName tyName)+ (toFieldName $ memberName unionMember) hm- pure (tyName, Just value)+ pure (unionMember, Just value) _ -> failure ("input union can have only one variant." :: Message) -isPosibeInputUnion :: [UnionMember IN] -> Value stage -> Either Message TypeName-isPosibeInputUnion tags (Enum name)- | name `elem` map memberName tags = pure name- | otherwise = failure $ msg name <> " is not posible union type"+isPosibeInputUnion :: [UnionMember IN s] -> Value stage -> Either Message (UnionMember IN s)+isPosibeInputUnion tags (Enum name) =+ selectBy+ (msg name <> " is not posible union type")+ name+ tags isPosibeInputUnion _ _ = failure $ "\"" <> msg __inputname <> "\" must be Enum"
src/Data/Morpheus/Types/Internal/Validation/Error.hs view
@@ -1,27 +1,28 @@ {-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Types.Internal.Validation.Error ( MissingRequired (..), KindViolation (..), Unknown (..),- InternalError (..), Target (..), Unused (..), ) where -- MORPHEUS-+import Data.Maybe (maybeToList) import Data.Morpheus.Error.Selection (unknownSelectionField)-import Data.Morpheus.Error.Utils (errorMessage) import Data.Morpheus.Types.Internal.AST ( Argument (..), Arguments,+ CONST, Directive (..), DirectiveDefinition (..), DirectiveDefinitions,@@ -29,25 +30,21 @@ FieldsDefinition, Fragment (..), Fragments,- GQLError (..),- GQLErrors, IN, OUT, Object, ObjectEntry (..),- RAW,- RESOLVED, Ref (..), Schema, TypeNameRef (..), TypeRef (..),+ ValidationError (..), Variable (..), VariableDefinitions, getOperationName, msg,- )-import Data.Morpheus.Types.Internal.Validation.SchemaValidator- ( TypeSystemContext,+ msgValidation,+ withPosition, ) import Data.Morpheus.Types.Internal.Validation.Validator ( CurrentSelection (..),@@ -59,206 +56,155 @@ renderInputPrefix, ) import Data.Semigroup ((<>))--class InternalError a where- internalError :: a -> GQLError--instance InternalError (FieldDefinition cat) where- internalError- FieldDefinition- { fieldName,- fieldType = TypeRef {typeConName}- } =- GQLError- { message =- "INTERNAL: Type " <> msg typeConName- <> " referenced by field "- <> msg fieldName- <> " can't found in Schema ",- locations = []- }+import Prelude (($)) class Unused ctx c where- unused :: ctx -> c -> GQLError+ unused :: ctx -> c -> ValidationError -- query M ( $v : String ) { a } -> "Variable \"$bla\" is never used in operation \"MyMutation\".",-instance Unused (OperationContext v) (Variable s) where+instance Unused (OperationContext s1 s2) (Variable s) where unused OperationContext {selection = CurrentSelection {operationName}} Variable {variableName, variablePosition} =- GQLError- { message =+ ValidationError+ { validationMessage = "Variable " <> msg ("$" <> variableName) <> " is never used in operation " <> msg (getOperationName operationName) <> ".",- locations = [variablePosition]+ validationLocations = [variablePosition] } -instance Unused (OperationContext v) Fragment where+instance Unused (OperationContext s1 s2) (Fragment s) where unused _ Fragment {fragmentName, fragmentPosition} =- GQLError- { message =+ ValidationError+ { validationMessage = "Fragment " <> msg fragmentName <> " is never used.",- locations = [fragmentPosition]+ validationLocations = [fragmentPosition] } class MissingRequired c ctx where- missingRequired :: ctx -> Ref -> c -> GQLError+ missingRequired :: Scope -> ctx -> Ref -> c -> ValidationError -instance MissingRequired (Arguments s) (OperationContext v) where+instance MissingRequired (Arguments s) ctx where missingRequired- OperationContext- { scope = Scope {position, kind},- selection = CurrentSelection {selectionName}- }+ Scope {position, kind, fieldname}+ _ Ref {refName} _ =- GQLError- { message =+ ValidationError+ { validationMessage = inScope kind <> " argument " <> msg refName <> " is required but not provided.",- locations = [position]+ validationLocations = maybeToList position } where- inScope DIRECTIVE = "Directive " <> msg ("@" <> selectionName)- inScope _ = "Field " <> msg selectionName--instance MissingRequired (Object s) (InputContext (OperationContext v)) where- missingRequired- input@InputContext- { sourceContext =- OperationContext- { scope = Scope {position}- }- }- Ref {refName}- _ =- GQLError- { message =- renderInputPrefix input- <> "Undefined Field "- <> msg refName- <> ".",- locations = [position]- }+ inScope DIRECTIVE = "Directive " <> msg ("@" <> fieldname)+ inScope _ = "Field " <> msg fieldname -instance MissingRequired (Object s) (InputContext (TypeSystemContext ctx)) where+instance MissingRequired (Object s) (InputContext ctx) where missingRequired- input+ Scope {position}+ ctx Ref {refName} _ =- GQLError- { message =- renderInputPrefix input- <> "Undefined Field "- <> msg refName- <> ".",- locations = []- }+ withPosition+ position+ ( renderInputPrefix+ ctx+ <> "Undefined Field "+ <> msgValidation refName+ <> "."+ ) -instance MissingRequired (VariableDefinitions s) (OperationContext v) where+instance MissingRequired (VariableDefinitions s) (OperationContext s1 s2) where missingRequired+ _ OperationContext { selection = CurrentSelection {operationName} } Ref {refName, refPosition} _ =- GQLError- { message =+ ValidationError+ { validationMessage = "Variable " <> msg refName <> " is not defined by operation " <> msg (getOperationName operationName) <> ".",- locations = [refPosition]+ validationLocations = [refPosition] } -class Unknown c ctx where- type UnknownSelector c- unknown :: ctx -> c -> UnknownSelector c -> GQLErrors+class Unknown c ref ctx where+ -- type UnknownSelector c+ unknown :: Scope -> ctx -> c -> ref -> ValidationError -- {...H} -> "Unknown fragment \"H\"."-instance Unknown Fragments ctx where- type UnknownSelector Fragments = Ref- unknown _ _ (Ref name pos) =- errorMessage- pos- ("Unknown Fragment " <> msg name <> ".")--instance Unknown Schema ctx where- type UnknownSelector Schema = TypeNameRef- unknown _ _ TypeNameRef {typeNameRef, typeNamePosition} =- errorMessage typeNamePosition ("Unknown type " <> msg typeNameRef <> ".")+instance Unknown (Fragments s) Ref ctx where+ unknown _ _ _ (Ref name pos) =+ ValidationError+ { validationMessage = "Unknown Fragment " <> msg name <> ".",+ validationLocations = [pos]+ } -instance Unknown (FieldDefinition OUT) ctx where- type UnknownSelector (FieldDefinition OUT) = Argument RESOLVED- unknown _ FieldDefinition {fieldName} Argument {argumentName, argumentPosition} =- errorMessage- argumentPosition- ("Unknown Argument " <> msg argumentName <> " on Field " <> msg fieldName <> ".")+instance Unknown (Schema s) TypeNameRef ctx where+ unknown _ _ _ TypeNameRef {typeNameRef, typeNamePosition} =+ ValidationError+ { validationMessage = "Unknown type " <> msg typeNameRef <> ".",+ validationLocations = [typeNamePosition]+ } -instance Unknown (FieldsDefinition IN) (InputContext (OperationContext v)) where- type UnknownSelector (FieldsDefinition IN) = ObjectEntry RESOLVED- unknown- input@InputContext {sourceContext = OperationContext {scope = Scope {position}}}- _- ObjectEntry {entryName} =- [ GQLError- { message = renderInputPrefix input <> "Unknown Field " <> msg entryName <> ".",- locations = [position]- }- ]+instance Unknown (FieldDefinition OUT s) (Argument CONST) ctx where+ unknown _ _ FieldDefinition {fieldName} Argument {argumentName, argumentPosition} =+ ValidationError+ { validationMessage = "Unknown Argument " <> msg argumentName <> " on Field " <> msg fieldName <> ".",+ validationLocations = [argumentPosition]+ } -instance Unknown (FieldsDefinition IN) (InputContext (TypeSystemContext ctx)) where- type UnknownSelector (FieldsDefinition IN) = ObjectEntry RESOLVED+instance Unknown (FieldsDefinition IN s) (ObjectEntry valueS) (InputContext ctx) where unknown- input+ Scope {position}+ ctx _ ObjectEntry {entryName} =- [ GQLError- { message = renderInputPrefix input <> "Unknown Field " <> msg entryName <> ".",- locations = []- }- ]+ withPosition position $+ renderInputPrefix ctx <> "Unknown Field " <> msgValidation entryName <> "." -instance Unknown DirectiveDefinition ctx where- type UnknownSelector DirectiveDefinition = Argument RESOLVED- unknown _ DirectiveDefinition {directiveDefinitionName} Argument {argumentName, argumentPosition} =- errorMessage- argumentPosition- ("Unknown Argument " <> msg argumentName <> " on Directive " <> msg directiveDefinitionName <> ".")+instance Unknown (DirectiveDefinition s) (Argument s') ctx where+ unknown _ _ DirectiveDefinition {directiveDefinitionName} Argument {argumentName, argumentPosition} =+ ValidationError+ { validationMessage = "Unknown Argument " <> msg argumentName <> " on Directive " <> msg directiveDefinitionName <> ".",+ validationLocations = [argumentPosition]+ } -instance Unknown DirectiveDefinitions ctx where- type UnknownSelector DirectiveDefinitions = Directive RAW- unknown _ _ Directive {directiveName, directivePosition} =- errorMessage- directivePosition- ("Unknown Directive " <> msg directiveName <> ".")+instance Unknown (DirectiveDefinitions s) (Directive s') ctx where+ unknown _ _ _ Directive {directiveName, directivePosition} =+ ValidationError+ { validationMessage = "Unknown Directive " <> msg directiveName <> ".",+ validationLocations = [directivePosition]+ } -instance Unknown (FieldsDefinition OUT) (OperationContext v) where- type UnknownSelector (FieldsDefinition OUT) = Ref- unknown OperationContext {scope = Scope {typename}} _ =- unknownSelectionField typename+instance Unknown (FieldsDefinition OUT s) Ref (OperationContext s1 s2) where+ unknown Scope {currentTypeName} _ _ = unknownSelectionField currentTypeName class KindViolation (t :: Target) ctx where- kindViolation :: c t -> ctx -> GQLError+ kindViolation :: c t -> ctx -> ValidationError -instance KindViolation 'TARGET_OBJECT Fragment where+instance KindViolation 'TARGET_IMPLEMENTABLE (Fragment s) where kindViolation _ Fragment {fragmentName, fragmentType, fragmentPosition} =- GQLError- { message =+ ValidationError+ { validationMessage = "Fragment " <> msg fragmentName <> " cannot condition on non composite type " <> msg fragmentType <> ".",- locations = [fragmentPosition]+ validationLocations = [fragmentPosition] } instance KindViolation 'TARGET_INPUT (Variable s) where@@ -269,12 +215,12 @@ variablePosition, variableType = TypeRef {typeConName} } =- GQLError- { message =+ ValidationError+ { validationMessage = "Variable " <> msg ("$" <> variableName) <> " cannot be non-input type " <> msg typeConName <> ".",- locations = [variablePosition]+ validationLocations = [variablePosition] }
+ src/Data/Morpheus/Types/Internal/Validation/Internal.hs view
@@ -0,0 +1,184 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.Types.Internal.Validation.Internal+ ( askType,+ askTypeMember,+ getOperationType,+ askInterfaceTypes,+ )+where++-- MORPHEUS++import Control.Applicative (Applicative, pure)+import Control.Monad ((>=>), Monad ((>>=)))+import Data.Functor ((<$>))+import Data.Maybe (Maybe (..), maybe)+import Data.Morpheus.Internal.Utils+ ( Failure (..),+ selectBy,+ )+import Data.Morpheus.Types.Internal.AST+ ( ANY,+ FieldsDefinition,+ FromCategory,+ IMPLEMENTABLE,+ IN,+ InternalError,+ OBJECT,+ OUT,+ Operation,+ Operation (..),+ Stage,+ TRUE,+ Token,+ TypeCategory,+ TypeContent (..),+ TypeContent,+ TypeDefinition (..),+ TypeName (..),+ TypeRef,+ Typed,+ UnionMember (..),+ VALID,+ fromAny,+ fromCategory,+ getOperationDataType,+ msgInternal,+ possibleInterfaceTypes,+ typeConName,+ typed,+ untyped,+ )+import Data.Morpheus.Types.Internal.Validation.Validator+ ( MonadContext,+ SelectionValidator,+ askSchema,+ )+import Data.Semigroup+ ( (<>),+ )+import Data.Traversable (traverse)+import Prelude+ ( ($),+ (.),+ )++askType ::+ Constraints m c cat s =>+ Typed cat s TypeRef ->+ m c (TypeDefinition cat s)+askType = untyped (__askType . typeConName)++askType2 ::+ Constraints m c cat s =>+ Typed cat s TypeName ->+ m c (TypeDefinition cat s)+askType2 = untyped __askType++__askType ::+ Constraints m c cat s => TypeName -> m c (TypeDefinition cat s)+__askType name =+ askSchema+ >>= selectBy (unknownType name) name+ >>= kindConstraint++askTypeMember ::+ Constraints m c cat s =>+ UnionMember cat s ->+ m c (TypeMemberResponse cat s)+askTypeMember = askType2 . typed memberName >=> constraintObject++askInterfaceTypes ::+ ( Failure InternalError (m c),+ Monad (m c),+ MonadContext m s c,+ FromCategory (TypeContent TRUE) ANY IMPLEMENTABLE+ ) =>+ TypeDefinition IMPLEMENTABLE s ->+ m c [TypeDefinition IMPLEMENTABLE s]+askInterfaceTypes typeDef@TypeDefinition {typeName} =+ (typeDef :)+ <$> ( askSchema+ >>= traverse (validate . fromCategory) . possibleInterfaceTypes typeName+ )+ where+ validate (Just x) = pure x+ validate Nothing = failure ("TODO: invalid interface Types" :: InternalError)++type family TypeMemberResponse (cat :: TypeCategory) (s :: Stage)++type instance TypeMemberResponse OUT s = TypeDefinition OBJECT s++type instance TypeMemberResponse IN s = (TypeDefinition IN s, FieldsDefinition IN s)++type Constraints m c cat s =+ ( Failure InternalError (m c),+ Monad (m c),+ MonadContext m s c,+ KindErrors cat,+ FromCategory (TypeContent TRUE) ANY cat+ )++getOperationType :: Operation a -> SelectionValidator (TypeDefinition OBJECT VALID)+getOperationType operation = askSchema >>= getOperationDataType operation++unknownType :: TypeName -> InternalError+unknownType name = "Type \"" <> msgInternal name <> "\" can't found in Schema."++type KindConstraint f c =+ ( Failure InternalError f,+ FromCategory TypeDefinition ANY c+ )++_kindConstraint ::+ KindConstraint f k =>+ Token ->+ TypeDefinition ANY s ->+ f (TypeDefinition k s)+_kindConstraint err anyType =+ maybe+ (failure $ violation err (typeName anyType))+ pure+ (fromAny anyType)++class KindErrors c where+ kindConstraint :: KindConstraint f c => TypeDefinition ANY s -> f (TypeDefinition c s)+ constraintObject ::+ ( Applicative f,+ Failure InternalError f+ ) =>+ TypeDefinition c s ->+ f (TypeMemberResponse c s)++instance KindErrors IN where+ kindConstraint = _kindConstraint "input type"+ constraintObject typeDef@TypeDefinition {typeContent = DataInputObject inputFields} = pure (typeDef, inputFields)+ constraintObject TypeDefinition {typeName} = failure (violation "input object" typeName)++instance KindErrors OUT where+ kindConstraint = _kindConstraint "output type"+ constraintObject TypeDefinition {typeContent = DataObject {..}, ..} = pure TypeDefinition {typeContent = DataObject {..}, ..}+ constraintObject TypeDefinition {typeName} = failure (violation "object" typeName)++violation ::+ Token ->+ TypeName ->+ InternalError+violation kind typeName =+ "Type \"" <> msgInternal typeName+ <> "\" must be an"+ <> msgInternal kind+ <> "."
src/Data/Morpheus/Types/Internal/Validation/SchemaValidator.hs view
@@ -10,10 +10,10 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Types.Internal.Validation.SchemaValidator ( SchemaValidator,- selectType, TypeSystemContext (..), constraintInterface, inField,@@ -26,35 +26,28 @@ ) where -import Control.Monad.Reader (asks) --import Data.Morpheus.Error.Document.Interface (unknownInterface)-import Data.Morpheus.Error.Utils (globalErrorMessage)+ -- MORPHEUS +import Control.Applicative (pure) import Data.Morpheus.Internal.Utils ( Failure (..),- fromElems,- selectBy, ) import Data.Morpheus.Types.Internal.AST ( ANY,+ CONST, FieldName, FieldsDefinition, OUT,- Position (..),- Schema, TypeContent (..), TypeDefinition (..), TypeName,- msg,+ ValidationError,+ msgValidation, )-import Data.Morpheus.Types.Internal.Resolving (Result (..)) import Data.Morpheus.Types.Internal.Validation.Validator- ( GetWith (..),- Scope (..),- ScopeKind (..),- SetWith (..),- Validator (..),+ ( Validator (..), renderField, withContext, )@@ -62,36 +55,16 @@ ( (<>), Semigroup (..), )+import Prelude+ ( (.),+ Show (..),+ const,+ ) -data TypeSystemContext c = TypeSystemContext- { types :: [TypeDefinition ANY],- local :: c- }+newtype TypeSystemContext c = TypeSystemContext+ {local :: c} deriving (Show) -instance GetWith (TypeSystemContext ctx) Schema where- getWith ctx = case fromElems (types ctx) of- Success {result} -> result- Failure {errors} -> error (show errors)--instance GetWith (TypeSystemContext a) Scope where- getWith _ =- Scope- { position = Position {line = 0, column = 0},- typename = "TODO:",- kind = TYPE- }--instance SetWith (TypeSystemContext a) Scope where- setWith _ = id --TODO:--selectType :: TypeName -> SchemaValidator ctx (TypeDefinition ANY)-selectType name =- asks types- >>= selectBy err name- where- err = globalErrorMessage $ "Unknown Type " <> msg name <> "."- inType :: TypeName -> SchemaValidator TypeName v ->@@ -126,19 +99,21 @@ fieldArgument :: FieldName } -withLocalContext :: (a -> b) -> Validator (TypeSystemContext b) v -> Validator (TypeSystemContext a) v+withLocalContext :: (a -> b) -> SchemaValidator b v -> SchemaValidator a v withLocalContext = withContext . updateLocal updateLocal :: (a -> b) -> TypeSystemContext a -> TypeSystemContext b updateLocal f ctx = ctx {local = f (local ctx)} -type SchemaValidator c = Validator (TypeSystemContext c)+type SchemaValidator c = Validator CONST (TypeSystemContext c) -constraintInterface :: TypeDefinition ANY -> SchemaValidator ctx (TypeName, FieldsDefinition OUT)+constraintInterface ::+ TypeDefinition ANY CONST ->+ SchemaValidator ctx (TypeName, FieldsDefinition OUT CONST) constraintInterface TypeDefinition { typeName, typeContent = DataInterface fields } = pure (typeName, fields) constraintInterface TypeDefinition {typeName} =- failure $ globalErrorMessage $ "type " <> msg typeName <> " must be an interface"+ failure ["type " <> msgValidation typeName <> " must be an interface" :: ValidationError]
src/Data/Morpheus/Types/Internal/Validation/Validator.hs view
@@ -1,16 +1,19 @@ {-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Types.Internal.Validation.Validator ( Validator (..),@@ -22,7 +25,7 @@ withScope, withScopeType, withPosition,- withInputScope,+ inField, inputMessagePrefix, InputSource (..), InputContext (..),@@ -43,53 +46,81 @@ withContext, renderField, asks,+ asksScope, askSchema, askVariables, askFragments,+ DirectiveValidator,+ ValidatorContext (..),+ FragmentValidator, ) where -import Control.Monad.Reader (MonadReader)+-- MORPHEUS++import Control.Applicative (Applicative)+import Control.Monad (Monad)+import Control.Monad.Reader (MonadReader (..)) import Control.Monad.Trans.Class (MonadTrans (..)) import Control.Monad.Trans.Reader ( ReaderT (..),- ask, withReaderT, )--- MORPHEUS+import Data.Functor ((<$>), Functor (..))+import Data.Maybe (Maybe (..))+import Data.Morpheus.Error.Utils+ ( validationErrorMessage,+ ) import Data.Morpheus.Internal.Utils ( Failure (..), )+import Data.Morpheus.Rendering.RenderGQL (RenderGQL (..)) import Data.Morpheus.Types.Internal.AST- ( Argument (..),- Directive (..),+ ( Directive (..),+ FieldDefinition (..), FieldName (..),- FieldsDefinition, Fragments, GQLError (..), GQLErrors,+ IMPLEMENTABLE, IN,+ InternalError, Message,- OUT, Position, RAW,- RESOLVED, Ref (..), Schema,- TypeDefinition,+ Stage,+ TypeDefinition (..),+ TypeKind (..), TypeName (..),+ TypeRef (..),+ TypeWrapper, VALID,+ ValidationError (..), Variable (..), VariableDefinitions, intercalateName,+ kindOf, msg,+ msgValidation, )+import Data.Morpheus.Types.Internal.Config (Config (..)) import Data.Morpheus.Types.Internal.Resolving ( Eventless, ) import Data.Semigroup ( (<>),+ stimes, )+import Prelude+ ( ($),+ (.),+ Bool,+ Int,+ Show,+ otherwise,+ ) data Prop = Prop { propName :: FieldName,@@ -99,25 +130,25 @@ type Path = [Prop] -renderPath :: Path -> Message+renderPath :: Path -> ValidationError renderPath [] = ""-renderPath path = "in field " <> msg (intercalateName "." $ map propName path) <> ": "+renderPath path = "in field " <> msgValidation (intercalateName "." $ fmap propName path) <> ": " -renderInputPrefix :: InputContext c -> Message+renderInputPrefix :: InputContext c -> ValidationError renderInputPrefix InputContext {inputPath, inputSource} = renderSource inputSource <> renderPath inputPath -renderSource :: InputSource -> Message-renderSource (SourceArgument Argument {argumentName}) =- "Argument " <> msg argumentName <> " got invalid value. "+renderSource :: InputSource -> ValidationError+renderSource (SourceArgument argumentName) =+ "Argument " <> msgValidation argumentName <> " got invalid value. " renderSource (SourceVariable Variable {variableName} _) =- "Variable " <> msg ("$" <> variableName) <> " got invalid value. "+ "Variable " <> msgValidation ("$" <> variableName) <> " got invalid value. " renderSource SourceInputField {sourceTypeName, sourceFieldName, sourceArgumentName} = "Field " <> renderField sourceTypeName sourceFieldName sourceArgumentName <> " got invalid default value. " -renderField :: TypeName -> FieldName -> Maybe FieldName -> Message+renderField :: TypeName -> FieldName -> Maybe FieldName -> ValidationError renderField (TypeName tname) (FieldName fname) arg =- msg (tname <> "." <> fname <> renderArg arg)+ msgValidation (tname <> "." <> fname <> renderArg arg) where renderArg (Just (FieldName argName)) = "(" <> argName <> ":)" renderArg Nothing = ""@@ -128,24 +159,27 @@ | TYPE deriving (Show) -data OperationContext vars = OperationContext- { schema :: Schema,- scope :: Scope,- fragments :: Fragments,- selection :: CurrentSelection,- variables :: vars+data+ OperationContext+ (s1 :: Stage)+ (s2 :: Stage) = OperationContext+ { fragments :: Fragments s2,+ variables :: VariableDefinitions s1,+ selection :: CurrentSelection } deriving (Show) -data CurrentSelection = CurrentSelection- { operationName :: Maybe FieldName,- selectionName :: FieldName+newtype CurrentSelection = CurrentSelection+ { operationName :: Maybe FieldName } deriving (Show) data Scope = Scope- { position :: Position,- typename :: TypeName,+ { position :: Maybe Position,+ currentTypeName :: TypeName,+ currentTypeKind :: TypeKind,+ currentTypeWrappers :: [TypeWrapper],+ fieldname :: FieldName, kind :: ScopeKind } deriving (Show)@@ -158,7 +192,7 @@ deriving (Show) data InputSource- = SourceArgument (Argument RESOLVED)+ = SourceArgument FieldName | SourceVariable { sourceVariable :: Variable RAW, isDefaultValue :: Bool@@ -171,95 +205,102 @@ deriving (Show) data Target- = TARGET_OBJECT+ = TARGET_IMPLEMENTABLE | TARGET_INPUT data Constraint (a :: Target) where- OBJECT :: Constraint 'TARGET_OBJECT+ IMPLEMENTABLE :: Constraint 'TARGET_IMPLEMENTABLE INPUT :: Constraint 'TARGET_INPUT -- UNION :: Constraint 'TARGET_UNION -type family Resolution (a :: Target)+type family Resolution (s :: Stage) (a :: Target) -type instance Resolution 'TARGET_OBJECT = (TypeName, FieldsDefinition OUT)+type instance Resolution s 'TARGET_IMPLEMENTABLE = TypeDefinition IMPLEMENTABLE s -type instance Resolution 'TARGET_INPUT = TypeDefinition IN+type instance Resolution s 'TARGET_INPUT = TypeDefinition IN s -withInputScope :: Prop -> InputValidator c a -> InputValidator c a-withInputScope prop = withContext update- where- update- InputContext- { inputPath = old,- ..- } =+inField :: FieldDefinition IN s -> InputValidator s c a -> InputValidator s c a+inField+ FieldDefinition+ { fieldName,+ fieldType = TypeRef {typeConName}+ } = withContext update+ where+ update InputContext- { inputPath = old <> [prop],+ { inputPath = old, ..- }+ } =+ InputContext+ { inputPath = old <> [Prop fieldName typeConName],+ ..+ } inputValueSource ::- forall m c.+ forall m c s. ( GetWith c InputSource,- MonadContext m c+ MonadContext m s c ) => m c InputSource inputValueSource = get asks ::- ( MonadContext m c,+ ( MonadContext m s c, GetWith c t ) => (t -> a) -> m c a asks f = f <$> get -setSelectionName ::- ( MonadContext m c,- SetWith c CurrentSelection+asksScope ::+ ( MonadContext m s c ) =>+ (Scope -> a) ->+ m c a+asksScope f = f <$> getGlobalContext scope++setSelectionName ::+ (MonadContext m s c) => FieldName -> m c a -> m c a-setSelectionName selectionName = set update+setSelectionName fieldname = setScope update where- update ctx = ctx {selectionName}+ update ctx = ctx {fieldname} askSchema ::- ( MonadContext m c,- GetWith c Schema+ ( MonadContext m s c ) =>- m c Schema-askSchema = get+ m c (Schema s)+askSchema = getGlobalContext schema askVariables ::- ( MonadContext m c,+ ( MonadContext m s c, GetWith c (VariableDefinitions VALID) ) => m c (VariableDefinitions VALID) askVariables = get askFragments ::- ( MonadContext m c,- GetWith c Fragments+ ( MonadContext m s c,+ GetWith c (Fragments s') ) =>- m c Fragments+ m c (Fragments s') askFragments = get -runValidator :: Validator ctx a -> ctx -> Eventless a-runValidator (Validator x) = runReaderT x+runValidator :: Validator s ctx a -> Config -> Schema s -> Scope -> ctx -> Eventless a+runValidator (Validator x) config schema scope validatorCTX =+ runReaderT x ValidatorContext {..} withContext :: (c' -> c) ->- Validator c a ->- Validator c' a-withContext f = Validator . withReaderT f . _runValidator+ Validator s c a ->+ Validator s c' a+withContext f = Validator . withReaderT (fmap f) . _runValidator withDirective ::- ( SetWith c CurrentSelection,- SetWith c Scope,- MonadContext m c+ ( MonadContext m schemaS c ) => Directive s -> m c a ->@@ -268,58 +309,68 @@ Directive { directiveName, directivePosition- } = setSelectionName directiveName . set update+ } = setSelectionName directiveName . setScope update where update Scope {..} = Scope- { position = directivePosition,+ { position = Just directivePosition, kind = DIRECTIVE, .. } withScope ::- ( SetWith c CurrentSelection,- MonadContext m c,- SetWith c Scope+ ( MonadContext m s c ) =>- TypeName ->+ TypeDefinition cat s -> Ref -> m c a -> m c a-withScope typeName (Ref selName pos) =- setSelectionName selName . set update+withScope t@TypeDefinition {typeName} (Ref selName pos) =+ setSelectionName selName . setScope update where- update Scope {..} = Scope {typename = typeName, position = pos, ..}+ update Scope {..} =+ Scope+ { currentTypeName = typeName,+ currentTypeKind = kindOf t,+ position = Just pos,+ ..+ } -withPosition ::- ( MonadContext m c,- SetWith c Scope+withScopeType ::+ ( MonadContext m s c ) =>- Position ->+ (TypeDefinition cat s, [TypeWrapper]) -> m c a -> m c a-withPosition pos = set update+withScopeType (t@TypeDefinition {typeName}, wrappers) = setScope update where- update Scope {..} = Scope {position = pos, ..}+ update Scope {..} =+ Scope+ { currentTypeName = typeName,+ currentTypeKind = kindOf t,+ currentTypeWrappers = wrappers,+ ..+ } -withScopeType ::- ( MonadContext m c,- SetWith c Scope+withPosition ::+ ( MonadContext m s c ) =>- TypeName ->+ Position -> m c a -> m c a-withScopeType name = set update+withPosition pos = setScope update where- update Scope {..} = Scope {typename = name, ..}+ update Scope {..} = Scope {position = Just pos, ..} -inputMessagePrefix :: InputValidator ctx Message-inputMessagePrefix = renderInputPrefix <$> Validator ask+inputMessagePrefix :: InputValidator s ctx ValidationError+inputMessagePrefix =+ renderInputPrefix+ . validatorCTX <$> Validator ask startInput :: InputSource ->- InputValidator ctx a ->- Validator ctx a+ InputValidator s ctx a ->+ Validator s ctx a startInput inputSource = withContext update where update sourceContext =@@ -329,114 +380,166 @@ sourceContext } -newtype Validator ctx a = Validator+data ValidatorContext (s :: Stage) (ctx :: *) = ValidatorContext+ { scope :: Scope,+ schema :: Schema s,+ validatorCTX :: ctx,+ config :: Config+ }+ deriving (Show, Functor)++newtype Validator s ctx a = Validator { _runValidator :: ReaderT- ctx+ (ValidatorContext s ctx) Eventless a } deriving newtype ( Functor, Applicative,- Monad,- MonadReader ctx+ Monad ) -type BaseValidator = Validator (OperationContext ())+instance MonadReader ctx (Validator s ctx) where+ ask = validatorCTX <$> Validator ask+ local = withContext -type SelectionValidator = Validator (OperationContext (VariableDefinitions VALID))+type BaseValidator = Validator VALID (OperationContext RAW RAW) -type InputValidator ctx = Validator (InputContext ctx)+type FragmentValidator (s :: Stage) = Validator VALID (OperationContext VALID s) --- Helpers-get :: (MonadContext m ctx, GetWith ctx a) => m ctx a-get = getContext getWith+type SelectionValidator = Validator VALID (OperationContext VALID VALID) -set ::- ( MonadContext m c,- SetWith c a- ) =>- (a -> a) ->+type InputValidator s ctx = Validator s (InputContext ctx)++type DirectiveValidator ctx = Validator ctx++setScope ::+ (MonadContext m s c) =>+ (Scope -> Scope) -> m c b -> m c b-set f = setContext (setWith f)+setScope f = setGlobalContext (mapScope f) +mapScope :: (Scope -> Scope) -> ValidatorContext s ctx -> ValidatorContext s ctx+mapScope f ValidatorContext {scope, ..} = ValidatorContext {scope = f scope, ..}++-- Helpers+get :: (MonadContext m s ctx, GetWith ctx a) => m ctx a+get = getContext getWith+ class Monad (m c) =>- MonadContext m c+ MonadContext m s c+ | m -> s where+ getGlobalContext :: (ValidatorContext s c -> a) -> m c a+ setGlobalContext :: (ValidatorContext s c -> ValidatorContext s c) -> m c b -> m c b getContext :: (c -> a) -> m c a setContext :: (c -> c) -> m c b -> m c b -instance MonadContext Validator c where- getContext f = f <$> Validator ask+instance MonadContext (Validator s) s c where+ getGlobalContext f = f <$> Validator ask+ getContext f = f . validatorCTX <$> Validator ask+ setGlobalContext f = Validator . withReaderT f . _runValidator setContext = withContext class GetWith (c :: *) (v :: *) where getWith :: c -> v -instance GetWith (OperationContext v) Scope where- getWith = scope--instance GetWith c Scope => GetWith (InputContext c) Scope where- getWith = getWith . sourceContext--instance GetWith (OperationContext c) Schema where- getWith = schema--instance GetWith c Schema => GetWith (InputContext c) Schema where- getWith = getWith . sourceContext--instance GetWith (OperationContext (VariableDefinitions VALID)) (VariableDefinitions VALID) where+instance GetWith (OperationContext VALID fragStage) (VariableDefinitions VALID) where getWith = variables instance GetWith (InputContext ctx) InputSource where getWith = inputSource -instance GetWith (OperationContext v) Fragments where+instance GetWith (OperationContext varStage fragStage) (Fragments fragStage) where getWith = fragments -- Setters class SetWith (c :: *) (v :: *) where setWith :: (v -> v) -> c -> c -instance SetWith (OperationContext v) CurrentSelection where+instance SetWith (OperationContext s1 s2) CurrentSelection where setWith f OperationContext {selection = selection, ..} = OperationContext { selection = f selection, .. } -instance SetWith (OperationContext v) Scope where- setWith f OperationContext {..} =- OperationContext- { scope = f scope,- ..- }+instance Failure [ValidationError] (Validator s ctx) where+ failure errors = do+ ctx <- Validator ask+ failValidator (fmap (fromValidatinError ctx) errors) -instance SetWith c Scope => SetWith (InputContext c) Scope where- setWith f InputContext {..} =- InputContext- { sourceContext = setWith f sourceContext,- ..+instance Failure ValidationError (Validator s ctx) where+ failure err = failure [err]++failValidator :: GQLErrors -> Validator s ctx a+failValidator = Validator . lift . failure++fromValidatinError :: ValidatorContext s ctx -> ValidationError -> GQLError+fromValidatinError+ context@ValidatorContext+ { config+ }+ (ValidationError text locations) =+ GQLError+ { message,+ locations }+ where+ message+ | debug config = text <> renderContext context+ | otherwise = text -- can be only used for internal errors instance- ( MonadContext Validator ctx,- GetWith ctx Scope- ) =>- Failure Message (Validator ctx)+ (MonadContext (Validator s) s ctx) =>+ Failure InternalError (Validator s ctx) where failure inputMessage = do- position <- asks position+ ctx <- Validator ask failure- [ GQLError- { message = "INTERNAL: " <> inputMessage,- locations = [position]- }- ]+ ( validationErrorMessage+ (position $ scope ctx)+ $ msg+ inputMessage+ <> renderContext ctx+ ) -instance Failure GQLErrors (Validator ctx) where- failure = Validator . lift . failure+renderContext :: ValidatorContext s ctx -> Message+renderContext+ ValidatorContext+ { schema,+ scope+ } =+ renderScope scope+ <> renderSection "SchemaDefinition" schema++renderScope :: Scope -> Message+renderScope+ Scope+ { currentTypeName,+ currentTypeKind,+ fieldname+ } =+ renderSection+ "Scope"+ ( "referenced by type "+ <> render currentTypeName+ <> " of kind "+ <> render currentTypeKind+ <> " in field "+ <> render fieldname+ )++renderSection :: RenderGQL a => Message -> a -> Message+renderSection label content =+ "\n\n" <> label <> ":\n" <> line+ <> "\n\n"+ <> msg (render content)+ <> "\n\n"+ where+ line = stimes (50 :: Int) "-"
src/Data/Morpheus/Types/SelectionTree.hs view
@@ -1,18 +1,30 @@ {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE NoImplicitPrelude #-} -- | -- Module : Data.Morpheus.Types.SelectionTree -- Description : A simple interface for Morpheus internal Selection Set's representation. module Data.Morpheus.Types.SelectionTree where +import Data.Bool (Bool (..))+import Data.Foldable (concatMap)+import Data.Monoid (mempty) import Data.Morpheus.Internal.Utils (elems, keyOf)-import Data.Morpheus.Types.Internal.AST.Base (FieldName, VALID)-import Data.Morpheus.Types.Internal.AST.Selection- ( Selection (..),+import Data.Morpheus.Types.Internal.AST+ ( FieldName (..),+ Selection (..), Selection (selectionContent),- SelectionContent (SelectionField, SelectionSet),+ SelectionContent (SelectionField, SelectionSet, UnionSelection),+ UnionTag (..),+ VALID, )+import Data.String+ ( IsString (..),+ )+import Data.Text (unpack)+import Prelude ((.)) -- | The 'SelectionTree' instance is a simple interface for interacting -- with morpheus's internal AST while keeping the ability to safely change the concrete@@ -26,7 +38,7 @@ getChildrenList :: nodeType -> [nodeType] -- | get a node's name- getName :: nodeType -> FieldName+ getName :: IsString name => nodeType -> name instance SelectionTree (Selection VALID) where isLeaf node = case selectionContent node of@@ -36,5 +48,13 @@ getChildrenList node = case selectionContent node of SelectionField -> mempty (SelectionSet deeperSel) -> elems deeperSel+ (UnionSelection sel) ->+ concatMap+ (elems . unionTagSelection)+ (elems sel) - getName = keyOf+ getName =+ fromString+ . unpack+ . readName+ . keyOf
src/Data/Morpheus/Validation/Document/Validation.hs view
@@ -1,62 +1,84 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-}+{-# LANGUAGE KindSignatures #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Validation.Document.Validation- ( validatePartialDocument,- validateSchema,+ ( ValidateSchema (..), ) where +import Control.Applicative ((*>), (<*>), Applicative (..), pure) import Control.Monad ((>=>)) import Control.Monad.Reader (asks) import Data.Foldable (traverse_)-import Data.Functor (($>))------ Morpheus-+import Data.Functor ((<$>), fmap)+import Data.Maybe (Maybe (..), maybe) import Data.Morpheus.Error.Document.Interface ( ImplementsError (..), PartialImplements (..), ) import Data.Morpheus.Internal.Utils- ( KeyOf (..),+ ( (<:>),+ KeyOf (..), Selectable (..), elems, empty, failure,+ ordTraverse, )-import Data.Morpheus.Schema.Schema (systemTypes)+import Data.Morpheus.Schema.Schema+ ( internalSchema,+ ) import Data.Morpheus.Types.Internal.AST- ( ANY,- ArgumentDefinition,+ ( ArgumentDefinition, ArgumentsDefinition (..),+ CONST,+ DataEnumValue (..),+ DirectiveDefinition (..),+ DirectiveLocation (..), FieldContent (..), FieldDefinition (..), FieldName (..), FieldsDefinition, IN, OUT,- ObjectEntry (..),- Schema,+ Schema (..),+ Schema (..), TRUE,+ TypeCategory, TypeContent (..), TypeDefinition (..),+ TypeKind (..), TypeName, TypeRef (..),+ Typed (..),+ UnionMember (..),+ VALID,+ Value, isWeaker, )+import Data.Morpheus.Types.Internal.Config (Config (..)) import Data.Morpheus.Types.Internal.Resolving ( Eventless, ) import Data.Morpheus.Types.Internal.Validation ( InputSource (..), InputValidator,- askInputFieldType,+ Scope (..),+ ScopeKind (..), runValidator,+ selectType, startInput, ) import Data.Morpheus.Types.Internal.Validation.SchemaValidator@@ -69,82 +91,243 @@ inField, inInterface, inType,- selectType, )-import Data.Morpheus.Validation.Internal.Value (validateInput)-import Data.Semigroup ((<>))+import Data.Morpheus.Validation.Internal.Directive+ ( validateDirectives,+ )+import Data.Morpheus.Validation.Internal.Value+ ( validateInputByTypeRef,+ )+import Data.Proxy (Proxy (..))+import Data.Traversable (traverse)+import Prelude+ ( ($),+ (&&),+ (.),+ (==),+ Bool,+ not,+ otherwise,+ ) -validateSchema :: Schema -> Eventless Schema-validateSchema schema = validatePartialDocument (elems schema) $> schema+class ValidateSchema s where+ validateSchema :: Bool -> Config -> Schema s -> Eventless (Schema VALID) -validatePartialDocument :: [TypeDefinition ANY] -> Eventless [TypeDefinition ANY]-validatePartialDocument types =- runValidator- (traverse validateType types)- TypeSystemContext- { types = systemTypes <> types,- local = ()- }+instance ValidateSchema CONST where+ validateSchema+ withSystem+ config+ schema@Schema+ { types,+ query,+ mutation,+ subscription,+ directiveDefinitions+ } = do+ sysSchema <-+ if withSystem+ then internalSchema <:> schema+ else pure schema+ runValidator+ __validateSchema+ config+ sysSchema+ Scope+ { position = Nothing,+ currentTypeName = "Root",+ currentTypeKind = KindObject Nothing,+ currentTypeWrappers = [],+ kind = TYPE,+ fieldname = "Root"+ }+ TypeSystemContext+ { local = ()+ }+ where+ __validateSchema :: SchemaValidator () (Schema VALID)+ __validateSchema =+ Schema+ <$> traverse validateType types+ <*> validateType query+ <*> validateOptional validateType mutation+ <*> validateOptional validateType subscription+ <*> traverse validateDirectiveDefinition directiveDefinitions +validateOptional :: Applicative f => (a -> f b) -> Maybe a -> f (Maybe b)+validateOptional f = maybe (pure Nothing) (fmap Just . f)++instance ValidateSchema VALID where+ validateSchema _ _ = pure+ validateType ::- TypeDefinition ANY ->- SchemaValidator () (TypeDefinition ANY)+ TypeDefinition cat CONST ->+ SchemaValidator () (TypeDefinition cat VALID) validateType- dt@TypeDefinition+ TypeDefinition { typeName,- typeContent =- DataObject- { objectImplements,- objectFields- }- } = inType typeName $+ typeFingerprint,+ typeDescription,+ typeDirectives,+ typeContent+ } =+ inType typeName $+ TypeDefinition+ typeName+ typeFingerprint+ typeDescription+ <$> validateDirectives (typeDirectiveLocation typeContent) typeDirectives+ <*> validateTypeContent typeContent++typeDirectiveLocation :: TypeContent a b c -> DirectiveLocation+typeDirectiveLocation DataObject {} = OBJECT+typeDirectiveLocation DataInputObject {} = INPUT_OBJECT+typeDirectiveLocation DataScalar {} = SCALAR+typeDirectiveLocation DataEnum {} = ENUM+typeDirectiveLocation DataInputUnion {} = OBJECT+typeDirectiveLocation DataUnion {} = UNION+typeDirectiveLocation DataInterface {} = INTERFACE++validateTypeContent ::+ TypeContent TRUE cat CONST ->+ SchemaValidator TypeName (TypeContent TRUE cat VALID)+validateTypeContent+ DataObject+ { objectImplements,+ objectFields+ } = do validateImplements objectImplements objectFields- traverse_ checkFieldArgsuments objectFields- pure dt-validateType- dt@TypeDefinition- { typeContent = DataInputObject {inputObjectFields},- typeName- } = inType typeName $ do- traverse_ validateFieldDefaultValue inputObjectFields- pure dt-validateType x = pure x+ DataObject objectImplements <$> ordTraverse validateField objectFields+validateTypeContent DataInputObject {inputObjectFields} =+ DataInputObject <$> ordTraverse validateField inputObjectFields+validateTypeContent DataScalar {..} = pure DataScalar {..}+validateTypeContent DataEnum {enumMembers} = DataEnum <$> traverse validateEnumMember enumMembers+validateTypeContent DataInputUnion {inputUnionMembers} =+ DataInputUnion <$> traverse validateUnionMember inputUnionMembers+validateTypeContent DataUnion {unionMembers} = DataUnion <$> traverse validateUnionMember unionMembers+validateTypeContent (DataInterface fields) =+ DataInterface <$> ordTraverse validateField fields +validateEnumMember ::+ DataEnumValue CONST -> SchemaValidator TypeName (DataEnumValue VALID)+validateEnumMember DataEnumValue {enumDirectives = directives, ..} = do+ enumDirectives <- validateDirectives ENUM_VALUE directives+ pure DataEnumValue {..}++validateUnionMember ::+ UnionMember cat CONST -> SchemaValidator TypeName (UnionMember cat VALID)+validateUnionMember UnionMember {..} = pure UnionMember {..}++class FieldDirectiveLocation (cat :: TypeCategory) where+ directiveLocation :: Proxy cat -> DirectiveLocation++instance FieldDirectiveLocation OUT where+ directiveLocation _ = FIELD_DEFINITION++instance FieldDirectiveLocation IN where+ directiveLocation _ = INPUT_FIELD_DEFINITION++validateField ::+ forall cat.+ FieldDirectiveLocation cat =>+ FieldDefinition cat CONST ->+ SchemaValidator TypeName (FieldDefinition cat VALID)+validateField field@FieldDefinition {..} =+ inField+ fieldName+ ( FieldDefinition+ fieldName+ fieldDescription+ fieldType+ <$> validateDirectives (directiveLocation (Proxy @cat)) fieldDirectives+ <*> validateOptional (checkFieldContent field) fieldContent+ )++checkFieldContent ::+ FieldDefinition cat CONST ->+ FieldContent TRUE cat CONST ->+ SchemaValidator (TypeName, FieldName) (FieldContent TRUE cat VALID)+checkFieldContent _ (FieldArgs argsDef) = FieldArgs <$> validateArgumentsDefinition argsDef+checkFieldContent FieldDefinition {fieldType} (DefaultInputValue value) = do+ (typeName, fName) <- asks local+ DefaultInputValue+ <$> startInput+ (SourceInputField typeName fName Nothing)+ (validateDefaultValue fieldType value)++validateArgumentsDefinition ::+ ArgumentsDefinition CONST ->+ SchemaValidator (TypeName, FieldName) (ArgumentsDefinition VALID)+validateArgumentsDefinition (ArgumentsDefinition meta args) =+ ArgumentsDefinition meta+ <$> ordTraverse+ validateArgumentDefinition+ args++validateArgumentDefinition ::+ ArgumentDefinition CONST ->+ SchemaValidator (TypeName, FieldName) (ArgumentDefinition VALID)+validateArgumentDefinition FieldDefinition {..} =+ FieldDefinition+ fieldName+ fieldDescription+ fieldType+ <$> validateDirectives ARGUMENT_DEFINITION fieldDirectives+ <*> validateOptional (validateArgumentDefaultValue fieldName fieldType) fieldContent++validateArgumentDefaultValue ::+ FieldName ->+ TypeRef ->+ FieldContent TRUE IN CONST ->+ SchemaValidator (TypeName, FieldName) (FieldContent TRUE IN VALID)+validateArgumentDefaultValue argName fieldType (DefaultInputValue value) =+ do+ (typeName, fName) <- asks local+ v <-+ startInput+ (SourceInputField typeName fName (Just argName))+ (validateDefaultValue fieldType value)+ pure (DefaultInputValue v)+ -- INETRFACE ---------------------------- validateImplements :: [TypeName] ->- FieldsDefinition OUT ->+ FieldsDefinition OUT CONST -> SchemaValidator TypeName () validateImplements objectImplements objectFields = do interface <- traverse selectInterface objectImplements traverse_ (mustBeSubset objectFields) interface mustBeSubset ::- FieldsDefinition OUT -> (TypeName, FieldsDefinition OUT) -> SchemaValidator TypeName ()+ FieldsDefinition OUT CONST ->+ (TypeName, FieldsDefinition OUT CONST) ->+ SchemaValidator TypeName () mustBeSubset objFields (typeName, fields) = inInterface typeName $ traverse_ (checkInterfaceField objFields) (elems fields) checkInterfaceField ::- FieldsDefinition OUT ->- FieldDefinition OUT ->+ FieldsDefinition OUT CONST ->+ FieldDefinition OUT CONST -> SchemaValidator Interface () checkInterfaceField objFields interfaceField@FieldDefinition- { fieldName+ { fieldName,+ fieldDirectives } = inField fieldName $- selectOr err (isSuptype interfaceField) fieldName objFields+ do+ _ <- validateDirectives FIELD_DEFINITION fieldDirectives+ selectOr err (isSuptype interfaceField) fieldName objFields where err = failImplements Missing class PartialImplements ctx => TypeEq a ctx where isSuptype :: a -> a -> SchemaValidator ctx () -instance TypeEq (FieldDefinition OUT) (Interface, FieldName) where+instance TypeEq (FieldDefinition OUT CONST) (Interface, FieldName) where FieldDefinition { fieldType, fieldContent = args1@@ -154,10 +337,10 @@ fieldContent = args2 } = (fieldType `isSuptype` fieldType') *> (args1 `isSuptype` args2) -instance TypeEq (Maybe (FieldContent TRUE OUT)) (Interface, FieldName) where+instance TypeEq (Maybe (FieldContent TRUE OUT s)) (Interface, FieldName) where f1 `isSuptype` f2 = toARgs f1 `isSuptype` toARgs f2 where- toARgs :: Maybe (FieldContent TRUE OUT) -> ArgumentsDefinition+ toARgs :: Maybe (FieldContent TRUE OUT s) -> ArgumentsDefinition s toARgs (Just (FieldArgs args)) = args toARgs _ = empty @@ -175,8 +358,8 @@ failImplements UnexpectedType {expectedType = t1, foundType = t2} elemIn ::- ( KeyOf a,- Selectable c a,+ ( KeyOf k a,+ Selectable k a c, TypeEq a ctx ) => a ->@@ -184,18 +367,18 @@ SchemaValidator ctx () elemIn el = selectOr (failImplements Missing) (isSuptype el) (keyOf el) -instance TypeEq ArgumentsDefinition (Interface, FieldName) where+instance TypeEq (ArgumentsDefinition s) (Interface, FieldName) where args1 `isSuptype` args2 = traverse_ validateArg (elems args1) where validateArg arg = inArgument (keyOf arg) $ elemIn arg args2 -instance TypeEq ArgumentDefinition (Interface, Field) where+instance TypeEq (ArgumentDefinition s) (Interface, Field) where arg1 `isSuptype` arg2 = fieldType arg1 `isSuptype` fieldType arg2 ------------------------------- selectInterface :: TypeName ->- SchemaValidator ctx (TypeName, FieldsDefinition OUT)+ SchemaValidator ctx (TypeName, FieldsDefinition OUT CONST) selectInterface = selectType >=> constraintInterface failImplements ::@@ -206,47 +389,21 @@ x <- asks local failure $ partialImplements x err -checkFieldArgsuments ::- FieldDefinition OUT ->- SchemaValidator TypeName ()-checkFieldArgsuments FieldDefinition {fieldContent = Nothing} = pure ()-checkFieldArgsuments FieldDefinition {fieldContent = Just (FieldArgs args), fieldName} = do- typeName <- asks local- traverse_ (validateArgumentDefaultValue typeName fieldName) (elems args)--validateArgumentDefaultValue ::- TypeName ->- FieldName ->- ArgumentDefinition ->- SchemaValidator TypeName ()-validateArgumentDefaultValue _ _ FieldDefinition {fieldContent = Nothing} = pure ()-validateArgumentDefaultValue- typeName- fName- inputField@FieldDefinition {fieldName = argName} =- startInput (SourceInputField typeName fName (Just argName)) $- validateDefaultValue inputField- -- DEFAULT VALUE--- TODO: implement default value validation-validateFieldDefaultValue ::- FieldDefinition IN ->- SchemaValidator TypeName ()-validateFieldDefaultValue inputField@FieldDefinition {fieldName} = do- typeName <- asks local- startInput (SourceInputField typeName fieldName Nothing) $- validateDefaultValue inputField validateDefaultValue ::- FieldDefinition IN ->- InputValidator (TypeSystemContext TypeName) ()-validateDefaultValue FieldDefinition {fieldContent = Nothing} = pure ()-validateDefaultValue- inputField@FieldDefinition- { fieldName,- fieldType = TypeRef {typeWrappers},- fieldContent = Just DefaultInputValue {defaultInputValue}- } = do- datatype <- askInputFieldType inputField- _ <- validateInput typeWrappers datatype (ObjectEntry fieldName defaultInputValue)- pure ()+ TypeRef ->+ Value CONST ->+ InputValidator+ CONST+ (TypeSystemContext (TypeName, FieldName))+ (Value VALID)+validateDefaultValue typeRef =+ validateInputByTypeRef (Typed typeRef)++-- TODO: validate directives+validateDirectiveDefinition :: DirectiveDefinition CONST -> SchemaValidator () (DirectiveDefinition VALID)+validateDirectiveDefinition DirectiveDefinition {directiveDefinitionArgs = args, ..} =+ inType "Directive" $ inField directiveDefinitionName $ do+ directiveDefinitionArgs <- validateArgumentsDefinition args+ pure DirectiveDefinition {..}
+ src/Data/Morpheus/Validation/Internal/Arguments.hs view
@@ -0,0 +1,179 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.Validation.Internal.Arguments+ ( validateDirectiveArguments,+ validateFieldArguments,+ ArgumentsConstraints,+ Resolve,+ )+where++import Control.Applicative ((*>), pure)+import Control.Monad ((>=>), (>>=))+import Data.Functor ((<$>), fmap)+import Data.Maybe (fromMaybe, maybe)+import Data.Morpheus.Internal.Utils+ ( empty,+ ordTraverse,+ ordTraverse_,+ )+import Data.Morpheus.Types.Internal.AST+ ( Argument (..),+ ArgumentDefinition,+ Arguments,+ ArgumentsDefinition (..),+ CONST,+ DirectiveDefinition,+ DirectiveDefinition (..),+ FieldDefinition (..),+ IN,+ OUT,+ ObjectEntry (..),+ Position (..),+ RAW,+ VALID,+ Value (..),+ VariableDefinitions,+ fieldContentArgs,+ typed,+ )+import Data.Morpheus.Types.Internal.Validation+ ( FragmentValidator,+ GetWith,+ InputSource (..),+ MissingRequired,+ Scope (..),+ Validator,+ askVariables,+ asksScope,+ selectKnown,+ selectRequired,+ selectWithDefaultValue,+ startInput,+ withPosition,+ )+import Data.Morpheus.Validation.Internal.Value+ ( ValidateWithDefault,+ validateInputByTypeRef,+ )+import Data.Traversable (traverse)+import Prelude+ ( ($),+ (.),+ flip,+ )++type VariableConstraints ctx =+ ( GetWith ctx (VariableDefinitions VALID),+ MissingRequired (VariableDefinitions VALID) ctx+ )++type ArgumentsConstraints c schemaS valueS =+ ( Resolve Argument valueS c,+ ValidateWithDefault c schemaS schemaS,+ ValidateWithDefault c schemaS CONST+ )++validateArgument ::+ ( ValidateWithDefault ctx schemaS valueS,+ ValidateWithDefault ctx schemaS schemaS+ ) =>+ Arguments valueS ->+ FieldDefinition IN schemaS ->+ Validator schemaS ctx (Argument VALID)+validateArgument+ requestArgs+ argumentDef =+ selectWithDefaultValue+ (toArgument argumentDef >=> validateArgumentValue argumentDef)+ (validateArgumentValue argumentDef)+ argumentDef+ requestArgs++toArgument :: FieldDefinition IN s -> Value schemaS -> Validator schemaStage ctx (Argument schemaS)+toArgument+ FieldDefinition {fieldName}+ value = flip (Argument fieldName) value . fromMaybe (Position 0 0) <$> asksScope position++validateArgumentValue ::+ (ValidateWithDefault ctx schemaS valueS) =>+ FieldDefinition IN schemaS ->+ Argument valueS ->+ Validator schemaS ctx (Argument VALID)+validateArgumentValue+ field+ Argument {argumentValue, ..} =+ withPosition argumentPosition+ $ startInput (SourceArgument argumentName)+ $ Argument+ argumentName+ argumentPosition+ <$> validateInputByTypeRef (typed fieldType field) argumentValue++validateFieldArguments ::+ FieldDefinition OUT VALID ->+ Arguments RAW ->+ FragmentValidator s (Arguments VALID)+validateFieldArguments fieldDef@FieldDefinition {fieldContent} =+ validateArguments+ (`selectKnown` fieldDef)+ argsDef+ where+ argsDef = maybe empty fieldContentArgs fieldContent++validateDirectiveArguments ::+ ArgumentsConstraints ctx schemaStage valueStage =>+ DirectiveDefinition schemaStage ->+ Arguments valueStage ->+ Validator schemaStage ctx (Arguments VALID)+validateDirectiveArguments+ directiveDef@DirectiveDefinition+ { directiveDefinitionArgs+ } =+ validateArguments+ (`selectKnown` directiveDef)+ directiveDefinitionArgs++validateArguments ::+ ArgumentsConstraints ctx schemaStage s =>+ (Argument CONST -> Validator schemaStage ctx (ArgumentDefinition schemaStage)) ->+ ArgumentsDefinition schemaStage ->+ Arguments s ->+ Validator schemaStage ctx (Arguments VALID)+validateArguments checkUnknown argsDef rawArgs = do+ args <- ordTraverse resolve rawArgs+ ordTraverse_ checkUnknown args+ *> ordTraverse (validateArgument args) (arguments argsDef)++class Resolve f s ctx where+ resolve :: f s -> Validator schemaS ctx (f CONST)++instance VariableConstraints ctx => Resolve Argument RAW ctx where+ resolve (Argument key position val) = Argument key position <$> resolve val++instance Resolve f CONST ctx where+ resolve = pure++instance VariableConstraints ctx => Resolve Value RAW ctx where+ resolve Null = pure Null+ resolve (Scalar x) = pure $ Scalar x+ resolve (Enum x) = pure $ Enum x+ resolve (List elems) = List <$> traverse resolve elems+ resolve (Object fields) = Object <$> traverse resolve fields+ resolve (VariableValue ref) =+ askVariables+ >>= fmap (ResolvedVariable ref)+ . selectRequired ref++instance VariableConstraints ctx => Resolve ObjectEntry RAW ctx where+ resolve (ObjectEntry name value) = ObjectEntry name <$> resolve value
src/Data/Morpheus/Validation/Internal/Directive.hs view
@@ -1,12 +1,16 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-}+{-# LANGUAGE KindSignatures #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Validation.Internal.Directive ( shouldIncludeSelection,@@ -14,14 +18,17 @@ ) where --- MORPHEUS-import Data.Morpheus.Error (errorMessage, globalErrorMessage)+import Control.Applicative ((*>), pure)+import Control.Monad ((>>=))+import Data.Functor ((<$>))+import Data.List (elem)+import Data.Morpheus.Error.Utils+ ( validationErrorMessage,+ ) import Data.Morpheus.Internal.Utils ( Failure (..),- selectBy, selectOr, )-import Data.Morpheus.Schema.Directives (defaultDirectives) import Data.Morpheus.Types.Internal.AST ( Argument (..), Directive (..),@@ -29,35 +36,61 @@ DirectiveLocation (..), Directives, FieldName,- RAW,+ Ref (..), ScalarValue (..),+ Schema (..), VALID, Value (..), msg, ) import Data.Morpheus.Types.Internal.Validation- ( SelectionValidator,+ ( Validator,+ askSchema, selectKnown,+ selectRequired, withDirective, )-import Data.Morpheus.Validation.Query.Arguments- ( validateDirectiveArguments,+import Data.Morpheus.Validation.Internal.Arguments+ ( ArgumentsConstraints,+ validateDirectiveArguments, ) import Data.Semigroup ((<>))+import Data.Traversable (traverse)+import Prelude+ ( ($),+ (&&),+ (==),+ Bool (..),+ Maybe (..),+ otherwise,+ ) -validateDirective :: DirectiveLocation -> [DirectiveDefinition] -> Directive RAW -> SelectionValidator (Directive VALID)-validateDirective location directiveDefs directive@Directive {directiveArgs, ..} =+validateDirectives ::+ ArgumentsConstraints ctx schemaS s =>+ DirectiveLocation ->+ Directives s ->+ Validator schemaS ctx (Directives VALID)+validateDirectives location = traverse (validate location)++validate ::+ ArgumentsConstraints c schemaS s =>+ DirectiveLocation ->+ Directive s ->+ Validator schemaS c (Directive VALID)+validate location directive@Directive {..} = withDirective directive $ do- directiveDef <- selectKnown directive directiveDefs- args <- validateDirectiveArguments directiveDef directiveArgs- validateDirectiveLocation location directive directiveDef- pure Directive {directiveArgs = args, ..}+ Schema {directiveDefinitions} <- askSchema+ directiveDef <- selectKnown directive directiveDefinitions+ Directive directiveName directivePosition+ <$> ( validateDirectiveLocation location directive directiveDef+ *> validateDirectiveArguments directiveDef directiveArgs+ ) validateDirectiveLocation :: DirectiveLocation -> Directive s ->- DirectiveDefinition ->- SelectionValidator ()+ DirectiveDefinition s' ->+ Validator schemaS ctx () validateDirectiveLocation loc Directive {directiveName, directivePosition}@@ -65,29 +98,40 @@ | loc `elem` directiveDefinitionLocations = pure () | otherwise = failure $- errorMessage- directivePosition+ validationErrorMessage+ (Just directivePosition) ("Directive " <> msg directiveName <> " may not to be used on " <> msg loc) -validateDirectives :: DirectiveLocation -> Directives RAW -> SelectionValidator (Directives VALID)-validateDirectives location = traverse (validateDirective location defaultDirectives)--directiveFulfilled :: Bool -> FieldName -> Directives s -> SelectionValidator Bool+directiveFulfilled ::+ Bool ->+ FieldName ->+ Directives s ->+ Validator schemaS ctx Bool directiveFulfilled target = selectOr (pure True) (argumentIf target) -shouldIncludeSelection :: Directives VALID -> SelectionValidator Bool+shouldIncludeSelection ::+ Directives VALID ->+ Validator schemaS ctx Bool shouldIncludeSelection directives = do dontSkip <- directiveFulfilled False "skip" directives include <- directiveFulfilled True "include" directives pure (dontSkip && include) -argumentIf :: Bool -> Directive s -> SelectionValidator Bool-argumentIf target Directive {directiveName, directiveArgs} =- selectBy err "if" directiveArgs+argumentIf ::+ Bool ->+ Directive s ->+ Validator schemaS ctx Bool+argumentIf target Directive {directiveArgs, directivePosition} =+ selectRequired (Ref "if" directivePosition) directiveArgs >>= assertArgument target- where- err = globalErrorMessage $ "Directive " <> msg ("@" <> directiveName) <> " argument \"if\" of type \"Boolean!\" is required but not provided." -assertArgument :: Bool -> Argument s -> SelectionValidator Bool+assertArgument ::+ Bool ->+ Argument s ->+ Validator schemaS ctx Bool assertArgument asserted Argument {argumentValue = Scalar (Boolean actual)} = pure (asserted == actual)-assertArgument _ Argument {argumentValue} = failure $ "Expected type Boolean!, found " <> msg argumentValue <> "."+assertArgument _ Argument {argumentValue, argumentPosition} =+ failure+ $ validationErrorMessage+ (Just argumentPosition)+ $ "Expected type Boolean!, found " <> msg argumentValue <> "."
src/Data/Morpheus/Validation/Internal/Value.hs view
@@ -1,29 +1,34 @@-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE NoImplicitPrelude #-} -module Data.Morpheus.Validation.Internal.Value (validateInput) where+module Data.Morpheus.Validation.Internal.Value+ ( validateInputByTypeRef,+ validateInputByType,+ ValidateWithDefault,+ )+where -import Data.Foldable (traverse_)+import Control.Applicative ((*>), pure)+import Data.Either (Either (..)) import Data.Function ((&))-import Data.List (elem)-import Data.Maybe (maybe)--- MORPHEUS-+import Data.Functor ((<$>), fmap)+import Data.List (any, elem)+import Data.Maybe (Maybe (..), maybe) import Data.Morpheus.Error.Input (typeViolation)-import Data.Morpheus.Error.Utils (errorMessage) import Data.Morpheus.Error.Variable (incompatibleVariableType) import Data.Morpheus.Internal.Utils ( Failure (..),- elems,- fromElems,+ ordTraverse_,+ traverseCollection, ) import Data.Morpheus.Types.Internal.AST- ( DataEnumValue (..),+ ( CONST,+ DataEnumValue (..), DataInputUnion, FieldDefinition (..), FieldsDefinition,@@ -31,79 +36,92 @@ Message, Object, ObjectEntry (..),- RESOLVED, Ref (..),- ResolvedValue, ScalarDefinition (..), ScalarValue (..),- Schema, TRUE, TypeContent (..), TypeDefinition (..), TypeName (..), TypeRef (..),- TypeRef (..), TypeWrapper (..),+ Typed (..),+ UnionMember (..), VALID, ValidValue, Value (..), Variable (..), Variable (..), VariableContent (..),- isNullableWrapper,+ isNullable, isWeaker, msg,+ msgValidation, toFieldName,+ typed,+ untyped,+ withPosition, )-import Data.Morpheus.Types.Internal.AST.OrderedMap+import Data.Morpheus.Types.Internal.AST.OrdMap ( unsafeFromValues, ) import Data.Morpheus.Types.Internal.Validation- ( GetWith,- InputContext,+ ( InputContext, InputSource (..), InputValidator,- MissingRequired,- MonadContext,- Prop (..), Scope (..),- ScopeKind (..),- SetWith,- Unknown, Validator,- askInputFieldType,- askInputMember,- asks,+ askType,+ askTypeMember,+ asksScope, constraintInputUnion,+ inField, inputMessagePrefix, inputValueSource, selectKnown, selectWithDefaultValue,- withInputScope, withScopeType, ) import Data.Semigroup ((<>))+import Data.Traversable (traverse)+import Prelude+ ( ($),+ (&&),+ (.),+ Bool (..),+ Eq (..),+ const,+ fst,+ id,+ not,+ otherwise,+ ) -castFailure ::- ( GetWith ctx Schema,- GetWith ctx Scope- ) =>- TypeRef ->+violation :: Maybe Message ->- ResolvedValue ->- InputValidator ctx a-castFailure expected message value = do- pos <- asks position+ Value s ->+ InputValidator schemaS ctx a+violation message value = do+ Scope+ { position,+ currentTypeName,+ currentTypeWrappers+ } <-+ asksScope id prefix <- inputMessagePrefix failure- $ errorMessage pos- $ prefix <> typeViolation expected value <> maybe "" (" " <>) message+ $ withPosition position+ $ prefix+ <> typeViolation+ (TypeRef currentTypeName Nothing currentTypeWrappers)+ value+ <> maybe "" ((" " <>) . msgValidation) message checkTypeEquality :: (TypeName, [TypeWrapper]) -> Ref -> Variable VALID ->- InputValidator ctx ValidValue+ InputValidator schemaS ctx ValidValue checkTypeEquality (tyConName, tyWrappers) ref var@Variable {variableValue = ValidVariableValue value, variableType} | typeConName variableType == tyConName && not@@ -120,202 +138,167 @@ typeArgs = Nothing } -type InputConstraints ctx =- ( GetWith ctx Schema,- GetWith ctx Scope,- GetWith (InputContext ctx) InputSource,- SetWith ctx Scope,- MissingRequired (Object RESOLVED) (InputContext ctx),- Unknown (FieldsDefinition IN) (InputContext ctx)- )+validateInputByTypeRef ::+ ValidateWithDefault c schemaS s =>+ Typed IN schemaS TypeRef ->+ Value s ->+ Validator schemaS (InputContext c) (Value VALID)+validateInputByTypeRef+ ref+ value = do+ inputTypeDef <- askType ref+ validateInputByType+ (untyped typeWrappers ref)+ inputTypeDef+ value +validateValueByField ::+ ValidateWithDefault c schemaS s =>+ FieldDefinition IN schemaS ->+ Value s ->+ Validator schemaS (InputContext c) (Value VALID)+validateValueByField field =+ inField field+ . validateInputByTypeRef+ (typed fieldType field)+ -- Validate input Values-validateInput ::- forall ctx.- ( InputConstraints ctx- ) =>+validateInputByType ::+ ValidateWithDefault ctx schemaS valueS => [TypeWrapper] ->- TypeDefinition IN ->- ObjectEntry RESOLVED ->- InputValidator ctx ValidValue-validateInput tyWrappers TypeDefinition {typeContent = tyCont, typeName} =- withScopeType typeName- . validateWrapped tyWrappers tyCont- where- mismatchError :: [TypeWrapper] -> Maybe Message -> ResolvedValue -> InputValidator ctx ValidValue- mismatchError wrappers = castFailure (TypeRef typeName Nothing wrappers)- -- VALIDATION- validateWrapped ::- [TypeWrapper] ->- TypeContent TRUE IN ->- ObjectEntry RESOLVED ->- InputValidator ctx ValidValue- -- Validate Null. value = null ?- validateWrapped wrappers _ ObjectEntry {entryValue = ResolvedVariable ref variable} =- checkTypeEquality (typeName, wrappers) ref variable- validateWrapped wrappers _ ObjectEntry {entryValue = Null}- | isNullableWrapper wrappers = pure Null- | otherwise = mismatchError wrappers Nothing Null- -- Validate LIST- validateWrapped [TypeMaybe] dt ObjectEntry {entryValue} =- validateUnwrapped (mismatchError [TypeMaybe]) dt entryValue- validateWrapped (TypeMaybe : wrappers) _ value =- validateWrapped wrappers tyCont value- validateWrapped (TypeList : wrappers) _ (ObjectEntry key (List list)) =- List <$> traverse validateElement list- where- validateElement = validateWrapped wrappers tyCont . ObjectEntry key- {-- 2. VALIDATE TYPES, all wrappers are already Processed --}- {-- VALIDATE OBJECT--}- validateWrapped [] dt ObjectEntry {entryValue} =- validateUnwrapped (mismatchError []) dt entryValue- {-- 3. THROW ERROR: on invalid values --}- validateWrapped wrappers _ ObjectEntry {entryValue} = mismatchError wrappers Nothing entryValue- validateUnwrapped ::- -- error- (Maybe Message -> ResolvedValue -> InputValidator ctx ValidValue) ->- TypeContent TRUE IN ->- Value RESOLVED ->- InputValidator ctx ValidValue- validateUnwrapped _ (DataInputObject parentFields) (Object fields) =- Object <$> validateInputObject parentFields fields- validateUnwrapped _ (DataInputUnion inputUnion) (Object rawFields) =- validatInputUnion typeName inputUnion rawFields- validateUnwrapped err (DataEnum tags) value =- validateEnum (err Nothing) tags value- validateUnwrapped err (DataScalar dataScalar) value =- validateScalar typeName dataScalar value err- validateUnwrapped err _ value = err Nothing value+ TypeDefinition IN schemaS ->+ Value valueS ->+ InputValidator schemaS ctx ValidValue+validateInputByType tyWrappers typeDef =+ withScopeType (typeDef, tyWrappers) . validateWrapped tyWrappers typeDef +-- VALIDATION+validateWrapped ::+ ValidateWithDefault ctx schemaS valueS =>+ [TypeWrapper] ->+ TypeDefinition IN schemaS ->+ Value valueS ->+ InputValidator schemaS ctx ValidValue+-- Validate Null. value = null ?+validateWrapped wrappers _ (ResolvedVariable ref variable) = do+ typeName <- asksScope currentTypeName+ checkTypeEquality (typeName, wrappers) ref variable+validateWrapped wrappers _ Null+ | isNullable wrappers = pure Null+ | otherwise = violation Nothing Null+-- Validate LIST+validateWrapped [TypeMaybe] TypeDefinition {typeContent} entryValue =+ validateUnwrapped typeContent entryValue+validateWrapped (TypeMaybe : wrappers) typeDef value =+ validateInputByType wrappers typeDef value+validateWrapped (TypeList : wrappers) tyCont (List list) =+ List <$> traverse (validateInputByType wrappers tyCont) list+{-- 2. VALIDATE TYPES, all wrappers are already Processed --}+{-- VALIDATE OBJECT--}+validateWrapped [] TypeDefinition {typeContent} entryValue =+ validateUnwrapped typeContent entryValue+{-- 3. THROW ERROR: on invalid values --}+validateWrapped _ _ entryValue = violation Nothing entryValue++validateUnwrapped ::+ ValidateWithDefault ctx schemaS valueS =>+ TypeContent TRUE IN schemaS ->+ Value valueS ->+ InputValidator schemaS ctx ValidValue+validateUnwrapped (DataInputObject parentFields) (Object fields) =+ Object <$> validateInputObject parentFields fields+validateUnwrapped (DataInputUnion inputUnion) (Object rawFields) =+ validatInputUnion inputUnion rawFields+validateUnwrapped (DataEnum tags) value =+ validateEnum tags value+validateUnwrapped (DataScalar dataScalar) value =+ validateScalar dataScalar value+validateUnwrapped _ value = violation Nothing value+ -- INPUT UNION validatInputUnion ::- ( InputConstraints ctx- ) =>- TypeName ->- DataInputUnion ->- Object RESOLVED ->- InputValidator ctx (Value VALID)-validatInputUnion typeName inputUnion rawFields =+ ValidateWithDefault ctx schemaS s =>+ DataInputUnion schemaS ->+ Object s ->+ InputValidator schemaS ctx (Value VALID)+validatInputUnion inputUnion rawFields = case constraintInputUnion inputUnion rawFields of- Left message -> castFailure (TypeRef typeName Nothing []) (Just message) (Object rawFields)- Right (name, Nothing) -> pure (mkInputObject name [])+ Left message -> violation (Just message) (Object rawFields)+ Right (UnionMember {memberName}, Nothing) -> pure (mkInputObject memberName []) Right (name, Just value) -> validatInputUnionMember name value validatInputUnionMember ::- ( InputConstraints ctx- ) =>- TypeName ->- Value RESOLVED ->- InputValidator ctx (Value VALID)-validatInputUnionMember name value = do- inputDef <- askInputMember name- validValue <-- validateInput- [TypeMaybe]- inputDef- (ObjectEntry (toFieldName name) value)- pure $ mkInputObject name [ObjectEntry (toFieldName name) validValue]+ ValidateWithDefault ctx schemaS valueS =>+ UnionMember IN schemaS ->+ Value valueS ->+ InputValidator schemaS ctx (Value VALID)+validatInputUnionMember member@UnionMember {memberName} value = do+ inputDef <- fst <$> askTypeMember member+ validValue <- validateInputByType [TypeMaybe] inputDef value+ pure $ mkInputObject memberName [ObjectEntry (toFieldName memberName) validValue] mkInputObject :: TypeName -> [ObjectEntry s] -> Value s mkInputObject name xs = Object $ unsafeFromValues $ ObjectEntry "__typename" (Enum name) : xs -- INUT Object validateInputObject ::- ( InputConstraints ctx- ) =>- FieldsDefinition IN ->- Object RESOLVED ->- InputValidator ctx (Object VALID)+ ValidateWithDefault ctx schemaS valueS =>+ FieldsDefinition IN schemaS ->+ Object valueS ->+ InputValidator schemaS ctx (Object VALID) validateInputObject fieldsDef object =- do- kind <- asks kind- case kind of- TYPE ->- traverse_ (`requiredFieldIsDefined` object) fieldsDef- *> traverse (`validateField` fieldsDef) object- _ ->- traverse_ (`selectKnown` fieldsDef) object- *> validateObjectWithDefaultValue object fieldsDef--validateField ::- ( InputConstraints ctx- ) =>- ObjectEntry RESOLVED ->- FieldsDefinition IN ->- InputValidator ctx (ObjectEntry VALID)-validateField entry parentFields = do- field <- selectKnown entry parentFields- validateInputField field entry--validateObjectWithDefaultValue ::- (InputConstraints c) =>- Object RESOLVED ->- FieldsDefinition IN ->- Validator (InputContext c) (Object VALID)-validateObjectWithDefaultValue object fieldsDef =- traverse (validateFieldWithDefaultValue object) (elems fieldsDef)- >>= fromElems+ ordTraverse_ (`selectKnown` fieldsDef) object+ *> traverseCollection (validateWithDefault object) fieldsDef -validateFieldWithDefaultValue ::- (InputConstraints c) =>- Object RESOLVED ->- FieldDefinition IN ->- Validator (InputContext c) (ObjectEntry VALID)-validateFieldWithDefaultValue object fieldDef@FieldDefinition {fieldName} = do- entry <- selectWithDefaultValue (ObjectEntry fieldName) fieldDef object- validateInputField fieldDef entry+class ValidateWithDefault c schemaS s where+ validateWithDefault ::+ Object s ->+ FieldDefinition IN schemaS ->+ Validator schemaS (InputContext c) (ObjectEntry VALID) -validateInputField ::- (InputConstraints c) =>- FieldDefinition IN ->- ObjectEntry RESOLVED ->- Validator (InputContext c) (ObjectEntry VALID)-validateInputField fieldDef@FieldDefinition {fieldName, fieldType = TypeRef {typeConName, typeWrappers}} entry = do- inputTypeDef <- askInputFieldType fieldDef- withInputScope (Prop fieldName typeConName) $+instance ValidateWithDefault c VALID s where+ validateWithDefault object fieldDef@FieldDefinition {fieldName} = ObjectEntry fieldName- <$> validateInput- typeWrappers- inputTypeDef- entry+ <$> selectWithDefaultValue+ pure+ (validateValueByField fieldDef . entryValue)+ fieldDef+ object -requiredFieldIsDefined ::- ( MissingRequired (Object RESOLVED) (InputContext ctx),- GetWith ctx Scope- ) =>- FieldDefinition IN ->- Object RESOLVED ->- InputValidator ctx (ObjectEntry RESOLVED)-requiredFieldIsDefined fieldDef@FieldDefinition {fieldName} =- selectWithDefaultValue (ObjectEntry fieldName) fieldDef+instance ValidateWithDefault c CONST s where+ validateWithDefault object fieldDef@FieldDefinition {fieldName} =+ ObjectEntry fieldName+ <$> selectWithDefaultValue+ (validateValueByField fieldDef)+ (validateValueByField fieldDef . entryValue)+ fieldDef+ object -- Leaf Validations validateScalar ::- forall m.- (Monad m) =>- TypeName -> ScalarDefinition ->- ResolvedValue ->- (Maybe Message -> ResolvedValue -> m ValidValue) ->- m ValidValue-validateScalar typeName ScalarDefinition {validateValue} value err = do- scalarValue <- toScalar value+ Value s ->+ InputValidator schemaS ctx ValidValue+validateScalar ScalarDefinition {validateValue} value = do+ typeName <- asksScope currentTypeName+ scalarValue <- toScalar typeName value case validateValue scalarValue of Right _ -> pure scalarValue- Left "" -> err Nothing value- Left message -> err (Just $ msg message) value+ Left "" -> violation Nothing value+ Left message -> violation (Just $ msg message) value where- toScalar :: ResolvedValue -> m ValidValue- toScalar (Scalar x) | isValidDefault typeName x = pure (Scalar x)- toScalar _ = err Nothing value- isValidDefault :: TypeName -> ScalarValue -> Bool- isValidDefault "Boolean" = isBoolean- isValidDefault "String" = isString- isValidDefault "Float" = oneOf [isFloat, isInt]- isValidDefault "Int" = isInt- isValidDefault "ID" = oneOf [isInt, isFloat, isString]- isValidDefault _ = const True+ toScalar :: TypeName -> Value s -> InputValidator schemaS ctx ValidValue+ toScalar typeName (Scalar x) | isValidDefault typeName x = pure (Scalar x)+ toScalar _ _ = violation Nothing value +isValidDefault :: TypeName -> ScalarValue -> Bool+isValidDefault "Boolean" = isBoolean+isValidDefault "String" = isString+isValidDefault "Float" = oneOf [isFloat, isInt]+isValidDefault "Int" = isInt+isValidDefault "ID" = oneOf [isInt, isFloat, isString]+isValidDefault _ = const True+ oneOf :: [a -> Bool] -> a -> Bool oneOf ls v = any (v &) ls @@ -335,7 +318,7 @@ isInt Int {} = True isInt _ = False -isVariableValue :: (MonadContext m c, GetWith c InputSource) => m c Bool+isVariableValue :: InputValidator schemaS c Bool isVariableValue = \case SourceVariable {isDefaultValue} -> not isDefaultValue@@ -343,22 +326,20 @@ <$> inputValueSource validateEnum ::- (MonadContext m c, GetWith c InputSource) =>- (ResolvedValue -> m c ValidValue) ->- [DataEnumValue] ->- ResolvedValue ->- m c ValidValue-validateEnum err enumValues value@(Scalar (String enumValue))+ [DataEnumValue s] ->+ Value valueS ->+ InputValidator schemaS c ValidValue+validateEnum enumValues value@(Scalar (String enumValue)) | TypeName enumValue `elem` tags = do isFromVariable <- isVariableValue if isFromVariable then pure (Enum (TypeName enumValue))- else err value+ else violation Nothing value where- tags = map enumName enumValues-validateEnum err enumValues value@(Enum enumValue)+ tags = fmap enumName enumValues+validateEnum enumValues value@(Enum enumValue) | enumValue `elem` tags = pure (Enum enumValue)- | otherwise = err value+ | otherwise = violation Nothing value where- tags = map enumName enumValues-validateEnum err _ value = err value+ tags = fmap enumName enumValues+validateEnum _ value = violation Nothing value
− src/Data/Morpheus/Validation/Query/Arguments.hs
@@ -1,147 +0,0 @@-{-# LANGUAGE GADTs #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE RecordWildCards #-}--module Data.Morpheus.Validation.Query.Arguments- ( validateDirectiveArguments,- validateFieldArguments,- )-where--import Data.Foldable (traverse_)-import Data.Morpheus.Internal.Utils- ( elems,- empty,- )-import Data.Morpheus.Types.Internal.AST- ( Argument (..),- ArgumentDefinition,- Arguments,- ArgumentsDefinition (..),- DirectiveDefinition,- DirectiveDefinition (..),- FieldDefinition (..),- OUT,- ObjectEntry (..),- RAW,- RESOLVED,- RawValue,- ResolvedValue,- TypeRef (..),- VALID,- Value (..),- fieldContentArgs,- )-import Data.Morpheus.Types.Internal.Validation- ( InputSource (..),- Scope (..),- SelectionValidator,- askInputFieldType,- askVariables,- asks,- selectKnown,- selectRequired,- selectWithDefaultValue,- startInput,- withPosition,- )-import Data.Morpheus.Validation.Internal.Value- ( validateInput,- )---- only Resolves , doesnot checks the types-resolveObject :: RawValue -> SelectionValidator ResolvedValue-resolveObject = resolve- where- resolveEntry :: ObjectEntry RAW -> SelectionValidator (ObjectEntry RESOLVED)- resolveEntry (ObjectEntry name v) = ObjectEntry name <$> resolve v- ------------------------------------------------- resolve :: RawValue -> SelectionValidator ResolvedValue- resolve Null = pure Null- resolve (Scalar x) = pure $ Scalar x- resolve (Enum x) = pure $ Enum x- resolve (List x) = List <$> traverse resolve x- resolve (Object obj) = Object <$> traverse resolveEntry obj- resolve (VariableValue ref) =- askVariables- >>= fmap (ResolvedVariable ref)- . selectRequired ref--resolveArgumentVariables ::- Arguments RAW ->- SelectionValidator (Arguments RESOLVED)-resolveArgumentVariables =- traverse resolveVariable- where- resolveVariable :: Argument RAW -> SelectionValidator (Argument RESOLVED)- resolveVariable (Argument key val position) = do- constValue <- resolveObject val- pure $ Argument key constValue position--validateArgument ::- Arguments RESOLVED ->- ArgumentDefinition ->- SelectionValidator (Argument VALID)-validateArgument- requestArgs- argumentDef@FieldDefinition- { fieldName,- fieldType = TypeRef {typeWrappers}- } =- do- argumentPosition <- asks position- argument <-- selectWithDefaultValue- (\argumentValue -> Argument {argumentName = fieldName, argumentValue, argumentPosition})- argumentDef- requestArgs- validateArgumentValue argument- where- -------------------------------------------------------------------------- validateArgumentValue :: Argument RESOLVED -> SelectionValidator (Argument VALID)- validateArgumentValue arg@Argument {argumentValue = value, ..} =- withPosition argumentPosition- $ startInput (SourceArgument arg)- $ do- datatype <- askInputFieldType argumentDef- argumentValue <-- validateInput- typeWrappers- datatype- (ObjectEntry fieldName value)- pure Argument {argumentValue, ..}--validateFieldArguments ::- FieldDefinition OUT ->- Arguments RAW ->- SelectionValidator (Arguments VALID)-validateFieldArguments- fieldDef@FieldDefinition {fieldContent}- rawArgs =- do- args <- resolveArgumentVariables rawArgs- traverse_ checkUnknown (elems args)- traverse (validateArgument args) argsDef- where- argsDef = maybe empty fieldContentArgs fieldContent- -------------------------------------------------- checkUnknown :: Argument RESOLVED -> SelectionValidator ArgumentDefinition- checkUnknown = (`selectKnown` fieldDef)--validateDirectiveArguments ::- DirectiveDefinition ->- Arguments RAW ->- SelectionValidator (Arguments VALID)-validateDirectiveArguments- directiveDef@DirectiveDefinition- { directiveDefinitionArgs = ArgumentsDefinition _ argsDef- }- rawArgs =- do- args <- resolveArgumentVariables rawArgs- traverse_ checkUnknown (elems args)- traverse (validateArgument args) argsDef- where- -------------------------------------------------- checkUnknown :: Argument RESOLVED -> SelectionValidator ArgumentDefinition- checkUnknown = (`selectKnown` directiveDef)
src/Data/Morpheus/Validation/Query/Fragment.hs view
@@ -1,150 +1,131 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-}+{-# LANGUAGE KindSignatures #-} {-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Validation.Query.Fragment ( validateFragments, castFragmentType, resolveSpread,+ validateFragment,+ selectFragmentType,+ ResolveFragment (..), ) where -import Data.Foldable (traverse_)+import Control.Applicative ((<*>), pure)+import Control.Monad ((>>=))+import Data.Functor ((<$>))+import Data.List (elem)+import Data.Maybe (Maybe (..)) -- MORPHEUS import Data.Morpheus.Error.Fragment ( cannotBeSpreadOnType,- cannotSpreadWithinItself, ) import Data.Morpheus.Internal.Utils ( Failure (..),- elems,- selectOr, ) import Data.Morpheus.Types.Internal.AST- ( FieldName,+ ( Directives,+ FieldName, Fragment (..), Fragments,+ IMPLEMENTABLE, Position, RAW, Ref (..),- Selection (..),- SelectionContent (..),+ Schema, SelectionSet,+ Stage,+ Stage,+ TypeDefinition, TypeName, TypeNameRef (..),+ UnionTag (..),+ VALID, ) import Data.Morpheus.Types.Internal.Validation- ( BaseValidator,- Constraint (..),- OperationContext,- Validator,+ ( Constraint (..),+ FragmentValidator, askFragments, askSchema,- checkUnused, constraint, selectKnown, )-import Data.Semigroup ((<>))+import Data.Traversable (traverse)+import Prelude+ ( ($),+ otherwise,+ ) -validateFragments :: SelectionSet RAW -> BaseValidator ()-validateFragments selectionSet =- fragmentsCycleChecking- *> checkUnusedFragments selectionSet- *> fragmentsConditionTypeChecking+class ResolveFragment (s :: Stage) where+ resolveValidFragment ::+ (Fragment RAW -> FragmentValidator s (SelectionSet VALID)) ->+ [TypeName] ->+ Ref ->+ FragmentValidator s UnionTag -checkUnusedFragments :: SelectionSet RAW -> BaseValidator ()-checkUnusedFragments selectionSet = do- fragments <- askFragments- checkUnused- (usedFragments fragments (elems selectionSet))- (elems fragments)+instance ResolveFragment VALID where+ resolveValidFragment _ allowedTargets ref = do+ Fragment {fragmentType, fragmentSelection} <- resolveSpread allowedTargets ref+ pure $ UnionTag fragmentType fragmentSelection +instance ResolveFragment RAW where+ resolveValidFragment f allowedTargets ref = do+ fragment@Fragment {fragmentType} <- resolveSpread allowedTargets ref+ UnionTag fragmentType <$> f fragment++validateFragment ::+ (Fragment RAW -> FragmentValidator s (SelectionSet VALID)) ->+ [TypeName] ->+ Fragment RAW ->+ FragmentValidator s (Fragment VALID)+validateFragment validate allowedTypes fragment@Fragment {fragmentPosition} =+ castFragmentType Nothing fragmentPosition allowedTypes fragment+ >>= onlyValidateFrag validate++validateFragments ::+ (Fragment RAW -> FragmentValidator RAW (SelectionSet VALID)) ->+ FragmentValidator RAW (Fragments VALID)+validateFragments f = askFragments >>= traverse (onlyValidateFrag f)++onlyValidateFrag ::+ (Fragment RAW -> FragmentValidator s (SelectionSet VALID)) ->+ Fragment RAW ->+ FragmentValidator s (Fragment VALID)+onlyValidateFrag validate f@Fragment {..} =+ Fragment+ fragmentName+ fragmentType+ fragmentPosition+ <$> validate f <*> validateFragmentDirectives fragmentDirectives++validateFragmentDirectives :: Directives RAW -> FragmentValidator s (Directives VALID)+validateFragmentDirectives _ = pure [] --TODO: validate fragment directives+ castFragmentType ::- Maybe FieldName -> Position -> [TypeName] -> Fragment -> Validator ctx Fragment+ Maybe FieldName ->+ Position ->+ [TypeName] ->+ Fragment s ->+ FragmentValidator s1 (Fragment s) castFragmentType key position typeMembers fragment@Fragment {fragmentType} | fragmentType `elem` typeMembers = pure fragment | otherwise = failure $ cannotBeSpreadOnType key fragmentType position typeMembers -resolveSpread :: [TypeName] -> Ref -> Validator (OperationContext v) Fragment+resolveSpread :: [TypeName] -> Ref -> FragmentValidator s (Fragment s) resolveSpread allowedTargets ref@Ref {refName, refPosition} = askFragments >>= selectKnown ref >>= castFragmentType (Just refName) refPosition allowedTargets -usedFragments :: Fragments -> [Selection RAW] -> [Node]-usedFragments fragments = concatMap findAllUses- where- findUsesSelectionContent :: SelectionContent RAW -> [Node]- findUsesSelectionContent (SelectionSet selectionSet) =- concatMap findAllUses selectionSet- findUsesSelectionContent SelectionField = []- findAllUses :: Selection RAW -> [Node]- findAllUses Selection {selectionContent} =- findUsesSelectionContent selectionContent- findAllUses (InlineFragment Fragment {fragmentSelection}) =- concatMap findAllUses fragmentSelection- findAllUses (Spread _ Ref {refName, refPosition}) =- [Ref refName refPosition] <> searchInFragment- where- searchInFragment =- selectOr- []- (concatMap findAllUses . fragmentSelection)- refName- fragments--fragmentsConditionTypeChecking :: BaseValidator ()-fragmentsConditionTypeChecking =- elems <$> askFragments- >>= traverse_ checkTypeExistence--checkTypeExistence :: Fragment -> BaseValidator ()-checkTypeExistence fr@Fragment {fragmentType, fragmentPosition} =- askSchema- >>= selectKnown (TypeNameRef fragmentType fragmentPosition)- >>= constraint OBJECT fr- >> pure ()--fragmentsCycleChecking :: BaseValidator ()-fragmentsCycleChecking = exploreSpreads >>= fragmentCycleChecking--exploreSpreads :: BaseValidator Graph-exploreSpreads = map exploreFragmentSpreads . elems <$> askFragments--exploreFragmentSpreads :: Fragment -> NodeEdges-exploreFragmentSpreads Fragment {fragmentName, fragmentSelection, fragmentPosition} =- (Ref fragmentName fragmentPosition, concatMap scanForSpread fragmentSelection)--scanForSpreadContent :: SelectionContent RAW -> [Node]-scanForSpreadContent SelectionField = []-scanForSpreadContent (SelectionSet selectionSet) =- concatMap scanForSpread selectionSet--scanForSpread :: Selection RAW -> [Node]-scanForSpread Selection {selectionContent} =- scanForSpreadContent selectionContent-scanForSpread (InlineFragment Fragment {fragmentSelection}) =- concatMap scanForSpread fragmentSelection-scanForSpread (Spread _ Ref {refName, refPosition}) =- [Ref refName refPosition]--type Node = Ref--type NodeEdges = (Node, [Node])--type Graph = [NodeEdges]--fragmentCycleChecking :: Graph -> BaseValidator ()-fragmentCycleChecking lib = traverse_ checkFragment lib- where- checkFragment (fragmentID, _) = checkForCycle lib fragmentID [fragmentID]--checkForCycle :: Graph -> Node -> [Node] -> BaseValidator Graph-checkForCycle lib parentNode history = case lookup parentNode lib of- Just node -> concat <$> traverse checkNode node- Nothing -> pure []- where- checkNode x = if x `elem` history then cycleError x else recurse x- recurse node = checkForCycle lib node $ history ++ [node]- cycleError n = failure $ cannotSpreadWithinItself (n : history)+selectFragmentType :: Fragment RAW -> FragmentValidator s (TypeDefinition IMPLEMENTABLE VALID)+selectFragmentType fr@Fragment {fragmentType, fragmentPosition} = do+ (schema :: Schema VALID) <- askSchema+ typeDef <- selectKnown (TypeNameRef fragmentType fragmentPosition) schema+ constraint IMPLEMENTABLE fr typeDef
+ src/Data/Morpheus/Validation/Query/FragmentPreconditions.hs view
@@ -0,0 +1,108 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.Validation.Query.FragmentPreconditions+ ( checkFragmentPreconditions,+ )+where++import Control.Applicative ((*>))+import Control.Monad ((>>=))+import Data.Foldable (concatMap)+import Data.Functor ((<$>), fmap)+-- MORPHEUS+import Data.Morpheus.Error.Fragment+ ( cannotSpreadWithinItself,+ )+import Data.Morpheus.Internal.Graph+ ( Edges,+ Graph,+ Node,+ cycleChecking,+ )+import Data.Morpheus.Internal.Utils+ ( Failure (..),+ elems,+ selectOr,+ )+import Data.Morpheus.Types.Internal.AST+ ( Fragment (..),+ Fragments,+ RAW,+ Ref (..),+ Selection (..),+ SelectionContent (..),+ SelectionSet,+ )+import Data.Morpheus.Types.Internal.Validation+ ( BaseValidator,+ askFragments,+ checkUnused,+ )+import Data.Semigroup ((<>))+import Prelude+ ( (.),+ )++checkUnusedFragments :: SelectionSet RAW -> BaseValidator ()+checkUnusedFragments selectionSet = do+ fragments <- askFragments+ checkUnused+ (usedFragments fragments (elems selectionSet))+ (elems fragments)++usedFragments :: Fragments RAW -> [Selection RAW] -> [Node]+usedFragments fragments = concatMap findAllUses+ where+ findUsesSelectionContent :: SelectionContent RAW -> [Node]+ findUsesSelectionContent (SelectionSet selectionSet) =+ concatMap findAllUses selectionSet+ findUsesSelectionContent SelectionField = []+ findAllUses :: Selection RAW -> [Node]+ findAllUses Selection {selectionContent} =+ findUsesSelectionContent selectionContent+ findAllUses (InlineFragment Fragment {fragmentSelection}) =+ concatMap findAllUses fragmentSelection+ findAllUses (Spread _ Ref {refName, refPosition}) =+ [Ref refName refPosition] <> searchInFragment+ where+ searchInFragment =+ selectOr+ []+ (concatMap findAllUses . fragmentSelection)+ refName+ fragments++checkFragmentPreconditions :: SelectionSet RAW -> BaseValidator ()+checkFragmentPreconditions selection =+ (exploreSpreads >>= cycleChecking (failure . cannotSpreadWithinItself))+ *> checkUnusedFragments selection++exploreSpreads :: BaseValidator Graph+exploreSpreads = fmap exploreFragmentSpreads . elems <$> askFragments++exploreFragmentSpreads :: Fragment RAW -> Edges+exploreFragmentSpreads Fragment {fragmentName, fragmentSelection, fragmentPosition} =+ (Ref fragmentName fragmentPosition, concatMap scanSpread fragmentSelection)++class ScanSpread a where+ scanSpread :: a -> [Node]++instance ScanSpread (Selection RAW) where+ scanSpread Selection {selectionContent} =+ scanSpread selectionContent+ scanSpread (InlineFragment Fragment {fragmentSelection}) =+ concatMap scanSpread fragmentSelection+ scanSpread (Spread _ Ref {refName, refPosition}) =+ [Ref refName refPosition]++instance ScanSpread (SelectionContent RAW) where+ scanSpread SelectionField = []+ scanSpread (SelectionSet selectionSet) =+ concatMap scanSpread selectionSet
src/Data/Morpheus/Validation/Query/Selection.hs view
@@ -6,14 +6,19 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Validation.Query.Selection ( validateOperation,+ vaidateFragmentSelection, ) where --- MORPHEUS+import Control.Applicative ((<*>), pure)+import Data.Foldable (null)+import Data.Functor ((<$>), fmap)+import Data.List (filter)+import Data.Maybe (Maybe (..), maybe) import Data.Morpheus.Error.Selection ( hasNoSubfields, subfieldsNotSelected,@@ -29,11 +34,11 @@ ( Arguments, DirectiveLocation (FIELD, FRAGMENT_SPREAD, INLINE_FRAGMENT, MUTATION, QUERY, SUBSCRIPTION), Directives,- FieldDefinition,+ FieldDefinition (fieldType), FieldName, FieldsDefinition, Fragment (..),- GQLError (..),+ IMPLEMENTABLE, OUT, Operation (..), OperationType (..),@@ -45,51 +50,55 @@ TRUE, TypeContent (..), TypeDefinition (..),- TypeName,+ UnionTag (..), VALID,- getOperationDataType,+ ValidationError (..), isEntNode,- msg,+ msgValidation,+ possibleTypes,+ toCategory,+ typed,+ withPosition, ) import Data.Morpheus.Types.Internal.AST.MergeSet ( concatTraverse, ) import Data.Morpheus.Types.Internal.Validation- ( SelectionValidator,- askFieldType,+ ( FragmentValidator,+ SelectionValidator, askSchema,+ askType,+ getOperationType, selectKnown, withScope, )+import Data.Morpheus.Validation.Internal.Arguments+ ( validateFieldArguments,+ ) import Data.Morpheus.Validation.Internal.Directive ( shouldIncludeSelection, validateDirectives, )-import Data.Morpheus.Validation.Query.Arguments- ( validateFieldArguments,- ) import Data.Morpheus.Validation.Query.Fragment- ( castFragmentType,- resolveSpread,+ ( ResolveFragment (..),+ resolveValidFragment,+ selectFragmentType,+ validateFragment, ) import Data.Morpheus.Validation.Query.UnionSelection- ( validateUnionSelection,+ ( validateInterfaceSelection,+ validateUnionSelection, ) import Data.Semigroup ((<>))--type TypeDef = (TypeName, FieldsDefinition OUT)--getOperationObject ::- Operation a -> SelectionValidator (TypeName, FieldsDefinition OUT)-getOperationObject operation = do- dt <- askSchema >>= getOperationDataType operation- case dt of- TypeDefinition {typeContent = DataObject {objectFields}, typeName} -> pure (typeName, objectFields)- TypeDefinition {typeName} ->- failure $- "Type Mismatch: operation \""- <> msg typeName- <> "\" must be an Object"+import Prelude+ ( ($),+ (&&),+ (.),+ Eq (..),+ const,+ not,+ otherwise,+ ) selectionsWitoutTypename :: SelectionSet VALID -> [Selection VALID] selectionsWitoutTypename = filter (("__typename" /=) . keyOf) . elems@@ -97,21 +106,18 @@ singleTopLevelSelection :: Operation RAW -> SelectionSet VALID -> SelectionValidator () singleTopLevelSelection Operation {operationType = Subscription, operationName} selSet = case selectionsWitoutTypename selSet of- (_ : xs) | not (null xs) -> failure $ map (singleTopLevelSelectionError operationName) xs+ (_ : xs) | not (null xs) -> failure $ fmap (singleTopLevelSelectionError operationName) xs _ -> pure () singleTopLevelSelection _ _ = pure () -singleTopLevelSelectionError :: Maybe FieldName -> Selection VALID -> GQLError+singleTopLevelSelectionError :: Maybe FieldName -> Selection VALID -> ValidationError singleTopLevelSelectionError name Selection {selectionPosition} =- GQLError- { message =- subscriptionName- <> " must select "- <> "only one top level field.",- locations = [selectionPosition]- }+ withPosition (Just selectionPosition) $+ subscriptionName+ <> " must select "+ <> "only one top level field." where- subscriptionName = maybe "Anonymous Subscription" (("Subscription " <>) . msg) name+ subscriptionName = maybe "Anonymous Subscription" (("Subscription " <>) . msgValidation) name validateOperation :: Operation RAW ->@@ -125,8 +131,8 @@ .. } = do- typeDef <- getOperationObject rawOperation- selection <- validateSelectionSet typeDef operationSelection+ typeDef <- getOperationType rawOperation+ selection <- validateSelectionSet (toCategory typeDef) operationSelection singleTopLevelSelection rawOperation selection directives <- validateDirectives@@ -150,8 +156,8 @@ processSelectionDirectives :: DirectiveLocation -> Directives RAW ->- (Directives VALID -> SelectionValidator (SelectionSet VALID)) ->- SelectionValidator (SelectionSet VALID)+ (Directives VALID -> FragmentValidator s (SelectionSet VALID)) ->+ FragmentValidator s (SelectionSet VALID) processSelectionDirectives location rawDirectives sel = do directives <- validateDirectives location rawDirectives include <- shouldIncludeSelection directives@@ -161,13 +167,27 @@ then selection else empty +vaidateFragmentSelection :: (ResolveFragment s) => Fragment RAW -> FragmentValidator s (SelectionSet VALID)+vaidateFragmentSelection f@Fragment {fragmentSelection} = do+ typeDef <- selectFragmentType f+ validateSelectionSet typeDef fragmentSelection++getFields :: TypeDefinition IMPLEMENTABLE s -> FieldsDefinition OUT s+getFields TypeDefinition {typeContent = DataObject {objectFields}} = objectFields+getFields TypeDefinition {typeContent = DataInterface fields} = fields+ validateSelectionSet ::- TypeDef -> SelectionSet RAW -> SelectionValidator (SelectionSet VALID)-validateSelectionSet dataType@(typeName, fieldsDef) =+ forall s.+ ( ResolveFragment s+ ) =>+ TypeDefinition IMPLEMENTABLE VALID ->+ SelectionSet RAW ->+ FragmentValidator s (SelectionSet VALID)+validateSelectionSet typeDef = concatTraverse validateSelection where -- validate single selection: InlineFragments and Spreads will Be resolved and included in SelectionSet- validateSelection :: Selection RAW -> SelectionValidator (SelectionSet VALID)+ validateSelection :: Selection RAW -> FragmentValidator s (SelectionSet VALID) validateSelection sel@Selection { selectionName,@@ -177,7 +197,7 @@ selectionDirectives } = withScope- typeName+ typeDef currentSelectionRef $ processSelectionDirectives FIELD@@ -185,19 +205,14 @@ (`validateSelectionContent` selectionContent) where currentSelectionRef = Ref selectionName selectionPosition- commonValidation :: SelectionValidator (TypeDefinition OUT, Arguments VALID)+ commonValidation :: FragmentValidator s (TypeDefinition OUT VALID, Arguments VALID) commonValidation = do- (fieldDef :: FieldDefinition OUT) <- selectKnown (Ref selectionName selectionPosition) fieldsDef- -- validate field Argument ------ arguments <-- validateFieldArguments- fieldDef- selectionArguments- -- check field Type existence ------ (typeDef :: TypeDefinition OUT) <- askFieldType fieldDef- pure (typeDef, arguments)+ fieldDef <- selectKnown (Ref selectionName selectionPosition) (getFields typeDef)+ (,)+ <$> askType (typed fieldType fieldDef)+ <*> validateFieldArguments fieldDef selectionArguments ------------------------------------------------------------------------------------ validateSelectionContent :: Directives VALID -> SelectionContent RAW -> SelectionValidator (SelectionSet VALID)+ validateSelectionContent :: Directives VALID -> SelectionContent RAW -> FragmentValidator s (SelectionSet VALID) validateSelectionContent directives SelectionField | null selectionArguments && selectionName == "__typename" = pure $ singleton $@@ -208,73 +223,90 @@ } | otherwise = do (datatype, validArgs) <- commonValidation- isLeaf datatype- pure $ singleton $- sel- { selectionArguments = validArgs,- selectionDirectives = directives,- selectionContent = SelectionField- }- where- ------------------------------------------------------------- isLeaf :: TypeDefinition OUT -> SelectionValidator ()- isLeaf TypeDefinition {typeName = typename, typeContent}- | isEntNode typeContent = pure ()- | otherwise =- failure $- subfieldsNotSelected selectionName typename selectionPosition+ selContent <-+ validateContentLeaf currentSelectionRef datatype+ pure $+ singleton+ ( sel+ { selectionArguments = validArgs,+ selectionDirectives = directives,+ selectionContent = selContent+ }+ ) ----- SelectionSet validateSelectionContent directives (SelectionSet rawSelectionSet) = do- (TypeDefinition {typeName = name, typeContent}, validArgs) <- commonValidation- selContent <- withScope name currentSelectionRef $ validateByTypeContent name typeContent+ (tyDef, validArgs) <- commonValidation+ selContent <- validateByTypeContent tyDef currentSelectionRef rawSelectionSet pure $ singleton $ sel { selectionArguments = validArgs, selectionDirectives = directives, selectionContent = selContent }- where- validateByTypeContent :: TypeName -> TypeContent TRUE OUT -> SelectionValidator (SelectionContent VALID)- -- Validate UnionSelection- validateByTypeContent _ DataUnion {unionMembers} =- validateUnionSelection- validateSelectionSet- rawSelectionSet- unionMembers- -- Validate Regular selection set- validateByTypeContent typename DataObject {objectFields} =- SelectionSet- <$> validateSelectionSet- (typename, objectFields)- rawSelectionSet- validateByTypeContent typename DataInterface {interfaceFields} =- SelectionSet- <$> validateSelectionSet- (typename, interfaceFields)- rawSelectionSet- validateByTypeContent typename _ =- failure $- hasNoSubfields- (Ref selectionName selectionPosition)- typename- validateSelection (Spread dirs ref) =- processSelectionDirectives FRAGMENT_SPREAD dirs- $ const- -- TODO: add directives to selection- $ resolveSpread [typeName] ref- >>= validateFragment+ validateSelection (Spread dirs ref) = do+ types <- possibleTypes typeDef <$> askSchema+ processSelectionDirectives+ FRAGMENT_SPREAD+ dirs+ (const $ unionTagSelection <$> resolveValidFragment vaidateFragmentSelection types ref) validateSelection ( InlineFragment fragment@Fragment- { fragmentDirectives,- fragmentPosition+ { fragmentDirectives }- ) =- processSelectionDirectives INLINE_FRAGMENT fragmentDirectives- $ const- -- TODO: add directives to selection- $ castFragmentType Nothing fragmentPosition [typeName] fragment- >>= validateFragment- --------------------------------------------------------------------------------- validateFragment Fragment {fragmentSelection} = validateSelectionSet dataType fragmentSelection+ ) = do+ types <- possibleTypes typeDef <$> askSchema+ processSelectionDirectives INLINE_FRAGMENT fragmentDirectives $+ const (validate types fragment)+ validate types = fmap fragmentSelection . validateFragment vaidateFragmentSelection types++validateContentLeaf ::+ Ref ->+ TypeDefinition OUT VALID ->+ FragmentValidator s' (SelectionContent s)+validateContentLeaf+ (Ref selectionName selectionPosition)+ TypeDefinition {typeName, typeContent}+ | isEntNode typeContent = pure SelectionField+ | otherwise =+ failure $ subfieldsNotSelected selectionName typeName selectionPosition++validateByTypeContent ::+ forall s.+ (ResolveFragment s) =>+ TypeDefinition OUT VALID ->+ Ref ->+ SelectionSet RAW ->+ FragmentValidator s (SelectionContent VALID)+validateByTypeContent+ typeDef@TypeDefinition {typeContent, ..}+ currentSelectionRef =+ withScope typeDef currentSelectionRef+ . __validate typeContent+ where+ __validate ::+ TypeContent TRUE OUT VALID ->+ SelectionSet RAW ->+ FragmentValidator s (SelectionContent VALID)+ -- Validate UnionSelection+ __validate DataUnion {unionMembers} =+ validateUnionSelection+ vaidateFragmentSelection+ validateSelectionSet+ unionMembers+ -- Validate Regular selection set+ __validate DataObject {..} =+ fmap SelectionSet . validateSelectionSet (TypeDefinition {typeContent = DataObject {..}, ..})+ -- TODO: Union Like Validation+ __validate DataInterface {..} =+ validateInterfaceSelection+ vaidateFragmentSelection+ validateSelectionSet+ (TypeDefinition {typeContent = DataInterface {..}, ..})+ __validate _ =+ const+ $ failure+ $ hasNoSubfields+ currentSelectionRef+ typeDef
src/Data/Morpheus/Validation/Query/UnionSelection.hs view
@@ -3,94 +3,119 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE TupleSections #-} module Data.Morpheus.Validation.Query.UnionSelection ( validateUnionSelection,+ validateInterfaceSelection, ) where import Control.Monad ((>=>))+import Data.Maybe (isJust) -- MORPHEUS-import Data.Morpheus.Error.Selection (unknownSelectionField) import Data.Morpheus.Internal.Utils- ( Failure (..),- elems,+ ( elems, empty, fromElems,- selectOr,- singleton, ) import Data.Morpheus.Types.Internal.AST ( DataUnion,- FieldsDefinition, Fragment (..),- OUT,+ IMPLEMENTABLE, RAW,- Ref (..), Selection (..), SelectionContent (..), SelectionSet, SelectionSet,+ TypeContent (..),+ TypeDefinition (..), TypeName,- UnionMember (..), UnionTag (..), VALID,+ mkType,+ toCategory, ) import qualified Data.Morpheus.Types.Internal.AST.MergeSet as MS ( join, ) import Data.Morpheus.Types.Internal.Validation- ( Scope (..),- SelectionValidator,+ ( FragmentValidator,+ Scope (..),+ askInterfaceTypes, askTypeMember,- asks,+ asksScope, ) import Data.Morpheus.Validation.Query.Fragment- ( castFragmentType,- resolveSpread,+ ( ResolveFragment (resolveValidFragment),+ castFragmentType, ) -type TypeDef = (TypeName, FieldsDefinition OUT)---- returns all Fragments used in Union-exploreUnionFragments ::- [UnionMember OUT] ->+-- returns all Fragments used for Possible Types+splitFragment ::+ (ResolveFragment s) =>+ ( Fragment RAW ->+ FragmentValidator s (SelectionSet VALID)+ ) ->+ [TypeDefinition IMPLEMENTABLE VALID] -> Selection RAW ->- SelectionValidator [Fragment]-exploreUnionFragments unionTags = splitFrag- where- packFragment fragment = [fragment]- splitFrag ::- Selection RAW -> SelectionValidator [Fragment]- splitFrag (Spread _ ref) = packFragment <$> resolveSpread (map memberName unionTags) ref- splitFrag Selection {selectionName = "__typename", selectionContent = SelectionField} = pure []- splitFrag Selection {selectionName, selectionPosition} = do- typeName <- asks typename- failure $ unknownSelectionField typeName (Ref selectionName selectionPosition)- splitFrag (InlineFragment fragment) =- packFragment- <$> castFragmentType Nothing (fragmentPosition fragment) (map memberName unionTags) fragment+ FragmentValidator s ([UnionTag], [Selection RAW])+splitFragment _ _ x@Selection {} = pure ([], [x])+splitFragment f types (Spread _ ref) = pureFirst <$> resolveValidFragment f (typeName <$> types) ref+splitFragment f types (InlineFragment fragment@Fragment {fragmentType}) =+ pureFirst . UnionTag fragmentType+ <$> ( castFragmentType Nothing (fragmentPosition fragment) (typeName <$> types) fragment+ >>= f+ ) +pureFirst :: a1 -> ([a1], [a2])+pureFirst x = ([x], [])++joinExploredSelection :: [([UnionTag], [Selection RAW])] -> FragmentValidator s ([UnionTag], SelectionSet RAW)+joinExploredSelection i = do+ let (x, y) = unzip i+ let x' = concat x+ let y' = concat y+ (x',) <$> fromElems y'++exploreFragments ::+ (ResolveFragment s) =>+ ( Fragment RAW ->+ FragmentValidator s (SelectionSet VALID)+ ) ->+ [TypeDefinition IMPLEMENTABLE VALID] ->+ SelectionSet RAW ->+ FragmentValidator s ([UnionTag], SelectionSet RAW)+exploreFragments validateFragment types selectionSet =+ traverse (splitFragment validateFragment types) (elems selectionSet)+ >>= joinExploredSelection+ -- sorts Fragment by contitional Types -- [ -- ( Type for Tag User , [ Fragment for User] ) -- ( Type for Tag Product , [ Fragment for Product] ) -- ] tagUnionFragments ::- [TypeDef] -> [Fragment] -> [(TypeDef, [Fragment])]+ [TypeDefinition IMPLEMENTABLE VALID] ->+ [UnionTag] ->+ [(TypeName, [SelectionSet VALID])] tagUnionFragments types fragments =- filter notEmpty $- map categorizeType types+ filter notEmpty (map categorizeType types) where notEmpty = not . null . snd- categorizeType :: (TypeName, FieldsDefinition OUT) -> (TypeDef, [Fragment])- categorizeType datatype = (datatype, filter matches fragments)+ categorizeType ::+ TypeDefinition IMPLEMENTABLE VALID ->+ (TypeName, [SelectionSet VALID])+ categorizeType datatype = (typeName datatype, unionTagSelection <$> filter matches fragments) where- matches fragment = fragmentType fragment == fst datatype+ matches = (`elem` subTypes datatype) . unionTagName +subTypes :: TypeDefinition a s -> [TypeName]+subTypes TypeDefinition {typeName, typeContent = DataObject {objectImplements}} =+ typeName : objectImplements+subTypes TypeDefinition {typeName} = [typeName]+ {- - all Variable and Fragment references will be: resolved and validated - unionTypes: will be clustered under type names@@ -102,29 +127,58 @@ UnionTag "T2" {<SelectionB>,<SelectionC>} ] -}-validateCluster ::- (TypeDef -> SelectionSet RAW -> SelectionValidator (SelectionSet VALID)) ->- SelectionSet RAW ->- [(TypeDef, [Fragment])] ->- SelectionValidator (SelectionContent VALID)-validateCluster validator __typename = traverse _validateCluster >=> fmap UnionSelection . fromElems+joinClusters ::+ forall s.+ SelectionSet VALID ->+ [(TypeName, [SelectionSet VALID])] ->+ FragmentValidator s (SelectionContent VALID)+joinClusters selSet =+ traverse joinCluster+ >=> fmap UnionSelection . fromElems where- _validateCluster :: (TypeDef, [Fragment]) -> SelectionValidator UnionTag- _validateCluster (unionType, fragmets) = do- fragmentSelections <- MS.join (__typename : map fragmentSelection fragmets)- UnionTag (fst unionType) <$> validator unionType fragmentSelections+ joinCluster (typeName, fragmets) = UnionTag typeName <$> MS.join (selSet : fragmets) +validateInterfaceSelection ::+ ResolveFragment s =>+ (Fragment RAW -> FragmentValidator s (SelectionSet VALID)) ->+ (TypeDefinition IMPLEMENTABLE VALID -> SelectionSet RAW -> FragmentValidator s (SelectionSet VALID)) ->+ TypeDefinition IMPLEMENTABLE VALID ->+ SelectionSet RAW ->+ FragmentValidator s (SelectionContent VALID)+validateInterfaceSelection+ validateFragment+ validate+ typeDef+ inputSelectionSet = do+ possibleTypes <- askInterfaceTypes typeDef+ (spreads, selectionSet) <- exploreFragments validateFragment possibleTypes inputSelectionSet+ validSelectionSet <- validate typeDef selectionSet+ let categories = tagUnionFragments possibleTypes spreads+ if null categories+ then pure (SelectionSet validSelectionSet)+ else joinClusters validSelectionSet (insertDefault (typeName typeDef) categories)++insertDefault :: TypeName -> [(TypeName, [a])] -> [(TypeName, [a])]+insertDefault interfaceName categories+ | isJust (lookup interfaceName categories) = categories+ | otherwise = (interfaceName, []) : categories++mkUnionRootType :: FragmentValidator s (TypeDefinition IMPLEMENTABLE VALID)+mkUnionRootType = (`mkType` DataObject [] empty) <$> asksScope currentTypeName+ validateUnionSelection ::- (TypeDef -> SelectionSet RAW -> SelectionValidator (SelectionSet VALID)) ->+ ResolveFragment s =>+ (Fragment RAW -> FragmentValidator s (SelectionSet VALID)) ->+ (TypeDefinition IMPLEMENTABLE VALID -> SelectionSet RAW -> FragmentValidator s (SelectionSet VALID)) ->+ DataUnion VALID -> SelectionSet RAW ->- DataUnion ->- SelectionValidator (SelectionContent VALID)-validateUnionSelection validate selectionSet members = do- let (__typename :: SelectionSet RAW) = selectOr empty singleton "__typename" selectionSet+ FragmentValidator s (SelectionContent VALID)+validateUnionSelection validateFragment validate members selectionSet = do -- get union Types defined in GraphQL schema -> (union Tag, union Selection set) -- [("User", FieldsDefinition { ... }), ("Product", FieldsDefinition { ...- unionTypes <- traverse askTypeMember members+ unionTypes <- traverse (fmap toCategory . askTypeMember) members -- find all Fragments used in Selection- spreads <- concat <$> traverse (exploreUnionFragments members) (elems selectionSet)- let categories = tagUnionFragments unionTypes spreads- validateCluster validate __typename categories+ (spreads, selSet) <- exploreFragments validateFragment unionTypes selectionSet+ typeDef <- mkUnionRootType+ validSelection <- validate typeDef selSet+ joinClusters validSelection (tagUnionFragments unionTypes spreads)
src/Data/Morpheus/Validation/Query/Validation.hs view
@@ -3,7 +3,6 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeOperators #-} module Data.Morpheus.Validation.Query.Validation ( validateRequest,@@ -11,13 +10,15 @@ where import Data.HashMap.Lazy (fromList)+import Data.Morpheus.Internal.Utils (empty) import Data.Morpheus.Types.Internal.AST ( GQLQuery (..), Operation (..), Schema (..),+ TypeKind (..), VALID,- VALIDATION_MODE, )+import Data.Morpheus.Types.Internal.Config (Config (..)) import Data.Morpheus.Types.Internal.Resolving ( Eventless, )@@ -31,21 +32,25 @@ import Data.Morpheus.Validation.Query.Fragment ( validateFragments, )+import Data.Morpheus.Validation.Query.FragmentPreconditions+ ( checkFragmentPreconditions,+ ) import Data.Morpheus.Validation.Query.Selection- ( validateOperation,+ ( vaidateFragmentSelection,+ validateOperation, ) import Data.Morpheus.Validation.Query.Variable ( resolveOperationVariables, ) validateRequest ::- Schema ->- VALIDATION_MODE ->+ Config ->+ Schema VALID -> GQLQuery -> Eventless (Operation VALID) validateRequest+ config schema- validationMode GQLQuery { fragments, inputVariables,@@ -57,20 +62,55 @@ } } = do- variables <- runValidator validateHelpers (ctx ())- runValidator (validateOperation operation) (ctx variables)+ variables <-+ runValidator+ validateHelpers+ config+ schema+ scope+ ( OperationContext+ { selection,+ fragments,+ variables = empty+ }+ )+ validFragments <-+ runValidator+ (validateFragments vaidateFragmentSelection)+ config+ schema+ scope+ ( OperationContext+ { selection,+ fragments,+ variables+ }+ )+ runValidator+ (validateOperation operation)+ config+ schema+ scope+ ( OperationContext+ { selection,+ fragments = validFragments,+ variables+ }+ ) where- ctx variables =- OperationContext- { schema,- fragments,- scope = Scope {typename = "Root", position = operationPosition, kind = SELECTION},- selection = CurrentSelection {operationName, selectionName = "Root"},- variables+ scope =+ Scope+ { kind = SELECTION,+ currentTypeName = "Root",+ currentTypeKind = KindObject Nothing,+ currentTypeWrappers = [],+ fieldname = "Root",+ position = Just operationPosition }+ selection = CurrentSelection {operationName} validateHelpers =- validateFragments operationSelection+ checkFragmentPreconditions operationSelection *> resolveOperationVariables+ config (fromList inputVariables)- validationMode operation
src/Data/Morpheus/Validation/Query/Variable.hs view
@@ -2,17 +2,21 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Validation.Query.Variable ( resolveOperationVariables, ) where +import Control.Applicative ((*>), pure)+import Control.Monad (Monad ((>>=)))+import Data.Foldable (concat, concatMap)+import Data.Functor ((<$>), fmap) import qualified Data.HashMap.Lazy as M ( lookup, )-import Data.Maybe (maybe)---- MORPHEUS+import Data.Maybe (Maybe (..), maybe) import Data.Morpheus.Error.Variable (uninitializedVariable) import Data.Morpheus.Internal.Utils ( Failure (..),@@ -37,7 +41,6 @@ TypeNameRef (..), TypeRef (..), VALID,- VALIDATION_MODE (..), ValidValue, Value (..), Variable (..),@@ -46,6 +49,10 @@ Variables, isNullable, )+import Data.Morpheus.Types.Internal.Config+ ( Config (..),+ VALIDATION_MODE (..),+ ) import Data.Morpheus.Types.Internal.Validation ( BaseValidator, Constraint (..),@@ -59,9 +66,21 @@ withPosition, ) import Data.Morpheus.Validation.Internal.Value- ( validateInput,+ ( validateInputByType, ) import Data.Semigroup ((<>))+import Data.Traversable+ ( traverse,+ )+import Prelude+ ( ($),+ (&&),+ (.),+ Bool (..),+ Eq (..),+ not,+ otherwise,+ ) class ExploreRefs a where exploreRefs :: a -> [Ref]@@ -105,13 +124,13 @@ ) resolveOperationVariables ::+ Config -> Variables ->- VALIDATION_MODE -> Operation RAW -> BaseValidator (VariableDefinitions VALID) resolveOperationVariables+ Config {validationMode} root- validationMode Operation { operationSelection, operationArguments@@ -134,14 +153,14 @@ validationMode var@Variable { variableName,- variableType,+ variableType = variableType@TypeRef {typeWrappers, typeConName}, variablePosition, variableValue = DefaultValue defaultValue } = withPosition variablePosition $ toVariable <$> ( askSchema- >>= selectKnown (TypeNameRef (typeConName variableType) variablePosition)+ >>= selectKnown (TypeNameRef typeConName variablePosition) >>= constraint INPUT var >>= checkType getVariable defaultValue )@@ -154,11 +173,11 @@ checkType :: Maybe ResolvedValue -> DefaultValue ->- TypeDefinition IN ->+ TypeDefinition IN VALID -> BaseValidator ValidValue checkType (Just variable) Nothing varType = validator varType False variable checkType (Just variable) (Just defValue) varType =- validator varType True defValue >> validator varType False variable+ validator varType True defValue *> validator varType False variable checkType Nothing (Just defValue) varType = validator varType True defValue checkType Nothing Nothing varType | validationMode /= WITHOUT_VARIABLES && not (isNullable variableType) =@@ -169,10 +188,12 @@ returnNull = maybe (pure Null) (validator varType False) (M.lookup variableName bodyVariables) ------------------------------------------------------------------------------------------------ validator :: TypeDefinition IN -> Bool -> ResolvedValue -> BaseValidator ValidValue- validator varType isDefaultValue varValue =- startInput (SourceVariable var isDefaultValue) $- validateInput- (typeWrappers variableType)- varType- (ObjectEntry variableName varValue)+ validator :: TypeDefinition IN VALID -> Bool -> ResolvedValue -> BaseValidator ValidValue+ validator varTypeDef isDefaultValue varValue =+ startInput+ (SourceVariable var isDefaultValue)+ ( validateInputByType+ typeWrappers+ varTypeDef+ varValue+ )
test/Lib.hs view
@@ -1,9 +1,12 @@+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE NoImplicitPrelude #-} module Lib ( getGQLBody,- getResponseBody,+ expectedResponse, getCases, maybeVariables, readSource,@@ -11,30 +14,69 @@ FileUrl (..), CaseTree (..), toString,+ getRequest,+ assertValidSchema,+ getSchema,+ getResolvers, ) where -import Control.Applicative ((<|>))+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.Maybe (fromMaybe)-import Data.Morpheus.Types.Internal.AST (FieldName (..))+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 -> String-path name = "test/" ++ unpack name+path :: Text -> FilePath+path name = "test/" <> unpack name -gqlLib :: Text -> String-gqlLib x = path x ++ "/query.gql"+gqlLib :: Text -> FilePath+gqlLib x = path x <> "/query.gql" -resLib :: Text -> String-resLib x = path x ++ "/response.json"+resLib :: Text -> FilePath+resLib x = path x <> "/response.json" data FileUrl = FileUrl { filePath :: [FilePath],@@ -44,7 +86,7 @@ data CaseTree = CaseTree { caseUrl :: FileUrl,- children :: Either [String] [CaseTree]+ children :: Either [FilePath] [CaseTree] } deriving (Show) @@ -65,12 +107,12 @@ deepScan = shouldScan . FileUrl [] where shouldScan :: FileUrl -> IO CaseTree- shouldScan caseUrl@FileUrl {..} = do+ shouldScan caseUrl = do children <- prefixed caseUrl pure CaseTree {..} isDirectory :: FileUrl -> IO Bool isDirectory = doesDirectoryExist . toString- prefixed :: FileUrl -> IO (Either [String] [CaseTree])+ prefixed :: FileUrl -> IO (Either [FilePath] [CaseTree]) prefixed p = do dir <- isDirectory p if dir@@ -83,13 +125,53 @@ else pure $ Left [] maybeVariables :: FieldName -> IO (Maybe Value)-maybeVariables (FieldName x) = decode <$> (L.readFile (path x ++ "/variables.json") <|> return "{}")+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 => String -> IO [a]-getCases dir = fromMaybe [] . decode <$> L.readFile ("test/" ++ dir ++ "/cases.json")+getCases :: FromJSON a => FilePath -> IO [a]+getCases dir = fromMaybe [] . decode <$> L.readFile ("test/" <> dir <> "/cases.json") -getResponseBody :: FieldName -> IO Value-getResponseBody (FieldName p) = fromMaybe Null . decode <$> L.readFile (resLib p)+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/Schema.hs
@@ -1,93 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}--module Schema- ( testSchema,- )-where--import Control.Monad ((<=<))-import Data.Aeson ((.:), (.=), FromJSON (..), ToJSON (..), Value (..), eitherDecode, encode, object)-import qualified Data.ByteString.Lazy as L (readFile)-import qualified Data.ByteString.Lazy.Char8 as LB (unpack)-import Data.ByteString.Lazy.Char8 (ByteString)-import Data.Either (either)-import Data.Morpheus.Core (parseFullGQLDocument, validateSchema)-import Data.Morpheus.Types.Internal.AST- ( GQLErrors,- Schema,- )-import Data.Morpheus.Types.Internal.Resolving- ( Eventless,- Result (..),- )-import Data.Semigroup ((<>))-import Data.Text (pack)-import GHC.Generics (Generic)-import Lib- ( CaseTree (..),- FileUrl (..),- scanSchemaTests,- toString,- )-import Test.Tasty (TestTree, testGroup)-import Test.Tasty.HUnit (assertFailure, testCase)--readSource :: String -> IO ByteString-readSource = L.readFile--readSchema :: String -> IO (Eventless Schema)-readSchema = fmap (validateSchema <=< parseFullGQLDocument) . readSource . (<> "/schema.gql")--readResponse :: String -> IO Response-readResponse = fmap (either AesonError id . eitherDecode) . readSource . (<> "/response.json")--data Response- = OK- | Errors {errors :: GQLErrors}- | AesonError String- deriving (Generic)--instance FromJSON Response where- parseJSON (Object v) =- Errors <$> v .: "errors"- parseJSON (String "OK") = pure OK- parseJSON v = pure $ AesonError (show v)--instance ToJSON Response where- toJSON OK = String "OK"- toJSON (Errors err) = object ["errors" .= toJSON err]- toJSON (AesonError err) = String (pack err)--toTests :: CaseTree -> TestTree-toTests CaseTree {caseUrl, children = Left {}} = schemaCase caseUrl-toTests CaseTree {caseUrl = FileUrl {fileName}, children = Right children} =- testGroup- fileName- (map toTests children)--testSchema :: IO TestTree-testSchema = toTests <$> scanSchemaTests "test/schema"--schemaCase :: FileUrl -> TestTree-schemaCase url = testCase (fileName url) $ do- schema <- readSchema (toString url)- expected <- readResponse (toString url)- assertion expected schema--assertion :: Response -> Eventless Schema -> IO ()-assertion OK Success {} = return ()-assertion Errors {errors = err} Failure {errors}- | err == errors =- pure- ()-assertion expected Success {} =- assertFailure $- LB.unpack- ("expected: \n " <> encode expected <> " \n but got: \n OK")-assertion expected Failure {errors} =- assertFailure $- LB.unpack- ("expected: \n " <> encode expected <> " \n but got: \n " <> encode (Errors errors))
test/Spec.hs view
@@ -1,154 +1,43 @@-{-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE NoImplicitPrelude #-} module Main ( main, ) where -import qualified Data.Aeson as A-import Data.Aeson (decode, encode)-import Data.Functor.Identity (Identity (..))-import Data.Morpheus.Core (runApi)-import Data.Morpheus.Internal.Utils- ( fromElems,- )-import Data.Morpheus.QuasiQuoter (dsl)-import Data.Morpheus.Types.IO- ( GQLRequest (..),- )-import Data.Morpheus.Types.Internal.AST- ( FieldName,- Schema,- VALID,- Value (..),- msg,- )-import Data.Morpheus.Types.Internal.Resolving- ( ResModel,- Resolver,- ResponseStream,- Result (..),- ResultT (..),- RootResModel (..),- WithOperation,- mkList,- mkNull,- mkObject,- mkString,+import Test.Tasty+ ( defaultMain,+ testGroup, )-import Data.Semigroup ((<>))-import qualified Data.Text.Lazy as LT (toStrict)-import Data.Text.Lazy.Encoding (decodeUtf8)-import Lib- ( getGQLBody,- getResponseBody,- maybeVariables,+import Utils.Api+ ( apiTest, )-import Schema+import Utils.Schema ( testSchema, )-import Test.Tasty- ( TestTree,- defaultMain,- testGroup,- )-import Test.Tasty.HUnit- ( assertFailure,- testCase,+import Prelude+ ( ($),+ IO, ) -getSchema :: Monad m => ResponseStream e m Schema-getSchema =- fromElems- [dsl|- - type Query {- deity(name: String): Deity!- }-- interface Character {- name : String- }-- interface Supernatural {- power: [String!]!- }-- type Hero implements Character {- name: String- }-- type Deity implements Character Supernatural {- name: String!- power: [String!]!- }-|]--resolver :: Monad m => RootResModel e m-resolver =- RootResModel- { query =- pure $- mkObject- "Query"- [("deity", resolveDeity)],- mutation = pure mkNull,- subscription = pure mkNull- }--resolveDeity :: (WithOperation o, Monad m) => Resolver o e m (ResModel o e m)-resolveDeity =- pure $- mkObject- "Deity"- [ ("name", pure $ mkString "Morpheus"),- ("power", pure $ mkList [mkString "Shapeshifting"])- ]- main :: IO () main = do schema <- testSchema- defaultMain- $ testGroup+ defaultMain $+ testGroup "core tests"- $ map- (uncurry basicTest)- [ ("basic Test", "api/simple"),- ("test interface", "api/interface")+ [ schema,+ apiTest "api/deity" ["simple", "interface"],+ apiTest+ "api/validation/fragment"+ [ "on-type",+ "on-interface",+ "on-interface-inline",+ "on-union-type",+ "fail-unknown-field-on-interface",+ "on-interface-type-casting",+ "on-interface-type-casting-inline",+ "on-interface-fail-without-casting"+ ] ]- <> [schema]--basicTest :: String -> FieldName -> TestTree-basicTest description path = testCase description $ do- actual <- simpleTest <$> getRequest path- expected <- expectedResponse path- assertion expected actual--simpleTest :: GQLRequest -> ResponseStream e Identity (Value VALID)-simpleTest request = do- schema <- getSchema- runApi schema resolver request--expectedResponse :: FieldName -> IO A.Value-expectedResponse = getResponseBody--assertion :: A.Value -> ResponseStream e Identity (Value VALID) -> IO ()-assertion expected (ResultT (Identity Success {result}))- | Just expected == decode (encode result) = return ()- | otherwise =- assertFailure $ show ("expected: \n " <> msg expected <> " \n but got: \n " <> msg result)-assertion _ (ResultT (Identity Failure {errors})) = assertFailure (show errors)--getRequest :: FieldName -> IO GQLRequest-getRequest path = do- queryBS <- LT.toStrict . decodeUtf8 <$> getGQLBody path- variables <- maybeVariables path- pure $- GQLRequest- { operationName = Nothing,- query = queryBS,- variables- }
+ test/Utils/Api.hs view
@@ -0,0 +1,77 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Utils.Api+ ( apiTest,+ assertion,+ )+where++import Control.Applicative (pure)+import qualified Data.Aeson as A+import Data.Aeson (decode, encode)+import qualified Data.ByteString.Lazy.Char8 as LB (unpack)+import Data.Functor ((<$>), fmap)+import Data.Functor.Identity (Identity (..))+import Data.Maybe (Maybe (..))+import Data.Morpheus.Core (defaultConfig, runApi)+import Data.Morpheus.Types.IO+import Data.Morpheus.Types.Internal.AST+ ( FieldName (..),+ VALID,+ Value (..),+ )+import Data.Morpheus.Types.Internal.Resolving+ ( ResponseStream,+ ResultT (..),+ )+import Data.Semigroup ((<>))+import Data.Text (unpack)+import Lib+ ( assertValidSchema,+ expectedResponse,+ getRequest,+ getResolvers,+ )+import Test.Tasty+ ( TestTree,+ testGroup,+ )+import Test.Tasty.HUnit+ ( assertFailure,+ testCase,+ )+import Prelude+ ( ($),+ (==),+ IO,+ otherwise,+ )++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)++apiTest :: FieldName -> [FieldName] -> TestTree+apiTest apiPath requestPath =+ testGroup (unpack $ readName apiPath) $+ fmap (testApiRequest apiPath) requestPath++testApiRequest ::+ FieldName ->+ FieldName ->+ TestTree+testApiRequest apiPath path = testCase (unpack $ readName path) $ do+ schema <- assertValidSchema apiPath+ resolvers <- getResolvers apiPath+ let fullPath = apiPath <> "/" <> path+ actual <- runApi schema resolvers defaultConfig <$> getRequest fullPath+ expected <- expectedResponse fullPath+ assertion expected actual
+ test/Utils/Schema.hs view
@@ -0,0 +1,106 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Utils.Schema+ ( testSchema,+ )+where++import Control.Applicative (pure)+import Data.Aeson ((.:), (.=), FromJSON (..), ToJSON (..), Value (..), eitherDecode, encode, object)+import qualified Data.ByteString.Lazy as L (readFile)+import qualified Data.ByteString.Lazy.Char8 as LB (unpack)+import Data.ByteString.Lazy.Char8 (ByteString)+import Data.Either (Either (..), either)+import Data.Functor ((<$>), fmap)+import Data.Morpheus.Core (parseFullGQLDocument)+import Data.Morpheus.Types.Internal.AST+ ( GQLErrors,+ Schema,+ VALID,+ )+import Data.Morpheus.Types.Internal.Resolving+ ( Eventless,+ Result (..),+ )+import Data.Semigroup ((<>))+import Data.Text (pack)+import GHC.Generics (Generic)+import Lib+ ( CaseTree (..),+ FileUrl (..),+ scanSchemaTests,+ toString,+ )+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (assertFailure, testCase)+import Prelude+ ( ($),+ (.),+ Eq (..),+ FilePath,+ IO,+ String,+ id,+ show,+ )++readSource :: FilePath -> IO ByteString+readSource = L.readFile++readSchema :: FilePath -> IO (Eventless (Schema VALID))+readSchema = fmap parseFullGQLDocument . readSource . (<> "/schema.gql")++readResponse :: FilePath -> IO Response+readResponse = fmap (either AesonError id . eitherDecode) . readSource . (<> "/response.json")++data Response+ = OK+ | Errors {errors :: GQLErrors}+ | AesonError String+ deriving (Generic)++instance FromJSON Response where+ parseJSON (Object v) =+ Errors <$> v .: "errors"+ parseJSON (String "OK") = pure OK+ parseJSON v = pure $ AesonError (show v)++instance ToJSON Response where+ toJSON OK = String "OK"+ toJSON (Errors err) = object ["errors" .= toJSON err]+ toJSON (AesonError err) = String (pack err)++toTests :: CaseTree -> TestTree+toTests CaseTree {caseUrl, children = Left {}} = schemaCase caseUrl+toTests CaseTree {caseUrl = FileUrl {fileName}, children = Right children} =+ testGroup+ fileName+ (fmap toTests children)++testSchema :: IO TestTree+testSchema = toTests <$> scanSchemaTests "test/schema"++schemaCase :: FileUrl -> TestTree+schemaCase url = testCase (fileName url) $ do+ schema <- readSchema (toString url)+ expected <- readResponse (toString url)+ assertion expected schema++assertion :: Response -> Eventless (Schema VALID) -> IO ()+assertion OK Success {} = pure ()+assertion Errors {errors = err} Failure {errors}+ | err == errors =+ pure+ ()+assertion expected Success {} =+ assertFailure $+ LB.unpack+ ("expected: \n " <> encode expected <> " \n but got: \n OK")+assertion expected Failure {errors} =+ assertFailure $+ LB.unpack+ ("expected: \n " <> encode expected <> " \n but got: \n " <> encode (Errors errors))
+ test/api/deity/interface/query.gql view
@@ -0,0 +1,79 @@+query Get__Type {+ interface: __type(name: "Character") {+ ...FullType+ }+ deity: __type(name: "Deity") {+ ...FullType+ }+}++fragment FullType on __Type {+ kind+ name+ fields(includeDeprecated: true) {+ name+ args {+ ...InputValue+ }+ type {+ ...TypeRef+ }+ isDeprecated+ deprecationReason+ }+ inputFields {+ ...InputValue+ }+ interfaces {+ ...TypeRef+ }+ enumValues(includeDeprecated: true) {+ name+ isDeprecated+ deprecationReason+ }+ possibleTypes {+ ...TypeRef+ }+}++fragment InputValue on __InputValue {+ name+ type {+ ...TypeRef+ }+ defaultValue+}++fragment TypeRef on __Type {+ kind+ name+ ofType {+ kind+ name+ ofType {+ kind+ name+ ofType {+ kind+ name+ ofType {+ kind+ name+ ofType {+ kind+ name+ ofType {+ kind+ name+ ofType {+ kind+ name+ }+ }+ }+ }+ }+ }+ }+}
+ test/api/deity/interface/response.json view
@@ -0,0 +1,67 @@+{+ "data": {+ "interface": {+ "kind": "INTERFACE",+ "name": "Character",+ "fields": [+ {+ "name": "name",+ "args": [],+ "type": { "kind": "SCALAR", "name": "String", "ofType": null },+ "isDeprecated": false,+ "deprecationReason": null+ }+ ],+ "inputFields": null,+ "interfaces": null,+ "enumValues": null,+ "possibleTypes": [+ { "kind": "OBJECT", "name": "Deity", "ofType": null },+ { "kind": "OBJECT", "name": "Hero", "ofType": null }+ ]+ },+ "deity": {+ "kind": "OBJECT",+ "name": "Deity",+ "fields": [+ {+ "name": "name",+ "args": [],+ "type": {+ "kind": "NON_NULL",+ "name": null,+ "ofType": { "kind": "SCALAR", "name": "String", "ofType": null }+ },+ "isDeprecated": false,+ "deprecationReason": null+ },+ {+ "name": "power",+ "args": [],+ "type": {+ "kind": "NON_NULL",+ "name": null,+ "ofType": {+ "kind": "LIST",+ "name": null,+ "ofType": {+ "kind": "NON_NULL",+ "name": null,+ "ofType": { "kind": "SCALAR", "name": "String", "ofType": null }+ }+ }+ },+ "isDeprecated": false,+ "deprecationReason": null+ }+ ],+ "inputFields": null,+ "interfaces": [+ { "kind": "INTERFACE", "name": "Character", "ofType": null },+ { "kind": "INTERFACE", "name": "Supernatural", "ofType": null }+ ],+ "enumValues": null,+ "possibleTypes": null+ }+ }+}
+ test/api/deity/resolvers.json view
@@ -0,0 +1,8 @@+{+ "query": {+ "deity": {+ "name": "Morpheus",+ "power": ["Shapeshifting"]+ }+ }+}
+ test/api/deity/schema.gql view
@@ -0,0 +1,20 @@+type Query {+ deity(name: String): Deity!+}++interface Character {+ name: String+}++interface Supernatural {+ power: [String!]!+}++type Hero implements Character {+ name: String+}++type Deity implements Character & Supernatural {+ name: String!+ power: [String!]!+}
+ test/api/deity/simple/query.gql view
@@ -0,0 +1,6 @@+{+ deity {+ name+ power+ }+}
+ test/api/deity/simple/response.json view
@@ -0,0 +1,8 @@+{+ "data": {+ "deity": {+ "name": "Morpheus",+ "power": ["Shapeshifting"]+ }+ }+}
− test/api/interface/query.gql
@@ -1,79 +0,0 @@-query Get__Type {- interface: __type(name: "Character") {- ...FullType- }- deity: __type(name: "Deity") {- ...FullType- }-}--fragment FullType on __Type {- kind- name- fields(includeDeprecated: true) {- name- args {- ...InputValue- }- type {- ...TypeRef- }- isDeprecated- deprecationReason- }- inputFields {- ...InputValue- }- interfaces {- ...TypeRef- }- enumValues(includeDeprecated: true) {- name- isDeprecated- deprecationReason- }- possibleTypes {- ...TypeRef- }-}--fragment InputValue on __InputValue {- name- type {- ...TypeRef- }- defaultValue-}--fragment TypeRef on __Type {- kind- name- ofType {- kind- name- ofType {- kind- name- ofType {- kind- name- ofType {- kind- name- ofType {- kind- name- ofType {- kind- name- ofType {- kind- name- }- }- }- }- }- }- }-}
− test/api/interface/response.json
@@ -1,65 +0,0 @@-{- "interface": {- "kind": "INTERFACE",- "name": "Character",- "fields": [- {- "name": "name",- "args": [],- "type": { "kind": "SCALAR", "name": "String", "ofType": null },- "isDeprecated": false,- "deprecationReason": null- }- ],- "inputFields": null,- "interfaces": null,- "enumValues": null,- "possibleTypes": [- { "kind": "OBJECT", "name": "Deity", "ofType": null },- { "kind": "OBJECT", "name": "Hero", "ofType": null }- ]- },- "deity": {- "kind": "OBJECT",- "name": "Deity",- "fields": [- {- "name": "name",- "args": [],- "type": {- "kind": "NON_NULL",- "name": null,- "ofType": { "kind": "SCALAR", "name": "String", "ofType": null }- },- "isDeprecated": false,- "deprecationReason": null- },- {- "name": "power",- "args": [],- "type": {- "kind": "NON_NULL",- "name": null,- "ofType": {- "kind": "LIST",- "name": null,- "ofType": {- "kind": "NON_NULL",- "name": null,- "ofType": { "kind": "SCALAR", "name": "String", "ofType": null }- }- }- },- "isDeprecated": false,- "deprecationReason": null- }- ],- "inputFields": null,- "interfaces": [- { "kind": "INTERFACE", "name": "Character", "ofType": null },- { "kind": "INTERFACE", "name": "Supernatural", "ofType": null }- ],- "enumValues": null,- "possibleTypes": null- }-}
− test/api/simple/query.gql
@@ -1,6 +0,0 @@-{- deity {- name- power- }-}
− test/api/simple/response.json
@@ -1,6 +0,0 @@-{- "deity": {- "name": "Morpheus",- "power": ["Shapeshifting"]- }-}
+ test/api/validation/fragment/fail-unknown-field-on-interface/query.gql view
@@ -0,0 +1,16 @@+{+ deity {+ ...onSupernatural+ }+ character {+ ...OnCharacter+ }+}++fragment OnCharacter on Character {+ power+}++fragment onSupernatural on Supernatural {+ name+}
+ test/api/validation/fragment/fail-unknown-field-on-interface/response.json view
@@ -0,0 +1,12 @@+{+ "errors": [+ {+ "message": "Cannot query field \"power\" on type \"Character\".",+ "locations": [{ "line": 11, "column": 3 }]+ },+ {+ "message": "Cannot query field \"name\" on type \"Supernatural\".",+ "locations": [{ "line": 15, "column": 3 }]+ }+ ]+}
+ test/api/validation/fragment/on-interface-fail-without-casting/query.gql view
@@ -0,0 +1,7 @@+{+ character {+ name+ power+ nickname+ }+}
+ test/api/validation/fragment/on-interface-fail-without-casting/response.json view
@@ -0,0 +1,22 @@+{+ "errors": [+ {+ "message": "Cannot query field \"power\" on type \"Character\".",+ "locations": [+ {+ "line": 4,+ "column": 5+ }+ ]+ },+ {+ "message": "Cannot query field \"nickname\" on type \"Character\".",+ "locations": [+ {+ "line": 5,+ "column": 5+ }+ ]+ }+ ]+}
+ test/api/validation/fragment/on-interface-inline/query.gql view
@@ -0,0 +1,12 @@+{+ deity {+ ... on Supernatural {+ power+ }+ }+ character {+ ... on Character {+ name+ }+ }+}
+ test/api/validation/fragment/on-interface-inline/response.json view
@@ -0,0 +1,10 @@+{+ "data": {+ "deity": { "power": ["Shapeshifting"] },+ "character": [+ { "name": "Test Deity" },+ { "name": "Test Hero" },+ { "name": "Test Character" }+ ]+ }+}
+ test/api/validation/fragment/on-interface-type-casting-inline/query.gql view
@@ -0,0 +1,13 @@+{+ character {+ ... on Character {+ name+ }+ ... on Deity {+ power+ }+ ... on Hero {+ nickname+ }+ }+}
+ test/api/validation/fragment/on-interface-type-casting-inline/response.json view
@@ -0,0 +1,17 @@+{+ "data": {+ "character": [+ {+ "name": "Test Deity",+ "power": ["Show Only on Deity"]+ },+ {+ "name": "Test Hero",+ "nickname": "tets Hero NickName"+ },+ {+ "name": "Test Character"+ }+ ]+ }+}
+ test/api/validation/fragment/on-interface-type-casting/query.gql view
@@ -0,0 +1,19 @@+{+ character {+ ...OnCharacter+ ...onDeity+ ...onHero+ }+}++fragment OnCharacter on Character {+ name+}++fragment onDeity on Deity {+ power+}++fragment onHero on Hero {+ nickname+}
+ test/api/validation/fragment/on-interface-type-casting/response.json view
@@ -0,0 +1,17 @@+{+ "data": {+ "character": [+ {+ "name": "Test Deity",+ "power": ["Show Only on Deity"]+ },+ {+ "name": "Test Hero",+ "nickname": "tets Hero NickName"+ },+ {+ "name": "Test Character"+ }+ ]+ }+}
+ test/api/validation/fragment/on-interface/query.gql view
@@ -0,0 +1,16 @@+{+ deity {+ ...OnSupernatural+ }+ character {+ ...OnCharacter+ }+}++fragment OnSupernatural on Supernatural {+ power+}++fragment OnCharacter on Character {+ name+}
+ test/api/validation/fragment/on-interface/response.json view
@@ -0,0 +1,10 @@+{+ "data": {+ "deity": { "power": ["Shapeshifting"] },+ "character": [+ { "name": "Test Deity" },+ { "name": "Test Hero" },+ { "name": "Test Character" }+ ]+ }+}
+ test/api/validation/fragment/on-type/query.gql view
@@ -0,0 +1,9 @@+{+ deity {+ ...OnDeity+ }+}++fragment OnDeity on Deity {+ name+}
+ test/api/validation/fragment/on-type/response.json view
@@ -0,0 +1,7 @@+{+ "data": {+ "deity": {+ "name": "Morpheus"+ }+ }+}
+ test/api/validation/fragment/on-union-type/query.gql view
@@ -0,0 +1,9 @@+{+ myUnion {+ ...onDeity+ }+}++fragment onDeity on Deity {+ power+}
+ test/api/validation/fragment/on-union-type/response.json view
@@ -0,0 +1,5 @@+{+ "data": {+ "myUnion": null+ }+}
+ test/api/validation/fragment/resolvers.json view
@@ -0,0 +1,29 @@+{+ "query": {+ "deity": {+ "__typename": "Deity",+ "name": "Morpheus",+ "power": ["Shapeshifting"]+ },+ "character": [+ {+ "__typename": "Deity",+ "name": "Test Deity",+ "power": ["Show Only on Deity"],+ "nickname": "this should be skipped"+ },+ {+ "__typename": "Hero",+ "name": "Test Hero",+ "power": ["this should be skipped"],+ "nickname": "tets Hero NickName"+ },+ {+ "__typename": "Character",+ "name": "Test Character",+ "nickname": "this should be skipped",+ "power": ["this should be skipped"]+ }+ ]+ }+}
+ test/api/validation/fragment/schema.gql view
@@ -0,0 +1,25 @@+interface Character {+ name: String+}++interface Supernatural {+ power: [String!]!+}++type Hero implements Character {+ name: String+ nickname: String+}++type Deity implements Character & Supernatural {+ name: String!+ power: [String!]!+}++union MyUnion = Hero | Deity++type Query {+ deity: Deity!+ myUnion: MyUnion+ character: [Character!]+}
+ test/schema/parsing/directive/ok/simple/response.json view
@@ -0,0 +1,1 @@+"OK"
+ test/schema/parsing/directive/ok/simple/schema.gql view
@@ -0,0 +1,5 @@+directive @MyDirective on FIELD_DEFINITION | OBJECT++type Query {+ name: String+}
+ test/schema/parsing/directive/ok/sophisticated/response.json view
@@ -0,0 +1,1 @@+"OK"
+ test/schema/parsing/directive/ok/sophisticated/schema.gql view
@@ -0,0 +1,17 @@+directive @DirectiveA on SCALAR++directive @DirectiveB(arg: Int) on FIELD_DEFINITION | INPUT_OBJECT++directive @DirectiveC(arg: MyInput! = {}) on FIELD_DEFINITION | FIELD_DEFINITION++directive @DirectiveD(+ arg: String = "bla"+) on FIELD_DEFINITION | FIELD_DEFINITION++input MyInput {+ field: Int+}++type Query {+ name: String+}
+ test/schema/parsing/schema-definition/fail/dupplicate-field/query-mutation-subscription/response.json view
@@ -0,0 +1,20 @@+{+ "errors": [+ {+ "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.\"subscription\"",+ "locations": []+ },+ {+ "message": "There can Be only One TypeDefinition for schema.\"subscription\"",+ "locations": []+ }+ ]+}
+ test/schema/parsing/schema-definition/fail/dupplicate-field/query-mutation-subscription/schema.gql view
@@ -0,0 +1,21 @@+type Query {+ name: String+}++type Mutation {+ name: String+}++type Subscription {+ name: String+}++schema {+ query: Query+ query: Query+ mutation: Mutation+ mutation: Mutation+ subscription: Subscription+ subscription: Subscription+ subscription: Subscription+}
+ test/schema/parsing/schema-definition/fail/dupplicate-field/query/response.json view
@@ -0,0 +1,12 @@+{+ "errors": [+ {+ "message": "There can Be only One TypeDefinition for schema.\"query\"",+ "locations": []+ },+ {+ "message": "There can Be only One TypeDefinition for schema.\"query\"",+ "locations": []+ }+ ]+}
+ test/schema/parsing/schema-definition/fail/dupplicate-field/query/schema.gql view
@@ -0,0 +1,9 @@+type Query {+ name: String+}++schema {+ query: Query+ query: Query+ query: Query+}
+ test/schema/parsing/schema-definition/fail/dupplicate-schema-definition/response.json view
@@ -0,0 +1,6 @@+{+ "errors": [+ { "message": "There can Be only One SchemaDefinition.", "locations": [] },+ { "message": "There can Be only One SchemaDefinition.", "locations": [] }+ ]+}
+ test/schema/parsing/schema-definition/fail/dupplicate-schema-definition/schema.gql view
@@ -0,0 +1,29 @@+type Query {+ name: String+}++schema {+ query: Query+ mutation: Mutation+ subscription: Subscription+}++schema {+ query: Query+ mutation: Mutation+ subscription: Subscription+}++type Mutation {+ name: String+}++type Subscription {+ name: String+}++schema {+ query: Query+ mutation: Mutation+ subscription: Subscription+}
+ test/schema/parsing/schema-definition/fail/unknown-fields/bla/response.json view
@@ -0,0 +1,8 @@+{+ "errors": [+ {+ "message": "offset=78:\nunexpected 'b'\nexpecting '}', Ignored, IgnoredTokens, OperationType, or white space\n",+ "locations": [{ "line": 8, "column": 3 }]+ }+ ]+}
+ test/schema/parsing/schema-definition/fail/unknown-fields/bla/schema.gql view
@@ -0,0 +1,9 @@+type MyType {+ name: String+}++schema {+ query: MyType+ mutation: MyType+ bla: MyType+}
+ test/schema/parsing/schema-definition/fail/unknown-fields/uppercase/response.json view
@@ -0,0 +1,8 @@+{+ "errors": [+ {+ "message": "offset=43:\nunexpected 'Q'\nexpecting '}', Ignored, OperationType, or white space\n",+ "locations": [{ "line": 6, "column": 3 }]+ }+ ]+}
+ test/schema/parsing/schema-definition/fail/unknown-fields/uppercase/schema.gql view
@@ -0,0 +1,7 @@+type MyType {+ name: String+}++schema {+ Query: MyType+}
+ test/schema/parsing/schema-definition/ok/query-mutation-subscription/response.json view
@@ -0,0 +1,1 @@+"OK"
+ test/schema/parsing/schema-definition/ok/query-mutation-subscription/schema.gql view
@@ -0,0 +1,17 @@+type Query {+ name: String+}++type Mutation {+ name: String+}++type Subscription {+ name: String+}++schema {+ query: Query+ mutation: Mutation+ subscription: Subscription+}
+ test/schema/parsing/schema-definition/ok/query-mutation/response.json view
@@ -0,0 +1,1 @@+"OK"
+ test/schema/parsing/schema-definition/ok/query-mutation/schema.gql view
@@ -0,0 +1,12 @@+type Query {+ name: String+}++type Mutation {+ name: String+}++schema {+ query: Query+ mutation: Mutation+}
+ test/schema/parsing/schema-definition/ok/query/response.json view
@@ -0,0 +1,1 @@+"OK"
+ test/schema/parsing/schema-definition/ok/query/schema.gql view
@@ -0,0 +1,12 @@+type Query {+ name: String+}++type Mutation {+ name: String+}++schema {+ query: Query+ mutation: Mutation+}
test/schema/validation/default-value/argument/missing-field/response.json view
@@ -13,11 +13,11 @@ "locations": [] }, {- "message": "Field Query.field(i3:) got invalid default value. in field \"field\": Undefined Field \"field2\".",+ "message": "Field Query.field(i3:) got invalid default value. in field \"field.field\": Undefined Field \"field\".", "locations": [] }, {- "message": "Field Query.field(i3:) got invalid default value. in field \"field.field\": Undefined Field \"field\".",+ "message": "Field Query.field(i3:) got invalid default value. in field \"field\": Undefined Field \"field2\".", "locations": [] } ]
test/schema/validation/default-value/argument/unexpected-value/response.json view
@@ -2,31 +2,31 @@ "errors": [ { "message": "Field Query.field(i1:) got invalid default value. in field \"field1.field2\": Expected type \"String\" found 1.",- "locations": [{ "line": 0, "column": 0 }]+ "locations": [] }, { "message": "Field Query.field(i1:) got invalid default value. in field \"field2\": Expected type \"String\" found true.",- "locations": [{ "line": 0, "column": 0 }]+ "locations": [] }, { "message": "Field Query.field(i2:) got invalid default value. in field \"field1\": Expected type \"ID\" found {field3: 2344}.",- "locations": [{ "line": 0, "column": 0 }]+ "locations": [] }, { "message": "Field Query.field(i2:) got invalid default value. in field \"field2\": Expected type \"String\" found 123.",- "locations": [{ "line": 0, "column": 0 }]+ "locations": [] }, { "message": "Field Query.field(i3:) got invalid default value. Expected type \"TestEnum\" found EnumBB.",- "locations": [{ "line": 0, "column": 0 }]+ "locations": [] }, { "message": "Field Query.field(i4:) got invalid default value. Expected type \"ID\" found true.",- "locations": [{ "line": 0, "column": 0 }]+ "locations": [] }, { "message": "Field Query.field(i5:) got invalid default value. Expected type \"Int\" found \"some text\".",- "locations": [{ "line": 0, "column": 0 }]+ "locations": [] } ] }
test/schema/validation/default-value/argument/unknown-field/response.json view
@@ -1,7 +1,7 @@ { "errors": [ {- "message": "Field Query.field(i1:) got invalid default value. in field \"field.field\": Unknown Field \"field3\".",+ "message": "Field Query.field(i1:) got invalid default value. Unknown Field \"field2\".", "locations": [] }, {@@ -9,7 +9,7 @@ "locations": [] }, {- "message": "Field Query.field(i1:) got invalid default value. Unknown Field \"field2\".",+ "message": "Field Query.field(i1:) got invalid default value. in field \"field.field\": Unknown Field \"field3\".", "locations": [] } ]
test/schema/validation/default-value/field/missing-field/response.json view
@@ -1,11 +1,11 @@ { "errors": [ {- "message": "Field Input3.field1 got invalid default value. Undefined Field \"field2\".",+ "message": "Field Input3.field1 got invalid default value. in field \"field\": Undefined Field \"field\".", "locations": [] }, {- "message": "Field Input3.field1 got invalid default value. in field \"field\": Undefined Field \"field\".",+ "message": "Field Input3.field1 got invalid default value. Undefined Field \"field2\".", "locations": [] } ]
test/schema/validation/default-value/field/unexpected-value/response.json view
@@ -2,35 +2,43 @@ "errors": [ { "message": "Field Input2.objectField got invalid default value. in field \"field1\": Expected type \"ID!\" found true.",- "locations": [{ "line": 0, "column": 0 }]+ "locations": [] }, { "message": "Field Input2.objectField got invalid default value. in field \"field2\": Expected type \"String!\" found true.",- "locations": [{ "line": 0, "column": 0 }]+ "locations": [] }, { "message": "Field Input2.objectField got invalid default value. in field \"field3\": Expected type \"TestEnum\" found true.",- "locations": [{ "line": 0, "column": 0 }]+ "locations": [] }, {+ "message": "Field Input2.objectField got invalid default value. in field \"field4\": Expected type \"TestEnum!\" found 1.",+ "locations": []+ },+ {+ "message": "Field Input2.objectField got invalid default value. in field \"field5\": Expected type \"TestEnum\" found \"some text\".",+ "locations": []+ },+ { "message": "Field Input1.field1 got invalid default value. Expected type \"ID!\" found EnumA.",- "locations": [{ "line": 0, "column": 0 }]+ "locations": [] }, { "message": "Field Input1.field2 got invalid default value. Expected type \"String!\" found EnumA.",- "locations": [{ "line": 0, "column": 0 }]+ "locations": [] }, { "message": "Field Input1.field3 got invalid default value. Expected type \"TestEnum\" found UnknownEnum.",- "locations": [{ "line": 0, "column": 0 }]+ "locations": [] }, { "message": "Field Input1.field4 got invalid default value. Expected type \"TestEnum!\" found 1.",- "locations": [{ "line": 0, "column": 0 }]+ "locations": [] }, { "message": "Field Input1.field5 got invalid default value. Expected type \"TestEnum\" found \"some text\".",- "locations": [{ "line": 0, "column": 0 }]+ "locations": [] } ] }
test/schema/validation/default-value/field/unknown-field/response.json view
@@ -1,15 +1,15 @@ { "errors": [ {- "message": "Field Input3.field got invalid default value. in field \"field\": Unknown Field \"field2\".",+ "message": "Field Input3.field got invalid default value. Unknown Field \"field2\".", "locations": [] }, {- "message": "Field Input3.field got invalid default value. in field \"field\": Unknown Field \"field3\".",+ "message": "Field Input3.field got invalid default value. in field \"field\": Unknown Field \"field2\".", "locations": [] }, {- "message": "Field Input3.field got invalid default value. Unknown Field \"field2\".",+ "message": "Field Input3.field got invalid default value. in field \"field\": Unknown Field \"field3\".", "locations": [] } ]
+ test/schema/validation/directive/fail/at-invalid-place/response.json view
@@ -0,0 +1,36 @@+{+ "errors": [+ {+ "message": "Directive \"DirectiveA\" may not to be used on SCALAR",+ "locations": [{ "line": 9, "column": 17 }]+ },+ {+ "message": "Directive \"DirectiveB\" may not to be used on ENUM",+ "locations": [{ "line": 11, "column": 15 }]+ },+ {+ "message": "Directive \"DirectiveC\" may not to be used on ENUM_VALUE",+ "locations": [{ "line": 12, "column": 9 }]+ },+ {+ "message": "Directive \"deprecated\" may not to be used on INPUT_OBJECT",+ "locations": [{ "line": 16, "column": 14 }]+ },+ {+ "message": "Directive \"include\" may not to be used on INPUT_FIELD_DEFINITION",+ "locations": [{ "line": 17, "column": 14 }]+ },+ {+ "message": "Directive \"deprecated\" may not to be used on OBJECT",+ "locations": [{ "line": 21, "column": 12 }]+ },+ {+ "message": "Directive \"include\" may not to be used on FIELD_DEFINITION",+ "locations": [{ "line": 22, "column": 35 }]+ },+ {+ "message": "Directive \"DirectiveD\" may not to be used on ARGUMENT_DEFINITION",+ "locations": [{ "line": 22, "column": 17 }]+ }+ ]+}
+ test/schema/validation/directive/fail/at-invalid-place/schema.gql view
@@ -0,0 +1,23 @@+directive @DirectiveA on INPUT_OBJECT++directive @DirectiveB on SCALAR++directive @DirectiveC on ENUM++directive @DirectiveD on INPUT_FIELD_DEFINITION++scalar MyScalar @DirectiveA++enum TestEnum @DirectiveB {+ EnumA @DirectiveC+ EnumB+}++input Input1 @deprecated {+ field1: ID @include(if: true)+ field2: String+}++type Query @deprecated {+ field(a1: Int @DirectiveD): Int @include(if: true)+}
+ test/schema/validation/directive/fail/missing-argument/response.json view
@@ -0,0 +1,48 @@+{+ "errors": [+ {+ "message": "Directive \"@DirectiveA\" argument \"a1\" is required but not provided.",+ "locations": [{ "line": 9, "column": 17 }]+ },+ {+ "message": "Directive \"@DirectiveA\" argument \"a2\" is required but not provided.",+ "locations": [{ "line": 9, "column": 17 }]+ },+ {+ "message": "Directive \"@DirectiveC\" argument \"arg\" is required but not provided.",+ "locations": [{ "line": 19, "column": 13 }]+ },+ {+ "message": "Directive \"@DirectiveC\" argument \"arg\" is required but not provided.",+ "locations": [{ "line": 20, "column": 38 }]+ },+ {+ "message": "Directive \"@DirectiveC\" argument \"arg\" is required but not provided.",+ "locations": [{ "line": 20, "column": 17 }]+ },+ {+ "message": "Directive \"@DirectiveA\" argument \"a1\" is required but not provided.",+ "locations": [{ "line": 11, "column": 13 }]+ },+ {+ "message": "Directive \"@DirectiveA\" argument \"a2\" is required but not provided.",+ "locations": [{ "line": 11, "column": 13 }]+ },+ {+ "message": "Directive \"@DirectiveA\" argument \"a1\" is required but not provided.",+ "locations": [{ "line": 12, "column": 9 }]+ },+ {+ "message": "Directive \"@DirectiveA\" argument \"a2\" is required but not provided.",+ "locations": [{ "line": 12, "column": 9 }]+ },+ {+ "message": "Directive \"@DirectiveB\" argument \"a1\" is required but not provided.",+ "locations": [{ "line": 15, "column": 15 }]+ },+ {+ "message": "Directive \"@DirectiveB\" argument \"a1\" is required but not provided.",+ "locations": [{ "line": 16, "column": 17 }]+ }+ ]+}
+ test/schema/validation/directive/fail/missing-argument/schema.gql view
@@ -0,0 +1,25 @@+directive @DirectiveA(a1: String!, a2: Int!) on SCALAR | ENUM | ENUM_VALUE++directive @DirectiveB(a1: Int!) on INPUT_FIELD_DEFINITION | INPUT_OBJECT++directive @DirectiveC(+ arg: MyInput!+) on FIELD_DEFINITION | OBJECT | ARGUMENT_DEFINITION++scalar MyScalar @DirectiveA++enum MyEnum @DirectiveA {+ EnumA @DirectiveA+}++input MyInput @DirectiveB {+ field: String @DirectiveB+}++type MyType @DirectiveC {+ field(a1: Int @DirectiveC): String @DirectiveC+}++type Query {+ name: String+}
+ test/schema/validation/directive/fail/sopthisticated/response.json view
@@ -0,0 +1,64 @@+{+ "errors": [+ {+ "message": "Directive \"include\" may not to be used on INPUT_OBJECT",+ "locations": [{ "line": 13, "column": 13 }]+ },+ {+ "message": "Directive \"@include\" argument \"if\" is required but not provided.",+ "locations": [{ "line": 13, "column": 13 }]+ },+ {+ "message": "Directive \"skip\" may not to be used on INPUT_FIELD_DEFINITION",+ "locations": [{ "line": 14, "column": 13 }]+ },+ {+ "message": "Directive \"@skip\" argument \"if\" is required but not provided.",+ "locations": [{ "line": 14, "column": 13 }]+ },+ {+ "message": "Unknown Directive \"SomeDirective\".",+ "locations": [{ "line": 9, "column": 13 }]+ },+ {+ "message": "Directive \"DirectiveC\" may not to be used on FIELD_DEFINITION",+ "locations": [{ "line": 10, "column": 62 }]+ },+ {+ "message": "Directive \"@DirectiveC\" argument \"arg\" is required but not provided.",+ "locations": [{ "line": 10, "column": 62 }]+ },+ {+ "message": "Unknown Argument \"someargs\" on Directive \"DirectiveC\".",+ "locations": [{ "line": 10, "column": 40 }]+ },+ {+ "message": "Argument \"arg\" got invalid value. Expected type \"Int!\" found True.",+ "locations": [{ "line": 10, "column": 29 }]+ },+ {+ "message": "Directive \"DirectiveA\" may not to be used on ENUM_VALUE",+ "locations": [{ "line": 6, "column": 9 }]+ },+ {+ "message": "Directive \"@DirectiveA\" argument \"a1\" is required but not provided.",+ "locations": [{ "line": 6, "column": 9 }]+ },+ {+ "message": "Directive \"DirectiveC\" may not to be used on OBJECT",+ "locations": [{ "line": 17, "column": 12 }]+ },+ {+ "message": "Argument \"arg\" got invalid value. Expected type \"Int!\" found \"bla\".",+ "locations": [{ "line": 17, "column": 24 }]+ },+ {+ "message": "Directive \"skip\" may not to be used on FIELD_DEFINITION",+ "locations": [{ "line": 18, "column": 23 }]+ },+ {+ "message": "Directive \"@skip\" argument \"if\" is required but not provided.",+ "locations": [{ "line": 18, "column": 23 }]+ }+ ]+}
+ test/schema/validation/directive/fail/sopthisticated/schema.gql view
@@ -0,0 +1,19 @@+directive @DirectiveA(a1: String!) on SCALAR++directive @DirectiveC(arg: Int!) on ARGUMENT_DEFINITION++enum MyEnum {+ EnumA @DirectiveA+}++type MyType @SomeDirective {+ field(a1: Int @DirectiveC(arg: True, someargs: 1)): String @DirectiveC+}++input Input @include {+ field: ID @skip+}++type Query @DirectiveC(arg: "bla") {+ field(a1: Int): Int @skip+}
+ test/schema/validation/directive/fail/unexpected-value/response.json view
@@ -0,0 +1,36 @@+{+ "errors": [+ {+ "message": "Argument \"a1\" got invalid value. Expected type \"Int!\" found 0.1.",+ "locations": [{ "line": 9, "column": 29 }]+ },+ {+ "message": "Argument \"reason\" got invalid value. Expected type \"String\" found EnumA.",+ "locations": [{ "line": 14, "column": 21 }]+ },+ {+ "message": "Argument \"reason\" got invalid value. Expected type \"String\" found 1.",+ "locations": [{ "line": 15, "column": 21 }]+ },+ {+ "message": "Argument \"reason\" got invalid value. Expected type \"String\" found Bool.",+ "locations": [{ "line": 16, "column": 21 }]+ },+ {+ "message": "Argument \"a1\" got invalid value. in field \"field\": Expected type \"String\" found 2394.",+ "locations": [{ "line": 20, "column": 26 }]+ },+ {+ "message": "Argument \"a1\" got invalid value. Expected type \"MyInput!\" found [\"some deprication\"].",+ "locations": [{ "line": 21, "column": 30 }]+ },+ {+ "message": "Argument \"a1\" got invalid value. Expected type \"Int!\" found \"some string\".",+ "locations": [{ "line": 10, "column": 30 }]+ },+ {+ "message": "Argument \"a1\" got invalid value. Expected type \"Int!\" found True.",+ "locations": [{ "line": 11, "column": 30 }]+ }+ ]+}
+ test/schema/validation/directive/fail/unexpected-value/schema.gql view
@@ -0,0 +1,26 @@+directive @DirectiveA(a1: Int!) on SCALAR++directive @DirectiveB(a1: MyInput!) on INPUT_FIELD_DEFINITION++input MyInput {+ field: String+}++scalar MyScalar @DirectiveA(a1: 0.1)+scalar MyScalar2 @DirectiveA(a1: "some string")+scalar MyScalar3 @DirectiveA(a1: True)++enum TestEnum {+ EnumA @deprecated(reason: EnumA)+ EnumB @deprecated(reason: 1)+ EnumB @deprecated(reason: Bool)+}++input Input {+ field1: ID @DirectiveB(a1: { field: 2394 })+ field2: String @DirectiveB(a1: ["some deprication"])+}++type Query {+ field: Int+}
+ test/schema/validation/directive/fail/unknown-directive/response.json view
@@ -0,0 +1,56 @@+{+ "errors": [+ {+ "message": "Unknown Directive \"DirectveD\".",+ "locations": [{ "line": 15, "column": 15 }]+ },+ {+ "message": "Unknown Directive \"DirectveA\".",+ "locations": [{ "line": 1, "column": 17 }]+ },+ {+ "message": "Unknown Directive \"DirectveB\".",+ "locations": [{ "line": 3, "column": 15 }]+ },+ {+ "message": "Unknown Directive \"DirectveB\".",+ "locations": [{ "line": 4, "column": 9 }]+ },+ {+ "message": "Unknown Directive \"DirectveC\".",+ "locations": [{ "line": 7, "column": 8 }]+ },+ {+ "message": "Unknown Directive \"DirectveC\".",+ "locations": [{ "line": 8, "column": 17 }]+ },+ {+ "message": "Unknown Directive \"DirectveE\".",+ "locations": [{ "line": 17, "column": 14 }]+ },+ {+ "message": "Unknown Directive \"DirectveE\".",+ "locations": [{ "line": 18, "column": 14 }]+ },+ {+ "message": "Unknown Directive \"DirectveC\".",+ "locations": [{ "line": 11, "column": 8 }]+ },+ {+ "message": "Unknown Directive \"DirectveC\".",+ "locations": [{ "line": 12, "column": 17 }]+ },+ {+ "message": "Unknown Directive \"DirectveF\".",+ "locations": [{ "line": 22, "column": 12 }]+ },+ {+ "message": "Unknown Directive \"DirectveF\".",+ "locations": [{ "line": 23, "column": 34 }]+ },+ {+ "message": "Unknown Directive \"DirectveF\".",+ "locations": [{ "line": 23, "column": 17 }]+ }+ ]+}
+ test/schema/validation/directive/fail/unknown-directive/schema.gql view
@@ -0,0 +1,24 @@+scalar MyScalar @DirectveA++enum TestEnum @DirectveB {+ EnumA @DirectveB+ EnumB+}+type A @DirectveC {+ name: String! @DirectveC+}++type B @DirectveC {+ name: String! @DirectveC+}++union MyUnion @DirectveD = A | B++input Input1 @DirectveE {+ field1: ID @DirectveE+ field2: String+}++type Query @DirectveF {+ field(a1: Int @DirectveF): Int @DirectveF+}
+ test/schema/validation/directive/ok/deprecated/custom-directive/response.json view
@@ -0,0 +1,1 @@+"OK"
+ test/schema/validation/directive/ok/deprecated/custom-directive/schema.gql view
@@ -0,0 +1,25 @@+directive @DirectiveA on SCALAR | ENUM | ENUM_VALUE++directive @DirectiveB(arg: Int) on INPUT_FIELD_DEFINITION | INPUT_OBJECT++directive @DirectiveC(+ arg: MyInput! = {}+) on FIELD_DEFINITION | OBJECT | ARGUMENT_DEFINITION++scalar MyScalar @DirectiveA++enum MyEnum @DirectiveA {+ EnumA @DirectiveA+}++input MyInput @DirectiveB {+ field: String @DirectiveB+}++type MyType @DirectiveC {+ field(a1: Int @DirectiveC): String @DirectiveC+}++type Query {+ name: String+}
+ test/schema/validation/directive/ok/deprecated/response.json view
@@ -0,0 +1,1 @@+"OK"
+ test/schema/validation/directive/ok/deprecated/schema.gql view
@@ -0,0 +1,15 @@+scalar MyScalar++enum TestEnum {+ EnumA @deprecated+ EnumB+}++input Input1 {+ field1: ID+ field2: String+}++type Query {+ field(a1: Int): Int @deprecated+}
+ test/schema/validation/schema-definition/fail/non-object-kind/from-schema/response.json view
@@ -0,0 +1,16 @@+{+ "errors": [+ {+ "message": "Query root type must be Object type if provided, it cannot be \"MyType\"",+ "locations": []+ },+ {+ "message": "Mutation root type must be Object type if provided, it cannot be \"MyUnion\"",+ "locations": []+ },+ {+ "message": "Subscription root type must be Object type if provided, it cannot be \"MyEnum\"",+ "locations": []+ }+ ]+}
+ test/schema/validation/schema-definition/fail/non-object-kind/from-schema/schema.gql view
@@ -0,0 +1,19 @@+input MyType {+ name: String+}++type MyType1 {+ name: String+}++enum MyEnum {+ Enum1+}++union MyUnion = MyType1++schema {+ query: MyType+ mutation: MyUnion+ subscription: MyEnum+}
+ test/schema/validation/schema-definition/fail/non-object-kind/without-schema/response.json view
@@ -0,0 +1,16 @@+{+ "errors": [+ {+ "message": "Query root type must be Object type if provided, it cannot be \"Query\"",+ "locations": []+ },+ {+ "message": "Mutation root type must be Object type if provided, it cannot be \"Mutation\"",+ "locations": []+ },+ {+ "message": "Subscription root type must be Object type if provided, it cannot be \"Subscription\"",+ "locations": []+ }+ ]+}
+ test/schema/validation/schema-definition/fail/non-object-kind/without-schema/schema.gql view
@@ -0,0 +1,13 @@+type MyType1 {+ name: String+}++input Query {+ name: String+}++union Mutation = MyType1++enum Subscription {+ Enum1+}
+ test/schema/validation/schema-definition/fail/required query/empty/response.json view
@@ -0,0 +1,8 @@+{+ "errors": [+ {+ "message": "Query root type must be provided.",+ "locations": []+ }+ ]+}
+ test/schema/validation/schema-definition/fail/required query/empty/schema.gql view
@@ -0,0 +1,5 @@+type MyType {+ name: String+}++schema {}
+ test/schema/validation/schema-definition/fail/required query/no-query/response.json view
@@ -0,0 +1,8 @@+{+ "errors": [+ {+ "message": "Query root type must be provided.",+ "locations": []+ }+ ]+}
+ test/schema/validation/schema-definition/fail/required query/no-query/schema.gql view
@@ -0,0 +1,7 @@+type MyType {+ name: String+}++schema {+ mutation: MyType+}
+ test/schema/validation/schema-definition/fail/required query/no-schema-no-query/response.json view
@@ -0,0 +1,8 @@+{+ "errors": [+ {+ "message": "Query root type must be provided.",+ "locations": []+ }+ ]+}
+ test/schema/validation/schema-definition/fail/required query/no-schema-no-query/schema.gql view
@@ -0,0 +1,3 @@+type MyType {+ name: String+}
+ test/schema/validation/schema-definition/fail/required query/schema-with-query/response.json view
@@ -0,0 +1,8 @@+{+ "errors": [+ {+ "message": "Query root type must be provided.",+ "locations": []+ }+ ]+}
+ test/schema/validation/schema-definition/fail/required query/schema-with-query/schema.gql view
@@ -0,0 +1,11 @@+type MyType {+ name: String+}++type Query {+ name: String+}++schema {+ mutation: MyType+}
+ test/schema/validation/schema-definition/fail/unknown-type/unknown/response.json view
@@ -0,0 +1,16 @@+{+ "errors": [+ {+ "message": "Unknown type \"MyType1\".",+ "locations": []+ },+ {+ "message": "Unknown type \"Asfwe\".",+ "locations": []+ },+ {+ "message": "Unknown type \"GjsO\".",+ "locations": []+ }+ ]+}
+ test/schema/validation/schema-definition/fail/unknown-type/unknown/schema.gql view
@@ -0,0 +1,9 @@+type MyType {+ name: String+}++schema {+ query: MyType1+ mutation: Asfwe+ subscription: GjsO+}
+ test/schema/validation/schema-definition/ok/full/response.json view
@@ -0,0 +1,1 @@+"OK"
+ test/schema/validation/schema-definition/ok/full/schema.gql view
@@ -0,0 +1,17 @@+type Query {+ name: String+}++type Mutation {+ name: String+}++type Subscription {+ name: String+}++schema {+ query: Query+ mutation: Mutation+ subscription: Subscription+}