morpheus-graphql 0.6.2 → 0.7.0
raw patch · 122 files changed
+6828/−4913 lines, 122 filesdep +th-lift-instancesPVP ok
version bump matches the API change (PVP)
Dependencies added: th-lift-instances
API changes (from Hackage documentation)
- Data.Morpheus.Document: toMorpheusHaskellAPi :: String -> ByteString -> Either ByteString ByteString
- Data.Morpheus.Types: [FailedResolver] :: {unFailedResolver :: String} -> Resolver o m event value
- Data.Morpheus.Types: liftEitherM :: (PureOperation o, Monad m) => m (Either String a) -> Resolver o m e a
- Data.Morpheus.Types: liftM :: (PureOperation o, Monad m) => m a -> Resolver o m e a
+ Data.Morpheus.Document: parseDSL :: ByteString -> Either String DataTypeLib
+ Data.Morpheus.Types: lift :: (MonadTrans t, Monad m) => m a -> t m a
+ Data.Morpheus.Types: liftEither :: (LiftEither o res, Monad m) => m (Either (ResError res) a) -> res o event m a
+ Data.Morpheus.Types: type ResolveM e m a = MutRes e m (a (MutRes e m))
+ Data.Morpheus.Types: type ResolveQ e m a = Res e m (a (Res e m))
+ Data.Morpheus.Types: type ResolveS e m a = SubRes e m (a (Res e m))
+ Data.Morpheus.Types.Internal.AST: ArgsType :: Key -> ResolverKind -> ArgsType
+ Data.Morpheus.Types.Internal.AST: Argument :: Value -> ValueOrigin -> Position -> Argument
+ Data.Morpheus.Types.Internal.AST: Boolean :: Bool -> ScalarValue
+ Data.Morpheus.Types.Internal.AST: ClientQuery :: String -> [ClientType] -> Maybe TypeD -> ClientQuery
+ Data.Morpheus.Types.Internal.AST: ClientType :: TypeD -> DataTypeKind -> ClientType
+ Data.Morpheus.Types.Internal.AST: ConsD :: String -> [DataField] -> ConsD
+ Data.Morpheus.Types.Internal.AST: DataEnum :: DataEnum -> DataType
+ Data.Morpheus.Types.Internal.AST: DataEnumValue :: Name -> Maybe Meta -> DataEnumValue
+ Data.Morpheus.Types.Internal.AST: DataField :: Key -> [(Key, DataArgument)] -> Maybe ArgsType -> TypeAlias -> Maybe Meta -> DataField
+ Data.Morpheus.Types.Internal.AST: DataInputObject :: DataObject -> DataType
+ Data.Morpheus.Types.Internal.AST: DataInputUnion :: DataUnion -> DataType
+ Data.Morpheus.Types.Internal.AST: DataObject :: DataObject -> DataType
+ Data.Morpheus.Types.Internal.AST: DataScalar :: DataScalar -> DataType
+ Data.Morpheus.Types.Internal.AST: DataTyCon :: Key -> DataFingerprint -> Maybe Meta -> a -> DataTyCon a
+ Data.Morpheus.Types.Internal.AST: DataTypeLib :: HashMap Key DataType -> (Key, DataObject) -> Maybe (Key, DataObject) -> Maybe (Key, DataObject) -> DataTypeLib
+ Data.Morpheus.Types.Internal.AST: DataUnion :: DataUnion -> DataType
+ Data.Morpheus.Types.Internal.AST: DataValidator :: (Value -> Either Key Value) -> DataValidator
+ Data.Morpheus.Types.Internal.AST: Directive :: Name -> [(Name, Value)] -> Directive
+ Data.Morpheus.Types.Internal.AST: Enum :: Text -> Value
+ Data.Morpheus.Types.Internal.AST: ExternalResolver :: ResolverKind
+ Data.Morpheus.Types.Internal.AST: FinalDataType :: DataType -> RawDataType
+ Data.Morpheus.Types.Internal.AST: Float :: Float -> ScalarValue
+ Data.Morpheus.Types.Internal.AST: Fragment :: Key -> Position -> RawSelectionSet -> Fragment
+ Data.Morpheus.Types.Internal.AST: GQLQuery :: FragmentLib -> RawOperation -> [(Key, Value)] -> GQLQuery
+ Data.Morpheus.Types.Internal.AST: GQLTypeD :: TypeD -> DataTypeKind -> [TypeD] -> (Name, DataType) -> GQLTypeD
+ Data.Morpheus.Types.Internal.AST: INLINE :: ValueOrigin
+ Data.Morpheus.Types.Internal.AST: Implements :: [Key] -> DataObject -> RawDataType
+ Data.Morpheus.Types.Internal.AST: InlineFragment :: Fragment -> RawSelection
+ Data.Morpheus.Types.Internal.AST: Int :: Int -> ScalarValue
+ Data.Morpheus.Types.Internal.AST: Interface :: DataObject -> RawDataType
+ Data.Morpheus.Types.Internal.AST: KindEnum :: DataTypeKind
+ Data.Morpheus.Types.Internal.AST: KindInputObject :: DataTypeKind
+ Data.Morpheus.Types.Internal.AST: KindInputUnion :: DataTypeKind
+ Data.Morpheus.Types.Internal.AST: KindList :: DataTypeKind
+ Data.Morpheus.Types.Internal.AST: KindNonNull :: DataTypeKind
+ Data.Morpheus.Types.Internal.AST: KindObject :: Maybe OperationType -> DataTypeKind
+ Data.Morpheus.Types.Internal.AST: KindScalar :: DataTypeKind
+ Data.Morpheus.Types.Internal.AST: KindUnion :: DataTypeKind
+ Data.Morpheus.Types.Internal.AST: List :: [Value] -> Value
+ Data.Morpheus.Types.Internal.AST: ListType :: DataTypeWrapper
+ Data.Morpheus.Types.Internal.AST: Meta :: Maybe Description -> [Directive] -> Meta
+ Data.Morpheus.Types.Internal.AST: Mutation :: OperationType
+ Data.Morpheus.Types.Internal.AST: NonNullType :: DataTypeWrapper
+ Data.Morpheus.Types.Internal.AST: Null :: Value
+ Data.Morpheus.Types.Internal.AST: Object :: Object -> Value
+ Data.Morpheus.Types.Internal.AST: Operation :: Maybe Key -> OperationType -> args -> sel -> Position -> Operation args sel
+ Data.Morpheus.Types.Internal.AST: PlainResolver :: ResolverKind
+ Data.Morpheus.Types.Internal.AST: Position :: Int -> Int -> Position
+ Data.Morpheus.Types.Internal.AST: Query :: OperationType
+ Data.Morpheus.Types.Internal.AST: RawArgument :: Argument -> RawArgument
+ Data.Morpheus.Types.Internal.AST: RawSelectionField :: RawSelection' () -> RawSelection
+ Data.Morpheus.Types.Internal.AST: RawSelectionSet :: RawSelection' RawSelectionSet -> RawSelection
+ Data.Morpheus.Types.Internal.AST: Ref :: Key -> Position -> Ref
+ Data.Morpheus.Types.Internal.AST: Scalar :: ScalarValue -> Value
+ Data.Morpheus.Types.Internal.AST: Selection :: args -> Position -> Maybe Key -> rec -> Selection args rec
+ Data.Morpheus.Types.Internal.AST: SelectionField :: SelectionRec
+ Data.Morpheus.Types.Internal.AST: SelectionSet :: SelectionSet -> SelectionRec
+ Data.Morpheus.Types.Internal.AST: Spread :: Ref -> RawSelection
+ Data.Morpheus.Types.Internal.AST: String :: Text -> ScalarValue
+ Data.Morpheus.Types.Internal.AST: Subscription :: OperationType
+ Data.Morpheus.Types.Internal.AST: SystemFingerprint :: Key -> DataFingerprint
+ Data.Morpheus.Types.Internal.AST: TypeAlias :: Key -> Maybe Key -> [TypeWrapper] -> TypeAlias
+ Data.Morpheus.Types.Internal.AST: TypeD :: String -> [String] -> [ConsD] -> Maybe Meta -> TypeD
+ Data.Morpheus.Types.Internal.AST: TypeList :: TypeWrapper
+ Data.Morpheus.Types.Internal.AST: TypeMaybe :: TypeWrapper
+ Data.Morpheus.Types.Internal.AST: TypeVarResolver :: ResolverKind
+ Data.Morpheus.Types.Internal.AST: TypeableFingerprint :: [String] -> DataFingerprint
+ Data.Morpheus.Types.Internal.AST: UnionSelection :: UnionSelection -> SelectionRec
+ Data.Morpheus.Types.Internal.AST: VARIABLE :: ValueOrigin
+ Data.Morpheus.Types.Internal.AST: Variable :: Key -> Bool -> [TypeWrapper] -> Position -> a -> Variable a
+ Data.Morpheus.Types.Internal.AST: VariableRef :: Ref -> RawArgument
+ Data.Morpheus.Types.Internal.AST: [aliasArgs] :: TypeAlias -> Maybe Key
+ Data.Morpheus.Types.Internal.AST: [aliasTyCon] :: TypeAlias -> Key
+ Data.Morpheus.Types.Internal.AST: [aliasWrappers] :: TypeAlias -> [TypeWrapper]
+ Data.Morpheus.Types.Internal.AST: [argsTypeName] :: ArgsType -> Key
+ Data.Morpheus.Types.Internal.AST: [argumentOrigin] :: Argument -> ValueOrigin
+ Data.Morpheus.Types.Internal.AST: [argumentPosition] :: Argument -> Position
+ Data.Morpheus.Types.Internal.AST: [argumentValue] :: Argument -> Value
+ Data.Morpheus.Types.Internal.AST: [cFields] :: ConsD -> [DataField]
+ Data.Morpheus.Types.Internal.AST: [cName] :: ConsD -> String
+ Data.Morpheus.Types.Internal.AST: [clientKind] :: ClientType -> DataTypeKind
+ Data.Morpheus.Types.Internal.AST: [clientType] :: ClientType -> TypeD
+ Data.Morpheus.Types.Internal.AST: [column] :: Position -> Int
+ Data.Morpheus.Types.Internal.AST: [directiveArgs] :: Directive -> [(Name, Value)]
+ Data.Morpheus.Types.Internal.AST: [directiveName] :: Directive -> Name
+ Data.Morpheus.Types.Internal.AST: [enumMeta] :: DataEnumValue -> Maybe Meta
+ Data.Morpheus.Types.Internal.AST: [enumName] :: DataEnumValue -> Name
+ Data.Morpheus.Types.Internal.AST: [fieldArgsType] :: DataField -> Maybe ArgsType
+ Data.Morpheus.Types.Internal.AST: [fieldArgs] :: DataField -> [(Key, DataArgument)]
+ Data.Morpheus.Types.Internal.AST: [fieldMeta] :: DataField -> Maybe Meta
+ Data.Morpheus.Types.Internal.AST: [fieldName] :: DataField -> Key
+ Data.Morpheus.Types.Internal.AST: [fieldType] :: DataField -> TypeAlias
+ Data.Morpheus.Types.Internal.AST: [fragmentPosition] :: Fragment -> Position
+ Data.Morpheus.Types.Internal.AST: [fragmentSelection] :: Fragment -> RawSelectionSet
+ Data.Morpheus.Types.Internal.AST: [fragmentType] :: Fragment -> Key
+ Data.Morpheus.Types.Internal.AST: [fragments] :: GQLQuery -> FragmentLib
+ Data.Morpheus.Types.Internal.AST: [implementsInterfaces] :: RawDataType -> [Key]
+ Data.Morpheus.Types.Internal.AST: [inputVariables] :: GQLQuery -> [(Key, Value)]
+ Data.Morpheus.Types.Internal.AST: [isVariableRequired] :: Variable a -> Bool
+ Data.Morpheus.Types.Internal.AST: [line] :: Position -> Int
+ Data.Morpheus.Types.Internal.AST: [metaDescription] :: Meta -> Maybe Description
+ Data.Morpheus.Types.Internal.AST: [metaDirectives] :: Meta -> [Directive]
+ Data.Morpheus.Types.Internal.AST: [mutation] :: DataTypeLib -> Maybe (Key, DataObject)
+ Data.Morpheus.Types.Internal.AST: [operationArgs] :: Operation args sel -> args
+ Data.Morpheus.Types.Internal.AST: [operationName] :: Operation args sel -> Maybe Key
+ Data.Morpheus.Types.Internal.AST: [operationPosition] :: Operation args sel -> Position
+ Data.Morpheus.Types.Internal.AST: [operationSelection] :: Operation args sel -> sel
+ Data.Morpheus.Types.Internal.AST: [operationType] :: Operation args sel -> OperationType
+ Data.Morpheus.Types.Internal.AST: [operation] :: GQLQuery -> RawOperation
+ Data.Morpheus.Types.Internal.AST: [queryArgsType] :: ClientQuery -> Maybe TypeD
+ Data.Morpheus.Types.Internal.AST: [queryText] :: ClientQuery -> String
+ Data.Morpheus.Types.Internal.AST: [queryTypes] :: ClientQuery -> [ClientType]
+ Data.Morpheus.Types.Internal.AST: [query] :: DataTypeLib -> (Key, DataObject)
+ Data.Morpheus.Types.Internal.AST: [refName] :: Ref -> Key
+ Data.Morpheus.Types.Internal.AST: [refPosition] :: Ref -> Position
+ Data.Morpheus.Types.Internal.AST: [resKind] :: ArgsType -> ResolverKind
+ Data.Morpheus.Types.Internal.AST: [selectionAlias] :: Selection args rec -> Maybe Key
+ Data.Morpheus.Types.Internal.AST: [selectionArguments] :: Selection args rec -> args
+ Data.Morpheus.Types.Internal.AST: [selectionPosition] :: Selection args rec -> Position
+ Data.Morpheus.Types.Internal.AST: [selectionRec] :: Selection args rec -> rec
+ Data.Morpheus.Types.Internal.AST: [subscription] :: DataTypeLib -> Maybe (Key, DataObject)
+ Data.Morpheus.Types.Internal.AST: [tCons] :: TypeD -> [ConsD]
+ Data.Morpheus.Types.Internal.AST: [tMeta] :: TypeD -> Maybe Meta
+ Data.Morpheus.Types.Internal.AST: [tName] :: TypeD -> String
+ Data.Morpheus.Types.Internal.AST: [tNamespace] :: TypeD -> [String]
+ Data.Morpheus.Types.Internal.AST: [typeArgD] :: GQLTypeD -> [TypeD]
+ Data.Morpheus.Types.Internal.AST: [typeD] :: GQLTypeD -> TypeD
+ Data.Morpheus.Types.Internal.AST: [typeData] :: DataTyCon a -> a
+ Data.Morpheus.Types.Internal.AST: [typeFingerprint] :: DataTyCon a -> DataFingerprint
+ Data.Morpheus.Types.Internal.AST: [typeKindD] :: GQLTypeD -> DataTypeKind
+ Data.Morpheus.Types.Internal.AST: [typeMeta] :: DataTyCon a -> Maybe Meta
+ Data.Morpheus.Types.Internal.AST: [typeName] :: DataTyCon a -> Key
+ Data.Morpheus.Types.Internal.AST: [typeOriginal] :: GQLTypeD -> (Name, DataType)
+ Data.Morpheus.Types.Internal.AST: [types] :: DataTypeLib -> HashMap Key DataType
+ Data.Morpheus.Types.Internal.AST: [unImplements] :: RawDataType -> DataObject
+ Data.Morpheus.Types.Internal.AST: [validateValue] :: DataValidator -> Value -> Either Key Value
+ Data.Morpheus.Types.Internal.AST: [variablePosition] :: Variable a -> Position
+ Data.Morpheus.Types.Internal.AST: [variableTypeWrappers] :: Variable a -> [TypeWrapper]
+ Data.Morpheus.Types.Internal.AST: [variableType] :: Variable a -> Key
+ Data.Morpheus.Types.Internal.AST: [variableValue] :: Variable a -> a
+ Data.Morpheus.Types.Internal.AST: allDataTypes :: DataTypeLib -> [(Key, DataType)]
+ Data.Morpheus.Types.Internal.AST: anonymousRef :: Key -> Ref
+ Data.Morpheus.Types.Internal.AST: class GQLValue a
+ Data.Morpheus.Types.Internal.AST: coerceDataObject :: Failure error m => error -> DataType -> m DataObject
+ Data.Morpheus.Types.Internal.AST: convertToHaskellName :: Text -> Text
+ Data.Morpheus.Types.Internal.AST: convertToJSONName :: Text -> Text
+ Data.Morpheus.Types.Internal.AST: createAlias :: Key -> TypeAlias
+ Data.Morpheus.Types.Internal.AST: createArgument :: Key -> ([TypeWrapper], Key) -> (Key, DataField)
+ Data.Morpheus.Types.Internal.AST: createDataTypeLib :: [(Key, DataType)] -> Validation DataTypeLib
+ Data.Morpheus.Types.Internal.AST: createEnumType :: Key -> [Key] -> (Key, DataType)
+ Data.Morpheus.Types.Internal.AST: createEnumValue :: Key -> DataEnumValue
+ Data.Morpheus.Types.Internal.AST: createField :: DataArguments -> Key -> ([TypeWrapper], Key) -> DataField
+ Data.Morpheus.Types.Internal.AST: createInputUnionFields :: Key -> [Key] -> [(Key, DataField)]
+ Data.Morpheus.Types.Internal.AST: createScalarType :: Key -> (Key, DataType)
+ Data.Morpheus.Types.Internal.AST: createType :: Key -> a -> DataTyCon a
+ Data.Morpheus.Types.Internal.AST: createUnionType :: Key -> [Key] -> (Key, DataType)
+ Data.Morpheus.Types.Internal.AST: data ArgsType
+ Data.Morpheus.Types.Internal.AST: data Argument
+ Data.Morpheus.Types.Internal.AST: data ClientQuery
+ Data.Morpheus.Types.Internal.AST: data ClientType
+ Data.Morpheus.Types.Internal.AST: data ConsD
+ Data.Morpheus.Types.Internal.AST: data DataEnumValue
+ Data.Morpheus.Types.Internal.AST: data DataField
+ Data.Morpheus.Types.Internal.AST: data DataFingerprint
+ Data.Morpheus.Types.Internal.AST: data DataTyCon a
+ Data.Morpheus.Types.Internal.AST: data DataType
+ Data.Morpheus.Types.Internal.AST: data DataTypeKind
+ Data.Morpheus.Types.Internal.AST: data DataTypeLib
+ Data.Morpheus.Types.Internal.AST: data DataTypeWrapper
+ Data.Morpheus.Types.Internal.AST: data Directive
+ Data.Morpheus.Types.Internal.AST: data Fragment
+ Data.Morpheus.Types.Internal.AST: data GQLQuery
+ Data.Morpheus.Types.Internal.AST: data GQLTypeD
+ Data.Morpheus.Types.Internal.AST: data Meta
+ Data.Morpheus.Types.Internal.AST: data Operation args sel
+ Data.Morpheus.Types.Internal.AST: data OperationType
+ Data.Morpheus.Types.Internal.AST: data Position
+ Data.Morpheus.Types.Internal.AST: data RawArgument
+ Data.Morpheus.Types.Internal.AST: data RawDataType
+ Data.Morpheus.Types.Internal.AST: data RawSelection
+ Data.Morpheus.Types.Internal.AST: data Ref
+ Data.Morpheus.Types.Internal.AST: data ResolverKind
+ Data.Morpheus.Types.Internal.AST: data ScalarValue
+ Data.Morpheus.Types.Internal.AST: data Selection args rec
+ Data.Morpheus.Types.Internal.AST: data SelectionRec
+ Data.Morpheus.Types.Internal.AST: data TypeAlias
+ Data.Morpheus.Types.Internal.AST: data TypeD
+ Data.Morpheus.Types.Internal.AST: data TypeWrapper
+ Data.Morpheus.Types.Internal.AST: data Value
+ Data.Morpheus.Types.Internal.AST: data ValueOrigin
+ Data.Morpheus.Types.Internal.AST: data Variable a
+ Data.Morpheus.Types.Internal.AST: decodeScientific :: Scientific -> ScalarValue
+ Data.Morpheus.Types.Internal.AST: defineType :: (Key, DataType) -> DataTypeLib -> DataTypeLib
+ Data.Morpheus.Types.Internal.AST: fieldVisibility :: (Key, DataField) -> Bool
+ Data.Morpheus.Types.Internal.AST: fromDataType :: (DataTyCon () -> v) -> DataType -> v
+ Data.Morpheus.Types.Internal.AST: getDataType :: Failure error m => Key -> DataTypeLib -> error -> m DataType
+ Data.Morpheus.Types.Internal.AST: getOperationDataType :: Operation a b -> DataTypeLib -> Validation DataObject
+ Data.Morpheus.Types.Internal.AST: getOperationName :: Maybe Key -> Key
+ Data.Morpheus.Types.Internal.AST: gqlBoolean :: GQLValue a => Bool -> a
+ Data.Morpheus.Types.Internal.AST: gqlList :: GQLValue a => [a] -> a
+ Data.Morpheus.Types.Internal.AST: gqlNull :: GQLValue a => a
+ Data.Morpheus.Types.Internal.AST: gqlObject :: GQLValue a => [(Text, a)] -> a
+ Data.Morpheus.Types.Internal.AST: gqlScalar :: GQLValue a => ScalarValue -> a
+ Data.Morpheus.Types.Internal.AST: gqlString :: GQLValue a => Text -> a
+ Data.Morpheus.Types.Internal.AST: initTypeLib :: (Key, DataObject) -> DataTypeLib
+ Data.Morpheus.Types.Internal.AST: insertType :: (Key, DataType) -> TypeUpdater
+ Data.Morpheus.Types.Internal.AST: instance GHC.Show.Show Data.Morpheus.Types.Internal.AST.GQLQuery
+ Data.Morpheus.Types.Internal.AST: instance Language.Haskell.TH.Syntax.Lift Data.Morpheus.Types.Internal.AST.GQLQuery
+ Data.Morpheus.Types.Internal.AST: isDefaultTypeName :: Key -> Bool
+ Data.Morpheus.Types.Internal.AST: isEntNode :: DataType -> Bool
+ Data.Morpheus.Types.Internal.AST: isFieldNullable :: DataField -> Bool
+ Data.Morpheus.Types.Internal.AST: isInput :: DataTypeKind -> Bool
+ Data.Morpheus.Types.Internal.AST: isNullable :: [TypeWrapper] -> Bool
+ Data.Morpheus.Types.Internal.AST: isObject :: DataTypeKind -> Bool
+ Data.Morpheus.Types.Internal.AST: isOutputObject :: DataTypeKind -> Bool
+ Data.Morpheus.Types.Internal.AST: isPrimitiveTypeName :: Key -> Bool
+ Data.Morpheus.Types.Internal.AST: isSchemaTypeName :: Key -> Bool
+ Data.Morpheus.Types.Internal.AST: isSubscription :: DataTypeKind -> Bool
+ Data.Morpheus.Types.Internal.AST: isTypeDefined :: Key -> DataTypeLib -> Maybe DataFingerprint
+ Data.Morpheus.Types.Internal.AST: isWeaker :: [TypeWrapper] -> [TypeWrapper] -> Bool
+ Data.Morpheus.Types.Internal.AST: kindOf :: DataType -> DataTypeKind
+ Data.Morpheus.Types.Internal.AST: lookupDataObject :: (Monad m, Failure e m) => e -> Key -> DataTypeLib -> m DataObject
+ Data.Morpheus.Types.Internal.AST: lookupDataType :: Key -> DataTypeLib -> Maybe DataType
+ Data.Morpheus.Types.Internal.AST: lookupDataUnion :: (Monad m, Failure e m) => e -> Key -> DataTypeLib -> m DataUnion
+ Data.Morpheus.Types.Internal.AST: lookupDeprecated :: Meta -> Maybe Directive
+ Data.Morpheus.Types.Internal.AST: lookupDeprecatedReason :: Directive -> Maybe Key
+ Data.Morpheus.Types.Internal.AST: lookupField :: Failure error m => Key -> [(Key, field)] -> error -> m field
+ Data.Morpheus.Types.Internal.AST: lookupFieldAsSelectionSet :: (Monad m, Failure GQLErrors m) => Position -> Key -> DataTypeLib -> DataField -> m DataObject
+ Data.Morpheus.Types.Internal.AST: lookupInputType :: Failure e m => Key -> DataTypeLib -> e -> m DataType
+ Data.Morpheus.Types.Internal.AST: lookupSelectionField :: Failure GQLErrors Validation => Position -> Key -> DataObject -> Validation DataField
+ Data.Morpheus.Types.Internal.AST: lookupType :: Failure e m => e -> [(Key, a)] -> Key -> m a
+ Data.Morpheus.Types.Internal.AST: lookupUnionTypes :: (Monad m, Failure GQLErrors m) => Position -> Key -> DataTypeLib -> DataField -> m [DataObject]
+ Data.Morpheus.Types.Internal.AST: newtype DataValidator
+ Data.Morpheus.Types.Internal.AST: replaceValue :: Value -> Value
+ Data.Morpheus.Types.Internal.AST: sysTypes :: [Key]
+ Data.Morpheus.Types.Internal.AST: toGQLWrapper :: [TypeWrapper] -> [DataTypeWrapper]
+ Data.Morpheus.Types.Internal.AST: toHSWrappers :: [DataTypeWrapper] -> [TypeWrapper]
+ Data.Morpheus.Types.Internal.AST: toListField :: DataField -> DataField
+ Data.Morpheus.Types.Internal.AST: toNullableField :: DataField -> DataField
+ Data.Morpheus.Types.Internal.AST: type Arguments = Collection Argument
+ Data.Morpheus.Types.Internal.AST: type Collection a = [(Key, a)]
+ Data.Morpheus.Types.Internal.AST: type DataArgument = DataField
+ Data.Morpheus.Types.Internal.AST: type DataArguments = [(Key, DataArgument)]
+ Data.Morpheus.Types.Internal.AST: type DataEnum = DataTyCon [DataEnumValue]
+ Data.Morpheus.Types.Internal.AST: type DataObject = DataTyCon [(Key, DataField)]
+ Data.Morpheus.Types.Internal.AST: type DataScalar = DataTyCon DataValidator
+ Data.Morpheus.Types.Internal.AST: type DataUnion = DataTyCon [Key]
+ Data.Morpheus.Types.Internal.AST: type DefaultValue = Maybe Value
+ Data.Morpheus.Types.Internal.AST: type Description = Key
+ Data.Morpheus.Types.Internal.AST: type FragmentLib = [(Key, Fragment)]
+ Data.Morpheus.Types.Internal.AST: type Key = Text
+ Data.Morpheus.Types.Internal.AST: type MUTATION = 'Mutation
+ Data.Morpheus.Types.Internal.AST: type Message = Text
+ Data.Morpheus.Types.Internal.AST: type Name = Key
+ Data.Morpheus.Types.Internal.AST: type Object = [(Text, Value)]
+ Data.Morpheus.Types.Internal.AST: type QUERY = 'Query
+ Data.Morpheus.Types.Internal.AST: type RawArguments = Collection RawArgument
+ Data.Morpheus.Types.Internal.AST: type RawOperation = Operation VariableDefinitions RawSelectionSet
+ Data.Morpheus.Types.Internal.AST: type RawSelection' a = Selection RawArguments a
+ Data.Morpheus.Types.Internal.AST: type RawSelectionSet = Collection RawSelection
+ Data.Morpheus.Types.Internal.AST: type SUBSCRIPTION = 'Subscription
+ Data.Morpheus.Types.Internal.AST: type SelectionSet = Collection ValidSelection
+ Data.Morpheus.Types.Internal.AST: type TypeUpdater = LibUpdater DataTypeLib
+ Data.Morpheus.Types.Internal.AST: type ValidOperation = Operation Arguments SelectionSet
+ Data.Morpheus.Types.Internal.AST: type ValidSelection = Selection Arguments SelectionRec
+ Data.Morpheus.Types.Internal.AST: type ValidVariables = Collection (Variable Value)
+ Data.Morpheus.Types.Internal.AST: type VariableDefinitions = Collection (Variable DefaultValue)
+ Data.Morpheus.Types.Internal.AST: type Variables = Map Key Value
- Data.Morpheus.Client: defineByDocument :: IO ByteString -> (GQLQueryRoot, String) -> Q [Dec]
+ Data.Morpheus.Client: defineByDocument :: IO ByteString -> (GQLQuery, String) -> Q [Dec]
- Data.Morpheus.Client: defineByDocumentFile :: String -> (GQLQueryRoot, String) -> Q [Dec]
+ Data.Morpheus.Client: defineByDocumentFile :: String -> (GQLQuery, String) -> Q [Dec]
- Data.Morpheus.Client: defineByIntrospection :: IO ByteString -> (GQLQueryRoot, String) -> Q [Dec]
+ Data.Morpheus.Client: defineByIntrospection :: IO ByteString -> (GQLQuery, String) -> Q [Dec]
- Data.Morpheus.Client: defineByIntrospectionFile :: String -> (GQLQueryRoot, String) -> Q [Dec]
+ Data.Morpheus.Client: defineByIntrospectionFile :: String -> (GQLQuery, String) -> Q [Dec]
- Data.Morpheus.Client: defineQuery :: IO (Validation DataTypeLib) -> (GQLQueryRoot, String) -> Q [Dec]
+ Data.Morpheus.Client: defineQuery :: IO (Validation DataTypeLib) -> (GQLQuery, String) -> Q [Dec]
- Data.Morpheus.Kind: data ResContext (kind :: GQL_KIND) (operation :: OperationType) (m :: * -> *) event value
+ Data.Morpheus.Kind: data ResContext (kind :: GQL_KIND) (operation :: OperationType) event (m :: * -> *) value
- Data.Morpheus.Types: Errors :: [JSONError] -> GQLResponse
+ Data.Morpheus.Types: Errors :: [GQLError] -> GQLResponse
- Data.Morpheus.Types: GQLRootResolver :: query (Resolver QUERY m event) -> mut (Resolver MUTATION m event) -> sub (Resolver SUBSCRIPTION m event) -> GQLRootResolver event
+ Data.Morpheus.Types: GQLRootResolver :: query (Resolver QUERY event m) -> mut (Resolver MUTATION event m) -> sub (Resolver SUBSCRIPTION event m) -> GQLRootResolver event
- Data.Morpheus.Types: [MutResolver] :: {mutEvents :: [event], mutResolver :: ExceptT String m value} -> Resolver MUTATION m event value
+ Data.Morpheus.Types: [MutResolver] :: {unMutResolver :: ResultT event String 'True m ([event], value)} -> Resolver MUTATION event m value
- Data.Morpheus.Types: [QueryResolver] :: {unQueryResolver :: ExceptT String m value} -> Resolver QUERY m event value
+ Data.Morpheus.Types: [QueryResolver] :: {unQueryResolver :: ResultT () String 'True m value} -> Resolver QUERY event m value
- Data.Morpheus.Types: [SubResolver] :: {subChannels :: [StreamChannel event], subResolver :: event -> Resolver QUERY m event value} -> Resolver SUBSCRIPTION m event value
+ Data.Morpheus.Types: [SubResolver] :: {subChannels :: [StreamChannel event], subResolver :: event -> Resolver QUERY event m value} -> Resolver SUBSCRIPTION event m value
- Data.Morpheus.Types: [mutationResolver] :: GQLRootResolver event -> mut (Resolver MUTATION m event)
+ Data.Morpheus.Types: [mutationResolver] :: GQLRootResolver event -> mut (Resolver MUTATION event m)
- Data.Morpheus.Types: [queryResolver] :: GQLRootResolver event -> query (Resolver QUERY m event)
+ Data.Morpheus.Types: [queryResolver] :: GQLRootResolver event -> query (Resolver QUERY event m)
- Data.Morpheus.Types: [subscriptionResolver] :: GQLRootResolver event -> sub (Resolver SUBSCRIPTION m event)
+ Data.Morpheus.Types: [subscriptionResolver] :: GQLRootResolver event -> sub (Resolver SUBSCRIPTION event m)
- Data.Morpheus.Types: constMutRes :: Monad m => [e] -> a -> args -> MutRes m e a
+ Data.Morpheus.Types: constMutRes :: Monad m => [e] -> a -> args -> MutRes e m a
- Data.Morpheus.Types: constRes :: (PureOperation o, Monad m) => b -> a -> Resolver o m e b
+ Data.Morpheus.Types: constRes :: (LiftEither o Resolver, Monad m) => b -> a -> Resolver o e m b
- Data.Morpheus.Types: data Resolver (o :: OperationType) (m :: * -> *) event value
+ Data.Morpheus.Types: data Resolver (o :: OperationType) event (m :: * -> *) value
- Data.Morpheus.Types: type IOMutRes = MutRes IO
+ Data.Morpheus.Types: type IOMutRes e = MutRes e IO
- Data.Morpheus.Types: type IORes = Res IO
+ Data.Morpheus.Types: type IORes e = Res e IO
- Data.Morpheus.Types: type IOSubRes = SubRes IO
+ Data.Morpheus.Types: type IOSubRes e = SubRes e IO
Files
- README.md +41/−29
- changelog.md +133/−0
- morpheus-graphql.cabal +24/−19
- src/Data/Morpheus.hs +4/−2
- src/Data/Morpheus/Client.hs +32/−25
- src/Data/Morpheus/Document.hs +55/−31
- src/Data/Morpheus/Error/Arguments.hs +33/−27
- src/Data/Morpheus/Error/Client/Client.hs +50/−6
- src/Data/Morpheus/Error/Document/Interface.hs +32/−16
- src/Data/Morpheus/Error/Fragment.hs +52/−49
- src/Data/Morpheus/Error/Input.hs +33/−28
- src/Data/Morpheus/Error/Internal.hs +24/−15
- src/Data/Morpheus/Error/Mutation.hs +7/−4
- src/Data/Morpheus/Error/Schema.hs +11/−8
- src/Data/Morpheus/Error/Selection.hs +47/−24
- src/Data/Morpheus/Error/Subscription.hs +7/−4
- src/Data/Morpheus/Error/Utils.hs +26/−18
- src/Data/Morpheus/Error/Variable.hs +53/−50
- src/Data/Morpheus/Execution/Client/Aeson.hs +108/−90
- src/Data/Morpheus/Execution/Client/Build.hs +64/−34
- src/Data/Morpheus/Execution/Client/Compile.hs +48/−25
- src/Data/Morpheus/Execution/Client/Fetch.hs +29/−19
- src/Data/Morpheus/Execution/Client/Selection.hs +282/−181
- src/Data/Morpheus/Execution/Document/Compile.hs +42/−28
- src/Data/Morpheus/Execution/Document/Convert.hs +166/−126
- src/Data/Morpheus/Execution/Document/Declare.hs +54/−38
- src/Data/Morpheus/Execution/Document/Decode.hs +28/−15
- src/Data/Morpheus/Execution/Document/Encode.hs +85/−52
- src/Data/Morpheus/Execution/Document/GQLType.hs +75/−55
- src/Data/Morpheus/Execution/Document/Introspect.hs +24/−13
- src/Data/Morpheus/Execution/Internal/Declare.hs +76/−58
- src/Data/Morpheus/Execution/Internal/Decode.hs +50/−38
- src/Data/Morpheus/Execution/Internal/GraphScanner.hs +0/−14
- src/Data/Morpheus/Execution/Internal/Utils.hs +13/−8
- src/Data/Morpheus/Execution/Server/Decode.hs +57/−35
- src/Data/Morpheus/Execution/Server/Encode.hs +157/−95
- src/Data/Morpheus/Execution/Server/Generics/EnumRep.hs +12/−8
- src/Data/Morpheus/Execution/Server/Interpreter.hs +49/−28
- src/Data/Morpheus/Execution/Server/Introspect.hs +117/−94
- src/Data/Morpheus/Execution/Server/Resolve.hs +188/−112
- src/Data/Morpheus/Execution/Subscription/ClientRegister.hs +84/−69
- src/Data/Morpheus/Kind.hs +5/−3
- src/Data/Morpheus/Parsing/Document/Parser.hs +28/−19
- src/Data/Morpheus/Parsing/Document/TypeSystem.hs +137/−129
- src/Data/Morpheus/Parsing/Internal/Create.hs +0/−67
- src/Data/Morpheus/Parsing/Internal/Internal.hs +40/−19
- src/Data/Morpheus/Parsing/Internal/Pattern.hs +101/−40
- src/Data/Morpheus/Parsing/Internal/Terms.hs +103/−72
- src/Data/Morpheus/Parsing/Internal/Value.hs +64/−49
- src/Data/Morpheus/Parsing/JSONSchema/Parse.hs +81/−54
- src/Data/Morpheus/Parsing/Request/Arguments.hs +37/−19
- src/Data/Morpheus/Parsing/Request/Operation.hs +86/−57
- src/Data/Morpheus/Parsing/Request/Parser.hs +51/−34
- src/Data/Morpheus/Parsing/Request/Selection.hs +95/−62
- src/Data/Morpheus/Rendering/Haskell/Render.hs +0/−87
- src/Data/Morpheus/Rendering/Haskell/Terms.hs +0/−85
- src/Data/Morpheus/Rendering/Haskell/Types.hs +0/−93
- src/Data/Morpheus/Rendering/Haskell/Values.hs +0/−84
- src/Data/Morpheus/Rendering/RenderGQL.hs +71/−42
- src/Data/Morpheus/Rendering/RenderIntrospection.hs +190/−151
- src/Data/Morpheus/Schema/Schema.hs +8/−5
- src/Data/Morpheus/Schema/SchemaAPI.hs +75/−48
- src/Data/Morpheus/Schema/TypeKind.hs +5/−4
- src/Data/Morpheus/Server.hs +75/−49
- src/Data/Morpheus/Types.hs +44/−20
- src/Data/Morpheus/Types/Custom.hs +0/−44
- src/Data/Morpheus/Types/GQLScalar.hs +10/−5
- src/Data/Morpheus/Types/GQLType.hs +41/−27
- src/Data/Morpheus/Types/ID.hs +12/−8
- src/Data/Morpheus/Types/IO.hs +35/−22
- src/Data/Morpheus/Types/Internal/AST.hs +162/−0
- src/Data/Morpheus/Types/Internal/AST/Base.hs +55/−0
- src/Data/Morpheus/Types/Internal/AST/Data.hs +659/−0
- src/Data/Morpheus/Types/Internal/AST/Operation.hs +48/−22
- src/Data/Morpheus/Types/Internal/AST/RawSelection.hs +0/−61
- src/Data/Morpheus/Types/Internal/AST/Selection.hs +62/−14
- src/Data/Morpheus/Types/Internal/AST/Value.hs +156/−0
- src/Data/Morpheus/Types/Internal/Base.hs +0/−58
- src/Data/Morpheus/Types/Internal/Data.hs +0/−338
- src/Data/Morpheus/Types/Internal/DataD.hs +0/−37
- src/Data/Morpheus/Types/Internal/Resolver.hs +0/−263
- src/Data/Morpheus/Types/Internal/Resolving.hs +34/−0
- src/Data/Morpheus/Types/Internal/Resolving/Core.hs +160/−0
- src/Data/Morpheus/Types/Internal/Resolving/Resolver.hs +382/−0
- src/Data/Morpheus/Types/Internal/Stream.hs +0/−117
- src/Data/Morpheus/Types/Internal/TH.hs +5/−17
- src/Data/Morpheus/Types/Internal/Validation.hs +0/−34
- src/Data/Morpheus/Types/Internal/Value.hs +0/−195
- src/Data/Morpheus/Types/Internal/WebSocket.hs +15/−10
- src/Data/Morpheus/Types/Types.hs +38/−25
- src/Data/Morpheus/Validation/Document/Validation.hs +65/−41
- src/Data/Morpheus/Validation/Internal/Utils.hs +23/−53
- src/Data/Morpheus/Validation/Internal/Value.hs +98/−62
- src/Data/Morpheus/Validation/Query/Arguments.hs +137/−80
- src/Data/Morpheus/Validation/Query/Fragment.hs +92/−67
- src/Data/Morpheus/Validation/Query/Selection.hs +190/−122
- src/Data/Morpheus/Validation/Query/Utils/Selection.hs +0/−32
- src/Data/Morpheus/Validation/Query/Validation.hs +45/−38
- src/Data/Morpheus/Validation/Query/Variable.hs +132/−85
- test/Feature/Holistic/API.gql +0/−51
- test/Feature/Holistic/API.hs +43/−30
- test/Feature/Holistic/cases.json +24/−0
- test/Feature/Holistic/fragment/loopingFragment/response.json +4/−13
- test/Feature/Holistic/introspection/deprecated/enumValue/query.gql +9/−0
- test/Feature/Holistic/introspection/deprecated/enumValue/response.json +15/−0
- test/Feature/Holistic/introspection/deprecated/field/query.gql +9/−0
- test/Feature/Holistic/introspection/deprecated/field/response.json +19/−0
- test/Feature/Holistic/introspection/description/enum/query.gql +9/−0
- test/Feature/Holistic/introspection/description/enum/response.json +12/−0
- test/Feature/Holistic/introspection/description/inputObject/query.gql +9/−0
- test/Feature/Holistic/introspection/description/inputObject/response.json +17/−0
- test/Feature/Holistic/introspection/description/object/query.gql +13/−0
- test/Feature/Holistic/introspection/description/object/response.json +26/−0
- test/Feature/Holistic/introspection/description/union/query.gql +5/−0
- test/Feature/Holistic/introspection/description/union/response.json +7/−0
- test/Feature/Holistic/introspection/kinds/ENUM/response.json +4/−12
- test/Feature/Holistic/introspection/kinds/OBJECT/response.json +7/−22
- test/Feature/Holistic/parsing/complex/response.json +21/−6
- test/Feature/Holistic/parsing/extraCommas/response.json +25/−6
- test/Feature/Holistic/schema.gql +103/−0
- test/Feature/InputType/API.hs +22/−13
- test/Rendering/TestSchemaRendering.hs +1/−1
README.md view
@@ -29,7 +29,7 @@ resolver: lts-14.8 extra-deps:- - morpheus-graphql-0.6.2+ - morpheus-graphql-0.7.0 ``` As Morpheus is quite new, make sure stack can find morpheus-graphql by running `stack upgrade` and `stack update`@@ -45,9 +45,15 @@ deity(name: String!): Deity! } +"""+Description for Deity+""" type Deity {+ """+ Description for name+ """ name: String!- power: String+ power: String String! @deprecated(reason: "some reason for") } ``` @@ -95,6 +101,8 @@ Template Haskell Generates types: `Query` , `Deity`, `DeityArgs`, that can be used by `rootResolver` +`descriptions` and `deprecations` will be displayed in intropsection.+ `importGQLDocumentWithNamespace` will generate Types with namespaced fields. if you don't need napespacing use `importGQLDocument` ### with Native Haskell Types@@ -138,7 +146,7 @@ ```haskell resolveDeity :: DeityArgs -> IORes e Deity-resolveDeity DeityArgs { name, mythology } = liftEitherM $ dbDeity name mythology+resolveDeity DeityArgs { name, mythology } = liftEither $ dbDeity name mythology askDB :: Text -> Maybe Text -> IO (Either String Deity) askDB = ...@@ -281,25 +289,23 @@ ### Mutations In addition to queries, Morpheus also supports mutations. The behave just like regular queries and are defined similarly:-Just exchange deriving `GQLQuery` for `GQLMutation` and declare them separately at the `GQLRootResolver` definition ```haskell newtype Mutation m = Mutation- { createDeity :: Form -> m Deity+ { createDeity :: MutArgs -> m Deity } deriving (Generic, GQLType) -createDeityMutation :: Form -> m (Deity m)-createDeityMutation = ...--rootResolver :: GQLRootResolver IO Query Mutation Undefined+rootResolver :: GQLRootResolver IO () Query Mutation Undefined rootResolver = GQLRootResolver { queryResolver = Query {...}- , mutationResolver = Mutation {- createDeity = createDeityMutation- }+ , mutationResolver = Mutation { createDeity } , subscriptionResolver = Undefined }+ where+ -- Mutation Without Event Triggering+ createDeity :: MutArgs -> ResolveM () IO Deity+ createDeity_args = lift setDBAddress gqlApi :: ByteString -> IO ByteString gqlApi = interpreter rootResolver@@ -321,6 +327,8 @@ = ContentA Int | ContentB Text +type MyEvent = Event Channel Content+ newtype Query m = Query { deity :: () -> m Deity } deriving (Generic)@@ -329,29 +337,33 @@ { createDeity :: () -> m Deity } deriving (Generic) -newtype Subscription m = Subscription+newtype Subscription (m :: * -> * ) = Subscription { newDeity :: () -> m Deity } deriving (Generic) type APIEvent = Event Channel Content rootResolver :: GQLRootResolver IO APIEvent Query Mutation Subscription-rootResolver =- GQLRootResolver- { queryResolver = Query {deity = const fetchDeity}- , mutationResolver = Mutation {createDeity}- , subscriptionResolver = Subscription {newDeity}- }- where- createDeity _args = MutResolver events updateDeity- where- events = [Event {channels = [ChannelA], content = ContentA 1}]- updateDeity = updateDBDeity- newDeity _args = SubResolver [ChannelA] subResolver- where- subResolver (Event [ChannelA] (ContentA _value)) = fetchDeity -- resolve New State- subResolver (Event [ChannelA] (ContentB _value)) = fetchDeity -- resolve New State- subResolver _ = fetchDeity -- Resolve Old State+rootResolver = GQLRootResolver+ { queryResolver = Query { deity }+ , mutationResolver = Mutation { createDeity }+ , subscriptionResolver = Subscription { newDeity }+ }+ where+ deity _args = fetchDeity+ -- Mutation Without Event Triggering+ createDeity :: () -> ResolveM EVENT IO Address+ createDeity _args = MutResolver \$ do+ value <- lift dbCreateDeity+ pure (+ [Event { channels = [ChannelA], content = ContentA 1 }],+ value+ )+ newDeity _args = SubResolver [ChannelA] subResolver+ where+ subResolver (Event [ChannelA] (ContentA _value)) = fetchDeity -- resolve New State+ subResolver (Event [ChannelA] (ContentB _value)) = fetchDeity -- resolve New State+ subResolver _ = fetchDeity -- Resolve Old State ``` ## Morpheus `GraphQL Client` with Template haskell QuasiQuotes
changelog.md view
@@ -1,3 +1,136 @@+## [0.7.0] - 24.11.2019++## Removed++- `toMorpheusHaskellAPi` from `Data.Morpheus.Document` functionality will be migrated in `morpheus-graphql-cli`++## Changed++- `liftM` to `MonadTrans` instance method `lift`++- `liftEitherM` to `liftEither`++- `Resolver operation m event value` -> `Resolver operation event m value` , monad trans needs that last 2 type arguments are monad and value that why it was necessary++- exposed `Data.Morpheus.Types.Internal.AST`++- Mutation Resolver was changed from++```+resolver :: () -> ResolveM EVENT IO Address+resolver = MutResolver {+ mutEvents = [someEventForSubscription],+ mutResolver = lift setDBAddress+}+```++```haskell+-- Mutation Wit Event Triggering : sends events to subscription+resolver :: () -> ResolveM EVENT IO Address+resolver = MutResolver \$ do+ value <- lift setDBAddress+ pure ([someEventForSubscription], value)+-- or+-- Mutation Without Event Triggering+resolver :: () -> ResolveM EVENT IO Address+resolver _args = lift setDBAddress+```++### Added++- added `parseDSL` to `Data.Morpheus.Document`++- GraphQL SDL support fully supports descriptions: onTypes, fields , args ...+ with (enums, inputObjects , union, object)+ for example :++ ```gql+ """+ Description for Type Address+ """+ type Address {+ """+ Description for Field city+ """+ city: String!+ street(+ """+ Description argument id+ """+ id: ID!+ ): Int!+ }+ ```++ ###### GraphQL SDL++ ```gql+ type User {+ name: String! @deprecated(reason: "some reason")+ }+ ```++ will displayed in introspection++ ###### introspection.json++ ```json+ {+ "data": {+ "__type": {+ "fields": [+ {+ "name": "city",+ "isDeprecated": true,+ "deprecationReason": "test deprecation field with reason"+ }+ ]+ }+ }+ }+ ```++- basic support of directive `@deprecated` on `enumValue` and object `field`, only on introspection++- GraphQL Client deprecation warnings++ on type++ ```gql+ type Human {+ humanName: String!+ lifetime: Lifetime! @deprecated(reason: "some reason")+ profession: Profession+ }+ ```++ compiler output:++ ```+ warning:+ Morpheus Client Warning:+ {+ "message":"the field \"Human.lifetime\" is deprecated. some reason",+ "locations":[{"line":24,"column":15}]+ }+ ```++- new helper resolver types aliases:++ - ResolveQ : for Query+ - ResolveM : for Mutation+ - ResolveS : for Subscription++ `ResolveM EVENT IO Address` is same as `MutRes EVENT IO (Address (MutRes EVENT IO))`++ is helpfull wenn you want to resolve GraphQL object++### Fixed++- added missing Monad instance for Mutation resolver+- `defineByIntrospectionFile` does not breaks if schema contains interfaces+- Morpheus Client supports `Subscription` and `Mutation`operations+ ## [0.6.2] - 2.11.2019 ## Added
morpheus-graphql.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 6d77e1db039ce6a4a5ef7761d0b18beb9ea7067fbb99372bd3a97510e4da4a43+-- hash: a97de9ffea5713a01a84ffef6bd7ee41104d3d898f2d6a90ae0ef0be27ba7568 name: morpheus-graphql-version: 0.6.2+version: 0.7.0 synopsis: Morpheus GraphQL description: Build GraphQL APIs with your favourite functional language! category: web, graphql@@ -23,7 +23,6 @@ changelog.md README.md data-files:- test/Feature/Holistic/API.gql test/Feature/Holistic/arguments/nameConflict/query.gql test/Feature/Holistic/arguments/undefinedArgument/query.gql test/Feature/Holistic/arguments/unknownArguments/query.gql@@ -39,6 +38,12 @@ test/Feature/Holistic/introspection/defaultTypes/ID/query.gql test/Feature/Holistic/introspection/defaultTypes/Int/query.gql test/Feature/Holistic/introspection/defaultTypes/String/query.gql+ test/Feature/Holistic/introspection/deprecated/enumValue/query.gql+ test/Feature/Holistic/introspection/deprecated/field/query.gql+ test/Feature/Holistic/introspection/description/enum/query.gql+ test/Feature/Holistic/introspection/description/inputObject/query.gql+ test/Feature/Holistic/introspection/description/object/query.gql+ test/Feature/Holistic/introspection/description/union/query.gql test/Feature/Holistic/introspection/kinds/ENUM/query.gql test/Feature/Holistic/introspection/kinds/INPUT_OBJECT/query.gql test/Feature/Holistic/introspection/kinds/OBJECT/query.gql@@ -68,6 +73,7 @@ test/Feature/Holistic/parsing/missingCloseBrace/query.gql test/Feature/Holistic/parsing/notNullSpacing/query.gql test/Feature/Holistic/parsing/singleLineComments/query.gql+ test/Feature/Holistic/schema.gql test/Feature/Holistic/selection/AliasNameConflict/query.gql test/Feature/Holistic/selection/AliasResolve/query.gql test/Feature/Holistic/selection/AliasUnknownField/query.gql@@ -133,6 +139,12 @@ test/Feature/Holistic/introspection/defaultTypes/ID/response.json test/Feature/Holistic/introspection/defaultTypes/Int/response.json test/Feature/Holistic/introspection/defaultTypes/String/response.json+ test/Feature/Holistic/introspection/deprecated/enumValue/response.json+ test/Feature/Holistic/introspection/deprecated/field/response.json+ test/Feature/Holistic/introspection/description/enum/response.json+ test/Feature/Holistic/introspection/description/inputObject/response.json+ test/Feature/Holistic/introspection/description/object/response.json+ test/Feature/Holistic/introspection/description/union/response.json test/Feature/Holistic/introspection/kinds/ENUM/response.json test/Feature/Holistic/introspection/kinds/INPUT_OBJECT/response.json test/Feature/Holistic/introspection/kinds/OBJECT/response.json@@ -242,6 +254,7 @@ Data.Morpheus.Server Data.Morpheus.Document Data.Morpheus.Client+ Data.Morpheus.Types.Internal.AST other-modules: Data.Morpheus.Error.Arguments Data.Morpheus.Error.Client.Client@@ -269,7 +282,6 @@ Data.Morpheus.Execution.Document.Introspect Data.Morpheus.Execution.Internal.Declare Data.Morpheus.Execution.Internal.Decode- Data.Morpheus.Execution.Internal.GraphScanner Data.Morpheus.Execution.Internal.Utils Data.Morpheus.Execution.Server.Decode Data.Morpheus.Execution.Server.Encode@@ -281,7 +293,6 @@ Data.Morpheus.Execution.Subscription.ClientRegister Data.Morpheus.Parsing.Document.Parser Data.Morpheus.Parsing.Document.TypeSystem- Data.Morpheus.Parsing.Internal.Create Data.Morpheus.Parsing.Internal.Internal Data.Morpheus.Parsing.Internal.Pattern Data.Morpheus.Parsing.Internal.Terms@@ -292,30 +303,23 @@ Data.Morpheus.Parsing.Request.Operation Data.Morpheus.Parsing.Request.Parser Data.Morpheus.Parsing.Request.Selection- Data.Morpheus.Rendering.Haskell.Render- Data.Morpheus.Rendering.Haskell.Terms- Data.Morpheus.Rendering.Haskell.Types- Data.Morpheus.Rendering.Haskell.Values Data.Morpheus.Rendering.RenderGQL Data.Morpheus.Rendering.RenderIntrospection Data.Morpheus.Schema.Schema Data.Morpheus.Schema.SchemaAPI Data.Morpheus.Schema.TypeKind- Data.Morpheus.Types.Custom Data.Morpheus.Types.GQLScalar Data.Morpheus.Types.GQLType Data.Morpheus.Types.ID+ Data.Morpheus.Types.Internal.AST.Base+ Data.Morpheus.Types.Internal.AST.Data Data.Morpheus.Types.Internal.AST.Operation- Data.Morpheus.Types.Internal.AST.RawSelection Data.Morpheus.Types.Internal.AST.Selection- Data.Morpheus.Types.Internal.Base- Data.Morpheus.Types.Internal.Data- Data.Morpheus.Types.Internal.DataD- Data.Morpheus.Types.Internal.Resolver- Data.Morpheus.Types.Internal.Stream+ Data.Morpheus.Types.Internal.AST.Value+ Data.Morpheus.Types.Internal.Resolving+ Data.Morpheus.Types.Internal.Resolving.Core+ Data.Morpheus.Types.Internal.Resolving.Resolver Data.Morpheus.Types.Internal.TH- Data.Morpheus.Types.Internal.Validation- Data.Morpheus.Types.Internal.Value Data.Morpheus.Types.Internal.WebSocket Data.Morpheus.Types.IO Data.Morpheus.Types.Types@@ -325,7 +329,6 @@ Data.Morpheus.Validation.Query.Arguments Data.Morpheus.Validation.Query.Fragment Data.Morpheus.Validation.Query.Selection- Data.Morpheus.Validation.Query.Utils.Selection Data.Morpheus.Validation.Query.Validation Data.Morpheus.Validation.Query.Variable Paths_morpheus_graphql@@ -342,6 +345,7 @@ , scientific >=0.3.6.2 && <0.4 , template-haskell >=2.0 && <=2.16 , text >=1.2.3.0 && <1.3+ , th-lift-instances >=0.1.1 && <=0.2.0 , transformers >=0.3.0.0 && <0.6 , unordered-containers >=0.2.8.0 && <0.3 , uuid >=1.0 && <=1.4@@ -381,6 +385,7 @@ , tasty-hunit , template-haskell >=2.0 && <=2.16 , text >=1.2.3.0 && <1.3+ , th-lift-instances >=0.1.1 && <=0.2.0 , transformers >=0.3.0.0 && <0.6 , unordered-containers >=0.2.8.0 && <0.3 , uuid >=1.0 && <=1.4
src/Data/Morpheus.hs view
@@ -1,6 +1,8 @@ -- | Build GraphQL APIs with your favourite functional language! module Data.Morpheus ( Interpreter(..)- ) where+ )+where -import Data.Morpheus.Execution.Server.Interpreter (Interpreter (..))+import Data.Morpheus.Execution.Server.Interpreter+ ( Interpreter(..) )
src/Data/Morpheus/Client.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} module Data.Morpheus.Client@@ -10,38 +9,46 @@ , defineByDocumentFile , defineByIntrospection , defineByIntrospectionFile- ) where+ )+where -import Data.ByteString.Lazy (ByteString)-import qualified Data.ByteString.Lazy as L (readFile)+import Data.ByteString.Lazy ( ByteString )+import qualified Data.ByteString.Lazy as L+ ( readFile ) import Language.Haskell.TH import Language.Haskell.TH.Quote -import Data.Morpheus.Document (parseFullGQLDocument)+import Data.Morpheus.Document ( parseFullGQLDocument ) -- MORPHEUS-import Data.Morpheus.Execution.Client.Build (defineQuery)-import Data.Morpheus.Execution.Client.Compile (compileSyntax)-import Data.Morpheus.Execution.Client.Fetch (Fetch (..))-import Data.Morpheus.Parsing.JSONSchema.Parse (decodeIntrospection)-import Data.Morpheus.Types.Internal.Data (DataTypeLib)-import Data.Morpheus.Types.Internal.Validation (Validation)-import Data.Morpheus.Types.Types (GQLQueryRoot)+import Data.Morpheus.Execution.Client.Build+ ( defineQuery )+import Data.Morpheus.Execution.Client.Compile+ ( compileSyntax )+import Data.Morpheus.Execution.Client.Fetch+ ( Fetch(..) )+import Data.Morpheus.Parsing.JSONSchema.Parse+ ( decodeIntrospection )+import Data.Morpheus.Types.Internal.Resolving+ ( Validation )+import Data.Morpheus.Types.Internal.AST+ ( GQLQuery+ , DataTypeLib+ ) gql :: QuasiQuoter-gql =- QuasiQuoter- { quoteExp = compileSyntax- , quotePat = notHandled "Patterns"- , quoteType = notHandled "Types"- , quoteDec = notHandled "Declarations"- }- where- notHandled things = error $ things ++ " are not supported by the GraphQL QuasiQuoter"+gql = QuasiQuoter { quoteExp = compileSyntax+ , quotePat = notHandled "Patterns"+ , quoteType = notHandled "Types"+ , quoteDec = notHandled "Declarations"+ }+ where+ notHandled things =+ error $ things ++ " are not supported by the GraphQL QuasiQuoter" -defineByDocumentFile :: String -> (GQLQueryRoot, String) -> Q [Dec]+defineByDocumentFile :: String -> (GQLQuery, String) -> Q [Dec] defineByDocumentFile = defineByDocument . L.readFile -defineByIntrospectionFile :: String -> (GQLQueryRoot, String) -> Q [Dec]+defineByIntrospectionFile :: String -> (GQLQuery, String) -> Q [Dec] defineByIntrospectionFile = defineByIntrospection . L.readFile --@@ -49,11 +56,11 @@ -- TODO: Define By API -- Validates By Server API ---defineByDocument :: IO ByteString -> (GQLQueryRoot, String) -> Q [Dec]+defineByDocument :: IO ByteString -> (GQLQuery, String) -> Q [Dec] defineByDocument doc = defineQuery (schemaByDocument doc) schemaByDocument :: IO ByteString -> IO (Validation DataTypeLib) schemaByDocument documentGQL = parseFullGQLDocument <$> documentGQL -defineByIntrospection :: IO ByteString -> (GQLQueryRoot, String) -> Q [Dec]+defineByIntrospection :: IO ByteString -> (GQLQuery, String) -> Q [Dec] defineByIntrospection json = defineQuery (decodeIntrospection <$> json)
src/Data/Morpheus/Document.hs view
@@ -1,39 +1,64 @@+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE NamedFieldPuns #-} module Data.Morpheus.Document ( toGraphQLDocument- , toMorpheusHaskellAPi , gqlDocument , parseFullGQLDocument , importGQLDocument , importGQLDocumentWithNamespace- ) where+ , parseDSL+ )+where -import Control.Monad ((>=>))-import Data.ByteString.Lazy.Char8 (ByteString, pack)-import Data.Text (Text)-import qualified Data.Text.Lazy as LT (toStrict)-import Data.Text.Lazy.Encoding (decodeUtf8)+import Control.Monad ( (>=>) )+import Data.ByteString.Lazy.Char8 ( ByteString+ , pack+ )+import Data.Text ( Text )+import qualified Data.Text.Lazy as LT+ ( toStrict )+import Data.Text.Lazy.Encoding ( decodeUtf8 ) import Language.Haskell.TH -- -- MORPHEUS-import Data.Morpheus.Execution.Document.Compile (compileDocument, gqlDocument)-import Data.Morpheus.Execution.Server.Resolve (RootResCon, fullSchema)-import Data.Morpheus.Rendering.Haskell.Render (renderHaskellDocument)-import Data.Morpheus.Rendering.RenderGQL (renderGraphQLDocument)-import Data.Morpheus.Types (GQLRootResolver)+import Data.Morpheus.Execution.Document.Compile+ ( compileDocument+ , gqlDocument+ )+import Data.Morpheus.Execution.Server.Resolve+ ( RootResCon+ , fullSchema+ )+import Data.Morpheus.Parsing.Document.Parser+ ( parseTypes ) -import Data.Morpheus.Parsing.Document.Parser (parseTypes)-import Data.Morpheus.Parsing.Internal.Create (createDataTypeLib)-import Data.Morpheus.Schema.SchemaAPI (defaultTypes)-import Data.Morpheus.Types.Internal.Data (DataTypeLib)-import Data.Morpheus.Types.Internal.Validation (Validation)-import Data.Morpheus.Validation.Document.Validation (validatePartialDocument)+import Data.Morpheus.Rendering.RenderGQL+ ( renderGraphQLDocument )+import Data.Morpheus.Schema.SchemaAPI ( defaultTypes )+import Data.Morpheus.Types ( GQLRootResolver )+import Data.Morpheus.Types.Internal.AST+ ( DataTypeLib+ , createDataTypeLib+ )+import Data.Morpheus.Types.Internal.Resolving+ ( Validation+ , Result(..)+ )+import Data.Morpheus.Validation.Document.Validation+ ( validatePartialDocument ) + +parseDSL :: ByteString -> Either String DataTypeLib+parseDSL doc = case parseGraphQLDocument doc of+ Failure errors -> Left (show errors)+ Success { result } -> Right result++ parseDocument :: Text -> Validation DataTypeLib-parseDocument doc = parseTypes doc >>= validatePartialDocument >>= createDataTypeLib+parseDocument doc =+ parseTypes doc >>= validatePartialDocument >>= createDataTypeLib parseGraphQLDocument :: ByteString -> Validation DataTypeLib parseGraphQLDocument x = parseDocument (LT.toStrict $ decodeUtf8 x)@@ -42,20 +67,19 @@ parseFullGQLDocument = parseGraphQLDocument >=> defaultTypes -- | Generates schema.gql file from 'GQLRootResolver'-toGraphQLDocument :: RootResCon m event query mut sub => proxy (GQLRootResolver m event query mut sub) -> ByteString-toGraphQLDocument x =- case fullSchema x of- Left errors -> pack (show errors)- Right lib -> renderGraphQLDocument lib+toGraphQLDocument+ :: RootResCon m event query mut sub+ => proxy (GQLRootResolver m event query mut sub)+ -> ByteString+toGraphQLDocument x = case fullSchema x of+ Failure errors -> pack (show errors)+ Success { result = lib } -> renderGraphQLDocument lib -toMorpheusHaskellAPi :: String -> ByteString -> Either ByteString ByteString-toMorpheusHaskellAPi moduleName doc =- case parseGraphQLDocument doc of- Left errors -> Left $ pack (show errors)- Right lib -> Right $ renderHaskellDocument moduleName lib + importGQLDocument :: String -> Q [Dec] importGQLDocument src = runIO (readFile src) >>= compileDocument False importGQLDocumentWithNamespace :: String -> Q [Dec]-importGQLDocumentWithNamespace src = runIO (readFile src) >>= compileDocument True+importGQLDocumentWithNamespace src =+ runIO (readFile src) >>= compileDocument True
src/Data/Morpheus/Error/Arguments.hs view
@@ -5,13 +5,21 @@ , unknownArguments , argumentGotInvalidValue , argumentNameCollision- ) where+ )+where -import Data.Morpheus.Error.Utils (errorMessage)-import Data.Morpheus.Types.Internal.Base (EnhancedKey (..), Position)-import Data.Morpheus.Types.Internal.Validation (GQLError (..), GQLErrors)-import Data.Text (Text)-import qualified Data.Text as T (concat)+import Data.Morpheus.Error.Utils ( errorMessage )+import Data.Morpheus.Types.Internal.AST.Base+ ( Ref(..)+ , Position+ )+import Data.Morpheus.Types.Internal.Resolving.Core+ ( GQLError(..)+ , GQLErrors+ )+import Data.Text ( Text )+import qualified Data.Text as T+ ( concat ) {- ARGUMENTS:@@ -28,29 +36,27 @@ - date(name: "name") -> "Unknown argument \"name\" on field \"date\" of type \"Experience\"." -} argumentGotInvalidValue :: Text -> Text -> Position -> GQLErrors-argumentGotInvalidValue key' inputMessage' position' =- errorMessage position' text- where- text = T.concat ["Argument ", key', " got invalid value ;", inputMessage']+argumentGotInvalidValue key' inputMessage' position' = errorMessage position'+ text+ where+ text = T.concat ["Argument ", key', " got invalid value ;", inputMessage'] -unknownArguments :: Text -> [EnhancedKey] -> GQLErrors+unknownArguments :: Text -> [Ref] -> GQLErrors unknownArguments fieldName = map keyToError- where- keyToError (EnhancedKey argName pos) =- GQLError {desc = toMessage argName, positions = [pos]}- toMessage argName =- T.concat- ["Unknown Argument \"", argName, "\" on Field \"", fieldName, "\"."]+ where+ keyToError (Ref argName pos) =+ GQLError { message = toMessage argName, locations = [pos] }+ toMessage argName = T.concat+ ["Unknown Argument \"", argName, "\" on Field \"", fieldName, "\"."] -argumentNameCollision :: [EnhancedKey] -> GQLErrors+argumentNameCollision :: [Ref] -> GQLErrors argumentNameCollision = map keyToError- where- keyToError (EnhancedKey argName pos) =- GQLError {desc = toMessage argName, positions = [pos]}- toMessage argName =- T.concat ["There can Be only One Argument Named \"", argName, "\""]+ where+ keyToError (Ref argName pos) =+ GQLError { message = toMessage argName, locations = [pos] }+ toMessage argName =+ T.concat ["There can Be only One Argument Named \"", argName, "\""] -undefinedArgument :: EnhancedKey -> GQLErrors-undefinedArgument (EnhancedKey key' position') = errorMessage position' text- where- text = T.concat ["Required Argument: \"", key', "\" was not Defined"]+undefinedArgument :: Ref -> GQLErrors+undefinedArgument (Ref key' position') = errorMessage position' text+ where text = T.concat ["Required Argument: \"", key', "\" was not Defined"]
src/Data/Morpheus/Error/Client/Client.hs view
@@ -1,11 +1,55 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+ module Data.Morpheus.Error.Client.Client ( renderGQLErrors- ) where+ , deprecatedEnum+ , deprecatedField+ , gqlWarnings+ )+where -import Data.Aeson (encode)-import Data.ByteString.Lazy.Char8 (unpack)-import Data.Morpheus.Error.Utils (renderErrors)-import Data.Morpheus.Types.Internal.Validation (GQLErrors)+import Data.Aeson ( encode )+import Data.ByteString.Lazy.Char8 ( unpack )+import Data.Morpheus.Error.Utils ( errorMessage )+import Data.Morpheus.Types.Internal.Resolving.Core+ ( GQLErrors )+import Data.Morpheus.Types.Internal.AST.Base+ ( Ref(..)+ , Description+ , Name+ )+import Language.Haskell.TH ( Q+ , reportWarning+ )+import Data.Semigroup ( (<>) ) renderGQLErrors :: GQLErrors -> String-renderGQLErrors = unpack . encode . renderErrors+renderGQLErrors = unpack . encode++deprecatedEnum :: Name -> Ref -> Maybe Description -> GQLErrors+deprecatedEnum typeName Ref { refPosition, refName } reason =+ errorMessage refPosition+ $ "the enum value "+ <> typeName+ <> "."+ <> refName+ <> "\" is deprecated."+ <> maybe "" (" " <>) reason++deprecatedField :: Name -> Ref -> Maybe Description -> GQLErrors+deprecatedField typeName Ref { refPosition, refName } reason =+ errorMessage refPosition+ $ "the field \""+ <> typeName+ <> "."+ <> refName+ <> "\" is deprecated."+ <> maybe "" (" " <>) reason++gqlWarnings :: GQLErrors -> Q ()+gqlWarnings [] = pure ()+gqlWarnings warnings = mapM_ handleWarning warnings+ where+ handleWarning warning =+ reportWarning ("Morpheus GraphQL Warning: " <> (unpack . encode) warning)
src/Data/Morpheus/Error/Document/Interface.hs view
@@ -5,17 +5,21 @@ ( unknownInterface , partialImplements , ImplementsError(..)- ) where+ )+where -import Data.Morpheus.Error.Utils (globalErrorMessage)-import Data.Morpheus.Types.Internal.Base (Key)-import Data.Morpheus.Types.Internal.Validation (GQLError (..), GQLErrors)-import Data.Semigroup ((<>))+import Data.Morpheus.Error.Utils ( globalErrorMessage )+import Data.Morpheus.Types.Internal.AST.Base+ ( Key )+import Data.Morpheus.Types.Internal.Resolving.Core+ ( GQLError(..)+ , GQLErrors+ )+import Data.Semigroup ( (<>) ) unknownInterface :: Key -> GQLErrors unknownInterface name = globalErrorMessage message- where- message = "Unknown Interface \"" <> name <> "\"."+ where message = "Unknown Interface \"" <> name <> "\"." data ImplementsError = UnexpectedType { expectedType :: Key@@ -24,12 +28,24 @@ partialImplements :: Key -> [(Key, Key, ImplementsError)] -> GQLErrors partialImplements name = map impError- where- impError (interfaceName, key, errorType) = GQLError {desc = message, positions = []}- where- message =- "type \"" <> name <> "\" implements Interface \"" <> interfaceName <> "\" Partially," <>- detailedMessage errorType- detailedMessage UnexpectedType {expectedType, foundType} =- " on key \"" <> key <> "\" expected type \"" <> expectedType <> "\" found \"" <> foundType <> "\"."- detailedMessage UndefinedField = " key \"" <> key <> "\" not found ."+ where+ impError (interfaceName, key, errorType) = GQLError { message = message+ , locations = []+ }+ where+ message =+ "type \""+ <> name+ <> "\" implements Interface \""+ <> interfaceName+ <> "\" Partially,"+ <> detailedMessage errorType+ detailedMessage UnexpectedType { expectedType, foundType } =+ " on key \""+ <> key+ <> "\" expected type \""+ <> expectedType+ <> "\" found \""+ <> foundType+ <> "\"."+ detailedMessage UndefinedField = " key \"" <> key <> "\" not found ."
src/Data/Morpheus/Error/Fragment.hs view
@@ -7,16 +7,23 @@ , unknownFragment , cannotBeSpreadOnType , fragmentNameCollision- ) where+ )+where -import Data.Semigroup ((<>))-import Data.Text (Text)-import qualified Data.Text as T+import Data.Semigroup ( (<>) )+import Data.Text ( Text )+import qualified Data.Text as T -- MORPHEUS-import Data.Morpheus.Error.Utils (errorMessage)-import Data.Morpheus.Types.Internal.Base (EnhancedKey (..), Position)-import Data.Morpheus.Types.Internal.Validation (GQLError (..), GQLErrors)+import Data.Morpheus.Error.Utils ( errorMessage )+import Data.Morpheus.Types.Internal.AST.Base+ ( Ref(..)+ , Position+ )+import Data.Morpheus.Types.Internal.Resolving.Core+ ( GQLError(..)+ , GQLErrors+ ) {- FRAGMENT:@@ -29,57 +36,53 @@ fragment H on D {...} -> "Unknown type \"D\"." {...H} -> "Unknown fragment \"H\"." -}-fragmentNameCollision :: [EnhancedKey] -> GQLErrors+fragmentNameCollision :: [Ref] -> GQLErrors fragmentNameCollision = map toError- where- toError EnhancedKey {uid, location} =- GQLError- { desc = "There can be only one fragment named \"" <> uid <> "\"."- , positions = [location]- }+ where+ toError Ref { refName, refPosition } = GQLError+ { message = "There can be only one fragment named \"" <> refName <> "\"."+ , locations = [refPosition]+ } -unusedFragment :: [EnhancedKey] -> GQLErrors+unusedFragment :: [Ref] -> GQLErrors unusedFragment = map toError- where- toError EnhancedKey {uid, location} =- GQLError- { desc = "Fragment \"" <> uid <> "\" is never used."- , positions = [location]- }+ where+ toError Ref { refName, refPosition } = GQLError+ { message = "Fragment \"" <> refName <> "\" is never used."+ , locations = [refPosition]+ } -cannotSpreadWithinItself :: [EnhancedKey] -> GQLErrors+cannotSpreadWithinItself :: [Ref] -> GQLErrors cannotSpreadWithinItself fragments =- [GQLError {desc = text, positions = map location fragments}]- where- text =- T.concat- [ "Cannot spread fragment \""- , uid $ head fragments- , "\" within itself via "- , T.intercalate "," (map uid fragments)- , "."- ]+ [GQLError { message = text, locations = map refPosition fragments }]+ where+ text = T.concat+ [ "Cannot spread fragment \""+ , refName $ head fragments+ , "\" within itself via "+ , T.intercalate "," (map refName fragments)+ , "."+ ] -- {...H} -> "Unknown fragment \"H\"." unknownFragment :: Text -> Position -> GQLErrors unknownFragment key' position' = errorMessage position' text- where- text = T.concat ["Unknown Fragment \"", key', "\"."]+ where text = T.concat ["Unknown Fragment \"", key', "\"."] -- Fragment type mismatch -> "Fragment \"H\" cannot be spread here as objects of type \"Hobby\" can never be of type \"Experience\"." cannotBeSpreadOnType :: Maybe Text -> Text -> Position -> Text -> GQLErrors-cannotBeSpreadOnType key' type' position' selectionType' =- errorMessage position' text- where- text =- T.concat- [ "Fragment"- , getName key'- , " cannot be spread here as objects of type \""- , selectionType'- , "\" can never be of type \""- , type'- , "\"."- ]- getName (Just x') = T.concat [" \"", x', "\""]- getName Nothing = ""+cannotBeSpreadOnType key' type' position' selectionType' = errorMessage+ position'+ text+ where+ text = T.concat+ [ "Fragment"+ , getName key'+ , " cannot be spread here as objects of type \""+ , selectionType'+ , "\" can never be of type \""+ , type'+ , "\"."+ ]+ getName (Just x') = T.concat [" \"", x', "\""]+ getName Nothing = ""
src/Data/Morpheus/Error/Input.hs view
@@ -1,18 +1,25 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-} module Data.Morpheus.Error.Input ( inputErrorMessage , InputError(..) , Prop(..) , InputValidation- ) where--import Data.Aeson (encode)-import Data.ByteString.Lazy.Char8 (unpack)-import Data.Morpheus.Types.Internal.Value (Value)-import Data.Text (Text)-import qualified Data.Text as T (concat, intercalate, pack)+ )+where +import Data.Aeson ( encode )+import Data.ByteString.Lazy.Char8 ( unpack )+import Data.Morpheus.Types.Internal.AST.Value+ ( Value )+import Data.Text ( Text )+import qualified Data.Text as T+ ( concat+ , intercalate+ , pack+ ) type InputValidation a = Either InputError a data InputError@@ -30,33 +37,31 @@ inputErrorMessage (UnexpectedType path type' value errorMessage) = expectedTypeAFoundB path type' value errorMessage inputErrorMessage (UndefinedField path' field') = undefinedField path' field'-inputErrorMessage (UnknownField path' field') = unknownField path' field'+inputErrorMessage (UnknownField path' field') = unknownField path' field' pathToText :: [Prop] -> Text pathToText [] = "" pathToText path' = T.concat ["on ", T.intercalate "." $ fmap propKey path'] expectedTypeAFoundB :: [Prop] -> Text -> Value -> Maybe Text -> Text-expectedTypeAFoundB path' expected found Nothing =- T.concat- [ pathToText path'- , " Expected type \""- , expected- , "\" found "- , T.pack (unpack $ encode found)- , "."- ]-expectedTypeAFoundB path' expected found (Just errorMessage) =- T.concat- [ pathToText path'- , " Expected type \""- , expected- , "\" found "- , T.pack (unpack $ encode found)- , "; "- , errorMessage- , "."- ]+expectedTypeAFoundB path' expected found Nothing = T.concat+ [ pathToText path'+ , " Expected type \""+ , expected+ , "\" found "+ , T.pack (unpack $ encode found)+ , "."+ ]+expectedTypeAFoundB path' expected found (Just errorMessage) = T.concat+ [ pathToText path'+ , " Expected type \""+ , expected+ , "\" found "+ , T.pack (unpack $ encode found)+ , "; "+ , errorMessage+ , "."+ ] undefinedField :: [Prop] -> Text -> Text undefinedField path' field' =
src/Data/Morpheus/Error/Internal.hs view
@@ -5,33 +5,42 @@ , internalArgumentError , internalUnknownTypeMessage , internalError- ) where+ )+where -import Data.Text (Text)-import qualified Data.Text as T (concat, pack)+import Data.Text ( Text )+import qualified Data.Text as T+ ( concat+ , pack+ )+import Data.Semigroup ( (<>) ) -- MORPHEUS-import Data.Morpheus.Error.Utils (globalErrorMessage)-import Data.Morpheus.Types.Internal.Validation (GQLErrors)-import Data.Morpheus.Types.Internal.Value (Value (..))+import Data.Morpheus.Error.Utils ( globalErrorMessage )+import Data.Morpheus.Types.Internal.Resolving.Core+ ( Validation+ , GQLErrors+ , Failure(..)+ )+import Data.Morpheus.Types.Internal.AST.Value+ ( Value(..) ) -- GQL:: if no mutation defined -> "Schema is not configured for mutations." -- all kind internal error in development-internalError :: Text -> Either GQLErrors b-internalError x = Left $ globalErrorMessage $ T.concat ["INTERNAL ERROR: ", x]+internalError :: Text -> Validation b+internalError x = failure $ globalErrorMessage $ "INTERNAL ERROR: " <> x -- if type did not not found, but was defined by Schema internalUnknownTypeMessage :: Text -> GQLErrors-internalUnknownTypeMessage x =- globalErrorMessage $- T.concat ["type did not not found, but was defined by Schema", x]+internalUnknownTypeMessage x = globalErrorMessage+ $ T.concat ["type did not not found, but was defined by Schema", x] -- if arguments is already validated but has not found required argument-internalArgumentError :: Text -> Either GQLErrors b-internalArgumentError x = internalError $ T.concat ["Argument ", x]+internalArgumentError :: Text -> Validation b+internalArgumentError x = internalError $ "Argument " <> x -- if value is already validated but value has different type-internalTypeMismatch :: Text -> Value -> Either GQLErrors b+internalTypeMismatch :: Text -> Value -> Validation b internalTypeMismatch text jsType =- internalError $ T.concat ["Type mismatch ", text, T.pack $ show jsType]+ internalError $ "Type mismatch " <> text <> T.pack (show jsType)
src/Data/Morpheus/Error/Mutation.hs view
@@ -2,11 +2,14 @@ module Data.Morpheus.Error.Mutation ( mutationIsNotDefined- ) where+ )+where -import Data.Morpheus.Error.Utils (errorMessage)-import Data.Morpheus.Types.Internal.Base (Position)-import Data.Morpheus.Types.Internal.Validation (GQLErrors)+import Data.Morpheus.Error.Utils ( errorMessage )+import Data.Morpheus.Types.Internal.AST.Base+ ( Position )+import Data.Morpheus.Types.Internal.Resolving.Core+ ( GQLErrors ) mutationIsNotDefined :: Position -> GQLErrors mutationIsNotDefined position' =
src/Data/Morpheus/Error/Schema.hs view
@@ -3,12 +3,14 @@ module Data.Morpheus.Error.Schema ( nameCollisionError , schemaValidationError- ) where+ )+where -import Data.Morpheus.Error.Utils (globalErrorMessage)-import Data.Morpheus.Types.Internal.Validation (GQLErrors)-import Data.Semigroup ((<>))-import Data.Text (Text)+import Data.Morpheus.Error.Utils ( globalErrorMessage )+import Data.Morpheus.Types.Internal.Resolving.Core+ ( GQLErrors )+import Data.Semigroup ( (<>) )+import Data.Text ( Text ) schemaValidationError :: Text -> GQLErrors schemaValidationError error' =@@ -16,6 +18,7 @@ nameCollisionError :: Text -> GQLErrors nameCollisionError name =- schemaValidationError $- "Name collision: \"" <> name <>- "\" is used for different dataTypes in two separate modules"+ schemaValidationError+ $ "Name collision: \""+ <> name+ <> "\" is used for different dataTypes in two separate modules"
src/Data/Morpheus/Error/Selection.hs view
@@ -5,43 +5,66 @@ , subfieldsNotSelected , duplicateQuerySelections , hasNoSubfields- , fieldNotResolved- , resolverError- ) where+ , resolvingFailedError+ )+where -import Data.Morpheus.Error.Utils (errorMessage)-import Data.Morpheus.Types.Internal.Base (EnhancedKey (..), Position)-import Data.Morpheus.Types.Internal.Validation (GQLError (..), GQLErrors)-import Data.Text (Text, pack)-import qualified Data.Text as T (concat)+import Data.Semigroup ( (<>) )+import Data.Morpheus.Error.Utils ( errorMessage )+import Data.Morpheus.Types.Internal.AST.Base+ ( Ref(..)+ , Position+ )+import Data.Morpheus.Types.Internal.Resolving.Core+ ( GQLError(..)+ , GQLErrors+ )+import Data.Text ( Text )+import qualified Data.Text as T+ ( concat ) -resolverError :: Position -> Text -> String -> GQLErrors-resolverError pos name message = fieldNotResolved pos name (pack message) -fieldNotResolved :: Position -> Text -> Text -> GQLErrors-fieldNotResolved position' key' message' = errorMessage position' text- where- text = T.concat ["Failure on Resolving Field \"", key', "\": ", message']+resolvingFailedError :: Position -> Text -> Text -> GQLError+resolvingFailedError position name reason = GQLError+ { message = "Failure on Resolving Field \"" <> name <> "\": " <> reason+ , locations = [position]+ } + -- GQL: "Field \"default\" must not have a selection since type \"String!\" has no subfields." hasNoSubfields :: Text -> Text -> Position -> GQLErrors hasNoSubfields key typeName position = errorMessage position text- where- text = T.concat ["Field \"", key, "\" must not have a selection since type \"", typeName, "\" has no subfields."]+ where+ text = T.concat+ [ "Field \""+ , key+ , "\" must not have a selection since type \""+ , typeName+ , "\" has no subfields."+ ] cannotQueryField :: Text -> Text -> Position -> GQLErrors cannotQueryField key typeName position = errorMessage position text- where- text = T.concat ["Cannot query field \"", key, "\" on type \"", typeName, "\"."]+ where+ text =+ T.concat ["Cannot query field \"", key, "\" on type \"", typeName, "\"."] -duplicateQuerySelections :: Text -> [EnhancedKey] -> GQLErrors+duplicateQuerySelections :: Text -> [Ref] -> GQLErrors duplicateQuerySelections parentType = map keyToError- where- keyToError (EnhancedKey key' pos) = GQLError {desc = toMessage key', positions = [pos]}- toMessage key' = T.concat ["duplicate selection of key \"", key', "\" on type \"", parentType, "\"."]+ where+ keyToError (Ref key' pos) =+ GQLError { message = toMessage key', locations = [pos] }+ toMessage key' = T.concat+ ["duplicate selection of key \"", key', "\" on type \"", parentType, "\"."] -- GQL:: Field \"hobby\" of type \"Hobby!\" must have a selection of subfields. Did you mean \"hobby { ... }\"? subfieldsNotSelected :: Text -> Text -> Position -> GQLErrors subfieldsNotSelected key typeName position = errorMessage position text- where- text = T.concat ["Field \"", key, "\" of type \"", typeName, "\" must have a selection of subfields"]+ where+ text = T.concat+ [ "Field \""+ , key+ , "\" of type \""+ , typeName+ , "\" must have a selection of subfields"+ ]
src/Data/Morpheus/Error/Subscription.hs view
@@ -2,11 +2,14 @@ module Data.Morpheus.Error.Subscription ( subscriptionIsNotDefined- ) where+ )+where -import Data.Morpheus.Error.Utils (errorMessage)-import Data.Morpheus.Types.Internal.Base (Position)-import Data.Morpheus.Types.Internal.Validation (GQLErrors)+import Data.Morpheus.Error.Utils ( errorMessage )+import Data.Morpheus.Types.Internal.AST.Base+ ( Position )+import Data.Morpheus.Types.Internal.Resolving.Core+ ( GQLErrors ) subscriptionIsNotDefined :: Position -> GQLErrors subscriptionIsNotDefined position' =
src/Data/Morpheus/Error/Utils.hs view
@@ -3,31 +3,39 @@ module Data.Morpheus.Error.Utils ( errorMessage , globalErrorMessage- , renderErrors , badRequestError , toLocation- ) where+ )+where -import Data.ByteString.Lazy.Char8 (ByteString, pack)-import Data.Morpheus.Types.Internal.Base (Position)-import Data.Morpheus.Types.Internal.Validation (GQLError (..), GQLErrors, JSONError (..), Location (..))-import Data.Text (Text)-import Text.Megaparsec (SourcePos (SourcePos), sourceColumn, sourceLine, unPos)+import Data.ByteString.Lazy.Char8 ( ByteString+ , pack+ )+import Data.Morpheus.Types.Internal.AST.Base+ ( Position )+import Data.Morpheus.Types.Internal.Resolving.Core+ ( GQLError(..)+ , GQLErrors+ , GQLError(..)+ , Position(..)+ )+import Data.Text ( Text )+import Text.Megaparsec ( SourcePos(SourcePos)+ , sourceColumn+ , sourceLine+ , unPos+ ) errorMessage :: Position -> Text -> GQLErrors-errorMessage position text = [GQLError {desc = text, positions = [position]}]+errorMessage position message = [GQLError { message, locations = [position] }] globalErrorMessage :: Text -> GQLErrors-globalErrorMessage text = [GQLError {desc = text, positions = []}]--renderErrors :: [GQLError] -> [JSONError]-renderErrors = map renderError--renderError :: GQLError -> JSONError-renderError GQLError {desc, positions} = JSONError {message = desc, locations = positions}+globalErrorMessage message = [GQLError { message, locations = [] }] -toLocation :: SourcePos -> Location-toLocation SourcePos {sourceLine, sourceColumn} = Location {line = unPos sourceLine, column = unPos sourceColumn}+toLocation :: SourcePos -> Position+toLocation SourcePos { sourceLine, sourceColumn } =+ Position { line = unPos sourceLine, column = unPos sourceColumn } badRequestError :: String -> ByteString-badRequestError aesonError' = pack $ "Bad Request. Could not decode Request body: " ++ aesonError'+badRequestError aesonError' =+ pack $ "Bad Request. Could not decode Request body: " ++ aesonError'
src/Data/Morpheus/Error/Variable.hs view
@@ -7,75 +7,78 @@ , uninitializedVariable , unusedVariables , incompatibleVariableType- ) where+ )+where -import Data.Morpheus.Error.Utils (errorMessage)-import Data.Morpheus.Types.Internal.Base (EnhancedKey (..), Position)-import Data.Morpheus.Types.Internal.Validation (GQLError (..), GQLErrors)-import Data.Semigroup ((<>))-import Data.Text (Text)-import qualified Data.Text as T (concat)+import Data.Morpheus.Error.Utils ( errorMessage )+import Data.Morpheus.Types.Internal.AST.Base+ ( Ref(..)+ , Position+ )+import Data.Morpheus.Types.Internal.Resolving.Core+ ( GQLError(..)+ , GQLErrors+ )+import Data.Semigroup ( (<>) )+import Data.Text ( Text )+import qualified Data.Text as T+ ( concat ) -- query M ( $v : String ) { a(p:$v) } -> "Variable \"$v\" of type \"String\" used in position expecting type \"LANGUAGE\"." incompatibleVariableType :: Text -> Text -> Text -> Position -> GQLErrors incompatibleVariableType variableName variableType argType argPosition = errorMessage argPosition text- where- text =- "Variable \"$" <> variableName <> "\" of type \"" <> variableType <>- "\" used in position expecting type \"" <>- argType <>- "\"."+ where+ text =+ "Variable \"$"+ <> variableName+ <> "\" of type \""+ <> variableType+ <> "\" used in position expecting type \""+ <> argType+ <> "\"." -- query M ( $v : String ) { a } -> "Variable \"$bla\" is never used in operation \"MyMutation\".",-unusedVariables :: Text -> [EnhancedKey] -> GQLErrors+unusedVariables :: Text -> [Ref] -> GQLErrors unusedVariables operator' = map keyToError- where- keyToError (EnhancedKey key' position') =- GQLError {desc = text key', positions = [position']}- text key' =- T.concat- [ "Variable \"$"- , key'- , "\" is never used in operation \""- , operator'- , "\"."- ]+ where+ keyToError (Ref key' position') =+ GQLError { message = text key', locations = [position'] }+ text key' = T.concat+ ["Variable \"$", key', "\" is never used in operation \"", operator', "\"."] -- type mismatch -- { "v": 1 } "Variable \"$v\" got invalid value 1; Expected type LANGUAGE." variableGotInvalidValue :: Text -> Text -> Position -> GQLErrors-variableGotInvalidValue name' inputMessage' position' =- errorMessage position' text- where- text =- T.concat ["Variable \"$", name', "\" got invalid value; ", inputMessage']+variableGotInvalidValue name' inputMessage' position' = errorMessage+ position'+ text+ where+ text =+ T.concat ["Variable \"$", name', "\" got invalid value; ", inputMessage'] unknownType :: Text -> Position -> GQLErrors unknownType type' position' = errorMessage position' text- where- text = T.concat ["Unknown type \"", type', "\"."]+ where text = T.concat ["Unknown type \"", type', "\"."] undefinedVariable :: Text -> Position -> Text -> GQLErrors undefinedVariable operation' position' key' = errorMessage position' text- where- text =- T.concat- [ "Variable \""- , key'- , "\" is not defined by operation \""- , operation'- , "\"."- ]+ where+ text = T.concat+ [ "Variable \""+ , key'+ , "\" is not defined by operation \""+ , operation'+ , "\"."+ ] uninitializedVariable :: Position -> Text -> Text -> GQLErrors uninitializedVariable position' type' key' = errorMessage position' text- where- text =- T.concat- [ "Variable \"$"- , key'- , "\" of required type \""- , type'- , "!\" was not provided."- ]+ where+ text = T.concat+ [ "Variable \"$"+ , key'+ , "\" of required type \""+ , type'+ , "!\" was not provided."+ ]
src/Data/Morpheus/Execution/Client/Aeson.hs view
@@ -12,129 +12,147 @@ ( deriveFromJSON , deriveToJSON , takeValueType- ) where+ )+where import Data.Aeson import Data.Aeson.Types-import qualified Data.HashMap.Lazy as H (lookup)-import Data.Semigroup ((<>))-import Data.Text (unpack)+import qualified Data.HashMap.Lazy as H+ ( lookup )+import Data.Semigroup ( (<>) )+import Data.Text ( unpack ) import Language.Haskell.TH -import Data.Morpheus.Execution.Internal.Utils (nameSpaceTypeString)+import Data.Morpheus.Execution.Internal.Utils+ ( nameSpaceTypeString ) -- -- MORPHEUS-import Data.Morpheus.Types.Internal.Data (DataField (..), isFieldNullable)-import Data.Morpheus.Types.Internal.DataD (ConsD (..), TypeD (..))-import Data.Morpheus.Types.Internal.TH (destructRecord, instanceFunD, instanceHeadT)+import Data.Morpheus.Types.Internal.AST+ ( DataField(..)+ , isFieldNullable+ , ConsD(..)+ , TypeD(..)+ )+import Data.Morpheus.Types.Internal.TH+ ( destructRecord+ , instanceFunD+ , instanceHeadT+ ) -- FromJSON deriveFromJSON :: TypeD -> Q Dec-deriveFromJSON TypeD {tCons = []} = fail "Type Should Have at least one Constructor"-deriveFromJSON TypeD {tName, tNamespace, tCons = [cons]} = defineFromJSON name (aesonObject tNamespace) cons- where- name = nameSpaceTypeString tNamespace tName-deriveFromJSON typeD@TypeD {tName, tCons, tNamespace}+deriveFromJSON TypeD { tCons = [] } =+ fail "Type Should Have at least one Constructor"+deriveFromJSON TypeD { tName, tNamespace, tCons = [cons] } = defineFromJSON+ name+ (aesonObject tNamespace)+ cons+ where name = nameSpaceTypeString tNamespace tName+deriveFromJSON typeD@TypeD { tName, tCons, tNamespace } | isEnum tCons = defineFromJSON name aesonEnum tCons- | otherwise = defineFromJSON name (aesonUnionObject tNamespace) typeD- where- name = nameSpaceTypeString tNamespace tName+ | otherwise = defineFromJSON name (aesonUnionObject tNamespace) typeD+ where name = nameSpaceTypeString tNamespace tName aesonObject :: [String] -> ConsD -> ExpQ-aesonObject tNamespace con@ConsD {cName} =- appE [|withObject name|] (lamE [varP (mkName "o")] ((aesonObjectBody tNamespace) con))- where- name = nameSpaceTypeString tNamespace cName+aesonObject tNamespace con@ConsD { cName } = appE+ [|withObject name|]+ (lamE [varP (mkName "o")] (aesonObjectBody tNamespace con))+ where name = nameSpaceTypeString tNamespace cName aesonObjectBody :: [String] -> ConsD -> ExpQ-aesonObjectBody namespace ConsD {cName, cFields} = handleFields cFields- where- consName = mkName $ nameSpaceTypeString namespace cName- ------------------------------------------- handleFields [] = fail $ "No Empty Object"- handleFields fields = startExp fields- ----------------------------------------------------------------------------------- -- Optional Field- where- defField field@DataField {fieldName}- | isFieldNullable field = [|o .:? fName|]- | otherwise = [|o .: fName|]- where- fName = unpack fieldName- -------------------------------------------------------------------- startExp fNames = uInfixE (conE consName) (varE '(<$>)) (applyFields fNames)- where- applyFields [] = fail "No Empty fields"- applyFields [x] = defField x- applyFields (x:xs) = uInfixE (defField x) (varE '(<*>)) (applyFields xs)+aesonObjectBody namespace ConsD { cName, cFields } = handleFields cFields+ where+ consName = mkName $ nameSpaceTypeString namespace cName+ ------------------------------------------+ handleFields [] = fail "No Empty Object"+ handleFields fields = startExp fields+ ----------------------------------------------------------------------------------+ where+ defField field@DataField { fieldName }+ | isFieldNullable field = [|o .:? fName|]+ | otherwise = [|o .: fName|]+ where fName = unpack fieldName+ -------------------------------------------------------------------+ startExp fNames = uInfixE (conE consName)+ (varE '(<$>))+ (applyFields fNames)+ where+ applyFields [] = fail "No Empty fields"+ applyFields [x] = defField x+ applyFields (x : xs) =+ uInfixE (defField x) (varE '(<*>)) (applyFields xs) aesonUnionObject :: [String] -> TypeD -> ExpQ-aesonUnionObject namespace TypeD {tCons} =- appE (varE $ 'takeValueType) (lamCaseE ((map buildMatch tCons) <> [elseCaseEXP]))- where- buildMatch cons@ConsD {cName} = match pattern body []- where- pattern = tupP [litP (stringL cName), varP $ mkName "o"]- body = normalB $ aesonObjectBody namespace cons+aesonUnionObject namespace TypeD { tCons } = appE+ (varE 'takeValueType)+ (lamCaseE (map buildMatch tCons <> [elseCaseEXP]))+ where+ buildMatch cons@ConsD { cName } = match objectPattern body []+ where+ objectPattern = tupP [litP (stringL cName), varP $ mkName "o"]+ body = normalB $ aesonObjectBody namespace cons takeValueType :: ((String, Object) -> Parser a) -> Value -> Parser a-takeValueType f (Object hMap) =- case H.lookup "__typename" hMap of- Nothing -> fail "key \"__typename\" not found on object"- Just (String x) -> pure (unpack x, hMap) >>= f- Just val -> fail $ "key \"__typename\" should be string but found: " <> show val-takeValueType _ _ = fail $ "expected Object"+takeValueType f (Object hMap) = case H.lookup "__typename" hMap of+ Nothing -> fail "key \"__typename\" not found on object"+ Just (String x) -> pure (unpack x, hMap) >>= f+ Just val ->+ fail $ "key \"__typename\" should be string but found: " <> show val+takeValueType _ _ = fail "expected Object" defineFromJSON :: String -> (t -> ExpQ) -> t -> DecQ defineFromJSON tName parseJ cFields = instanceD (cxt []) iHead [method]- where- iHead = instanceHeadT ''FromJSON tName []- ------------------------------------------ method = instanceFunD 'parseJSON [] (parseJ cFields)+ where+ iHead = instanceHeadT ''FromJSON tName []+ -----------------------------------------+ method = instanceFunD 'parseJSON [] (parseJ cFields) isEnum :: [ConsD] -> Bool isEnum = not . isEmpty . filter (isEmpty . cFields)- where- isEmpty = (0 ==) . length+ where isEmpty = (0 ==) . length aesonEnum :: [ConsD] -> ExpQ aesonEnum cons = lamCaseE handlers- where- handlers = (map buildMatch cons) <> [elseCaseEXP]- where- buildMatch ConsD {cName} = match pattern body []- where- pattern = litP $ stringL cName- body = normalB $ appE (varE 'pure) (conE $ mkName cName)+ where+ handlers = map buildMatch cons <> [elseCaseEXP]+ where+ buildMatch ConsD { cName } = match enumPat body []+ where+ enumPat = litP $ stringL cName+ body = normalB $ appE (varE 'pure) (conE $ mkName cName) elseCaseEXP :: MatchQ elseCaseEXP = match (varP varName) body []- where- varName = mkName "invalidValue"- body =- normalB $- appE- (varE $ mkName "fail")- (uInfixE (appE (varE 'show) (varE varName)) (varE '(<>)) (stringE $ " is Not Valid Union Constructor"))+ where+ varName = mkName "invalidValue"+ body = normalB $ appE+ (varE $ mkName "fail")+ (uInfixE (appE (varE 'show) (varE varName))+ (varE '(<>))+ (stringE " is Not Valid Union Constructor")+ ) -- ToJSON deriveToJSON :: TypeD -> Q [Dec]-deriveToJSON TypeD {tCons = []} = fail "Type Should Have at least one Constructor"-deriveToJSON TypeD {tName, tCons = [ConsD {cFields}]} = pure <$> instanceD (cxt []) appHead methods- where- appHead = instanceHeadT ''ToJSON tName []- ------------------------------------------------------------------- -- defines: toJSON (User field1 field2 ...)= object ["name" .= name, "age" .= age, ...]- methods = [funD 'toJSON [clause argsE (normalB body) []]]- where- argsE = [destructRecord tName varNames]- body = appE (varE 'object) (listE $ map decodeVar varNames)- decodeVar name = [|name .= $(varName)|]- where- varName = varE $ mkName name- varNames = map (unpack . fieldName) cFields-deriveToJSON TypeD {tName, tCons}- | isEnum tCons = pure <$> instanceD (cxt []) (instanceHeadT ''ToJSON tName []) []+deriveToJSON TypeD { tCons = [] } =+ fail "Type Should Have at least one Constructor"+deriveToJSON TypeD { tName, tCons = [ConsD { cFields }] } =+ pure <$> instanceD (cxt []) appHead methods+ where+ appHead = instanceHeadT ''ToJSON tName []+ ------------------------------------------------------------------+ -- defines: toJSON (User field1 field2 ...)= object ["name" .= name, "age" .= age, ...]+ methods = [funD 'toJSON [clause argsE (normalB body) []]]+ where+ argsE = [destructRecord tName varNames]+ body = appE (varE 'object) (listE $ map decodeVar varNames)+ decodeVar name = [|name .= $(varName)|] where varName = varE $ mkName name+ varNames = map (unpack . fieldName) cFields+deriveToJSON TypeD { tName, tCons }+ | isEnum tCons+ = pure <$> instanceD (cxt []) (instanceHeadT ''ToJSON tName []) []+ | -- enum: uses default aeson instance derivation methods- | otherwise = fail "Input Unions are not yet supported"+ otherwise+ = fail "Input Unions are not yet supported"
src/Data/Morpheus/Execution/Client/Build.hs view
@@ -6,42 +6,70 @@ module Data.Morpheus.Execution.Client.Build ( defineQuery- ) where+ )+where -import Data.Semigroup ((<>))+import Data.Semigroup ( (<>) ) import Language.Haskell.TH -- -- MORPHEUS-import Data.Morpheus.Error.Client.Client (renderGQLErrors)-import Data.Morpheus.Execution.Client.Aeson (deriveFromJSON, deriveToJSON)-import Data.Morpheus.Execution.Client.Compile (validateWith)-import Data.Morpheus.Execution.Client.Fetch (deriveFetch)-import Data.Morpheus.Execution.Internal.Declare (declareType)-import Data.Morpheus.Types.Internal.Data (DataTypeKind (..), DataTypeLib, isOutputObject)-import Data.Morpheus.Types.Internal.DataD (GQLTypeD (..), QueryD (..), TypeD (..))-import Data.Morpheus.Types.Internal.Validation (Validation)-import Data.Morpheus.Types.Types (GQLQueryRoot (..))+import Data.Morpheus.Error.Client.Client+ ( renderGQLErrors+ , gqlWarnings+ )+import Data.Morpheus.Execution.Client.Aeson+ ( deriveFromJSON+ , deriveToJSON+ )+import Data.Morpheus.Execution.Client.Compile+ ( validateWith )+import Data.Morpheus.Execution.Client.Fetch+ ( deriveFetch )+import Data.Morpheus.Execution.Internal.Declare+ ( declareType ) -defineQuery :: IO (Validation DataTypeLib) -> (GQLQueryRoot, String) -> Q [Dec]+import Data.Morpheus.Types.Internal.AST+ ( GQLQuery(..)+ , DataTypeKind(..)+ , DataTypeLib+ , isOutputObject+ , ClientType(..)+ , ClientQuery(..)+ , TypeD(..)+ )+import Data.Morpheus.Types.Internal.Resolving+ ( Validation+ , Result(..)+ )++++defineQuery :: IO (Validation DataTypeLib) -> (GQLQuery, String) -> Q [Dec] defineQuery ioSchema queryRoot = do schema <- runIO ioSchema case schema >>= (`validateWith` queryRoot) of- Left errors -> fail (renderGQLErrors errors)- Right queryD -> defineQueryD queryD+ Failure errors -> fail (renderGQLErrors errors)+ Success { result, warnings } -> gqlWarnings warnings >> defineQueryD result -defineQueryD :: QueryD -> Q [Dec]-defineQueryD QueryD {queryTypes = rootType:subTypes, queryText, queryArgsType} = do- rootDecs <- defineOperationType (queryArgumentType queryArgsType) queryText rootType- subTypeDecs <- concat <$> traverse declareT subTypes- return $ rootDecs ++ subTypeDecs- where- declareT GQLTypeD {typeD, typeKindD}- | isOutputObject typeKindD || typeKindD == KindUnion = withToJSON declareOutputType typeD- | typeKindD == KindEnum = withToJSON declareInputType typeD- | otherwise = declareInputType typeD-defineQueryD QueryD {queryTypes = []} = return [] +defineQueryD :: ClientQuery -> Q [Dec]+defineQueryD ClientQuery { queryTypes = rootType : subTypes, queryText, queryArgsType }+ = do+ rootDecs <- defineOperationType (queryArgumentType queryArgsType)+ queryText+ rootType+ subTypeDecs <- concat <$> traverse declareT subTypes+ return $ rootDecs ++ subTypeDecs+ where+ declareT ClientType { clientType, clientKind }+ | isOutputObject clientKind || clientKind == KindUnion = withToJSON+ declareOutputType+ clientType+ | clientKind == KindEnum = withToJSON declareInputType clientType+ | otherwise = declareInputType clientType+defineQueryD ClientQuery { queryTypes = [] } = return []+ declareOutputType :: TypeD -> Q [Dec] declareOutputType typeD = pure [declareType False Nothing [''Show] typeD] @@ -53,16 +81,18 @@ withToJSON :: (TypeD -> Q [Dec]) -> TypeD -> Q [Dec] withToJSON f datatype = do toJson <- deriveFromJSON datatype- dec <- f datatype+ dec <- f datatype pure (toJson : dec) queryArgumentType :: Maybe TypeD -> (Type, Q [Dec])-queryArgumentType Nothing = (ConT $ mkName "()", pure [])-queryArgumentType (Just rootType@TypeD {tName}) = (ConT $ mkName tName, declareInputType rootType)+queryArgumentType Nothing = (ConT $ mkName "()", pure [])+queryArgumentType (Just rootType@TypeD { tName }) =+ (ConT $ mkName tName, declareInputType rootType) -defineOperationType :: (Type, Q [Dec]) -> String -> GQLTypeD -> Q [Dec]-defineOperationType (argType, argumentTypes) query GQLTypeD {typeD} = do- rootType <- withToJSON declareOutputType typeD- typeClassFetch <- deriveFetch argType (tName typeD) query- argsT <- argumentTypes- pure $ rootType <> typeClassFetch <> argsT+defineOperationType :: (Type, Q [Dec]) -> String -> ClientType -> Q [Dec]+defineOperationType (argType, argumentTypes) query ClientType { clientType } =+ do+ rootType <- withToJSON declareOutputType clientType+ typeClassFetch <- deriveFetch argType (tName clientType) query+ argsT <- argumentTypes+ pure $ rootType <> typeClassFetch <> argsT
src/Data/Morpheus/Execution/Client/Compile.hs view
@@ -5,37 +5,60 @@ module Data.Morpheus.Execution.Client.Compile ( compileSyntax , validateWith- ) where+ )+where -import qualified Data.Text as T (pack)+import qualified Data.Text as T+ ( pack ) import Language.Haskell.TH -import Data.Morpheus.Error.Client.Client (renderGQLErrors)--import Data.Morpheus.Execution.Client.Selection (operationTypes)-import Data.Morpheus.Parsing.Request.Parser (parseGQL)-import qualified Data.Morpheus.Types.Internal.AST.Operation as O (Operation (..))-import Data.Morpheus.Types.Internal.Data (DataTypeLib)-import Data.Morpheus.Types.IO (GQLRequest (..))- -- -- Morpheus-import Data.Morpheus.Types.Internal.DataD (QueryD (..))-import Data.Morpheus.Types.Internal.Validation (Validation)-import Data.Morpheus.Types.Types (GQLQueryRoot (..))-import Data.Morpheus.Validation.Internal.Utils (VALIDATION_MODE (..))-import Data.Morpheus.Validation.Query.Validation (validateRequest) ++import Data.Morpheus.Error.Client.Client+ ( renderGQLErrors+ , gqlWarnings+ )++import Data.Morpheus.Execution.Client.Selection+ ( operationTypes )+import Data.Morpheus.Parsing.Request.Parser+ ( parseGQL )+import qualified Data.Morpheus.Types.Internal.AST+ as O+ ( Operation(..) )+import Data.Morpheus.Types.IO ( GQLRequest(..) )++import Data.Morpheus.Types.Internal.AST+ ( GQLQuery(..)+ , DataTypeLib+ , ClientQuery(..)+ )+import Data.Morpheus.Types.Internal.Resolving+ ( Validation+ , Result(..)+ )+import Data.Morpheus.Validation.Internal.Utils+ ( VALIDATION_MODE(..) )+import Data.Morpheus.Validation.Query.Validation+ ( validateRequest )+ compileSyntax :: String -> Q Exp-compileSyntax queryText =- case parseGQL request of- Left errors -> fail (renderGQLErrors errors)- Right root -> [|(root, queryText)|]- where- request = GQLRequest {query = T.pack queryText, operationName = Nothing, variables = Nothing}+compileSyntax queryText = case parseGQL request of+ Failure errors -> fail (renderGQLErrors errors)+ Success { result, warnings } ->+ gqlWarnings warnings >> [|(result, queryText)|]+ where+ request = GQLRequest { query = T.pack queryText+ , operationName = Nothing+ , variables = Nothing+ } -validateWith :: DataTypeLib -> (GQLQueryRoot, String) -> Validation QueryD-validateWith schema (rawRequest@GQLQueryRoot {operation}, queryText) = do+validateWith :: DataTypeLib -> (GQLQuery, String) -> Validation ClientQuery+validateWith schema (rawRequest@GQLQuery { operation }, queryText) = do validOperation <- validateRequest schema WITHOUT_VARIABLES rawRequest- (queryArgsType, queryTypes) <- operationTypes schema (O.operationArgs operation) validOperation- return QueryD {queryText, queryTypes, queryArgsType}+ (queryArgsType, queryTypes) <- operationTypes schema+ (O.operationArgs operation)+ validOperation+ return ClientQuery { queryText, queryTypes, queryArgsType }
src/Data/Morpheus/Execution/Client/Fetch.hs view
@@ -8,27 +8,36 @@ module Data.Morpheus.Execution.Client.Fetch ( Fetch(..) , deriveFetch- ) where+ )+where -import Control.Monad ((>=>))-import Data.Aeson (FromJSON, ToJSON (..), eitherDecode, encode)-import Data.ByteString.Lazy (ByteString)-import Data.Text (pack)+import Control.Monad ( (>=>) )+import Data.Aeson ( FromJSON+ , ToJSON(..)+ , eitherDecode+ , encode+ )+import Data.ByteString.Lazy ( ByteString )+import Data.Text ( pack ) import Language.Haskell.TH -import qualified Data.Aeson as A-import qualified Data.Aeson.Types as A+import qualified Data.Aeson as A+import qualified Data.Aeson.Types as A -- -- MORPHEUS-import Data.Morpheus.Types.Internal.TH (instanceHeadT, typeInstanceDec)-import Data.Morpheus.Types.IO (GQLRequest (..), JSONResponse (..))+import Data.Morpheus.Types.Internal.TH+ ( instanceHeadT+ , typeInstanceDec+ )+import Data.Morpheus.Types.IO ( GQLRequest(..)+ , JSONResponse(..)+ ) fixVars :: A.Value -> Maybe A.Value-fixVars x- | x == A.emptyArray = Nothing- | otherwise = Just x+fixVars x | x == A.emptyArray = Nothing+ | otherwise = Just x class Fetch a where type Args a :: *@@ -48,10 +57,11 @@ fetch :: (Monad m, FromJSON a) => (ByteString -> m ByteString) -> Args a -> m (Either String a) deriveFetch :: Type -> String -> String -> Q [Dec]-deriveFetch resultType typeName query = pure <$> instanceD (cxt []) iHead methods- where- iHead = instanceHeadT ''Fetch typeName []- methods =- [ funD 'fetch [clause [] (normalB [|__fetch query typeName|]) []]- , pure $ typeInstanceDec ''Args (ConT $ mkName typeName) resultType- ]+deriveFetch resultType typeName queryString =+ pure <$> instanceD (cxt []) iHead methods+ where+ iHead = instanceHeadT ''Fetch typeName []+ methods =+ [ funD 'fetch [clause [] (normalB [|__fetch queryString typeName|]) []]+ , pure $ typeInstanceDec ''Args (ConT $ mkName typeName) resultType+ ]
src/Data/Morpheus/Execution/Client/Selection.hs view
@@ -6,201 +6,302 @@ module Data.Morpheus.Execution.Client.Selection ( operationTypes- ) where+ )+where -import Data.Maybe (fromMaybe)-import Data.Semigroup ((<>))-import Data.Text (Text, pack, unpack)+import Data.Maybe ( fromMaybe )+import Data.Semigroup ( (<>) )+import Data.Text ( Text+ , pack+ , unpack+ ) -- -- MORPHEUS-import Data.Morpheus.Error.Utils (globalErrorMessage)-import Data.Morpheus.Execution.Internal.GraphScanner (LibUpdater, resolveUpdates)-import Data.Morpheus.Execution.Internal.Utils (nameSpaceType)-import Data.Morpheus.Types.Internal.AST.Operation (DefaultValue, Operation (..), ValidOperation,- Variable (..), VariableDefinitions, getOperationName)-import Data.Morpheus.Types.Internal.AST.Selection (Selection (..), SelectionRec (..), SelectionSet,- ValidSelection)-import Data.Morpheus.Types.Internal.Data (DataField (..), DataType (..), DataTyCon (..),- DataTypeKind (..), DataTypeLib (..), Key,- TypeAlias (..), allDataTypes)-import Data.Morpheus.Types.Internal.DataD (ConsD (..), GQLTypeD (..), TypeD (..))-import Data.Morpheus.Types.Internal.Validation (GQLErrors, Validation)-import Data.Morpheus.Validation.Internal.Utils (lookupType)-import Data.Set (fromList, toList)+import Data.Morpheus.Error.Client.Client+ ( deprecatedField )+import Data.Morpheus.Error.Utils ( globalErrorMessage )+import Data.Morpheus.Execution.Internal.Utils+ ( nameSpaceType )+import Data.Morpheus.Types.Internal.AST+ ( DefaultValue+ , Operation(..)+ , ValidOperation+ , Variable(..)+ , VariableDefinitions+ , getOperationName+ , getOperationDataType+ , Selection(..)+ , SelectionRec(..)+ , SelectionSet+ , ValidSelection+ , Ref(..) + , DataField(..)+ , DataTyCon(..)+ , DataType(..)+ , DataTypeKind(..)+ , DataTypeLib(..)+ , Key+ , TypeAlias(..)+ , DataEnumValue(..)+ , allDataTypes+ , lookupType+ , ConsD(..)+ , ClientType(..)+ , TypeD(..)+ , lookupDeprecated+ , lookupDeprecatedReason+ )+import Data.Morpheus.Types.Internal.Resolving+ ( GQLErrors+ , Validation+ , Failure(..)+ , Result(..)+ , Position+ , LibUpdater+ , resolveUpdates+ )+import Data.Set ( fromList+ , toList+ ) removeDuplicates :: [Text] -> [Text] removeDuplicates = toList . fromList compileError :: Text -> GQLErrors-compileError x = globalErrorMessage $ "Unhandled Compile Time Error: \"" <> x <> "\" ;"+compileError x =+ globalErrorMessage $ "Unhandled Compile Time Error: \"" <> x <> "\" ;" -operationTypes :: DataTypeLib -> VariableDefinitions -> ValidOperation -> Validation (Maybe TypeD, [GQLTypeD])+operationTypes+ :: DataTypeLib+ -> VariableDefinitions+ -> ValidOperation+ -> Validation (Maybe TypeD, [ClientType]) operationTypes lib variables = genOperation- where- genOperation Operation {operationName, operationSelection} = do- (queryTypes, enums) <- genRecordType [] (getOperationName operationName) queryDataType operationSelection- inputTypeRequests <- resolveUpdates [] $ map (scanInputTypes lib . variableType . snd) variables- inputTypesAndEnums <- buildListedTypes (inputTypeRequests <> enums)- pure (rootArguments (getOperationName operationName <> "Args"), queryTypes <> inputTypesAndEnums)- where- queryDataType = DataObject $ snd $ query lib- -------------------------------------------------------------------------- buildListedTypes = fmap concat . traverse (buildInputType lib) . removeDuplicates- -------------------------------------------------------------------------- -- generates argument types for Operation Head- rootArguments :: Text -> Maybe TypeD- rootArguments argsName- | null variables = Nothing- | otherwise = Just rootArgumentsType+ where+ genOperation operation@Operation { operationName, operationSelection } = do+ datatype <- DataObject <$> getOperationDataType operation lib+ (queryTypes, enums) <- genRecordType []+ (getOperationName operationName)+ datatype+ operationSelection+ inputTypeRequests <- resolveUpdates []+ $ map (scanInputTypes lib . variableType . snd) variables+ inputTypesAndEnums <- buildListedTypes (inputTypeRequests <> enums)+ pure+ ( rootArguments (getOperationName operationName <> "Args")+ , queryTypes <> inputTypesAndEnums+ )+ -------------------------------------------------------------------------+ buildListedTypes =+ fmap concat . traverse (buildInputType lib) . removeDuplicates+ -------------------------------------------------------------------------+ -- generates argument types for Operation Head+ rootArguments :: Text -> Maybe TypeD+ rootArguments argsName | null variables = Nothing+ | otherwise = Just rootArgumentsType++ where+ rootArgumentsType :: TypeD+ rootArgumentsType = TypeD+ { tName = unpack argsName+ , tNamespace = []+ , tCons = [ ConsD { cName = unpack argsName+ , cFields = map fieldD variables+ }+ ]+ , tMeta = Nothing+ }+ where+ fieldD :: (Text, Variable DefaultValue) -> DataField+ fieldD (key, Variable { variableType, variableTypeWrappers }) = DataField+ { fieldName = key+ , fieldArgs = []+ , fieldArgsType = Nothing+ , fieldType = TypeAlias { aliasWrappers = variableTypeWrappers+ , aliasTyCon = variableType+ , aliasArgs = Nothing+ }+ , fieldMeta = Nothing+ }+ ---------------------------------------------------------+ -- generates selection Object Types+ genRecordType+ :: [Key]+ -> Key+ -> DataType+ -> SelectionSet+ -> Validation ([ClientType], [Text])+ genRecordType path name dataType recordSelSet = do+ (con, subTypes, requests) <- genConsD (unpack name) dataType recordSelSet+ pure+ ( ClientType+ { clientType = TypeD { tName+ , tNamespace = map unpack path+ , tCons = [con]+ , tMeta = Nothing+ }+ , clientKind = KindObject Nothing+ }+ : subTypes+ , requests+ )+ where+ tName = unpack name+ genConsD+ :: String+ -> DataType+ -> SelectionSet+ -> Validation (ConsD, [ClientType], [Text])+ genConsD cName datatype selSet = do+ (cFields, subTypes, requests) <- unzip3 <$> traverse genField selSet+ pure (ConsD { cName, cFields }, concat subTypes, concat requests)+ where+ genField+ :: (Text, ValidSelection)+ -> Validation (DataField, [ClientType], [Text])+ genField (fName, sel@Selection { selectionAlias, selectionPosition }) =+ do+ (fieldDataType, fieldType) <- lookupFieldType lib+ fieldPath+ datatype+ selectionPosition+ fName+ (subTypes, requests) <- subTypesBySelection fieldDataType sel+ pure+ ( DataField { fieldName+ , fieldArgs = []+ , fieldArgsType = Nothing+ , fieldType+ , fieldMeta = Nothing+ }+ , subTypes+ , requests+ )+ where+ fieldPath = path <> [fieldName]+ -------------------------------+ fieldName = fromMaybe fName selectionAlias ------------------------------------------- where- rootArgumentsType :: TypeD- rootArgumentsType =- TypeD- { tName = unpack argsName- , tNamespace = []- , tCons = [ConsD {cName = unpack argsName, cFields = map fieldD variables}]- }- where- fieldD :: (Text, Variable DefaultValue) -> DataField- fieldD (key, Variable {variableType, variableTypeWrappers}) =- DataField- { fieldName = key- , fieldArgs = []- , fieldArgsType = Nothing- , fieldType =- TypeAlias {aliasWrappers = variableTypeWrappers, aliasTyCon = variableType, aliasArgs = Nothing}- , fieldHidden = False- }- ---------------------------------------------------------- -- generates selection Object Types- genRecordType :: [Key] -> Key -> DataType -> SelectionSet -> Validation ([GQLTypeD], [Text])- genRecordType path name dataType recordSelSet = do- (con, subTypes, requests) <- genConsD (unpack name) dataType recordSelSet- pure- ( GQLTypeD- { typeD = TypeD {tName, tNamespace = map unpack path, tCons = [con]}- , typeKindD = KindObject Nothing- , typeArgD = []- } :- subTypes- , requests)- where- tName = unpack name- genConsD :: String -> DataType -> SelectionSet -> Validation (ConsD, [GQLTypeD], [Text])- genConsD cName datatype selSet = do- cFields <- traverse genField selSet- (subTypes, requests) <- newFieldTypes datatype selSet- pure (ConsD {cName, cFields}, concat subTypes, concat requests)- ---------------------------------------------------------------------------------------------- where- genField :: (Text, ValidSelection) -> Validation DataField- genField (fieldName, sel@Selection { selectionAlias }) = genFieldD sel- where- fieldPath = path <> [fromMaybe fieldName selectionAlias]- -------------------------------- genFieldD Selection {selectionAlias = Just aliasFieldName} = do- fieldType <- snd <$> lookupFieldType lib fieldPath datatype fieldName- pure $ DataField {fieldName = aliasFieldName, fieldArgs = [], fieldArgsType = Nothing, fieldType, fieldHidden = False}- genFieldD _ = do- fieldType <- snd <$> lookupFieldType lib fieldPath datatype fieldName- pure $ DataField {fieldName, fieldArgs = [], fieldArgsType = Nothing, fieldType, fieldHidden = False}- ------------------------------------------------------------------------------------------------------------- newFieldTypes :: DataType -> SelectionSet -> Validation ([[GQLTypeD]], [[Text]])- newFieldTypes parentType seSet = unzip <$> mapM valSelection seSet- where- valSelection (key, selection@Selection { selectionAlias }) = do- fieldDatatype <- fst <$> lookupFieldType lib fieldPath parentType key- validateSelection fieldDatatype selection- --------------------------------------------------------------------- where- fieldPath = path <> [fromMaybe key selectionAlias]- --------------------------------------------------------------------- validateSelection :: DataType -> ValidSelection -> Validation ([GQLTypeD], [Text])- validateSelection dType Selection {selectionRec = SelectionField} = leafType dType- --withLeaf buildLeaf dType- validateSelection dType Selection {selectionRec = SelectionSet selectionSet} =- genRecordType fieldPath (typeFrom [] dType) dType selectionSet- ---- UNION- validateSelection dType Selection {selectionRec = UnionSelection unionSelections} = do- (tCons, subTypes, requests) <- unzip3 <$> mapM getUnionType unionSelections- pure- ( GQLTypeD- { typeD =- TypeD {tNamespace = map unpack fieldPath, tName = unpack $ typeFrom [] dType, tCons}- , typeKindD = KindUnion- , typeArgD = []- } :- concat subTypes- , concat requests)- where- getUnionType (selectedTyName, selectionVariant) = do- conDatatype <- getType lib selectedTyName- genConsD (unpack selectedTyName) conDatatype selectionVariant+ subTypesBySelection+ :: DataType -> ValidSelection -> Validation ([ClientType], [Text])+ subTypesBySelection dType Selection { selectionRec = SelectionField } =+ leafType dType+ --withLeaf buildLeaf dType+ subTypesBySelection dType Selection { selectionRec = SelectionSet selectionSet }+ = genRecordType fieldPath (typeFrom [] dType) dType selectionSet+ ---- UNION+ subTypesBySelection dType Selection { selectionRec = UnionSelection unionSelections }+ = do+ (tCons, subTypes, requests) <-+ unzip3 <$> mapM getUnionType unionSelections+ pure+ ( ClientType+ { clientType = TypeD { tNamespace = map unpack fieldPath+ , tName = unpack $ typeFrom [] dType+ , tCons+ , tMeta = Nothing+ }+ , clientKind = KindUnion+ }+ : concat subTypes+ , concat requests+ )+ where+ getUnionType (selectedTyName, selectionVariant) = do+ conDatatype <- getType lib selectedTyName+ genConsD (unpack selectedTyName) conDatatype selectionVariant scanInputTypes :: DataTypeLib -> Key -> LibUpdater [Key]-scanInputTypes lib name collected- | name `elem` collected = pure collected- | otherwise = getType lib name >>= scanType- where- scanType (DataInputObject DataTyCon {typeData}) = resolveUpdates (name : collected) (map toInputTypeD typeData)- where- toInputTypeD :: (Text, DataField) -> LibUpdater [Key]- toInputTypeD (_, DataField {fieldType = TypeAlias {aliasTyCon}}) = scanInputTypes lib aliasTyCon- scanType (DataEnum DataTyCon {typeName}) = pure (collected <> [typeName])- scanType _ = pure collected+scanInputTypes lib name collected | name `elem` collected = pure collected+ | otherwise = getType lib name >>= scanType+ where+ scanType (DataInputObject DataTyCon { typeData }) = resolveUpdates+ (name : collected)+ (map toInputTypeD typeData)+ where+ toInputTypeD :: (Text, DataField) -> LibUpdater [Key]+ toInputTypeD (_, DataField { fieldType = TypeAlias { aliasTyCon } }) =+ scanInputTypes lib aliasTyCon+ scanType (DataEnum DataTyCon { typeName }) = pure (collected <> [typeName])+ scanType _ = pure collected -buildInputType :: DataTypeLib -> Text -> Validation [GQLTypeD]+buildInputType :: DataTypeLib -> Text -> Validation [ClientType] buildInputType lib name = getType lib name >>= subTypes- where- subTypes (DataInputObject DataTyCon {typeName, typeData}) = do- fields <- traverse toFieldD typeData- pure- [ GQLTypeD- { typeD =- TypeD- { tName = unpack typeName- , tNamespace = []- , tCons = [ConsD {cName = unpack typeName, cFields = fields}]- }- , typeArgD = []- , typeKindD = KindInputObject- }- ]- ----------------------------------------------------------------- where- toFieldD :: (Text, DataField) -> Validation DataField- toFieldD (_, field@DataField {fieldType}) = do- aliasTyCon <- typeFrom [] <$> getType lib (aliasTyCon fieldType)- pure $ field {fieldType = fieldType {aliasTyCon}}- subTypes (DataEnum DataTyCon {typeName, typeData}) =- pure- [ GQLTypeD- { typeD = TypeD {tName = unpack typeName, tNamespace = [], tCons = map enumOption typeData}- , typeArgD = []- , typeKindD = KindEnum- }- ]- where- enumOption eName = ConsD {cName = unpack eName, cFields = []}- subTypes _ = pure []+ where+ subTypes (DataInputObject DataTyCon { typeName, typeData }) = do+ fields <- traverse toFieldD typeData+ pure+ [ ClientType+ { clientType =+ TypeD+ { tName = unpack typeName+ , tNamespace = []+ , tCons = [ConsD { cName = unpack typeName, cFields = fields }]+ , tMeta = Nothing+ }+ , clientKind = KindInputObject+ }+ ] -lookupFieldType :: DataTypeLib -> [Key] -> DataType -> Text -> Validation (DataType, TypeAlias)-lookupFieldType lib path (DataObject DataTyCon {typeData}) key =- case lookup key typeData of- Just DataField {fieldType = alias@TypeAlias {aliasTyCon}} -> trans <$> getType lib aliasTyCon- where trans x = (x, alias {aliasTyCon = typeFrom path x, aliasArgs = Nothing})- Nothing -> Left (compileError $ "cant find field \""<> key<>"\"")-lookupFieldType _ _ dt _ = Left (compileError $ "Type should be output Object \"" <> pack (show dt))+ where+ toFieldD :: (Text, DataField) -> Validation DataField+ toFieldD (_, field@DataField { fieldType }) = do+ aliasTyCon <- typeFrom [] <$> getType lib (aliasTyCon fieldType)+ pure $ field { fieldType = fieldType { aliasTyCon } }+ subTypes (DataEnum DataTyCon { typeName, typeData }) = pure+ [ ClientType+ { clientType = TypeD { tName = unpack typeName+ , tNamespace = []+ , tCons = map enumOption typeData+ , tMeta = Nothing+ }+ , clientKind = KindEnum+ }+ ]+ where+ enumOption DataEnumValue { enumName } =+ ConsD { cName = unpack enumName, cFields = [] }+ subTypes _ = pure [] -leafType :: DataType -> Validation ([GQLTypeD], [Text])-leafType (DataEnum DataTyCon {typeName}) = pure ([],[typeName])-leafType DataScalar {} = pure ([],[])-leafType _ = Left $ compileError "Invalid schema Expected scalar"+lookupFieldType+ :: DataTypeLib+ -> [Key]+ -> DataType+ -> Position+ -> Text+ -> Validation (DataType, TypeAlias)+lookupFieldType lib path (DataObject DataTyCon { typeData, typeName }) refPosition key+ = case lookup key typeData of+ Just DataField { fieldType = alias@TypeAlias { aliasTyCon }, fieldMeta } ->+ checkDeprecated >> (trans <$> getType lib aliasTyCon)+ where+ trans x =+ (x, alias { aliasTyCon = typeFrom path x, aliasArgs = Nothing })+ ------------------------------------------------------------------+ checkDeprecated :: Validation ()+ checkDeprecated = case fieldMeta >>= lookupDeprecated of+ Just deprecation -> Success { result = (), warnings, events = [] }+ where+ warnings = deprecatedField typeName+ Ref { refName = key, refPosition }+ (lookupDeprecatedReason deprecation)+ Nothing -> pure ()+ ------------------+ Nothing -> failure+ (compileError $ "cant find field \"" <> pack (show typeData) <> "\"")+lookupFieldType _ _ dt _ _ =+ failure (compileError $ "Type should be output Object \"" <> pack (show dt)) ++leafType :: DataType -> Validation ([ClientType], [Text])+leafType (DataEnum DataTyCon { typeName }) = pure ([], [typeName])+leafType DataScalar{} = pure ([], [])+leafType _ = failure $ compileError "Invalid schema Expected scalar"+ getType :: DataTypeLib -> Text -> Validation DataType-getType lib typename = lookupType (compileError typename) (allDataTypes lib) typename+getType lib typename =+ lookupType (compileError typename) (allDataTypes lib) typename typeFromScalar :: Text -> Text typeFromScalar "Boolean" = "Bool"@@ -211,9 +312,9 @@ typeFromScalar _ = "ScalarValue" typeFrom :: [Key] -> DataType -> Text-typeFrom _ (DataScalar DataTyCon {typeName}) = typeFromScalar typeName-typeFrom _ (DataEnum x) = typeName x-typeFrom _ (DataInputObject x) = typeName x-typeFrom path (DataObject x) = pack $ nameSpaceType path $ typeName x-typeFrom path (DataUnion x) = pack $ nameSpaceType path $ typeName x-typeFrom _ (DataInputUnion x) = typeName x+typeFrom _ (DataScalar DataTyCon { typeName }) = typeFromScalar typeName+typeFrom _ (DataEnum x) = typeName x+typeFrom _ (DataInputObject x) = typeName x+typeFrom path (DataObject x) = pack $ nameSpaceType path $ typeName x+typeFrom path (DataUnion x) = pack $ nameSpaceType path $ typeName x+typeFrom _ (DataInputUnion x) = typeName x
src/Data/Morpheus/Execution/Document/Compile.hs view
@@ -1,47 +1,61 @@-{-# LANGUAGE NamedFieldPuns #-}+{-#LANGUAGE NamedFieldPuns #-} module Data.Morpheus.Execution.Document.Compile ( compileDocument , gqlDocument , gqlDocumentNamespace- ) where+ )+where -import qualified Data.Text as T (pack)+import qualified Data.Text as T+ ( pack ) import Language.Haskell.TH import Language.Haskell.TH.Quote -- -- Morpheus-import Data.Morpheus.Error.Client.Client (renderGQLErrors)-import Data.Morpheus.Execution.Document.Convert (renderTHTypes)-import Data.Morpheus.Execution.Document.Declare (declareTypes)-import Data.Morpheus.Parsing.Document.Parser (parseTypes)+import Data.Morpheus.Error.Client.Client+ ( renderGQLErrors+ , gqlWarnings+ )+import Data.Morpheus.Execution.Document.Convert+ ( renderTHTypes )+import Data.Morpheus.Execution.Document.Declare+ ( declareTypes )+import Data.Morpheus.Parsing.Document.Parser+ ( parseTypes ) import Data.Morpheus.Validation.Document.Validation+import Data.Morpheus.Types.Internal.Resolving+ ( Result(..) ) + gqlDocumentNamespace :: QuasiQuoter-gqlDocumentNamespace =- QuasiQuoter- { quoteExp = notHandled "Expressions"- , quotePat = notHandled "Patterns"- , quoteType = notHandled "Types"- , quoteDec = compileDocument True- }- where- notHandled things = error $ things ++ " are not supported by the GraphQL QuasiQuoter"+gqlDocumentNamespace = QuasiQuoter { quoteExp = notHandled "Expressions"+ , quotePat = notHandled "Patterns"+ , quoteType = notHandled "Types"+ , quoteDec = compileDocument True+ }+ where+ notHandled things =+ error $ things ++ " are not supported by the GraphQL QuasiQuoter" gqlDocument :: QuasiQuoter-gqlDocument =- QuasiQuoter- { quoteExp = notHandled "Expressions"- , quotePat = notHandled "Patterns"- , quoteType = notHandled "Types"- , quoteDec = compileDocument False- }- where- notHandled things = error $ things ++ " are not supported by the GraphQL QuasiQuoter"+gqlDocument = QuasiQuoter { quoteExp = notHandled "Expressions"+ , quotePat = notHandled "Patterns"+ , quoteType = notHandled "Types"+ , quoteDec = compileDocument False+ }+ where+ notHandled things =+ error $ things ++ " are not supported by the GraphQL QuasiQuoter" compileDocument :: Bool -> String -> Q [Dec] compileDocument namespace documentTXT =- case parseTypes (T.pack documentTXT) >>= validatePartialDocument >>= renderTHTypes namespace of- Left errors -> fail (renderGQLErrors errors)- Right schema -> declareTypes namespace schema+ case+ parseTypes (T.pack documentTXT)+ >>= validatePartialDocument+ >>= renderTHTypes namespace+ of+ Failure errors -> fail (renderGQLErrors errors)+ Success { result = schema, warnings } ->+ gqlWarnings warnings >> declareTypes namespace schema
src/Data/Morpheus/Execution/Document/Convert.hs view
@@ -6,140 +6,180 @@ module Data.Morpheus.Execution.Document.Convert ( renderTHTypes- ) where+ )+where -import Data.Semigroup ((<>))-import Data.Text (Text, pack, unpack)+import Data.Semigroup ( (<>) )+import Data.Text ( Text+ , pack+ , unpack+ ) -- -- MORPHEUS-import Data.Morpheus.Error.Internal (internalError)-import Data.Morpheus.Execution.Internal.Utils (capital)-import Data.Morpheus.Types.Internal.Data (ArgsType (..), DataField (..), DataType (..),- DataTyCon (..), DataTypeKind (..), OperationType (..),- ResolverKind (..), TypeAlias (..), sysTypes)-import Data.Morpheus.Types.Internal.DataD (ConsD (..), GQLTypeD (..), TypeD (..))-import Data.Morpheus.Types.Internal.Validation (Validation)+import Data.Morpheus.Error.Internal ( internalError )+import Data.Morpheus.Execution.Internal.Utils+ ( capital )+import Data.Morpheus.Types.Internal.AST+ ( ArgsType(..)+ , DataField(..)+ , DataTyCon(..)+ , DataType(..)+ , DataTypeKind(..)+ , OperationType(..)+ , ResolverKind(..)+ , TypeAlias(..)+ , DataEnumValue(..)+ , sysTypes+ , ConsD(..)+ , GQLTypeD(..)+ , TypeD(..)+ )+import Data.Morpheus.Types.Internal.Resolving+ ( Validation ) renderTHTypes :: Bool -> [(Text, DataType)] -> Validation [GQLTypeD] renderTHTypes namespace lib = traverse renderTHType lib- where- renderTHType :: (Text, DataType) -> Validation GQLTypeD- renderTHType (tyConName, x) = genType x- where- genArgsTypeName fieldName- | namespace = sysName tyConName <> argTName- | otherwise = argTName- where- argTName = capital fieldName <> "Args"- genArgumentType :: (Text, DataField) -> Validation [TypeD]- genArgumentType (_, DataField {fieldArgs = []}) = pure []- genArgumentType (fieldName, DataField {fieldArgs}) =- pure- [ TypeD- { tName- , tNamespace = []- , tCons = [ConsD {cName = sysName $ pack tName, cFields = map genField fieldArgs}]- }- ]- where- tName = genArgsTypeName $ sysName fieldName- -------------------------------------------- genFieldTypeName = genTypeName- ------------------------------- --genTypeName :: Text -> Text- genTypeName "String" = "Text"- genTypeName "Boolean" = "Bool"- genTypeName name- | name `elem` sysTypes = "S" <> name- genTypeName name = name- ----------------------------------------- sysName = unpack . genTypeName- ---------------------------------------------------------------------------------------------- genField :: (Text, DataField) -> DataField- genField (_, field@DataField {fieldType = alias@TypeAlias {aliasTyCon}}) =- field {fieldType = alias {aliasTyCon = genFieldTypeName aliasTyCon}}- ---------------------------------------------------------------------------------------------- genResField :: (Text, DataField) -> DataField- genResField (_, field@DataField {fieldName, fieldArgs, fieldType = alias@TypeAlias {aliasTyCon}}) =- field {fieldType = alias {aliasTyCon = ftName, aliasArgs}, fieldArgsType}- where- ftName = genFieldTypeName aliasTyCon- ---------------------------------------- aliasArgs =- case lookup aliasTyCon lib of- Just DataObject {} -> Just "m"- Just DataUnion {} -> Just "m"- _ -> Nothing- ------------------------------------ fieldArgsType = Just $ ArgsType {argsTypeName, resKind = getFieldType ftName}- where- argsTypeName- | null fieldArgs = "()"- | otherwise = pack $ genArgsTypeName $ unpack fieldName- --------------------------------------- getFieldType key =- case lookup key lib of- Nothing -> ExternalResolver- Just DataObject {} -> TypeVarResolver- Just DataUnion {} -> TypeVarResolver- Just _ -> PlainResolver- --------------------------------------------- genType (DataEnum DataTyCon {typeName, typeData}) =- pure- GQLTypeD- { typeD = TypeD {tName = sysName typeName, tNamespace = [], tCons = map enumOption typeData}- , typeKindD = KindEnum- , typeArgD = []- }- where- enumOption name = ConsD {cName = sysName name, cFields = []}- genType (DataScalar _) = internalError "Scalar Types should defined By Native Haskell Types"- genType (DataInputUnion _) = internalError "Input Unions not Supported"- genType (DataInputObject DataTyCon {typeName, typeData}) =- pure- GQLTypeD- { typeD =- TypeD- { tName = sysName typeName- , tNamespace = []- , tCons = [ConsD {cName = sysName typeName, cFields = map genField typeData}]- }- , typeKindD = KindInputObject- , typeArgD = []+ where+ renderTHType :: (Text, DataType) -> Validation GQLTypeD+ renderTHType (tyConName, x) = genType x+ where+ genArgsTypeName fieldName | namespace = sysName tyConName <> argTName+ | otherwise = argTName+ where argTName = capital fieldName <> "Args"+ genArgumentType :: (Text, DataField) -> Validation [TypeD]+ genArgumentType (_ , DataField { fieldArgs = [] }) = pure []+ genArgumentType (fieldName, DataField { fieldArgs } ) = pure+ [ TypeD+ { tName+ , tNamespace = []+ , tCons = [ ConsD { cName = sysName $ pack tName+ , cFields = map genField fieldArgs+ }+ ]+ , tMeta = Nothing+ }+ ]+ where tName = genArgsTypeName $ sysName fieldName+ -------------------------------------------+ genFieldTypeName = genTypeName+ ------------------------------+ --genTypeName :: Text -> Text+ genTypeName "String" = "Text"+ genTypeName "Boolean" = "Bool"+ genTypeName name | name `elem` sysTypes = "S" <> name+ genTypeName name = name+ ----------------------------------------+ sysName = unpack . genTypeName+ ---------------------------------------------------------------------------------------------+ genField :: (Text, DataField) -> DataField+ genField (_, field@DataField { fieldType = alias@TypeAlias { aliasTyCon } })+ = field { fieldType = alias { aliasTyCon = genFieldTypeName aliasTyCon } }- genType (DataObject DataTyCon {typeName, typeData}) = do- typeArgD <- concat <$> traverse genArgumentType typeData- pure- GQLTypeD- { typeD =- TypeD- { tName = sysName typeName- , tNamespace = []- , tCons = [ConsD {cName = sysName typeName, cFields = map genResField typeData}]- }- , typeKindD =- if typeName == "Subscription"- then KindObject (Just Subscription)- else KindObject Nothing- , typeArgD+ ---------------------------------------------------------------------------------------------+ genResField :: (Text, DataField) -> DataField+ genResField (_, field@DataField { fieldName, fieldArgs, fieldType = alias@TypeAlias { aliasTyCon } })+ = field { fieldType = alias { aliasTyCon = ftName, aliasArgs }+ , fieldArgsType }- genType (DataUnion DataTyCon {typeName, typeData}) = do- let tCons = map unionCon typeData- pure- GQLTypeD- {typeD = TypeD {tName = unpack typeName, tNamespace = [], tCons}, typeKindD = KindUnion, typeArgD = []}- where- unionCon field@DataField {fieldType} =- ConsD- { cName- , cFields =- [ field- { fieldName = pack $ "un" <> cName- , fieldType = TypeAlias {aliasTyCon = pack utName, aliasArgs = Just "m", aliasWrappers = []}+ where+ ftName = genFieldTypeName aliasTyCon+ ---------------------------------------+ aliasArgs = case lookup aliasTyCon lib of+ Just DataObject{} -> Just "m"+ Just DataUnion{} -> Just "m"+ _ -> Nothing+ -----------------------------------+ fieldArgsType = Just+ $ ArgsType { argsTypeName, resKind = getFieldType ftName }+ where+ argsTypeName | null fieldArgs = "()"+ | otherwise = pack $ genArgsTypeName $ unpack fieldName+ --------------------------------------+ getFieldType key = case lookup key lib of+ Nothing -> ExternalResolver+ Just DataObject{} -> TypeVarResolver+ Just DataUnion{} -> TypeVarResolver+ Just _ -> PlainResolver+ --------------------------------------------+ genType dt@(DataEnum DataTyCon { typeName, typeData, typeMeta }) = pure+ GQLTypeD+ { typeD = TypeD { tName = sysName typeName+ , tNamespace = []+ , tCons = map enumOption typeData+ , tMeta = typeMeta+ }+ , typeKindD = KindEnum+ , typeArgD = []+ , typeOriginal = (typeName, dt)+ }+ where+ enumOption DataEnumValue { enumName } =+ ConsD { cName = sysName enumName, cFields = [] }+ genType (DataScalar _) =+ internalError "Scalar Types should defined By Native Haskell Types"+ genType (DataInputUnion _) = internalError "Input Unions not Supported"+ genType dt@(DataInputObject DataTyCon { typeName, typeData, typeMeta }) =+ pure GQLTypeD+ { typeD =+ TypeD+ { tName = sysName typeName+ , tNamespace = []+ , tCons = [ ConsD { cName = sysName typeName+ , cFields = map genField typeData+ }+ ]+ , tMeta = typeMeta+ }+ , typeKindD = KindInputObject+ , typeArgD = []+ , typeOriginal = (typeName, dt)+ }+ genType dt@(DataObject DataTyCon { typeName, typeData, typeMeta }) = do+ typeArgD <- concat <$> traverse genArgumentType typeData+ pure GQLTypeD+ { typeD = TypeD+ { tName = sysName typeName+ , tNamespace = []+ , tCons = [ ConsD { cName = sysName typeName+ , cFields = map genResField typeData+ }+ ]+ , tMeta = typeMeta+ }+ , typeKindD = if typeName == "Subscription"+ then KindObject (Just Subscription)+ else KindObject Nothing+ , typeArgD+ , typeOriginal = (typeName, dt)+ }+ genType dt@(DataUnion DataTyCon { typeName, typeData, typeMeta }) = do+ let tCons = map unionCon typeData+ pure GQLTypeD+ { typeD = TypeD { tName = unpack typeName+ , tNamespace = []+ , tCons+ , tMeta = typeMeta+ }+ , typeKindD = KindUnion+ , typeArgD = []+ , typeOriginal = (typeName, dt)+ }+ where+ unionCon memberName = ConsD+ { cName+ , cFields = [ DataField+ { fieldName = pack $ "un" <> cName+ , fieldType = TypeAlias { aliasTyCon = pack utName+ , aliasArgs = Just "m"+ , aliasWrappers = []+ }+ , fieldMeta = Nothing+ , fieldArgs = []+ , fieldArgsType = Nothing } ]- }- where- cName = sysName typeName <> utName- utName = sysName $ aliasTyCon fieldType+ }+ where+ cName = sysName typeName <> utName+ utName = sysName memberName
src/Data/Morpheus/Execution/Document/Declare.hs view
@@ -4,51 +4,67 @@ module Data.Morpheus.Execution.Document.Declare ( declareTypes- ) where+ )+where -import Data.Semigroup ((<>))+import Data.Semigroup ( (<>) ) import Language.Haskell.TH -- -- MORPHEUS-import Data.Morpheus.Execution.Document.Decode (deriveDecode)-import Data.Morpheus.Execution.Document.Encode (deriveEncode)-import Data.Morpheus.Execution.Document.GQLType (deriveGQLType)-import Data.Morpheus.Execution.Document.Introspect (deriveObjectRep)-import Data.Morpheus.Execution.Internal.Declare (declareType)-import Data.Morpheus.Types.Internal.Data (isInput, isObject)-import Data.Morpheus.Types.Internal.DataD (GQLTypeD (..))+import Data.Morpheus.Execution.Document.Decode+ ( deriveDecode )+import Data.Morpheus.Execution.Document.Encode+ ( deriveEncode )+import Data.Morpheus.Execution.Document.GQLType+ ( deriveGQLType )+import Data.Morpheus.Execution.Document.Introspect+ ( deriveObjectRep+ , instanceIntrospect+ )+import Data.Morpheus.Execution.Internal.Declare+ ( declareType )+import Data.Morpheus.Types.Internal.AST+ ( isInput+ , isObject+ , GQLTypeD(..)+ ) declareTypes :: Bool -> [GQLTypeD] -> Q [Dec] declareTypes namespace = fmap concat . traverse (declareGQLType namespace) declareGQLType :: Bool -> GQLTypeD -> Q [Dec]-declareGQLType namespace gqlType@GQLTypeD {typeD, typeKindD, typeArgD} = do- mainType <- declareMainType- argTypes <- declareArgTypes- gqlInstances <- deriveGQLInstances- typeClasses <- deriveGQLType gqlType- pure $ mainType <> typeClasses <> argTypes <> gqlInstances- where- deriveGQLInstances = concat <$> sequence gqlInstances- where- gqlInstances- | isObject typeKindD && isInput typeKindD = [deriveObjectRep (typeD, Just typeKindD), deriveDecode typeD]- | isObject typeKindD = [deriveObjectRep (typeD, Just typeKindD), deriveEncode gqlType]- | otherwise = []- --------------------------------------------------- declareArgTypes = do- introspectArgs <- concat <$> traverse deriveArgsRep typeArgD- decodeArgs <- concat <$> traverse deriveDecode typeArgD- return $ argsTypeDecs <> decodeArgs <> introspectArgs- where- deriveArgsRep args = deriveObjectRep (args, Nothing)- ----------------------------------------------------- argsTypeDecs = map (declareType namespace Nothing []) typeArgD- --------------------------------------------------- declareMainType = declareT- where- declareT = pure [declareType namespace (Just typeKindD) derivingClasses typeD]- derivingClasses- | isInput typeKindD = [''Show]- | otherwise = []+declareGQLType namespace gqlType@GQLTypeD { typeD, typeKindD, typeArgD, typeOriginal }+ = do+ mainType <- declareMainType+ argTypes <- declareArgTypes+ gqlInstances <- deriveGQLInstances+ typeClasses <- deriveGQLType gqlType+ introspectEnum <- instanceIntrospect typeOriginal+ pure $ mainType <> typeClasses <> argTypes <> gqlInstances <> introspectEnum+ where+ deriveGQLInstances = concat <$> sequence gqlInstances+ where+ gqlInstances+ | isObject typeKindD && isInput typeKindD+ = [deriveObjectRep (typeD, Just typeKindD), deriveDecode typeD]+ | isObject typeKindD+ = [deriveObjectRep (typeD, Just typeKindD), deriveEncode gqlType]+ | otherwise+ = []+ --------------------------------------------------+ declareArgTypes = do+ introspectArgs <- concat <$> traverse deriveArgsRep typeArgD+ decodeArgs <- concat <$> traverse deriveDecode typeArgD+ return $ argsTypeDecs <> decodeArgs <> introspectArgs+ where+ deriveArgsRep args = deriveObjectRep (args, Nothing)+ ----------------------------------------------------+ argsTypeDecs = map (declareType namespace Nothing []) typeArgD+ --------------------------------------------------+ declareMainType = declareT+ where+ declareT =+ pure [declareType namespace (Just typeKindD) derivingClasses typeD]+ derivingClasses | isInput typeKindD = [''Show]+ | otherwise = []
src/Data/Morpheus/Execution/Document/Decode.hs view
@@ -7,29 +7,42 @@ module Data.Morpheus.Execution.Document.Decode ( deriveDecode- ) where+ )+where -import Data.Text (Text)+import Data.Text ( Text ) import Language.Haskell.TH -- -- MORPHEUS-import Data.Morpheus.Execution.Internal.Decode (decodeFieldWith, decodeObjectExpQ)-import Data.Morpheus.Execution.Server.Decode (Decode (..), DecodeObject (..))-import Data.Morpheus.Types.Internal.DataD (TypeD (..))-import Data.Morpheus.Types.Internal.TH (instanceHeadT)-import Data.Morpheus.Types.Internal.Validation (Validation)-import Data.Morpheus.Types.Internal.Value (Object)+import Data.Morpheus.Execution.Internal.Decode+ ( decodeFieldWith+ , decodeObjectExpQ+ )+import Data.Morpheus.Execution.Server.Decode+ ( Decode(..)+ , DecodeObject(..)+ )+import Data.Morpheus.Types.Internal.AST+ ( TypeD(..)+ , Object+ )+import Data.Morpheus.Types.Internal.TH+ ( instanceHeadT )+import Data.Morpheus.Types.Internal.Resolving+ ( Validation ) + (.:) :: Decode a => Object -> Text -> Validation a object .: selectorName = decodeFieldWith decode selectorName object deriveDecode :: TypeD -> Q [Dec]-deriveDecode TypeD {tName, tCons = [cons]} = pure <$> instanceD (cxt []) appHead methods- where- appHead = instanceHeadT ''DecodeObject tName []- methods = [funD 'decodeObject [clause argsE (normalB body) []]]- where- argsE = map (varP . mkName) ["o"]- body = decodeObjectExpQ [|(.:)|] cons+deriveDecode TypeD { tName, tCons = [cons] } =+ pure <$> instanceD (cxt []) appHead methods+ where+ appHead = instanceHeadT ''DecodeObject tName []+ methods = [funD 'decodeObject [clause argsE (normalB body) []]]+ where+ argsE = map (varP . mkName) ["o"]+ body = decodeObjectExpQ [|(.:)|] cons deriveDecode _ = pure []
src/Data/Morpheus/Execution/Document/Encode.hs view
@@ -5,65 +5,98 @@ module Data.Morpheus.Execution.Document.Encode ( deriveEncode- ) where+ )+where -import Data.Text (unpack)-import Data.Typeable (Typeable)+import Data.Text ( unpack )+import Data.Typeable ( Typeable ) import Language.Haskell.TH-import Data.Semigroup ((<>))+import Data.Semigroup ( (<>) ) -- -- MORPHEUS-import Data.Morpheus.Execution.Server.Encode (Encode (..), ObjectResolvers (..))-import Data.Morpheus.Types.GQLType (TRUE)-import Data.Morpheus.Types.Internal.Data (DataField (..), QUERY, SUBSCRIPTION, isSubscription)-import Data.Morpheus.Types.Internal.DataD (ConsD (..), GQLTypeD (..), TypeD (..))-import Data.Morpheus.Types.Internal.Resolver (Resolver, MapGraphQLT (..), Resolving,PureOperation)-import Data.Morpheus.Types.Internal.TH (applyT, destructRecord, instanceHeadMultiT, typeT)+import Data.Morpheus.Execution.Server.Encode+ ( Encode(..)+ , ObjectResolvers(..)+ )+import Data.Morpheus.Types.GQLType ( TRUE )+import Data.Morpheus.Types.Internal.AST+ ( DataField(..)+ , QUERY+ , SUBSCRIPTION+ , isSubscription+ , ConsD(..)+ , GQLTypeD(..)+ , TypeD(..)+ )+import Data.Morpheus.Types.Internal.Resolving+ ( Resolver+ , MapStrategy(..)+ , LiftEither+ , ResolvingStrategy+ )+import Data.Morpheus.Types.Internal.TH+ ( applyT+ , destructRecord+ , instanceHeadMultiT+ , typeT+ ) --- @Subscription:------ instance (Monad m, Typeable m) => ObjectResolvers 'True (<Subscription> (SubResolver m e c)) (SubResolveT m e c Value) where--- objectResolvers _ (<Subscription> x y) = [("newAddress", encode x), ("newUser", encode y)]------ @Object:------ instance (Monad m, Typeable m) => ObjectResolvers 'True (<Object> (Resolver m)) (ResolveT m Value) where--- objectResolvers _ (<Object> x y) = [("field1", encode x), ("field2", encode y)]------- +encodeVars :: [String]+encodeVars = ["e", "m"] +encodeVarsT :: [TypeQ]+encodeVarsT = map (varT . mkName) encodeVars+ deriveEncode :: GQLTypeD -> Q [Dec]-deriveEncode GQLTypeD {typeKindD, typeD = TypeD {tName, tCons = [ConsD {cFields}]}} =- pure <$> instanceD (cxt constrains) appHead methods- where - subARgs = conT ''SUBSCRIPTION : map (varT . mkName) ["m","e"]- instanceArgs- | isSubscription typeKindD = subARgs- | otherwise = map (varT . mkName) ["o","m","e"]- mainType = applyT (mkName tName) [mainTypeArg]- where- mainTypeArg- | isSubscription typeKindD = applyT ''Resolver subARgs- | otherwise = typeT ''Resolver ["fieldOKind","m","e"] - -----------------------------------------------------------------------------------------+deriveEncode GQLTypeD { typeKindD, typeD = TypeD { tName, tCons = [ConsD { cFields }] } }+ = pure <$> instanceD (cxt constrains) appHead methods+ where+ subARgs = conT ''SUBSCRIPTION : encodeVarsT+ instanceArgs | isSubscription typeKindD = subARgs+ | otherwise = map (varT . mkName) ("o" : encodeVars)+ mainType = applyT (mkName tName) [mainTypeArg]+ where+ mainTypeArg | isSubscription typeKindD = applyT ''Resolver subARgs+ | otherwise = typeT ''Resolver (fo_ : encodeVars)+ -----------------------------------------------------------------------------------------+ fo_ = "fieldOperationKind"+ po_ = "o"+ ---------------------+ typeables+ | isSubscription typeKindD+ = [applyT ''MapStrategy $ map conT [''QUERY, ''SUBSCRIPTION]]+ | otherwise+ = [ iLiftEither ''ResolvingStrategy+ , iLiftEither ''Resolver+ , typeT ''MapStrategy [fo_, po_]+ , iTypeable fo_+ , iTypeable po_+ ]+ -------------------------+ iLiftEither name = applyT ''LiftEither [varT $ mkName fo_, conT name]+ -------------------------+ iTypeable name = typeT ''Typeable [name]+ -------------------------------------------+ -- defines Constraint: (Typeable m, Monad m)+ constrains = typeables- | isSubscription typeKindD = [applyT ''MapGraphQLT $ map conT [''QUERY, ''SUBSCRIPTION],applyT ''Resolving [conT ''QUERY, varT $ mkName "m", varT $ mkName "e"]]- | otherwise = [typeT ''PureOperation ["fieldOKind"],typeT ''MapGraphQLT ["fieldOKind","o"] , typeT ''Resolving ["fieldOKind","m","e"] , typeT ''Typeable ["fieldOKind"] , typeT ''Typeable ["o"]]- -- defines Constraint: (Typeable m, Monad m)- constrains = typeables <>[typeT ''Monad ["m"], applyT ''Encode (mainType:instanceArgs) , typeT ''Typeable ["m"],typeT ''Typeable ["e"]]- -------------------------------------------------------------------- -- defines: instance <constraint> => ObjectResolvers ('TRUE) (<Type> (ResolveT m)) (ResolveT m value) where- appHead = instanceHeadMultiT ''ObjectResolvers (conT ''TRUE) (mainType: instanceArgs)- ------------------------------------------------------------------- -- defines: objectResolvers <Type field1 field2 ...> = [("field1",encode field1),("field2",encode field2), ...]- methods = [funD 'objectResolvers [clause argsE (normalB body) []]]- where- argsE = [varP (mkName "_"), destructRecord tName varNames]- body = listE $ map decodeVar varNames- decodeVar name = [|(name, encode $(varName))|]- where- varName = varE $ mkName name- varNames = map (unpack . fieldName) cFields+ <> [ typeT ''Monad ["m"]+ , applyT ''Encode (mainType : instanceArgs)+ , iTypeable "e"+ , iTypeable "m"+ ]+ -------------------------------------------------------------------+ -- defines: instance <constraint> => ObjectResolvers ('TRUE) (<Type> (ResolveT m)) (ResolveT m value) where+ appHead =+ instanceHeadMultiT ''ObjectResolvers (conT ''TRUE) (mainType : instanceArgs)+ ------------------------------------------------------------------+ -- defines: objectResolvers <Type field1 field2 ...> = [("field1",encode field1),("field2",encode field2), ...]+ methods = [funD 'objectResolvers [clause argsE (normalB body) []]]+ where+ argsE = [varP (mkName "_"), destructRecord tName varNames]+ body = listE $ map decodeVar varNames+ decodeVar name = [|(name, encode $(varName))|]+ where varName = varE $ mkName name+ varNames = map (unpack . fieldName) cFields deriveEncode _ = pure []
src/Data/Morpheus/Execution/Document/GQLType.hs view
@@ -6,67 +6,87 @@ module Data.Morpheus.Execution.Document.GQLType ( deriveGQLType- ) where+ )+where -import Data.Text (pack)+import Data.Text ( pack+ , unpack+ ) import Language.Haskell.TH-+import Data.Semigroup ( (<>) ) -- -- MORPHEUS-import Data.Morpheus.Execution.Internal.Declare (tyConArgs)-import Data.Morpheus.Kind (ENUM, INPUT_OBJECT, INPUT_UNION, OBJECT, SCALAR, UNION,- WRAPPER)-import Data.Morpheus.Types.GQLType (GQLType (..), TRUE)-import Data.Morpheus.Types.Internal.Data (DataTypeKind (..), isObject, isSchemaTypeName)-import Data.Morpheus.Types.Internal.DataD (GQLTypeD (..), TypeD (..))-import Data.Morpheus.Types.Internal.TH (instanceHeadT, typeT, typeInstanceDec)-import Data.Typeable (Typeable)+import Data.Morpheus.Execution.Internal.Declare+ ( tyConArgs )+import Data.Morpheus.Kind ( ENUM+ , INPUT_OBJECT+ , INPUT_UNION+ , OBJECT+ , SCALAR+ , UNION+ , WRAPPER+ )+import Data.Morpheus.Types.GQLType ( GQLType(..)+ , TRUE+ )+import Data.Morpheus.Types.Internal.AST+ ( DataTypeKind(..)+ , Meta(..)+ , isObject+ , isSchemaTypeName+ , GQLTypeD(..)+ , TypeD(..)+ )+import Data.Morpheus.Types.Internal.TH+ ( instanceHeadT+ , typeT+ , typeInstanceDec+ , instanceProxyFunD+ )+import Data.Typeable ( Typeable ) genTypeName :: String -> String-genTypeName ('S':name)- | isSchemaTypeName (pack name) = name+genTypeName ('S' : name) | isSchemaTypeName (pack name) = name genTypeName name = name -- deriveGQLType :: GQLTypeD -> Q [Dec]-deriveGQLType GQLTypeD {typeD = TypeD {tName}, typeKindD} =- pure <$> instanceD (cxt constrains) iHead (def__typeName : typeFamilies)- where- def__typeName = funD '__typeName [clause argsE (normalB body) []]- where- body = [|pack name|]- where- name = genTypeName tName- -- defines method: __typeName _ = tName- argsE = map (varP . mkName) ["_"]- typeArgs = tyConArgs typeKindD- ----------------------------------------------- iHead = instanceHeadT ''GQLType tName typeArgs- headSig = typeT (mkName tName) typeArgs- ------------------------------------------------ constrains = map conTypeable typeArgs- where- conTypeable name = typeT ''Typeable [name]- ------------------------------------------------ typeFamilies- | isObject typeKindD = [deriveCUSTOM, deriveKind]- | otherwise = [deriveKind]- ---------------------------------------------- where- deriveCUSTOM = do- typeN <- headSig- pure $ typeInstanceDec ''CUSTOM typeN (ConT ''TRUE)- ---------------------------------------------------------------- deriveKind = do- typeN <- headSig- pure $ typeInstanceDec ''KIND typeN (ConT $ toKIND typeKindD)- ---------------------------------- toKIND KindScalar = ''SCALAR- toKIND KindEnum = ''ENUM- toKIND (KindObject _) = ''OBJECT- toKIND KindUnion = ''UNION- toKIND KindInputObject = ''INPUT_OBJECT- toKIND KindList = ''WRAPPER- toKIND KindNonNull = ''WRAPPER- toKIND KindInputUnion = ''INPUT_UNION+deriveGQLType GQLTypeD { typeD = TypeD { tName, tMeta }, typeKindD } =+ pure <$> instanceD (cxt constrains) iHead (functions <> typeFamilies)+ where+ functions = map+ instanceProxyFunD+ [ ('__typeName , [|pack (genTypeName tName)|])+ , ('description, descriptionValue)+ ]+ where+ descriptionValue = case tMeta >>= metaDescription of+ Nothing -> [| Nothing |]+ Just x -> [| Just (pack desc)|] where desc = unpack x+ -------------------------------------------------+ typeArgs = tyConArgs typeKindD+ ----------------------------------------------+ iHead = instanceHeadT ''GQLType tName typeArgs+ headSig = typeT (mkName tName) typeArgs+ -----------------------------------------------+ constrains = map conTypeable typeArgs+ where conTypeable name = typeT ''Typeable [name]+ -----------------------------------------------+ typeFamilies | isObject typeKindD = [deriveCUSTOM, deriveKind]+ | otherwise = [deriveKind]+ where+ deriveCUSTOM = do+ typeN <- headSig+ pure $ typeInstanceDec ''CUSTOM typeN (ConT ''TRUE)+ ---------------------------------------------------------------+ deriveKind = do+ typeN <- headSig+ pure $ typeInstanceDec ''KIND typeN (ConT $ toKIND typeKindD)+ ---------------------------------+ toKIND KindScalar = ''SCALAR+ toKIND KindEnum = ''ENUM+ toKIND (KindObject _) = ''OBJECT+ toKIND KindUnion = ''UNION+ toKIND KindInputObject = ''INPUT_OBJECT+ toKIND KindList = ''WRAPPER+ toKIND KindNonNull = ''WRAPPER+ toKIND KindInputUnion = ''INPUT_UNION
src/Data/Morpheus/Execution/Document/Introspect.hs view
@@ -4,32 +4,43 @@ {-# LANGUAGE TemplateHaskell #-} module Data.Morpheus.Execution.Document.Introspect- ( deriveObjectRep+ ( deriveObjectRep , instanceIntrospect ) where +import Data.Maybe(maybeToList) import Data.Proxy (Proxy (..)) import Data.Text (unpack) import Data.Typeable (Typeable)-import Language.Haskell.TH+import Language.Haskell.TH -- MORPHEUS import Data.Morpheus.Execution.Internal.Declare (tyConArgs) import Data.Morpheus.Execution.Server.Introspect (Introspect (..), ObjectFields (..)) import Data.Morpheus.Types.GQLType (GQLType (__typeName), TRUE)-import Data.Morpheus.Types.Internal.Data (ArgsType (..), DataField (..), DataTypeKind, TypeAlias (..))-import Data.Morpheus.Types.Internal.DataD (ConsD (..), TypeD (..))-import Data.Morpheus.Types.Internal.TH (instanceFunD, instanceHeadMultiT, typeT)+import Data.Morpheus.Types.Internal.AST (ConsD (..), TypeD (..), ArgsType (..),Key, DataType(..), DataField (..),insertType,DataTypeKind(..), TypeAlias (..))+import Data.Morpheus.Types.Internal.TH (instanceFunD, instanceProxyFunD,instanceHeadT, instanceHeadMultiT, typeT) ++instanceIntrospect :: (Key,DataType) -> Q [Dec]+-- FIXME: dirty fix for introspection+instanceIntrospect ("__DirectiveLocation",_) = pure []+instanceIntrospect ("__TypeKind",_) = pure []+instanceIntrospect (name, DataEnum enumType) =+ pure <$> instanceD (cxt []) iHead [defineIntrospect]+ where+ -----------------------------------------------+ iHead = instanceHeadT ''Introspect (unpack name) []+ defineIntrospect = instanceProxyFunD ('introspect,body)+ where+ body =[| insertType (name, DataEnum enumType) |]+instanceIntrospect _ = pure []+ -- [((Text, DataField), TypeUpdater)] deriveObjectRep :: (TypeD, Maybe DataTypeKind) -> Q [Dec] deriveObjectRep (TypeD {tName, tCons = [ConsD {cFields}]}, tKind) =- pure <$> instanceWithOverlapD overlapping (cxt constrains) iHead methods+ pure <$> instanceD (cxt constrains) iHead methods where- overlapping = Just Overlapping- typeArgs =- case tKind of- Just typeKind -> tyConArgs typeKind- Nothing -> []+ typeArgs = concatMap tyConArgs (maybeToList tKind) constrains = map conTypeable typeArgs where conTypeable name = typeT ''Typeable [name]@@ -61,14 +72,14 @@ buildFields :: [DataField] -> ExpQ buildFields = listE . map buildField where- buildField DataField {fieldName, fieldArgs, fieldType = alias@TypeAlias {aliasArgs, aliasWrappers}} =+ buildField DataField {fieldName, fieldArgs, fieldType = alias@TypeAlias {aliasArgs, aliasWrappers}, fieldMeta} = [|( fName , DataField { fieldName = fName , fieldArgs = fArgs , fieldArgsType = Nothing , fieldType = TypeAlias {aliasTyCon = __typeName $(proxyT alias), aliasArgs = aArgs, aliasWrappers}- , fieldHidden = False+ , fieldMeta })|] where fName = unpack fieldName
src/Data/Morpheus/Execution/Internal/Declare.hs view
@@ -10,74 +10,92 @@ module Data.Morpheus.Execution.Internal.Declare ( declareType , tyConArgs- ) where+ )+where -import Data.Maybe (maybe)-import Data.Text (pack, unpack)-import GHC.Generics (Generic)+import Data.Maybe ( maybe )+import Data.Text ( pack+ , unpack+ )+import GHC.Generics ( Generic ) import Language.Haskell.TH -- MORPHEUS-import Data.Morpheus.Execution.Internal.Utils (nameSpaceType, nameSpaceWith)-import Data.Morpheus.Types.Internal.Data (ArgsType (..), DataField (..), DataTypeKind (..),- DataTypeKind (..), TypeAlias (..), WrapperD (..),- isOutputObject, isSubscription)-import Data.Morpheus.Types.Internal.DataD (ConsD (..), TypeD (..))-import Data.Morpheus.Types.Internal.Resolver (UnSubResolver)+import Data.Morpheus.Execution.Internal.Utils+ ( nameSpaceType+ , nameSpaceWith+ )+import Data.Morpheus.Types.Internal.AST+ ( ArgsType(..)+ , DataField(..)+ , DataTypeKind(..)+ , DataTypeKind(..)+ , TypeAlias(..)+ , TypeWrapper(..)+ , isOutputObject+ , isSubscription+ , ConsD(..)+ , TypeD(..)+ )+import Data.Morpheus.Types.Internal.Resolving+ ( UnSubResolver ) type Arrow = (->) declareTypeAlias :: Bool -> TypeAlias -> Type-declareTypeAlias isSub TypeAlias {aliasTyCon, aliasWrappers, aliasArgs} = wrappedT aliasWrappers- where- wrappedT :: [WrapperD] -> Type- wrappedT (ListD:xs) = AppT (ConT ''[]) $ wrappedT xs- wrappedT (MaybeD:xs) = AppT (ConT ''Maybe) $ wrappedT xs- wrappedT [] = decType aliasArgs- ------------------------------------------------------- typeName = ConT (mkName $ unpack aliasTyCon)- --------------------------------------------- decType _- | isSub = AppT typeName (AppT (ConT ''UnSubResolver) (VarT $ mkName "m"))- decType (Just par) = AppT typeName (VarT $ mkName $ unpack par)- decType _ = typeName+declareTypeAlias isSub TypeAlias { aliasTyCon, aliasWrappers, aliasArgs } =+ wrappedT aliasWrappers+ where+ wrappedT :: [TypeWrapper] -> Type+ wrappedT (TypeList : xs) = AppT (ConT ''[]) $ wrappedT xs+ wrappedT (TypeMaybe : xs) = AppT (ConT ''Maybe) $ wrappedT xs+ wrappedT [] = decType aliasArgs+ ------------------------------------------------------+ typeName = ConT (mkName $ unpack aliasTyCon)+ --------------------------------------------+ decType _ | isSub =+ AppT typeName (AppT (ConT ''UnSubResolver) (VarT $ mkName "m"))+ decType (Just par) = AppT typeName (VarT $ mkName $ unpack par)+ decType _ = typeName tyConArgs :: DataTypeKind -> [String]-tyConArgs kindD- | isOutputObject kindD || kindD == KindUnion = ["m"]- | otherwise = []+tyConArgs kindD | isOutputObject kindD || kindD == KindUnion = ["m"]+ | otherwise = [] -- declareType declareType :: Bool -> Maybe DataTypeKind -> [Name] -> TypeD -> Dec-declareType namespace kindD derivingList TypeD {tName, tCons, tNamespace} =- DataD [] (genName tName) tVars Nothing (map cons tCons) $ map derive (''Generic : derivingList)- where- genName = mkName . nameSpaceType (map pack tNamespace) . pack- tVars = maybe [] (declareTyVar . tyConArgs) kindD- where- declareTyVar = map (PlainTV . mkName)- defBang = Bang NoSourceUnpackedness NoSourceStrictness- derive className = DerivClause Nothing [ConT className]- cons ConsD {cName, cFields} = RecC (genName cName) (map declareField cFields)- where- declareField DataField {fieldName, fieldArgsType, fieldType} = (fName, defBang, fiType)- where- fName- | namespace = mkName (nameSpaceWith tName (unpack fieldName))- | otherwise = mkName (unpack fieldName)- fiType = genFieldT fieldArgsType- where- monadVar = VarT $ mkName "m"- ---------------------------- genFieldT Nothing = fType False- genFieldT (Just ArgsType {argsTypeName}) = AppT (AppT arrowType argType) (fType True)- where- argType = ConT $ mkName (unpack argsTypeName)- arrowType = ConT ''Arrow- ------------------------------------------------- fType isResolver- | maybe False isSubscription kindD = AppT monadVar result- | isResolver = AppT monadVar result- | otherwise = result- ------------------------------------------------- result = declareTypeAlias (maybe False isSubscription kindD) fieldType+declareType namespace kindD derivingList TypeD { tName, tCons, tNamespace } =+ DataD [] (genName tName) tVars Nothing (map cons tCons)+ $ map derive (''Generic : derivingList)+ where+ genName = mkName . nameSpaceType (map pack tNamespace) . pack+ tVars = maybe [] (declareTyVar . tyConArgs) kindD+ where declareTyVar = map (PlainTV . mkName)+ defBang = Bang NoSourceUnpackedness NoSourceStrictness+ derive className = DerivClause Nothing [ConT className]+ cons ConsD { cName, cFields } = RecC (genName cName)+ (map declareField cFields)+ where+ declareField DataField { fieldName, fieldArgsType, fieldType } =+ (fName, defBang, fiType)+ where+ fName | namespace = mkName (nameSpaceWith tName (unpack fieldName))+ | otherwise = mkName (unpack fieldName)+ fiType = genFieldT fieldArgsType+ where+ monadVar = VarT $ mkName "m"+ ---------------------------+ genFieldT Nothing = fType False+ genFieldT (Just ArgsType { argsTypeName }) = AppT+ (AppT arrowType argType)+ (fType True)+ where+ argType = ConT $ mkName (unpack argsTypeName)+ arrowType = ConT ''Arrow+ ------------------------------------------------+ fType isResolver+ | maybe False isSubscription kindD = AppT monadVar result+ | isResolver = AppT monadVar result+ | otherwise = result+ ------------------------------------------------+ result = declareTypeAlias (maybe False isSubscription kindD) fieldType
src/Data/Morpheus/Execution/Internal/Decode.hs view
@@ -10,62 +10,74 @@ , withUnion , decodeFieldWith , decodeObjectExpQ- ) where+ )+where -import Data.Semigroup ((<>))-import Data.Text (unpack)-import Language.Haskell.TH (ExpQ, conE, mkName, uInfixE, varE)+import Data.Semigroup ( (<>) )+import Data.Text ( unpack )+import Language.Haskell.TH ( ExpQ+ , conE+ , mkName+ , uInfixE+ , varE+ ) -- MORPHEUS-import Data.Morpheus.Error.Internal (internalArgumentError, internalTypeMismatch)-import Data.Morpheus.Types.Internal.Data (DataField (..), Key)-import Data.Morpheus.Types.Internal.DataD (ConsD (..))-import Data.Morpheus.Types.Internal.Validation (Validation)-import Data.Morpheus.Types.Internal.Value (Object, Value (..))+import Data.Morpheus.Error.Internal ( internalArgumentError+ , internalTypeMismatch+ )+import Data.Morpheus.Types.Internal.AST+ ( DataField(..)+ , Key+ , ConsD(..) + , Object+ , Value(..))+import Data.Morpheus.Types.Internal.Resolving+ ( Validation ) + decodeObjectExpQ :: ExpQ -> ConsD -> ExpQ-decodeObjectExpQ fieldDecoder ConsD {cName, cFields} = handleFields cFields- where- consName = conE (mkName cName)- ----------------------------------------------------------------------------------- handleFields fNames = uInfixE consName (varE '(<$>)) (applyFields fNames)- where- applyFields [] = fail "No Empty fields"- applyFields [x] = defField x- applyFields (x:xs) = uInfixE (defField x) (varE '(<*>)) (applyFields xs)- ------------------------------------------------------------------------- defField DataField {fieldName} = uInfixE (varE (mkName "o")) fieldDecoder [|fName|]- where- fName = unpack fieldName+decodeObjectExpQ fieldDecoder ConsD { cName, cFields } = handleFields cFields+ where+ consName = conE (mkName cName)+ ----------------------------------------------------------------------------------+ handleFields fNames = uInfixE consName (varE '(<$>)) (applyFields fNames)+ where+ applyFields [] = fail "No Empty fields"+ applyFields [x ] = defField x+ applyFields (x : xs) = uInfixE (defField x) (varE '(<*>)) (applyFields xs)+ ------------------------------------------------------------------------+ defField DataField { fieldName } = uInfixE (varE (mkName "o"))+ fieldDecoder+ [|fName|]+ where fName = unpack fieldName withObject :: (Object -> Validation a) -> Value -> Validation a withObject f (Object object) = f object withObject _ isType = internalTypeMismatch "Object" isType withMaybe :: Monad m => (Value -> m a) -> Value -> m (Maybe a)-withMaybe _ Null = pure Nothing-withMaybe decode x = Just <$> decode x+withMaybe _ Null = pure Nothing+withMaybe decode x = Just <$> decode x withList :: (Value -> Validation a) -> Value -> Validation [a] withList decode (List li) = mapM decode li-withList _ isType = internalTypeMismatch "List" isType+withList _ isType = internalTypeMismatch "List" isType withEnum :: (Key -> Validation a) -> Value -> Validation a withEnum decode (Enum value) = decode value-withEnum _ isType = internalTypeMismatch "Enum" isType+withEnum _ isType = internalTypeMismatch "Enum" isType withUnion :: (Key -> Object -> Object -> Validation a) -> Object -> Validation a-withUnion decoder unions =- case lookup "tag" unions of- Just (Enum key) ->- case lookup key unions of- Nothing -> internalArgumentError ("type \"" <> key <> "\" was not provided on object")- Just value -> withObject (decoder key unions) value- Just _ -> internalArgumentError "tag must be Enum"- Nothing -> internalArgumentError "tag not found on Input Union"+withUnion decoder unions = case lookup "tag" unions of+ Just (Enum key) -> case lookup key unions of+ Nothing -> internalArgumentError+ ("type \"" <> key <> "\" was not provided on object")+ Just value -> withObject (decoder key unions) value+ Just _ -> internalArgumentError "tag must be Enum"+ Nothing -> internalArgumentError "tag not found on Input Union" decodeFieldWith :: (Value -> Validation a) -> Key -> Object -> Validation a-decodeFieldWith decoder name object =- case lookup name object of- Nothing -> internalArgumentError ("Missing Field: \"" <> name <> "\"")- Just value -> decoder value+decodeFieldWith decoder name object = case lookup name object of+ Nothing -> internalArgumentError ("Missing Field: \"" <> name <> "\"")+ Just value -> decoder value
− src/Data/Morpheus/Execution/Internal/GraphScanner.hs
@@ -1,14 +0,0 @@-module Data.Morpheus.Execution.Internal.GraphScanner- ( LibUpdater- , resolveUpdates- ) where--import Control.Monad (foldM)-import Data.Function ((&))-import Data.Morpheus.Types.Internal.Validation (Validation)--type LibUpdater lib = lib -> Validation lib---- Helper Functions-resolveUpdates :: lib -> [LibUpdater lib] -> Validation lib-resolveUpdates = foldM (&)
src/Data/Morpheus/Execution/Internal/Utils.hs view
@@ -4,11 +4,16 @@ , nameSpaceWith , nameSpaceType , nameSpaceTypeString- ) where+ )+where -import Data.Char (toLower, toUpper)-import Data.Semigroup ((<>))-import Data.Text (Text, unpack)+import Data.Char ( toLower+ , toUpper+ )+import Data.Semigroup ( (<>) )+import Data.Text ( Text+ , unpack+ ) nameSpaceTypeString :: [String] -> String -> String@@ -21,9 +26,9 @@ nameSpaceWith nSpace name = nonCapital nSpace <> capital name nonCapital :: String -> String-nonCapital [] = []-nonCapital (x:xs) = toLower x : xs+nonCapital [] = []+nonCapital (x : xs) = toLower x : xs capital :: String -> String-capital [] = []-capital (x:xs) = toUpper x : xs+capital [] = []+capital (x : xs) = toUpper x : xs
src/Data/Morpheus/Execution/Server/Decode.hs view
@@ -14,26 +14,49 @@ ( decodeArguments , Decode(..) , DecodeObject(..)- ) where+ )+where -import Data.Proxy (Proxy (..))-import Data.Semigroup ((<>))-import Data.Text (pack)+import Data.Proxy ( Proxy(..) )+import Data.Semigroup ( (<>) )+import Data.Text ( pack ) import GHC.Generics -- MORPHEUS-import Data.Morpheus.Error.Internal (internalArgumentError, internalTypeMismatch)-import Data.Morpheus.Execution.Internal.Decode (decodeFieldWith, withEnum, withList, withMaybe,- withObject, withUnion)-import Data.Morpheus.Execution.Server.Generics.EnumRep (EnumRep (..))-import Data.Morpheus.Kind (ENUM, GQL_KIND, INPUT_OBJECT, INPUT_UNION, SCALAR)-import Data.Morpheus.Types.GQLScalar (GQLScalar (..), toScalar)-import Data.Morpheus.Types.GQLType (GQLType (KIND, __typeName))-import Data.Morpheus.Types.Internal.AST.Selection (Argument (..), Arguments)-import Data.Morpheus.Types.Internal.Base (Key)-import Data.Morpheus.Types.Internal.Validation (Validation)-import Data.Morpheus.Types.Internal.Value (Object, Value (..))+import Data.Morpheus.Error.Internal ( internalArgumentError+ , internalTypeMismatch+ )+import Data.Morpheus.Execution.Internal.Decode+ ( decodeFieldWith+ , withEnum+ , withList+ , withMaybe+ , withObject+ , withUnion+ )+import Data.Morpheus.Execution.Server.Generics.EnumRep+ ( EnumRep(..) )+import Data.Morpheus.Kind ( ENUM+ , GQL_KIND+ , INPUT_OBJECT+ , INPUT_UNION+ , SCALAR+ )+import Data.Morpheus.Types.GQLScalar ( GQLScalar(..)+ , toScalar+ )+import Data.Morpheus.Types.GQLType ( GQLType(KIND, __typeName) )+import Data.Morpheus.Types.Internal.AST+ ( Key+ , Argument(..)+ , Arguments+ , Object+ , Value(..)+ )+import Data.Morpheus.Types.Internal.Resolving+ ( Validation ) + -- | Decode GraphQL query arguments and input values class Decode a where decode :: Value -> Validation a@@ -53,10 +76,9 @@ -- SCALAR instance (GQLScalar a) => DecodeKind SCALAR a where- decodeKind _ value =- case toScalar value >>= parseValue of- Right scalar -> return scalar- Left errorMessage -> internalTypeMismatch errorMessage value+ decodeKind _ value = case toScalar value >>= parseValue of+ Right scalar -> return scalar+ Left errorMessage -> internalTypeMismatch errorMessage value -- ENUM instance (Generic a, EnumRep (Rep a)) => DecodeKind ENUM a where@@ -73,8 +95,7 @@ -- GENERIC decodeArguments :: DecodeObject p => Arguments -> Validation p decodeArguments = decodeObject . fmap toObject- where- toObject (x, y) = (x, argumentValue y)+ where toObject (x, y) = (x, argumentValue y) class DecodeObject a where decodeObject :: Object -> Validation a@@ -98,11 +119,11 @@ -- Recursive Decoding: (Selector (Rec1 )) instance (Selector s, GQLType a, Decode a) => GDecode (M1 S s (K1 i a)) where unionTags _ = [__typeName (Proxy @a)]- decodeUnion = fmap (M1 . K1) . decode . Object+ decodeUnion = fmap (M1 . K1) . decode . Object __decodeObject = fmap (M1 . K1) . decodeRec- where- fieldName = pack $ selName (undefined :: M1 S s f a)- decodeRec = withObject (decodeFieldWith decode fieldName)+ where+ fieldName = pack $ selName (undefined :: M1 S s f a)+ decodeRec = withObject (decodeFieldWith decode fieldName) instance (Datatype c, GDecode f) => GDecode (M1 D c f) where decodeUnion = fmap M1 . decodeUnion@@ -119,14 +140,15 @@ instance (GDecode a, GDecode b) => GDecode (a :+: b) where decodeUnion = withUnion handleUnion- where- handleUnion name unions object- | [name] == l1Tags = L1 <$> decodeUnion object- | [name] == r1Tags = R1 <$> decodeUnion object- | name `elem` l1Tags = L1 <$> decodeUnion unions- | name `elem` r1Tags = R1 <$> decodeUnion unions- | otherwise = internalArgumentError ("type \"" <> name <> "\" could not find in union")- where- l1Tags = unionTags $ Proxy @a- r1Tags = unionTags $ Proxy @b+ where+ handleUnion name unions object+ | [name] == l1Tags = L1 <$> decodeUnion object+ | [name] == r1Tags = R1 <$> decodeUnion object+ | name `elem` l1Tags = L1 <$> decodeUnion unions+ | name `elem` r1Tags = R1 <$> decodeUnion unions+ | otherwise = internalArgumentError+ ("type \"" <> name <> "\" could not find in union")+ where+ l1Tags = unionTags $ Proxy @a+ r1Tags = unionTags $ Proxy @b unionTags _ = unionTags (Proxy @a) ++ unionTags (Proxy @b)
src/Data/Morpheus/Execution/Server/Encode.hs view
@@ -19,109 +19,153 @@ , encodeSubscription , encodeMutation , ObjectResolvers(..)- ) where+ )+where -import Data.Map (Map)-import qualified Data.Map as M (toList)-import Data.Maybe (fromMaybe)-import Data.Proxy (Proxy (..))-import Data.Semigroup ((<>))-import Data.Set (Set)-import qualified Data.Set as S (toList)-import Data.Text (pack)+import Data.Map ( Map )+import qualified Data.Map as M+ ( toList )+import Data.Maybe ( fromMaybe )+import Data.Proxy ( Proxy(..) )+import Data.Semigroup ( (<>) )+import Data.Set ( Set )+import qualified Data.Set as S+ ( toList )+import Data.Text ( pack ) import GHC.Generics -- MORPHEUS-import Data.Morpheus.Error.Internal (internalUnknownTypeMessage)-import Data.Morpheus.Execution.Server.Decode (DecodeObject, decodeArguments)-import Data.Morpheus.Execution.Server.Generics.EnumRep (EnumRep (..))-import Data.Morpheus.Kind (ENUM, GQL_KIND, OBJECT, ResContext (..), SCALAR,- UNION, VContext (..))-import Data.Morpheus.Types.Custom (MapKind, Pair (..), mapKindFromList)-import Data.Morpheus.Types.GQLScalar (GQLScalar (..))-import Data.Morpheus.Types.GQLType (GQLType (CUSTOM, KIND, __typeName))-import Data.Morpheus.Types.Internal.AST.Operation (Operation (..), ValidOperation)-import Data.Morpheus.Types.Internal.AST.Selection (Selection (..), SelectionRec (..), ValidSelection)-import Data.Morpheus.Types.Internal.Base (Key)-import Data.Morpheus.Types.Internal.Data (MUTATION, OperationType, QUERY, SUBSCRIPTION)-import Data.Morpheus.Types.Internal.Resolver (MapGraphQLT (..), PureOperation (..), Resolver (..),- Resolving (..), ResolvingStrategy (..), resolveObject,- withObject)-import Data.Morpheus.Types.Internal.Validation (Validation)-import Data.Morpheus.Types.Internal.Value (GQLValue (..), Value (..))+import Data.Morpheus.Error.Internal ( internalUnknownTypeMessage )+import Data.Morpheus.Execution.Server.Decode+ ( DecodeObject+ , decodeArguments+ )+import Data.Morpheus.Execution.Server.Generics.EnumRep+ ( EnumRep(..) )+import Data.Morpheus.Kind ( ENUM+ , GQL_KIND+ , OBJECT+ , ResContext(..)+ , SCALAR+ , UNION+ , VContext(..)+ )+import Data.Morpheus.Types.Types ( MapKind+ , Pair(..)+ , mapKindFromList+ )+import Data.Morpheus.Types.GQLScalar ( GQLScalar(..) )+import Data.Morpheus.Types.GQLType ( GQLType+ ( CUSTOM+ , KIND+ , __typeName+ )+ )+import Data.Morpheus.Types.Internal.AST+ ( Operation(..)+ , ValidOperation+ , Key,+ MUTATION+ , OperationType+ , QUERY+ , SUBSCRIPTION+ , Selection(..)+ , SelectionRec(..)+ , ValidSelection+ , GQLValue(..)+ , Value(..)+ )+import Data.Morpheus.Types.Internal.Resolving+ ( MapStrategy(..)+ , LiftEither(..)+ , Resolver(..)+ , resolving+ , toResolver+ , ResolvingStrategy(..)+ , resolveObject+ , withObject+ , Validation+ , failure+ ) -class Encode resolver o m e where- encode :: PureOperation o => resolver -> (Key, ValidSelection) -> ResolvingStrategy o m e Value+class Encode resolver o e (m :: * -> *) where+ encode :: resolver -> (Key, ValidSelection) -> ResolvingStrategy o e m Value -instance {-# OVERLAPPABLE #-} (EncodeKind (KIND a) a o m e , PureOperation o) => Encode a o m e where+instance {-# OVERLAPPABLE #-} (EncodeKind (KIND a) a o e m , LiftEither o ResolvingStrategy) => Encode a o e m where encode resolver = encodeKind (VContext resolver :: VContext (KIND a) a) -- MAYBE-instance (Monad m , Encode a o m e) => Encode (Maybe a) o m e where+instance (Monad m , LiftEither o ResolvingStrategy,Encode a o e m) => Encode (Maybe a) o e m where encode = maybe (const $ pure gqlNull) encode -- Tuple (a,b)-instance Encode (Pair k v) o m e => Encode (k, v) o m e where+instance Encode (Pair k v) o e m => Encode (k, v) o e m where encode (key, value) = encode (Pair key value) -- Set-instance Encode [a] o m e => Encode (Set a) o m e where+instance Encode [a] o e m => Encode (Set a) o e m where encode = encode . S.toList -- Map-instance (Eq k, Monad m, Encode (MapKind k v (Resolver o m e)) o m e) => Encode (Map k v) o m e where- encode value = encode ((mapKindFromList $ M.toList value) :: MapKind k v (Resolver o m e))+instance (Eq k, Monad m,LiftEither o Resolver, Encode (MapKind k v (Resolver o e m)) o e m) => Encode (Map k v) o e m where+ encode value =+ encode ((mapKindFromList $ M.toList value) :: MapKind k v (Resolver o e m)) -- LIST []-instance (Monad m, Encode a o m e) => Encode [a] o m e where+instance (Monad m, Encode a o e m, LiftEither o ResolvingStrategy) => Encode [a] o e m where encode list query = gqlList <$> traverse (`encode` query) list - -- GQL a -> Resolver b, MUTATION, SUBSCRIPTION, QUERY-instance (DecodeObject a, Resolving fO m e ,Monad m,PureOperation fO, MapGraphQLT fO o, Encode b fO m e ) => Encode (a -> Resolver fO m e b) o m e where- encode resolver selection@(_, Selection { selectionArguments }) = mapGraphQLT $ resolving encode (getArgs args resolver) selection- where- args :: Validation a- args = decodeArguments selectionArguments+instance (DecodeObject a, Monad m,LiftEither fo Resolver, MapStrategy fo o, Encode b fo e m) => Encode (a -> Resolver fo e m b) o e m where+ encode resolver selection@(_, Selection { selectionArguments }) =+ mapStrategy $ resolving encode (toResolver args resolver) selection+ where+ args :: Validation a+ args = decodeArguments selectionArguments -- ENCODE GQL KIND-class EncodeKind (kind :: GQL_KIND) a o m e where- encodeKind :: PureOperation o => VContext kind a -> (Key, ValidSelection) -> ResolvingStrategy o m e Value+class EncodeKind (kind :: GQL_KIND) a o e (m :: * -> *) where+ encodeKind :: LiftEither o ResolvingStrategy => VContext kind a -> (Key, ValidSelection) -> ResolvingStrategy o e m Value -- SCALAR-instance (GQLScalar a, Monad m) => EncodeKind SCALAR a o m e where+instance (GQLScalar a, Monad m) => EncodeKind SCALAR a o e m where encodeKind = pure . pure . gqlScalar . serialize . unVContext -- ENUM-instance (Generic a, EnumRep (Rep a), Monad m) => EncodeKind ENUM a o m e where+instance (Generic a, EnumRep (Rep a), Monad m) => EncodeKind ENUM a o e m where encodeKind = pure . pure . gqlString . encodeRep . from . unVContext -- OBJECT-instance (Monad m, EncodeCon o m e a, Monad m, GResolver OBJECT (Rep a) o m e) => EncodeKind OBJECT a o m e where- encodeKind (VContext value) = withObject encodeK- where- encodeK selection = resolveObject selection (__typenameResolver : objectResolvers (Proxy :: Proxy (CUSTOM a)) value)- __typenameResolver = ("__typename", const $ pure $ gqlString $ __typeName (Proxy @a))+instance (Monad m, EncodeCon o e m a, Monad m, GResolver OBJECT (Rep a) o e m) => EncodeKind OBJECT a o e m where+ encodeKind (VContext value) = withObject encodeK+ where+ encodeK selection = resolveObject+ selection+ (__typenameResolver : objectResolvers (Proxy :: Proxy (CUSTOM a)) value)+ __typenameResolver =+ ("__typename", const $ pure $ gqlString $ __typeName (Proxy @a)) -- exploreKindChannels -- UNION-instance (Monad m, GQL_RES a, GResolver UNION (Rep a) o m e) => EncodeKind UNION a o m e where- encodeKind (VContext value) (key, sel@Selection {selectionRec = UnionSelection selections}) =- resolver (key, sel {selectionRec = SelectionSet lookupSelection})- -- SPEC: if there is no any fragment that supports current object Type GQL returns {}- where- lookupSelection = fromMaybe [] $ lookup typeName selections- (typeName, resolver) = unionResolver value- encodeKind _ _ = Fail $ internalUnknownTypeMessage "union Resolver only should recieve UnionSelection"+instance (Monad m, GQL_RES a, GResolver UNION (Rep a) o e m) => EncodeKind UNION a o e m where+ encodeKind (VContext value) (key, sel@Selection { selectionRec = UnionSelection selections })+ = resolver (key, sel { selectionRec = SelectionSet lookupSelection })+ where+ lookupSelection = fromMaybe [] $ lookup typeName selections+ (typeName, resolver) = unionResolver value+ encodeKind _ _ = failure $ internalUnknownTypeMessage+ "union Resolver only should recieve UnionSelection" -- Types & Constrains ------------------------------------------------------- type GQL_RES a = (Generic a, GQLType a) -type EncodeOperator o m e a = a -> ValidOperation -> ResolvingStrategy o m e Value+type EncodeOperator o e m a+ = a -> ValidOperation -> ResolvingStrategy o e m Value -type EncodeCon o m e a = (GQL_RES a, ObjectResolvers (CUSTOM a) a o m e)+type EncodeCon o e m a = (GQL_RES a, ObjectResolvers (CUSTOM a) a o e m) -type FieldRes o m e = (Key, (Key, ValidSelection) -> ResolvingStrategy o m e Value)+type FieldRes o e m+ = (Key, (Key, ValidSelection) -> ResolvingStrategy o e m Value) type family GRes (kind :: GQL_KIND) value :: * @@ -130,64 +174,82 @@ type instance GRes UNION v = (Key, (Key, ValidSelection) -> v) --- GENERICS -------------------------------------------------class ObjectResolvers (custom :: Bool) a (o :: OperationType) (m :: * -> *) e where- objectResolvers :: PureOperation o => Proxy custom -> a -> [(Key, (Key, ValidSelection) -> ResolvingStrategy o m e Value)]+class ObjectResolvers (custom :: Bool) a (o :: OperationType) e (m :: * -> *) where+ objectResolvers :: Proxy custom -> a -> [(Key, (Key, ValidSelection) -> ResolvingStrategy o e m Value)] -instance (Generic a, GResolver OBJECT (Rep a) o m e ) => ObjectResolvers 'False a o m e where- objectResolvers _ = getResolvers (ResContext :: ResContext OBJECT o m e value) . from+instance (Generic a, GResolver OBJECT (Rep a) o e m ) => ObjectResolvers 'False a o e m where+ objectResolvers _ =+ getResolvers (ResContext :: ResContext OBJECT o e m value) . from -unionResolver :: (Generic a, PureOperation o, GResolver UNION (Rep a) o m e) => a -> (Key, (Key, ValidSelection) -> ResolvingStrategy o m e Value)-unionResolver = getResolvers (ResContext :: ResContext UNION o m e value) . from+unionResolver+ :: (Generic a, GResolver UNION (Rep a) o e m)+ => a+ -> (Key, (Key, ValidSelection) -> ResolvingStrategy o e m Value)+unionResolver =+ getResolvers (ResContext :: ResContext UNION o e m value) . from -- | Derives resolvers for OBJECT and UNION-class GResolver (kind :: GQL_KIND) f o m e where- getResolvers :: PureOperation o => ResContext kind o m e value -> f a -> GRes kind (ResolvingStrategy o m e Value)+class GResolver (kind :: GQL_KIND) f o e (m :: * -> *) where+ getResolvers :: ResContext kind o e m value -> f a -> GRes kind (ResolvingStrategy o e m Value) -instance GResolver kind f o m e => GResolver kind (M1 D c f) o m e where+instance GResolver kind f o e m => GResolver kind (M1 D c f) o e m where getResolvers context (M1 src) = getResolvers context src -instance GResolver kind f o m e => GResolver kind (M1 C c f) o m e where+instance GResolver kind f o e m => GResolver kind (M1 C c f) o e m where getResolvers context (M1 src) = getResolvers context src -- OBJECT-instance GResolver OBJECT U1 o m e where+instance GResolver OBJECT U1 o e m where getResolvers _ _ = [] -instance (Selector s, GQLType a, Encode a o m e) => GResolver OBJECT (M1 S s (K1 s2 a)) o m e where+instance (Selector s, GQLType a, Encode a o e m) => GResolver OBJECT (M1 S s (K1 s2 a)) o e m where getResolvers _ m@(M1 (K1 src)) = [(pack (selName m), encode src)] -instance (GResolver OBJECT f o m e, GResolver OBJECT g o m e) => GResolver OBJECT (f :*: g) o m e where- getResolvers context (a :*: b) = getResolvers context a ++ getResolvers context b+instance (GResolver OBJECT f o e m, GResolver OBJECT g o e m) => GResolver OBJECT (f :*: g) o e m where+ getResolvers context (a :*: b) =+ getResolvers context a ++ getResolvers context b -- UNION-instance (Selector s, GQLType a, Encode a o m e ) => GResolver UNION (M1 S s (K1 s2 a)) o m e where+instance (Selector s, GQLType a, Encode a o e m ) => GResolver UNION (M1 S s (K1 s2 a)) o e m where getResolvers _ (M1 (K1 src)) = (__typeName (Proxy @a), encode src) -instance (GResolver UNION a o m e, GResolver UNION b o m e) => GResolver UNION (a :+: b) o m e where+instance (GResolver UNION a o e m, GResolver UNION b o e m) => GResolver UNION (a :+: b) o e m where getResolvers context (L1 x) = getResolvers context x getResolvers context (R1 x) = getResolvers context x ----- HELPERS -----------------------------encodeQuery ::- forall m event query (schema :: (* -> *) -> *). (Monad m, EncodeCon QUERY m event (schema (Resolver QUERY m event)), EncodeCon QUERY m event query, Resolving QUERY m event)- => schema (Resolver QUERY m event)- -> EncodeOperator QUERY m event query-encodeQuery schema = encodeOperationWith (objectResolvers (Proxy :: Proxy (CUSTOM (schema (Resolver QUERY m event)))) schema)+encodeQuery+ :: forall m event query (schema :: (* -> *) -> *)+ . ( Monad m+ , EncodeCon QUERY event m (schema (Resolver QUERY event m))+ , EncodeCon QUERY event m query+ )+ => schema (Resolver QUERY event m)+ -> EncodeOperator QUERY event m query+encodeQuery schema = encodeOperationWith+ (objectResolvers (Proxy :: Proxy (CUSTOM (schema (Resolver QUERY event m))))+ schema+ ) -encodeMutation ::- forall m event mut. (Monad m, EncodeCon MUTATION m event mut, Resolving MUTATION m event)- => EncodeOperator MUTATION m event mut+encodeMutation+ :: forall event m mut+ . (Monad m, EncodeCon MUTATION event m mut)+ => EncodeOperator MUTATION event m mut encodeMutation = encodeOperationWith [] -encodeSubscription ::- forall m event mut. (Monad m, EncodeCon SUBSCRIPTION m event mut, Resolving SUBSCRIPTION m event)- => EncodeOperator SUBSCRIPTION m event mut+encodeSubscription+ :: forall m event mut+ . (Monad m, EncodeCon SUBSCRIPTION event m mut)+ => EncodeOperator SUBSCRIPTION event m mut encodeSubscription = encodeOperationWith [] -encodeOperationWith ::- forall o m e a . (Monad m, EncodeCon o m e a, Resolving o m e, PureOperation o)- => [FieldRes o m e]- -> EncodeOperator o m e a-encodeOperationWith externalRes rootResolver Operation {operationSelection} = resolveObject operationSelection resolvers- where- resolvers = externalRes <> objectResolvers (Proxy :: Proxy (CUSTOM a)) rootResolver+encodeOperationWith+ :: forall o e m a+ . (Monad m, EncodeCon o e m a, LiftEither o ResolvingStrategy)+ => [FieldRes o e m]+ -> EncodeOperator o e m a+encodeOperationWith externalRes rootResolver Operation { operationSelection } =+ resolveObject operationSelection resolvers+ where+ resolvers =+ externalRes <> objectResolvers (Proxy :: Proxy (CUSTOM a)) rootResolver
src/Data/Morpheus/Execution/Server/Generics/EnumRep.hs view
@@ -6,16 +6,20 @@ module Data.Morpheus.Execution.Server.Generics.EnumRep ( EnumRep(..)- ) where+ )+where -import Data.Proxy (Proxy (..))-import Data.Semigroup ((<>))-import Data.Text (Text, pack)+import Data.Proxy ( Proxy(..) )+import Data.Semigroup ( (<>) )+import Data.Text ( Text+ , pack+ ) import GHC.Generics -- MORPHEUS-import Data.Morpheus.Error.Internal (internalError)-import Data.Morpheus.Types.Internal.Validation (Validation)+import Data.Morpheus.Error.Internal ( internalError )+import Data.Morpheus.Types.Internal.Resolving+ ( Validation ) class EnumRep f where encodeRep :: f a -> Text@@ -38,6 +42,6 @@ decodeEnum name | name `elem` enumTags (Proxy @a) = L1 <$> decodeEnum name | name `elem` enumTags (Proxy @b) = R1 <$> decodeEnum name- | otherwise =- internalError ("Constructor \"" <> name <> "\" could not find in Union")+ | otherwise = internalError+ ("Constructor \"" <> name <> "\" could not find in Union") enumTags _ = enumTags (Proxy @a) ++ enumTags (Proxy @b)
src/Data/Morpheus/Execution/Server/Interpreter.hs view
@@ -5,22 +5,44 @@ module Data.Morpheus.Execution.Server.Interpreter ( Interpreter(..)- ) where+ )+where -import Data.Aeson (encode)-import Data.ByteString (ByteString)-import qualified Data.ByteString.Lazy.Char8 as LB (ByteString, fromStrict, toStrict)-import Data.Text (Text)-import qualified Data.Text.Lazy as LT (Text, fromStrict, toStrict)-import Data.Text.Lazy.Encoding (decodeUtf8, encodeUtf8)+import Data.Aeson ( encode )+import Data.ByteString ( ByteString )+import qualified Data.ByteString.Lazy.Char8 as LB+ ( ByteString+ , fromStrict+ , toStrict+ )+import Data.Text ( Text )+import qualified Data.Text.Lazy as LT+ ( Text+ , fromStrict+ , toStrict+ )+import Data.Text.Lazy.Encoding ( decodeUtf8+ , encodeUtf8+ ) -- MORPHEUS-import Data.Morpheus.Execution.Server.Resolve (RootResCon, byteStringIO, statefulResolver,- statelessResolver, streamResolver)-import Data.Morpheus.Execution.Subscription.ClientRegister (GQLState)-import Data.Morpheus.Types.Internal.Resolver (GQLRootResolver (..))-import Data.Morpheus.Types.Internal.Stream (ResponseStream)-import Data.Morpheus.Types.IO (GQLRequest, GQLResponse)+import Data.Morpheus.Execution.Server.Resolve+ ( RootResCon+ , byteStringIO+ , statefulResolver+ , statelessResolver+ , streamResolver+ , coreResolver+ )+import Data.Morpheus.Execution.Subscription.ClientRegister+ ( GQLState )+import Data.Morpheus.Types.Internal.Resolving+ ( GQLRootResolver(..)+ , ResponseStream+ )+import Data.Morpheus.Types.IO ( GQLRequest+ , GQLResponse+ ) -- | main query processor and resolver -- possible versions of interpreter@@ -50,7 +72,7 @@ -} type StateLess m a = a -> m a -instance Interpreter (GQLRequest -> m GQLResponse) m e where+instance Interpreter (GQLRequest -> m GQLResponse) m e where interpreter = statelessResolver instance Interpreter (StateLess m LB.ByteString) m e where@@ -60,11 +82,11 @@ interpreter root request = decodeUtf8 <$> interpreter root (encodeUtf8 request) -instance Interpreter (StateLess m ByteString) m e where+instance Interpreter (StateLess m ByteString) m e where interpreter root request = LB.toStrict <$> interpreter root (LB.fromStrict request) -instance Interpreter (StateLess m Text) m e where+instance Interpreter (StateLess m Text) m e where interpreter root request = LT.toStrict <$> interpreter root (LT.fromStrict request) @@ -72,17 +94,16 @@ HTTP Interpreter with state and side effects, every mutation will trigger subscriptions in shared `GQLState` -}-type WSPub m e a = GQLState m e -> a -> m a+type WSPub m e a = GQLState m e -> a -> m a -instance Interpreter (WSPub IO e LB.ByteString) IO e where- interpreter root state =- statefulResolver state (byteStringIO (streamResolver root))+instance Interpreter (WSPub IO e LB.ByteString) IO e where+ interpreter root state = statefulResolver state (coreResolver root) -instance Interpreter (WSPub IO e LT.Text) IO e where+instance Interpreter (WSPub IO e LT.Text) IO e where interpreter root state request = decodeUtf8 <$> interpreter root state (encodeUtf8 request) -instance Interpreter (WSPub IO e ByteString) IO e where+instance Interpreter (WSPub IO e ByteString) IO e where interpreter root state request = LB.toStrict <$> interpreter root state (LB.fromStrict request) @@ -94,22 +115,22 @@ WebSocket Interpreter without state and side effects, mutations and subscription will return Actions that will be executed in WebSocket server -}-type WSSub m e a = a -> ResponseStream m e a+type WSSub m e a = a -> ResponseStream e m a -instance Interpreter (GQLRequest -> ResponseStream m e LB.ByteString) m e where+instance Interpreter (GQLRequest -> ResponseStream e m LB.ByteString) m e where interpreter root request = encode <$> streamResolver root request -instance Interpreter (WSSub m e LB.ByteString) m e where+instance Interpreter (WSSub m e LB.ByteString) m e where interpreter root = byteStringIO (streamResolver root) -instance Interpreter (WSSub m e LT.Text) m e where+instance Interpreter (WSSub m e LT.Text) m e where interpreter root request = decodeUtf8 <$> interpreter root (encodeUtf8 request) -instance Interpreter (WSSub m e ByteString) m e where+instance Interpreter (WSSub m e ByteString) m e where interpreter root request = LB.toStrict <$> interpreter root (LB.fromStrict request) -instance Interpreter (WSSub m e Text) m e where+instance Interpreter (WSSub m e Text) m e where interpreter root request = LT.toStrict <$> interpreter root (LT.fromStrict request)
src/Data/Morpheus/Execution/Server/Introspect.hs view
@@ -21,28 +21,54 @@ , IntroCon , updateLib , buildType- ) where+ )+where -import Data.Map (Map)-import Data.Proxy (Proxy (..))-import Data.Semigroup ((<>))-import Data.Set (Set)-import Data.Text (Text, pack)+import Data.Map ( Map )+import Data.Proxy ( Proxy(..) )+import Data.Set ( Set )+import Data.Text ( Text+ , pack+ ) import GHC.Generics -- MORPHEUS-import Data.Morpheus.Error.Schema (nameCollisionError)-import Data.Morpheus.Execution.Internal.GraphScanner (LibUpdater, resolveUpdates)-import Data.Morpheus.Execution.Server.Generics.EnumRep (EnumRep (..))-import Data.Morpheus.Kind (Context (..), ENUM, GQL_KIND, INPUT_OBJECT,- INPUT_UNION, OBJECT, SCALAR, UNION)-import Data.Morpheus.Types.Custom (MapKind, Pair)-import Data.Morpheus.Types.GQLScalar (GQLScalar (..))-import Data.Morpheus.Types.GQLType (GQLType (..))-import Data.Morpheus.Types.Internal.Data (DataArguments, DataField (..), DataType (..),- DataTyCon (..), DataTypeLib, TypeAlias (..),- defineType, isTypeDefined, toListField,- toNullableField)+import Data.Morpheus.Error.Schema ( nameCollisionError )+import Data.Morpheus.Execution.Server.Generics.EnumRep+ ( EnumRep(..) )+import Data.Morpheus.Kind ( Context(..)+ , ENUM+ , GQL_KIND+ , INPUT_OBJECT+ , INPUT_UNION+ , OBJECT+ , SCALAR+ , UNION+ )+import Data.Morpheus.Types.Types ( MapKind+ , Pair+ )+import Data.Morpheus.Types.GQLScalar ( GQLScalar(..) )+import Data.Morpheus.Types.GQLType ( GQLType(..) )+import Data.Morpheus.Types.Internal.Resolving+ ( Failure(..)+ , resolveUpdates+ )+import Data.Morpheus.Types.Internal.AST+ ( DataArguments+ , Meta(..)+ , DataField(..)+ , DataTyCon(..)+ , DataType(..)+ , Key+ , createAlias+ , defineType+ , isTypeDefined+ , toListField+ , toNullableField+ , createEnumValue+ , TypeUpdater+ ) type IntroCon a = (GQLType a, ObjectFields (CUSTOM a) a)@@ -86,11 +112,14 @@ -- Resolver : a -> Resolver b instance (ObjectFields 'False a, Introspect b) => Introspect (a -> m b) where- field _ name = (field (Proxy @b) name) {fieldArgs = fst $ objectFields (Proxy :: Proxy 'False) (Proxy @a)}- introspect _ typeLib = resolveUpdates typeLib (introspect (Proxy @b) : argTypes)- where- argTypes :: [TypeUpdater]- argTypes = snd $ objectFields (Proxy :: Proxy 'False) (Proxy @a)+ field _ name = (field (Proxy @b) name)+ { fieldArgs = fst $ objectFields (Proxy :: Proxy 'False) (Proxy @a)+ }+ introspect _ typeLib = resolveUpdates typeLib+ (introspect (Proxy @b) : argTypes)+ where+ argTypes :: [TypeUpdater]+ argTypes = snd $ objectFields (Proxy :: Proxy 'False) (Proxy @a) -- | Introspect With specific Kind: 'kind': object, scalar, enum ... class IntrospectKind (kind :: GQL_KIND) a where@@ -99,72 +128,56 @@ -- SCALAR instance (GQLType a, GQLScalar a) => IntrospectKind SCALAR a where introspectKind _ = updateLib scalarType [] (Proxy @a)- where- scalarType = DataScalar . buildType (scalarValidator (Proxy @a))+ where scalarType = DataScalar . buildType (scalarValidator (Proxy @a)) -- ENUM instance (GQL_TYPE a, EnumRep (Rep a)) => IntrospectKind ENUM a where introspectKind _ = updateLib enumType [] (Proxy @a)- where- enumType = DataEnum . buildType (enumTags (Proxy @(Rep a)))+ where+ enumType =+ DataEnum . buildType (map createEnumValue $ enumTags (Proxy @(Rep a))) -- INPUT_OBJECT instance (GQL_TYPE a, ObjectFields (CUSTOM a) a) => IntrospectKind INPUT_OBJECT a where- introspectKind _ = updateLib (DataInputObject . buildType fields) types (Proxy @a)- where- (fields, types) = objectFields (Proxy @(CUSTOM a)) (Proxy @a)+ introspectKind _ = updateLib (DataInputObject . buildType fields)+ types+ (Proxy @a)+ where (fields, types) = objectFields (Proxy @(CUSTOM a)) (Proxy @a) -- OBJECTS instance (GQL_TYPE a, ObjectFields (CUSTOM a) a) => IntrospectKind OBJECT a where- introspectKind _ = updateLib (DataObject . buildType (__typename : fields)) types (Proxy @a)- where- __typename =- ( "__typename"- , DataField- { fieldName = "__typename"- , fieldArgs = []- , fieldArgsType = Nothing- , fieldType = buildAlias "String"- , fieldHidden = True- })- (fields, types) = objectFields (Proxy @(CUSTOM a)) (Proxy @a)+ introspectKind _ = updateLib (DataObject . buildType (__typename : fields))+ types+ (Proxy @a)+ where+ __typename =+ ( "__typename"+ , DataField { fieldName = "__typename"+ , fieldArgs = []+ , fieldArgsType = Nothing+ , fieldType = createAlias "String"+ , fieldMeta = Nothing+ }+ )+ (fields, types) = objectFields (Proxy @(CUSTOM a)) (Proxy @a) -- UNION instance (GQL_TYPE a, GQLRep UNION (Rep a)) => IntrospectKind UNION a where- introspectKind _ = updateLib (DataUnion . buildType fields) stack (Proxy @a)- where- (fields, stack) = unzip $ gqlRep (Context :: Context UNION (Rep a))+ introspectKind _ = updateLib (DataUnion . buildType memberTypes)+ stack+ (Proxy @a)+ where+ (memberTypes, stack) = unzip $ gqlRep (Context :: Context UNION (Rep a)) -- INPUT_UNION instance (GQL_TYPE a, GQLRep UNION (Rep a)) => IntrospectKind INPUT_UNION a where- introspectKind _ = updateLib (DataInputUnion . buildType (fieldTag : fields)) (tagsEnumType : stack) (Proxy @a)- where- (inputUnions, stack) = unzip $ gqlRep (Context :: Context UNION (Rep a))- fields = map toNullableField inputUnions- -- for every input Union 'User' adds enum type of possible TypeNames 'UserTags'- tagsEnumType :: TypeUpdater- tagsEnumType x = pure $ defineType (typeName, DataEnum tagsEnum) x- where- tagsEnum =- DataTyCon- { typeName- -- has same fingerprint as object because it depends on it- , typeFingerprint = __typeFingerprint (Proxy @a)- , typeDescription = Nothing- , typeData = map fieldName inputUnions- }- typeName = __typeName (Proxy @a) <> "Tags"- fieldTag =- DataField- { fieldName = "tag"- , fieldArgs = []- , fieldArgsType = Nothing- , fieldType = buildAlias typeName- , fieldHidden = False- }+ introspectKind _ = updateLib (DataInputUnion . buildType memberTypes)+ stack+ (Proxy @a)+ where+ (memberTypes, stack) = unzip $ gqlRep (Context :: Context UNION (Rep a)) -- Types-type TypeUpdater = LibUpdater DataTypeLib type GQL_TYPE a = (Generic a, GQLType a) @@ -179,7 +192,7 @@ type instance GQLRepResult OBJECT = (Text, DataField) -type instance GQLRepResult UNION = DataField+type instance GQLRepResult UNION = Key -- GENERIC UNION class GQLRep (kind :: GQL_KIND) f where@@ -193,45 +206,55 @@ -- | recursion for Object types, both of them : 'UNION' and 'INPUT_UNION' instance (GQLRep UNION a, GQLRep UNION b) => GQLRep UNION (a :+: b) where- gqlRep _ = gqlRep (Context :: Context UNION a) ++ gqlRep (Context :: Context UNION b)+ gqlRep _ =+ gqlRep (Context :: Context UNION a) ++ gqlRep (Context :: Context UNION b) instance (GQL_TYPE a, Introspect a) => GQLRep UNION (M1 S s (Rec0 a)) where- gqlRep _ = [(buildField (Proxy @a) [] (__typeName (Proxy @a)), introspect (Proxy @a))]+ gqlRep _ = [(__typeName (Proxy @a), introspect (Proxy @a))] -- | recursion for Object types, both of them : 'INPUT_OBJECT' and 'OBJECT' instance (GQLRep OBJECT a, GQLRep OBJECT b) => GQLRep OBJECT (a :*: b) where- gqlRep _ = gqlRep (Context :: Context OBJECT a) ++ gqlRep (Context :: Context OBJECT b)+ gqlRep _ =+ gqlRep (Context :: Context OBJECT a) ++ gqlRep (Context :: Context OBJECT b) instance (Selector s, Introspect a) => GQLRep OBJECT (M1 S s (Rec0 a)) where gqlRep _ = [((name, field (Proxy @a) name), introspect (Proxy @a))]- where- name = pack $ selName (undefined :: M1 S s (Rec0 ()) ())+ where name = pack $ selName (undefined :: M1 S s (Rec0 ()) ()) instance GQLRep OBJECT U1 where gqlRep _ = [] -buildAlias :: Text -> TypeAlias-buildAlias aliasTyCon = TypeAlias {aliasTyCon, aliasWrappers = [], aliasArgs = Nothing} buildField :: GQLType a => Proxy a -> DataArguments -> Text -> DataField-buildField proxy fieldArgs fieldName =- DataField- {fieldName, fieldArgs, fieldArgsType = Nothing, fieldType = buildAlias $ __typeName proxy, fieldHidden = False}+buildField proxy fieldArgs fieldName = DataField+ { fieldName+ , fieldArgs+ , fieldArgsType = Nothing+ , fieldType = createAlias $ __typeName proxy+ , fieldMeta = Nothing+ } buildType :: GQLType a => t -> Proxy a -> DataTyCon t-buildType typeData proxy =- DataTyCon- { typeName = __typeName proxy- , typeFingerprint = __typeFingerprint proxy- , typeDescription = description proxy- , typeData- }+buildType typeData proxy = DataTyCon+ { typeName = __typeName proxy+ , typeFingerprint = __typeFingerprint proxy+ , typeMeta = Just Meta { metaDescription = description proxy+ , metaDirectives = []+ }+ , typeData+ } -updateLib :: GQLType a => (Proxy a -> DataType) -> [TypeUpdater] -> Proxy a -> TypeUpdater+updateLib+ :: GQLType a+ => (Proxy a -> DataType)+ -> [TypeUpdater]+ -> Proxy a+ -> TypeUpdater updateLib typeBuilder stack proxy lib' = case isTypeDefined (__typeName proxy) lib' of- Nothing -> resolveUpdates (defineType (__typeName proxy, typeBuilder proxy) lib') stack- Just fingerprint'- | fingerprint' == __typeFingerprint proxy -> return lib'+ Nothing -> resolveUpdates+ (defineType (__typeName proxy, typeBuilder proxy) lib')+ stack+ Just fingerprint' | fingerprint' == __typeFingerprint proxy -> return lib' -- throw error if 2 different types has same name- Just _ -> Left $ nameCollisionError (__typeName proxy)+ Just _ -> failure $ nameCollisionError (__typeName proxy)
src/Data/Morpheus/Execution/Server/Resolve.hs view
@@ -14,138 +14,214 @@ , statefulResolver , RootResCon , fullSchema- ) where+ , coreResolver+ )+where -import Control.Monad.Except (liftEither)-import Control.Monad.Trans.Except (runExceptT)-import Data.Aeson (encode)-import Data.Aeson.Internal (formatError, ifromJSON)-import Data.Aeson.Parser (eitherDecodeWith, jsonNoDup)-import qualified Data.ByteString.Lazy.Char8 as L-import Data.Functor.Identity (Identity (..))-import Data.Proxy (Proxy (..))+import Data.Aeson ( encode )+import Data.Aeson.Internal ( formatError+ , ifromJSON+ )+import Data.Aeson.Parser ( eitherDecodeWith+ , jsonNoDup+ )+import qualified Data.ByteString.Lazy.Char8 as L+import Data.Functor.Identity ( Identity(..) )+import Data.Proxy ( Proxy(..) ) -- MORPHEUS-import Data.Morpheus.Error.Utils (badRequestError)-import Data.Morpheus.Execution.Internal.GraphScanner (resolveUpdates)-import Data.Morpheus.Execution.Server.Encode (EncodeCon, encodeMutation, encodeQuery,- encodeSubscription)-import Data.Morpheus.Execution.Server.Introspect (IntroCon, ObjectFields (..))-import Data.Morpheus.Execution.Subscription.ClientRegister (GQLState, publishUpdates)-import Data.Morpheus.Parsing.Request.Parser (parseGQL)+import Data.Morpheus.Error.Utils ( badRequestError )+import Data.Morpheus.Execution.Server.Encode+ ( EncodeCon+ , encodeMutation+ , encodeQuery+ , encodeSubscription+ )+import Data.Morpheus.Execution.Server.Introspect+ ( IntroCon+ , ObjectFields(..)+ )+import Data.Morpheus.Execution.Subscription.ClientRegister+ ( GQLState+ , publishUpdates+ )+import Data.Morpheus.Parsing.Request.Parser+ ( parseGQL ) --import Data.Morpheus.Schema.Schema (Root)-import Data.Morpheus.Schema.SchemaAPI (defaultTypes, hiddenRootFields, schemaAPI)-import Data.Morpheus.Types.GQLType (GQLType (CUSTOM))-import Data.Morpheus.Types.Internal.AST.Operation (Operation (..), ValidOperation)-import Data.Morpheus.Types.Internal.Data (DataFingerprint (..), DataTyCon (..),- DataTypeLib (..), MUTATION, OperationType (..),- QUERY, SUBSCRIPTION, initTypeLib)-import Data.Morpheus.Types.Internal.Resolver (GQLRootResolver (..), Resolver (..), ResponseT,- toResponseRes)-import Data.Morpheus.Types.Internal.Stream (GQLChannel (..), ResponseEvent (..),- ResponseStream, closeStream)-import Data.Morpheus.Types.Internal.Validation (Validation)-import Data.Morpheus.Types.IO (GQLRequest (..), GQLResponse (..), renderResponse)-import Data.Morpheus.Validation.Internal.Utils (VALIDATION_MODE (..))-import Data.Morpheus.Validation.Query.Validation (validateRequest)-import Data.Typeable (Typeable)+import Data.Morpheus.Schema.SchemaAPI ( defaultTypes+ , hiddenRootFields+ , schemaAPI+ )+import Data.Morpheus.Types.GQLType ( GQLType(CUSTOM) )+import Data.Morpheus.Types.Internal.AST+ ( Operation(..)+ , ValidOperation+ , DataFingerprint(..)+ , DataTyCon(..)+ , DataTypeLib(..)+ , MUTATION+ , OperationType(..)+ , QUERY+ , SUBSCRIPTION+ , initTypeLib+ , Value+ )+import Data.Morpheus.Types.Internal.Resolving+ ( GQLRootResolver(..)+ , Resolver(..)+ , toResponseRes+ , GQLChannel(..)+ , ResponseEvent(..)+ , ResponseStream+ , Validation+ , cleanEvents+ , ResultT(..)+ , unpackEvents+ , Failure(..)+ , resolveUpdates+ )+import Data.Morpheus.Types.IO ( GQLRequest(..)+ , GQLResponse(..)+ , renderResponse+ )+import Data.Morpheus.Validation.Internal.Utils+ ( VALIDATION_MODE(..) )+import Data.Morpheus.Validation.Query.Validation+ ( validateRequest )+import Data.Typeable ( Typeable ) -type EventCon event = (Eq (StreamChannel event), Typeable event, GQLChannel event) -type IntrospectConstraint m event query mutation subscription = (- IntroCon (query (Resolver QUERY m event))- , IntroCon (mutation (Resolver MUTATION m event))- , IntroCon (subscription (Resolver SUBSCRIPTION m event)))+type EventCon event+ = (Eq (StreamChannel event), Typeable event, GQLChannel event) +type IntrospectConstraint m event query mutation subscription+ = ( IntroCon (query (Resolver QUERY event m))+ , IntroCon (mutation (Resolver MUTATION event m))+ , IntroCon (subscription (Resolver SUBSCRIPTION event m))+ )+ type RootResCon m event query mutation subscription- = ( EventCon event- , Typeable m- , IntrospectConstraint m event query mutation subscription- -- , OBJ_RES m (Root (Resolver m)) Value- -- Resolving- , EncodeCon QUERY m event (query (Resolver QUERY m event))- , EncodeCon MUTATION m event (mutation (Resolver MUTATION m event))- , EncodeCon SUBSCRIPTION m event (subscription (Resolver SUBSCRIPTION m event)))+ = ( EventCon event+ , Typeable m+ , IntrospectConstraint m event query mutation subscription+ , EncodeCon QUERY event m (query (Resolver QUERY event m))+ , EncodeCon MUTATION event m (mutation (Resolver MUTATION event m))+ , EncodeCon+ SUBSCRIPTION+ event+ m+ (subscription (Resolver SUBSCRIPTION event m))+ ) -decodeNoDup :: L.ByteString -> Either String GQLRequest-decodeNoDup str =- case eitherDecodeWith jsonNoDup ifromJSON str of- Left (path, x) -> Left $ formatError path x- Right value -> Right value+decodeNoDup :: Failure String m => L.ByteString -> m GQLRequest+decodeNoDup str = case eitherDecodeWith jsonNoDup ifromJSON str of+ Left (path, x) -> failure $ formatError path x+ Right value -> pure value -byteStringIO :: Monad m => (GQLRequest -> m GQLResponse) -> L.ByteString -> m L.ByteString-byteStringIO resolver request =- case decodeNoDup request of- Left aesonError' -> return $ badRequestError aesonError'- Right req -> encode <$> resolver req -statelessResolver ::- (Monad m, RootResCon m event query mut sub)- => GQLRootResolver m event query mut sub+byteStringIO+ :: Monad m => (GQLRequest -> m GQLResponse) -> L.ByteString -> m L.ByteString+byteStringIO resolver request = case decodeNoDup request of+ Left aesonError' -> return $ badRequestError aesonError'+ Right req -> encode <$> resolver req++statelessResolver+ :: (Monad m, RootResCon m event query mut sub)+ => GQLRootResolver m event query mut sub -> GQLRequest -> m GQLResponse-statelessResolver root = fmap snd . closeStream . streamResolver root+statelessResolver root req =+ renderResponse <$> runResultT (coreResolver root req) -streamResolver ::- (Monad m, RootResCon m event query mut sub)+streamResolver+ :: forall event m query mut sub+ . (Monad m, RootResCon m event query mut sub) => GQLRootResolver m event query mut sub -> GQLRequest- -> ResponseStream m event GQLResponse-streamResolver root@GQLRootResolver {queryResolver, mutationResolver, subscriptionResolver} request =- renderResponse <$> runExceptT (validRequest >>= execOperator)- ------------------------------------------------------------- where- ---------------------------------------------------------- validRequest :: Monad m => ResponseT m event (DataTypeLib, ValidOperation)- validRequest =- liftEither $ do- schema <- fullSchema $ Identity root- query <- parseGQL request >>= validateRequest schema FULL_VALIDATION- Right (schema, query)- ----------------------------------------------------------- execOperator (schema, operation@Operation {operationType = Query}) =- toResponseRes (encodeQuery (schemaAPI schema) queryResolver operation)- execOperator (_, operation@Operation {operationType = Mutation}) = toResponseRes (encodeMutation mutationResolver operation)- execOperator (_, operation@Operation {operationType = Subscription}) = response- where- response = toResponseRes (encodeSubscription subscriptionResolver operation)+ -> ResponseStream event m GQLResponse+streamResolver root req =+ ResultT $ pure . renderResponse <$> runResultT (coreResolver root req) -statefulResolver ::- EventCon s- => GQLState IO s- -> (L.ByteString -> ResponseStream IO s L.ByteString)+coreResolver+ :: forall event m query mut sub+ . (Monad m, RootResCon m event query mut sub)+ => GQLRootResolver m event query mut sub+ -> GQLRequest+ -> ResponseStream event m Value+coreResolver root@GQLRootResolver { queryResolver, mutationResolver, subscriptionResolver } request+ = validRequest >>= execOperator+ where+ validRequest+ :: Monad m => ResponseStream event m (DataTypeLib, ValidOperation)+ validRequest = cleanEvents $ ResultT $ pure $ do+ schema <- fullSchema $ Identity root+ query <- parseGQL request >>= validateRequest schema FULL_VALIDATION+ pure (schema, query)+ ----------------------------------------------------------+ execOperator (schema, operation@Operation { operationType = Query }) =+ toResponseRes (encodeQuery (schemaAPI schema) queryResolver operation)+ execOperator (_, operation@Operation { operationType = Mutation }) =+ toResponseRes (encodeMutation mutationResolver operation)+ execOperator (_, operation@Operation { operationType = Subscription }) =+ response+ where+ response =+ toResponseRes (encodeSubscription subscriptionResolver operation)++statefulResolver+ :: EventCon event+ => GQLState IO event+ -> (GQLRequest -> ResponseStream event IO Value) -> L.ByteString -> IO L.ByteString-statefulResolver state streamApi request = do- (actions, value) <- closeStream (streamApi request)- mapM_ execute actions- pure value- where- execute (Publish updates) = publishUpdates state updates- execute Subscribe {} = pure ()+statefulResolver state streamApi requestText = do+ res <- runResultT (decodeNoDup requestText >>= streamApi)+ mapM_ execute (unpackEvents res)+ pure $ encode $ renderResponse res+ where+ execute (Publish updates) = publishUpdates state updates+ execute Subscribe{} = pure () -fullSchema ::- forall proxy m event query mutation subscription. (IntrospectConstraint m event query mutation subscription)++fullSchema+ :: forall proxy m event query mutation subscription+ . (IntrospectConstraint m event query mutation subscription) => proxy (GQLRootResolver m event query mutation subscription) -> Validation DataTypeLib fullSchema _ = querySchema >>= mutationSchema >>= subscriptionSchema- where- querySchema =- resolveUpdates (initTypeLib (operatorType (hiddenRootFields ++ fields) "Query")) (defaultTypes : types)- where- (fields, types) = objectFields (Proxy @(CUSTOM (query (Resolver QUERY m event)))) (Proxy @(query (Resolver QUERY m event)))- ------------------------------- mutationSchema lib = resolveUpdates (lib {mutation = maybeOperator fields "Mutation"}) types- where- (fields, types) = objectFields (Proxy @(CUSTOM (mutation (Resolver MUTATION m event)))) (Proxy @(mutation (Resolver MUTATION m event)))- ------------------------------- subscriptionSchema lib = resolveUpdates (lib {subscription = maybeOperator fields "Subscription"}) types- where- (fields, types) = objectFields (Proxy @(CUSTOM (subscription (Resolver SUBSCRIPTION m event)))) (Proxy @(subscription (Resolver SUBSCRIPTION m event)))- -- maybeOperator :: [a] -> Text -> Maybe (Text, DataTyCon[a])- maybeOperator [] = const Nothing- maybeOperator fields = Just . operatorType fields- -- operatorType :: [a] -> Text -> (Text, DataTyCon[a])- operatorType typeData typeName =- ( typeName- , DataTyCon {typeData, typeName, typeFingerprint = SystemFingerprint typeName, typeDescription = Nothing})+ where+ querySchema = resolveUpdates+ (initTypeLib (operatorType (hiddenRootFields ++ fields) "Query"))+ (defaultTypes : types)+ where+ (fields, types) = objectFields+ (Proxy @(CUSTOM (query (Resolver QUERY event m))))+ (Proxy @(query (Resolver QUERY event m)))+ ------------------------------+ mutationSchema lib = resolveUpdates+ (lib { mutation = maybeOperator fields "Mutation" })+ types+ where+ (fields, types) = objectFields+ (Proxy @(CUSTOM (mutation (Resolver MUTATION event m))))+ (Proxy @(mutation (Resolver MUTATION event m)))+ ------------------------------+ subscriptionSchema lib = resolveUpdates+ (lib { subscription = maybeOperator fields "Subscription" })+ types+ where+ (fields, types) = objectFields+ (Proxy @(CUSTOM (subscription (Resolver SUBSCRIPTION event m))))+ (Proxy @(subscription (Resolver SUBSCRIPTION event m)))+ -- maybeOperator :: [a] -> Text -> Maybe (Text, DataTyCon[a])+ maybeOperator [] = const Nothing+ maybeOperator fields = Just . operatorType fields+ -- operatorType :: [a] -> Text -> (Text, DataTyCon[a])+ operatorType typeData typeName =+ ( typeName+ , DataTyCon { typeData+ , typeName+ , typeFingerprint = SystemFingerprint typeName+ , typeMeta = Nothing+ }+ )
src/Data/Morpheus/Execution/Subscription/ClientRegister.hs view
@@ -10,25 +10,41 @@ , publishUpdates , addClientSubscription , removeClientSubscription- ) where+ )+where -import Control.Concurrent (MVar, modifyMVar, modifyMVar_, newMVar, readMVar)-import Data.Foldable (traverse_)-import Data.List (intersect)-import Data.Text (Text)-import Data.UUID.V4 (nextRandom)-import Network.WebSockets (Connection, sendTextData)- +import Control.Concurrent ( MVar+ , modifyMVar+ , modifyMVar_+ , newMVar+ , readMVar+ )+import Data.Foldable ( traverse_ )+import Data.List ( intersect )+import Data.Text ( Text )+import Data.UUID.V4 ( nextRandom )+import Network.WebSockets ( Connection+ , sendTextData+ ) -- MORPHEUS-import Data.Morpheus.Execution.Subscription.Apollo (toApolloResponse)-import Data.Morpheus.Types.Internal.Stream (Event (..),GQLChannel(..), SubEvent)-import Data.Morpheus.Types.Internal.WebSocket (ClientID, ClientSession (..), GQLClient (..))+import Data.Morpheus.Execution.Subscription.Apollo+ ( toApolloResponse )+import Data.Morpheus.Types.Internal.Resolving+ ( Event(..)+ , GQLChannel(..)+ , SubEvent+ )+import Data.Morpheus.Types.Internal.WebSocket+ ( ClientID+ , ClientSession(..)+ , GQLClient(..)+ ) -type ClientRegister m e = [(ClientID, GQLClient m e)]+type ClientRegister m e = [(ClientID, GQLClient m e)] -- | shared GraphQL state between __websocket__ and __http__ server, -- stores information about subscriptions-type GQLState m e = MVar (ClientRegister m e ) -- SharedState+type GQLState m e = MVar (ClientRegister m e) -- SharedState -- | initializes empty GraphQL state initGQLState :: IO (GQLState m e)@@ -39,71 +55,70 @@ client' <- newClient modifyMVar_ varState' (addClient client') return (snd client')- where- newClient = do- clientID <- nextRandom- return- (clientID, GQLClient {clientID, clientConnection, clientSessions = []})- addClient client' state' = return (client' : state')+ where+ newClient = do+ clientID <- nextRandom+ return+ (clientID, GQLClient { clientID, clientConnection, clientSessions = [] })+ addClient client' state' = return (client' : state') -disconnectClient ::- GQLClient m e -> GQLState m e -> IO (ClientRegister m e)+disconnectClient :: GQLClient m e -> GQLState m e -> IO (ClientRegister m e) disconnectClient client state = modifyMVar state removeUser- where- removeUser state' =- let s' = removeClient state'- in return (s', s')- removeClient :: ClientRegister m e -> ClientRegister m e- removeClient = filter ((/= clientID client) . fst)+ where+ removeUser state' = let s' = removeClient state' in return (s', s')+ removeClient :: ClientRegister m e -> ClientRegister m e+ removeClient = filter ((/= clientID client) . fst) -updateClientByID ::- ClientID+updateClientByID+ :: ClientID -> (GQLClient m e -> GQLClient m e)- -> MVar (ClientRegister m e )+ -> MVar (ClientRegister m e) -> IO ()-updateClientByID id' updateFunc state =- modifyMVar_ state (return . map updateClient)- where- updateClient (key', client')- | key' == id' = (key', updateFunc client')- updateClient state' = state'+updateClientByID id' updateFunc state = modifyMVar_+ state+ (return . map updateClient)+ where+ updateClient (key, client') | key == id' = (key, updateFunc client')+ updateClient state' = state' -publishUpdates :: (Eq (StreamChannel e), GQLChannel e) => GQLState IO e -> e -> IO ()+publishUpdates+ :: (Eq (StreamChannel e), GQLChannel e) => GQLState IO e -> e -> IO () publishUpdates state event = do state' <- readMVar state traverse_ sendMessage state'- where- sendMessage (_, GQLClient {clientSessions = []}) = return ()- sendMessage (_, GQLClient {clientSessions, clientConnection}) =- mapM_ __send (filterByChannels clientSessions)- where- __send ClientSession { sessionId- , sessionSubscription = Event {content = subscriptionRes}- } =- subscriptionRes event >>=- sendTextData clientConnection . toApolloResponse sessionId- filterByChannels =- filter- (not . null .- intersect (streamChannels event) . channels . sessionSubscription)+ where+ sendMessage (_, GQLClient { clientSessions = [] } ) = return ()+ sendMessage (_, GQLClient { clientSessions, clientConnection }) = mapM_+ __send+ (filterByChannels clientSessions)+ where+ __send ClientSession { sessionId, sessionSubscription = Event { content = subscriptionRes } }+ = subscriptionRes event+ >>= sendTextData clientConnection+ . toApolloResponse sessionId+ ---------------------------+ filterByChannels = filter+ ( not+ . null+ . intersect (streamChannels event)+ . channels+ . sessionSubscription+ ) -removeClientSubscription :: ClientID -> Text -> GQLState m e -> IO ()+removeClientSubscription :: ClientID -> Text -> GQLState m e -> IO () removeClientSubscription id' sid' = updateClientByID id' stopSubscription- where- stopSubscription client' =- client'- { clientSessions =- filter ((sid' /=) . sessionId) (clientSessions client')- }+ where+ stopSubscription client' = client'+ { clientSessions = filter ((sid' /=) . sessionId) (clientSessions client')+ } -addClientSubscription ::- ClientID -> SubEvent m e -> Text -> GQLState m e -> IO ()-addClientSubscription id' sessionSubscription sessionId =- updateClientByID id' startSubscription- where- startSubscription client' =- client'- { clientSessions =- ClientSession {sessionId, sessionSubscription} :- clientSessions client'- }+addClientSubscription+ :: ClientID -> SubEvent m e -> Text -> GQLState m e -> IO ()+addClientSubscription id' sessionSubscription sessionId = updateClientByID+ id'+ startSubscription+ where+ startSubscription client' = client'+ { clientSessions = ClientSession { sessionId, sessionSubscription }+ : clientSessions client'+ }
src/Data/Morpheus/Kind.hs view
@@ -16,9 +16,11 @@ , Context(..) , VContext(..) , ResContext(..)- ) where+ )+where -import Data.Morpheus.Types.Internal.Data (OperationType (..))+import Data.Morpheus.Types.Internal.AST+ ( OperationType(..) ) data GQL_KIND = SCALAR@@ -30,7 +32,7 @@ | WRAPPER -data ResContext (kind :: GQL_KIND) (operation:: OperationType) (m :: * -> * ) event value = ResContext+data ResContext (kind :: GQL_KIND) (operation:: OperationType) event (m :: * -> * ) value = ResContext --type ObjectConstraint a = -- | context , like Proxy with multiple parameters
src/Data/Morpheus/Parsing/Document/Parser.hs view
@@ -1,28 +1,37 @@-{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} module Data.Morpheus.Parsing.Document.Parser ( parseTypes- ) where+ )+where -import Data.Text (Text)-import Text.Megaparsec (eof, label, manyTill, runParser)+import Data.Text ( Text )+import Text.Megaparsec ( eof+ , label+ , manyTill+ , runParser+ ) -- MORPHEUS-import Data.Morpheus.Parsing.Document.TypeSystem (parseDataType)-import Data.Morpheus.Parsing.Internal.Internal (processErrorBundle)-import Data.Morpheus.Parsing.Internal.Terms (spaceAndComments)-import Data.Morpheus.Types.Internal.Data (RawDataType)-import Data.Morpheus.Types.Internal.Validation (Validation)+import Data.Morpheus.Parsing.Document.TypeSystem+ ( parseDataType )+import Data.Morpheus.Parsing.Internal.Internal+ ( processErrorBundle )+import Data.Morpheus.Parsing.Internal.Terms+ ( spaceAndComments )+import Data.Morpheus.Types.Internal.AST+ ( RawDataType )+import Data.Morpheus.Types.Internal.Resolving+ ( Validation+ , Failure(..)+ ) parseTypes :: Text -> Validation [(Text, RawDataType)]-parseTypes doc =- case parseDoc of- Right root -> Right root- Left parseError -> Left $ processErrorBundle parseError- where- parseDoc = runParser request "<input>" doc- request =- label "DocumentTypes" $ do- spaceAndComments- manyTill parseDataType eof+parseTypes doc = case parseDoc of+ Right root -> pure root+ Left parseError -> failure (processErrorBundle parseError)+ where+ parseDoc = runParser request "<input>" doc+ request = label "DocumentTypes" $ do+ spaceAndComments+ manyTill parseDataType eof
src/Data/Morpheus/Parsing/Document/TypeSystem.hs view
@@ -3,20 +3,44 @@ module Data.Morpheus.Parsing.Document.TypeSystem ( parseDataType- ) where+ )+where -import Data.Text (Text)-import Text.Megaparsec (label, sepBy1, (<|>))+import Data.Text ( Text )+import Text.Megaparsec ( label+ , sepBy1+ , (<|>)+ ) -- MORPHEUS-import Data.Morpheus.Parsing.Internal.Create (createField)-import Data.Morpheus.Parsing.Internal.Internal (Parser)-import Data.Morpheus.Parsing.Internal.Pattern (fieldsDefinition, inputValueDefinition, optionalDirectives,- typDeclaration)-import Data.Morpheus.Parsing.Internal.Terms (keyword, operator, optDescription, parseName, pipeLiteral,- sepByAnd, setOf)-import Data.Morpheus.Types.Internal.Data (DataField, DataFingerprint (..), DataType (..),- DataTyCon (..), DataValidator (..), Key, RawDataType (..))+import Data.Morpheus.Parsing.Internal.Internal+ ( Parser )+import Data.Morpheus.Parsing.Internal.Pattern+ ( fieldsDefinition+ , inputValueDefinition+ , optionalDirectives+ , typDeclaration+ , enumValueDefinition+ )+import Data.Morpheus.Parsing.Internal.Terms+ ( keyword+ , operator+ , optDescription+ , parseName+ , pipeLiteral+ , sepByAnd+ , setOf+ )+import Data.Morpheus.Types.Internal.AST+ ( DataField+ , DataFingerprint(..)+ , DataTyCon(..)+ , DataType(..)+ , DataValidator(..)+ , Key+ , RawDataType(..)+ , Meta(..)+ ) -- Scalars : https://graphql.github.io/graphql-spec/June2018/#sec-Scalars@@ -25,20 +49,18 @@ -- Description(opt) scalar Name Directives(Const)(opt) -- scalarTypeDefinition :: Maybe Text -> Parser (Text, DataType)-scalarTypeDefinition typeDescription =- label "ScalarTypeDefinition" $ do- typeName <- typDeclaration "scalar"- -- TODO: handle directives- _ <- optionalDirectives- pure (- typeName,- DataScalar DataTyCon {- typeName,- typeDescription,- typeFingerprint = SystemFingerprint typeName,- typeData = DataValidator pure- }- )+scalarTypeDefinition metaDescription = label "ScalarTypeDefinition" $ do+ typeName <- typDeclaration "scalar"+ metaDirectives <- optionalDirectives+ pure+ ( typeName+ , DataScalar DataTyCon+ { typeName+ , typeMeta = Just Meta { metaDescription, metaDirectives }+ , typeFingerprint = SystemFingerprint typeName+ , typeData = DataValidator pure+ }+ ) -- Objects : https://graphql.github.io/graphql-spec/June2018/#sec-Objects --@@ -56,27 +78,27 @@ -- Description(opt) Name ArgumentsDefinition(opt) : Type Directives(Const)(opt) -- objectTypeDefinition :: Maybe Text -> Parser (Text, RawDataType)-objectTypeDefinition typeDescription =- label "ObjectTypeDefinition" $ do- name <- typDeclaration "type"- interfaces <- optionalImplementsInterfaces- -- TODO: handle directives- _directives <- optionalDirectives- fields <- fieldsDefinition- --------------------------- pure (name,- Implements interfaces $ DataTyCon {- typeName = name,- typeDescription,- typeFingerprint = SystemFingerprint name,- typeData = fields- }- )+objectTypeDefinition metaDescription = label "ObjectTypeDefinition" $ do+ name <- typDeclaration "type"+ interfaces <- optionalImplementsInterfaces+ metaDirectives <- optionalDirectives+ fields <- fieldsDefinition+ --------------------------+ pure+ ( name+ , Implements interfaces $ DataTyCon+ { typeName = name+ , typeMeta = Just Meta { metaDescription, metaDirectives }+ , typeFingerprint = SystemFingerprint name+ , typeData = fields+ }+ ) optionalImplementsInterfaces :: Parser [Text] optionalImplementsInterfaces = implements <|> pure []- where- implements = label "ImplementsInterfaces" $ keyword "implements" *> sepByAnd parseName+ where+ implements =+ label "ImplementsInterfaces" $ keyword "implements" *> sepByAnd parseName -- Interfaces: https://graphql.github.io/graphql-spec/June2018/#sec-Interfaces --@@ -84,21 +106,19 @@ -- Description(opt) interface Name Directives(Const)(opt) FieldsDefinition(opt) -- interfaceTypeDefinition :: Maybe Text -> Parser (Text, RawDataType)-interfaceTypeDefinition typeDescription =- label "InterfaceTypeDefinition" $ do- typeName <- typDeclaration "interface"- -- TODO: handle directives- _directives <- optionalDirectives- fields <- fieldsDefinition- pure (- typeName,- Interface DataTyCon {- typeName,- typeDescription,- typeFingerprint = SystemFingerprint typeName,- typeData = fields- }- )+interfaceTypeDefinition metaDescription = label "InterfaceTypeDefinition" $ do+ typeName <- typDeclaration "interface"+ metaDirectives <- optionalDirectives+ fields <- fieldsDefinition+ pure+ ( typeName+ , Interface DataTyCon+ { typeName+ , typeMeta = Just Meta { metaDescription, metaDirectives }+ , typeFingerprint = SystemFingerprint typeName+ , typeData = fields+ }+ ) -- Unions : https://graphql.github.io/graphql-spec/June2018/#sec-Unions@@ -110,25 +130,21 @@ -- = |(opt) NamedType -- UnionMemberTypes | NamedType ---unionTypeDefinition :: Maybe Text -> Parser (Text, DataType)-unionTypeDefinition typeDescription =- label "UnionTypeDefinition" $ do- typeName <- typDeclaration "union"- -- TODO: handle directives- _directives <- optionalDirectives- memberTypes <- unionMemberTypes- pure (- typeName,- DataUnion DataTyCon {- typeName,- typeDescription,- typeFingerprint = SystemFingerprint typeName,- typeData = map unionField memberTypes- }- )- where- unionField fieldType = createField [] "" ([], fieldType)- unionMemberTypes = operator '=' *> parseName `sepBy1` pipeLiteral+unionTypeDefinition :: Maybe Text -> Parser (Text, DataType)+unionTypeDefinition metaDescription = label "UnionTypeDefinition" $ do+ typeName <- typDeclaration "union"+ metaDirectives <- optionalDirectives+ memberTypes <- unionMemberTypes+ pure+ ( typeName+ , DataUnion DataTyCon+ { typeName+ , typeMeta = Just Meta { metaDescription, metaDirectives }+ , typeFingerprint = SystemFingerprint typeName+ , typeData = memberTypes+ }+ )+ where unionMemberTypes = operator '=' *> parseName `sepBy1` pipeLiteral -- Enums : https://graphql.github.io/graphql-spec/June2018/#sec-Enums --@@ -142,30 +158,21 @@ -- Description(opt) EnumValue Directives(Const)(opt) -- enumTypeDefinition :: Maybe Text -> Parser (Text, DataType)-enumTypeDefinition typeDescription =- label "EnumTypeDefinition" $ do- typeName <- typDeclaration "enum"- -- TODO: handle directives- _directives <- optionalDirectives- enumValuesDefinition <- setOf enumValueDefinition- pure (- typeName,- DataEnum DataTyCon {- typeName,- typeDescription,- typeFingerprint = SystemFingerprint typeName,- typeData = enumValuesDefinition- }- )- where- enumValueDefinition = label "EnumValueDefinition" $ do- -- TODO: handle Description- _fieldDescription <- optDescription- enumValueName <- parseName- -- TODO: handle directives- _directive <- optionalDirectives- return enumValueName+enumTypeDefinition metaDescription = label "EnumTypeDefinition" $ do+ typeName <- typDeclaration "enum"+ metaDirectives <- optionalDirectives+ enumValuesDefinitions <- setOf enumValueDefinition+ pure+ ( typeName+ , DataEnum DataTyCon+ { typeName+ , typeMeta = Just Meta { metaDescription, metaDirectives }+ , typeFingerprint = SystemFingerprint typeName+ , typeData = enumValuesDefinitions+ }+ ) + -- Input Objects : https://graphql.github.io/graphql-spec/June2018/#sec-Input-Objects -- -- InputObjectTypeDefinition@@ -175,43 +182,44 @@ -- { InputValueDefinition(list) } -- inputObjectTypeDefinition :: Maybe Text -> Parser (Text, DataType)-inputObjectTypeDefinition typeDescription =+inputObjectTypeDefinition metaDescription = label "InputObjectTypeDefinition" $ do- typeName <- typDeclaration "input"- -- TODO: handle directives- _directives <- optionalDirectives- fields <- inputFieldsDefinition+ typeName <- typDeclaration "input"+ metaDirectives <- optionalDirectives+ fields <- inputFieldsDefinition pure- (- typeName,- DataInputObject DataTyCon {- typeName,- typeDescription,- typeFingerprint = SystemFingerprint typeName,- typeData = fields- }- )- where- inputFieldsDefinition :: Parser [(Key, DataField)]- inputFieldsDefinition = label "InputFieldsDefinition" $ setOf inputValueDefinition+ ( typeName+ , DataInputObject DataTyCon+ { typeName+ , typeMeta = Just Meta { metaDescription, metaDirectives }+ , typeFingerprint = SystemFingerprint typeName+ , typeData = fields+ }+ )+ where+ inputFieldsDefinition :: Parser [(Key, DataField)]+ inputFieldsDefinition =+ label "InputFieldsDefinition" $ setOf inputValueDefinition parseFinalDataType :: Maybe Text -> Parser (Text, DataType)-parseFinalDataType description = label "TypeDefinition" $- inputObjectTypeDefinition description+parseFinalDataType description =+ label "TypeDefinition"+ $ inputObjectTypeDefinition description <|> unionTypeDefinition description <|> enumTypeDefinition description <|> scalarTypeDefinition description parseDataType :: Parser (Text, RawDataType) parseDataType = label "TypeDefinition" $ do- description <- optDescription- types description- where- types description = interfaceTypeDefinition description <|>- objectTypeDefinition description <|>- finalDataT- where- finalDataT = do- (name, datatype) <- parseFinalDataType description- pure (name, FinalDataType datatype)+ description <- optDescription+ types description+ where+ types description =+ interfaceTypeDefinition description+ <|> objectTypeDefinition description+ <|> finalDataT+ where+ finalDataT = do+ (name, datatype) <- parseFinalDataType description+ pure (name, FinalDataType datatype)
− src/Data/Morpheus/Parsing/Internal/Create.hs
@@ -1,67 +0,0 @@-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}--module Data.Morpheus.Parsing.Internal.Create- ( createField- , createArgument- , createType- , createScalarType- , createEnumType- , createUnionType- , createDataTypeLib- ) where--import Data.Text (Text)---- MORPHEUS-import Data.Morpheus.Error.Internal (internalError)-import Data.Morpheus.Types.Internal.Data (DataArguments, DataField (..), DataFingerprint (..),- DataTyCon (..), DataType (..), DataTypeLib (..),- DataValidator (..), TypeAlias (..), WrapperD, defineType,- initTypeLib)-import Data.Morpheus.Types.Internal.Validation (Validation)---createField :: DataArguments -> Text -> ([WrapperD], Text) -> DataField-createField fieldArgs fieldName (aliasWrappers, aliasTyCon) =- DataField- { fieldArgs- , fieldArgsType = Nothing- , fieldName- , fieldType = TypeAlias {aliasTyCon, aliasWrappers, aliasArgs = Nothing}- , fieldHidden = False- }--createArgument :: Text -> ([WrapperD], Text) -> (Text, DataField)-createArgument fieldName x = (fieldName, createField [] fieldName x)--createType :: Text -> a -> DataTyCon a-createType typeName typeData =- DataTyCon {typeName, typeDescription = Nothing, typeFingerprint = SystemFingerprint "", typeData}--createScalarType :: Text -> (Text, DataType)-createScalarType typeName = (typeName, DataScalar $ createType typeName (DataValidator pure))--createEnumType :: Text -> [Text] -> (Text, DataType)-createEnumType typeName typeData = (typeName, DataEnum $ createType typeName typeData)--createUnionType :: Text -> [Text] -> (Text, DataType)-createUnionType typeName typeData = (typeName, DataUnion $ createType typeName $ map unionField typeData)- where- unionField fieldType = createField [] "" ([], fieldType)--createDataTypeLib :: [(Text, DataType)] -> Validation DataTypeLib-createDataTypeLib types =- case takeByKey "Query" types of- (Just query, lib1) ->- case takeByKey "Mutation" lib1 of- (mutation, lib2) ->- case takeByKey "Subscription" lib2 of- (subscription, lib3) -> pure ((foldr defineType (initTypeLib query) lib3) {mutation, subscription})- _ -> internalError "Query Not Defined"- ----------------------------------------------------------------------------- where- takeByKey key lib =- case lookup key lib of- Just (DataObject value) -> (Just (key, value), filter ((/= key) . fst) lib)- _ -> (Nothing, lib)
src/Data/Morpheus/Parsing/Internal/Internal.hs view
@@ -5,31 +5,52 @@ , Position , processErrorBundle , getLocation- ) where+ )+where -import qualified Data.List.NonEmpty as NonEmpty-import Data.Morpheus.Error.Utils (toLocation)-import Data.Morpheus.Types.Internal.Base (Location)-import Data.Morpheus.Types.Internal.Validation (GQLError (..), GQLErrors)-import Data.Text (Text, pack)-import Data.Void (Void)-import Text.Megaparsec (ParseError, ParseErrorBundle (ParseErrorBundle), Parsec,- SourcePos, attachSourcePos, bundleErrors, bundlePosState,- errorOffset, getSourcePos, parseErrorPretty)+import qualified Data.List.NonEmpty as NonEmpty+import Data.Morpheus.Error.Utils ( toLocation )+import Data.Morpheus.Types.Internal.AST+ ( Position )+import Data.Morpheus.Types.Internal.Resolving+ ( GQLError(..)+ , GQLErrors+ )+import Data.Text ( Text+ , pack+ )+import Data.Void ( Void )+import Text.Megaparsec ( ParseError+ , ParseErrorBundle+ ( ParseErrorBundle+ )+ , Parsec+ , SourcePos+ , attachSourcePos+ , bundleErrors+ , bundlePosState+ , errorOffset+ , getSourcePos+ , parseErrorPretty+ ) -type Position = Location -getLocation :: Parser Location+getLocation :: Parser Position getLocation = fmap toLocation getSourcePos type Parser = Parsec Void Text processErrorBundle :: ParseErrorBundle Text Void -> GQLErrors processErrorBundle = fmap parseErrorToGQLError . bundleToErrors- where- parseErrorToGQLError :: (ParseError Text Void, SourcePos) -> GQLError- parseErrorToGQLError (err, position) =- GQLError {desc = pack (parseErrorPretty err), positions = [toLocation position]}- bundleToErrors :: ParseErrorBundle Text Void -> [(ParseError Text Void, SourcePos)]- bundleToErrors ParseErrorBundle {bundleErrors, bundlePosState} =- NonEmpty.toList $ fst $ attachSourcePos errorOffset bundleErrors bundlePosState+ where+ parseErrorToGQLError :: (ParseError Text Void, SourcePos) -> GQLError+ parseErrorToGQLError (err, position) = GQLError+ { message = pack (parseErrorPretty err)+ , locations = [toLocation position]+ }+ bundleToErrors+ :: ParseErrorBundle Text Void -> [(ParseError Text Void, SourcePos)]+ bundleToErrors ParseErrorBundle { bundleErrors, bundlePosState } =+ NonEmpty.toList $ fst $ attachSourcePos errorOffset+ bundleErrors+ bundlePosState
src/Data/Morpheus/Parsing/Internal/Pattern.hs view
@@ -1,38 +1,89 @@+{-# LANGUAGE NamedFieldPuns #-}+ module Data.Morpheus.Parsing.Internal.Pattern- ( inputValueDefinition- , fieldsDefinition- , typDeclaration- , optionalDirectives- ) where+ ( inputValueDefinition+ , fieldsDefinition+ , typDeclaration+ , optionalDirectives+ , enumValueDefinition+ )+where -import Text.Megaparsec (label, many)+import Text.Megaparsec ( label+ , many+ ) -- MORPHEUS-import Data.Morpheus.Parsing.Internal.Create (createField)-import Data.Morpheus.Parsing.Internal.Internal (Parser)-import Data.Morpheus.Parsing.Internal.Terms (keyword, optDescription, litAssignment, operator, parseAssignment,- parseMaybeTuple, parseName, parseType, setOf)-import Data.Morpheus.Parsing.Internal.Value (parseDefaultValue, parseValue)-import Data.Morpheus.Types.Internal.Data (DataField, Key, Name)+import Data.Morpheus.Parsing.Internal.Internal+ ( Parser )+import Data.Morpheus.Parsing.Internal.Terms+ ( keyword+ , litAssignment+ , operator+ , optDescription+ , parseAssignment+ , parseMaybeTuple+ , parseName+ , parseType+ , setOf+ )+import Data.Morpheus.Parsing.Internal.Value+ ( parseDefaultValue+ , parseValue+ )+import Data.Morpheus.Types.Internal.AST+ ( DataField(..)+ , Directive(..)+ , Key+ , Meta(..)+ , DataEnumValue(..)+ , Name+ , TypeAlias(..)+ ) +-- EnumValueDefinition: https://graphql.github.io/graphql-spec/June2018/#EnumValueDefinition+--+-- EnumValueDefinition+-- Description(opt) EnumValue Directives(Const)(opt)+--+enumValueDefinition :: Parser DataEnumValue+enumValueDefinition = label "EnumValueDefinition" $ do+ metaDescription <- optDescription+ enumName <- parseName+ metaDirectives <- optionalDirectives+ return $ DataEnumValue+ { enumName+ , enumMeta = Just Meta { metaDescription, metaDirectives }+ }+ -- InputValue : https://graphql.github.io/graphql-spec/June2018/#InputValueDefinition -- -- InputValueDefinition -- Description(opt) Name : Type DefaultValue(opt) Directives (Const)(opt) ---inputValueDefinition :: Parser (Key, DataField)+inputValueDefinition :: Parser (Key, DataField) inputValueDefinition = label "InputValueDefinition" $ do- -- TODO: handle Description(opt)- _description <- optDescription- name <- parseName+ metaDescription <- optDescription+ fieldName <- parseName litAssignment -- ':'- type_ <- parseType- -- TODO: handle default value- _ <- parseDefaultValue- _ <- optionalDirectives- pure (name, createField [] name type_)+ (aliasWrappers, aliasTyCon) <- parseType+ _ <- parseDefaultValue+ metaDirectives <- optionalDirectives+ pure+ ( fieldName+ , DataField+ { fieldArgs = []+ , fieldArgsType = Nothing+ , fieldName+ , fieldType = TypeAlias { aliasTyCon+ , aliasWrappers+ , aliasArgs = Nothing+ }+ , fieldMeta = Just Meta { metaDescription, metaDirectives }+ }+ ) -- Field Arguments: https://graphql.github.io/graphql-spec/June2018/#sec-Field-Arguments --@@ -40,7 +91,8 @@ -- ( InputValueDefinition(list) ) -- argumentsDefinition :: Parser [(Key, DataField)]-argumentsDefinition = label "ArgumentsDefinition" $ parseMaybeTuple inputValueDefinition+argumentsDefinition =+ label "ArgumentsDefinition" $ parseMaybeTuple inputValueDefinition -- FieldsDefinition : https://graphql.github.io/graphql-spec/June2018/#FieldsDefinition@@ -55,17 +107,27 @@ -- FieldDefinition -- Description(opt) Name ArgumentsDefinition(opt) : Type Directives(Const)(opt) ---fieldDefinition :: Parser (Key, DataField)-fieldDefinition = label "FieldDefinition" $ do- -- TODO: handle Description(opt)- _description <- optDescription- name <- parseName- args <- argumentsDefinition+fieldDefinition :: Parser (Key, DataField)+fieldDefinition = label "FieldDefinition" $ do+ metaDescription <- optDescription+ fieldName <- parseName+ fieldArgs <- argumentsDefinition litAssignment -- ':'- type_ <- parseType- -- TODO: handle directives- _ <- optionalDirectives- pure (name, createField args name type_)+ (aliasWrappers, aliasTyCon) <- parseType+ metaDirectives <- optionalDirectives+ pure+ ( fieldName+ , DataField+ { fieldName+ , fieldArgs+ , fieldArgsType = Nothing+ , fieldType = TypeAlias { aliasTyCon+ , aliasWrappers+ , aliasArgs = Nothing+ }+ , fieldMeta = Just Meta { metaDescription, metaDirectives }+ }+ ) -- Directives : https://graphql.github.io/graphql-spec/June2018/#sec-Language.Directives --@@ -74,19 +136,18 @@ -- Directives[Const] -- Directive[Const](list) ---optionalDirectives :: Parser [()]+optionalDirectives :: Parser [Directive] optionalDirectives = label "Directives" $ many directive -- Directive[Const] -- -- @ Name Arguments[Const](opt)--- TODO: returns real DataType-directive :: Parser ()+directive :: Parser Directive directive = label "Directive" $ do operator '@'- _name <- parseName- _args <- parseMaybeTuple (parseAssignment parseName parseValue)- pure ()+ directiveName <- parseName+ directiveArgs <- parseMaybeTuple (parseAssignment parseName parseValue)+ pure Directive { directiveName, directiveArgs } -- typDeclaration : Not in spec ,start part of type definitions@@ -96,5 +157,5 @@ -- typDeclaration :: Name -> Parser Name typDeclaration kind = do- keyword kind- parseName+ keyword kind+ parseName
src/Data/Morpheus/Parsing/Internal/Terms.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} @@ -27,19 +26,52 @@ , keyword , operator , optDescription- ) where+ )+where -import Data.Functor (($>))-import Data.Text (Text, pack)-import Text.Megaparsec (between, label,try, many, manyTill, optional, sepBy, sepEndBy,- skipMany, skipManyTill, try, (<?>), (<|>))-import Text.Megaparsec.Char (char, digitChar, letterChar, newline, printChar, space,- space1, string)+import Data.Functor ( ($>) )+import Data.Text ( Text+ , pack+ , strip+ )+import Text.Megaparsec ( between+ , label+ , try+ , many+ , manyTill+ , optional+ , sepBy+ , sepEndBy+ , skipMany+ , skipManyTill+ , try+ , (<?>)+ , (<|>)+ )+import Text.Megaparsec.Char ( char+ , digitChar+ , letterChar+ , newline+ , printChar+ , space+ , space1+ , string+ ) -- MORPHEUS-import Data.Morpheus.Parsing.Internal.Internal (Parser, Position, getLocation)-import Data.Morpheus.Types.Internal.Data (DataTypeWrapper (..), Key, Description , Name, WrapperD (..), toHSWrappers)-import Data.Morpheus.Types.Internal.Value (convertToHaskellName)+import Data.Morpheus.Parsing.Internal.Internal+ ( Parser+ , Position+ , getLocation+ )+import Data.Morpheus.Types.Internal.AST+ ( DataTypeWrapper(..)+ , Key+ , Description+ , Name+ , TypeWrapper(..)+ , toHSWrappers+ , convertToHaskellName ) -- Name : https://graphql.github.io/graphql-spec/June2018/#sec-Names@@ -57,7 +89,8 @@ -- LITERALS setLiteral :: Parser [a] -> Parser [a]-setLiteral = between (char '{' *> spaceAndComments) (char '}' *> spaceAndComments)+setLiteral =+ between (char '{' *> spaceAndComments) (char '}' *> spaceAndComments) pipeLiteral :: Parser () pipeLiteral = char '|' *> spaceAndComments@@ -71,19 +104,17 @@ -- PRIMITIVE ------------------------------------ token :: Parser Text-token =- label "token" $ do- firstChar <- letterChar <|> char '_'- restToken <- many $ letterChar <|> char '_' <|> digitChar- spaceAndComments- return $ convertToHaskellName $ pack $ firstChar : restToken+token = label "token" $ do+ firstChar <- letterChar <|> char '_'+ restToken <- many $ letterChar <|> char '_' <|> digitChar+ spaceAndComments+ return $ convertToHaskellName $ pack $ firstChar : restToken qualifier :: Parser (Text, Position)-qualifier =- label "qualifier" $ do- position <- getLocation- value <- token- return (value, position)+qualifier = label "qualifier" $ do+ position <- getLocation+ value <- token+ return (value, position) -- Variable : https://graphql.github.io/graphql-spec/June2018/#Variable@@ -91,12 +122,11 @@ -- Variable : $Name -- variable :: Parser (Text, Position)-variable =- label "variable" $ do- position' <- getLocation- _ <- char '$'- varName' <- token- return (varName', position')+variable = label "variable" $ do+ position' <- getLocation+ _ <- char '$'+ varName' <- token+ return (varName', position') spaceAndComments1 :: Parser () spaceAndComments1 = space1 *> spaceAndComments@@ -111,15 +141,18 @@ optDescription = optional parseDescription parseDescription :: Parser Text-parseDescription = pack <$> (blockDescription <|> singleLine) <* spaceAndComments- where- blockDescription = blockQuotes *> manyTill (printChar <|> newline) blockQuotes <* spaceAndComments- where- blockQuotes = string "\"\"\""- ----------------------------- singleLine = stringQuote *> manyTill printChar stringQuote <* spaceAndComments- where- stringQuote = char '"'+parseDescription =+ strip . pack <$> (blockDescription <|> singleLine) <* spaceAndComments+ where+ blockDescription =+ blockQuotes+ *> manyTill (printChar <|> newline) blockQuotes+ <* spaceAndComments+ where blockQuotes = string "\"\"\""+ ----------------------------+ singleLine =+ stringQuote *> manyTill printChar stringQuote <* spaceAndComments+ where stringQuote = char '"' -- Ignored Tokens : https://graphql.github.io/graphql-spec/June2018/#sec-Source-Text.Ignored-Tokens -- Ignored:@@ -133,9 +166,9 @@ spaceAndComments = ignoredTokens ignoredTokens :: Parser ()-ignoredTokens = label "IgnoredTokens" $ space *> skipMany inlineComment *> space- where- inlineComment = char '#' *> skipManyTill printChar newline *> space+ignoredTokens =+ label "IgnoredTokens" $ space *> skipMany inlineComment *> space+ where inlineComment = char '#' *> skipManyTill printChar newline *> space ------------------------------------------------------------------------ -- COMPLEX@@ -156,20 +189,18 @@ parseMaybeTuple parser = parseTuple parser <|> pure [] parseTuple :: Parser a -> Parser [a]-parseTuple parser =- label "Tuple" $- between- (char '(' *> spaceAndComments)- (char ')' *> spaceAndComments)- (parser `sepBy` (many (char ',') *> spaceAndComments) <?> "empty Tuple value!")+parseTuple parser = label "Tuple" $ between+ (char '(' *> spaceAndComments)+ (char ')' *> spaceAndComments)+ (parser `sepBy` (many (char ',') *> spaceAndComments) <?> "empty Tuple value!"+ ) parseAssignment :: (Show a, Show b) => Parser a -> Parser b -> Parser (a, b)-parseAssignment nameParser valueParser =- label "assignment" $ do- name' <- nameParser- litAssignment- value' <- valueParser- pure (name', value')+parseAssignment nameParser valueParser = label "assignment" $ do+ name' <- nameParser+ litAssignment+ value' <- valueParser+ pure (name', value') -- Type Conditions: https://graphql.github.io/graphql-spec/June2018/#sec-Type-Conditions --@@ -185,36 +216,36 @@ spreadLiteral :: Parser Position spreadLiteral = do index <- getLocation- _ <- string "..."+ _ <- string "..." space return index parseWrappedType :: Parser ([DataTypeWrapper], Text) parseWrappedType = (unwrapped <|> wrapped) <* spaceAndComments- where- unwrapped :: Parser ([DataTypeWrapper], Text)- unwrapped = ([], ) <$> token <* spaceAndComments- ----------------------------------------------- wrapped :: Parser ([DataTypeWrapper], Text)- wrapped =- between- (char '[' *> spaceAndComments)- (char ']' *> spaceAndComments)- (do (wrappers, name) <- unwrapped <|> wrapped- nonNull' <- parseNonNull- return ((ListType : nonNull') ++ wrappers, name))+ where+ unwrapped :: Parser ([DataTypeWrapper], Text)+ unwrapped = ([], ) <$> token <* spaceAndComments+ ----------------------------------------------+ wrapped :: Parser ([DataTypeWrapper], Text)+ 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 Key) parseAlias = try (optional alias) <|> pure Nothing- where- alias = label "alias" $ token <* char ':' <* spaceAndComments+ where alias = label "alias" $ token <* char ':' <* spaceAndComments -parseType :: Parser ([WrapperD],Key)+parseType :: Parser ([TypeWrapper], Key) parseType = do- (wrappers, fieldType) <- parseWrappedType- nonNull <- parseNonNull- pure (toHSWrappers $ nonNull ++ wrappers, fieldType)+ (wrappers, fieldType) <- parseWrappedType+ nonNull <- parseNonNull+ pure (toHSWrappers $ nonNull ++ wrappers, fieldType)
src/Data/Morpheus/Parsing/Internal/Value.hs view
@@ -5,49 +5,68 @@ ( parseValue , enumValue , parseDefaultValue- ) where+ )+where -import Data.Functor (($>))-import Data.Text (pack)-import Text.Megaparsec (anySingleBut, between, choice, label, many, optional, sepBy,- (<|>))-import Text.Megaparsec.Char (char, string)-import Text.Megaparsec.Char.Lexer (scientific)+import Data.Functor ( ($>) )+import Data.Text ( pack )+import Text.Megaparsec ( anySingleBut+ , between+ , choice+ , label+ , many+ , optional+ , sepBy+ , (<|>)+ )+import Text.Megaparsec.Char ( char+ , string+ )+import Text.Megaparsec.Char.Lexer ( scientific ) -- -- MORPHEUS-import Data.Morpheus.Parsing.Internal.Internal (Parser)-import Data.Morpheus.Parsing.Internal.Terms (litEquals, parseAssignment, setOf, spaceAndComments, token)-import Data.Morpheus.Types.Internal.Value (ScalarValue (..), Value (..), decodeScientific)+import Data.Morpheus.Parsing.Internal.Internal+ ( Parser )+import Data.Morpheus.Parsing.Internal.Terms+ ( litEquals+ , parseAssignment+ , setOf+ , spaceAndComments+ , token+ )+import Data.Morpheus.Types.Internal.AST+ ( ScalarValue(..)+ , Value(..)+ , decodeScientific+ ) parseDefaultValue :: Parser (Maybe Value)-parseDefaultValue =- optional $ do- litEquals- parseValue+parseDefaultValue = optional $ do+ litEquals+ parseValue parseValue :: Parser Value-parseValue =- label "value" $ do- value <- - valueNull - <|> booleanValue - <|> valueNumber - <|> enumValue - <|> stringValue - <|> objectValue - <|> listValue- spaceAndComments- return value+parseValue = label "value" $ do+ value <-+ valueNull+ <|> booleanValue+ <|> valueNumber+ <|> enumValue+ <|> stringValue+ <|> objectValue+ <|> listValue+ spaceAndComments+ return value valueNull :: Parser Value valueNull = string "null" $> Null booleanValue :: Parser Value booleanValue = boolTrue <|> boolFalse- where- boolTrue = string "true" $> Scalar (Boolean True)- boolFalse = string "false" $> Scalar (Boolean False)+ where+ boolTrue = string "true" $> Scalar (Boolean True)+ boolFalse = string "false" $> Scalar (Boolean False) valueNumber :: Parser Value valueNumber = Scalar . decodeScientific <$> scientific@@ -59,30 +78,26 @@ 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+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-stringValue = label "stringValue" $ Scalar . String . pack <$> between (char '"') (char '"') (many escaped)+stringValue = label "stringValue" $ Scalar . String . pack <$> between+ (char '"')+ (char '"')+ (many escaped) listValue :: Parser Value-listValue =- label "listValue" $- List <$>- between- (char '[' *> spaceAndComments)- (char ']' *> spaceAndComments)- (parseValue `sepBy` (char ',' *> spaceAndComments))+listValue = label "listValue" $ List <$> between+ (char '[' *> spaceAndComments)+ (char ']' *> spaceAndComments)+ (parseValue `sepBy` (char ',' *> spaceAndComments)) objectValue :: Parser Value objectValue = label "objectValue" $ Object <$> setOf entry- where- entry = parseAssignment token parseValue+ where entry = parseAssignment token parseValue
src/Data/Morpheus/Parsing/JSONSchema/Parse.hs view
@@ -6,73 +6,100 @@ module Data.Morpheus.Parsing.JSONSchema.Parse ( decodeIntrospection- ) where+ )+where import Data.Aeson-import Data.ByteString.Lazy (ByteString)-import Data.Morpheus.Error.Internal (internalError)-import Data.Morpheus.Parsing.Internal.Create (createArgument, createDataTypeLib, createEnumType,- createField, createScalarType, createType, createUnionType)-import Data.Morpheus.Parsing.JSONSchema.Types (EnumValue (..), Field (..), InputValue (..),- Introspection (..), Schema (..), Type (..))-import Data.Morpheus.Schema.TypeKind (TypeKind (..))-import Data.Morpheus.Types.Internal.Data (DataField, DataType (..), DataTypeLib,- DataTypeWrapper (..), Key, WrapperD, toHSWrappers)-import Data.Morpheus.Types.Internal.Validation (Validation)-import Data.Morpheus.Types.IO (JSONResponse (..))-import Data.Semigroup ((<>))-import Data.Text (Text, pack)+import Data.ByteString.Lazy ( ByteString )+import Data.Morpheus.Error.Internal ( internalError )+import Data.Morpheus.Parsing.JSONSchema.Types+ ( EnumValue(..)+ , Field(..)+ , InputValue(..)+ , Introspection(..)+ , Schema(..)+ , Type(..)+ )+import Data.Morpheus.Schema.TypeKind ( TypeKind(..) )+import Data.Morpheus.Types.Internal.AST+ ( DataField+ , DataType(..)+ , DataTypeLib+ , DataTypeWrapper(..)+ , Key+ , TypeWrapper+ , createArgument+ , createDataTypeLib+ , createEnumType+ , createField+ , createScalarType+ , createType+ , createUnionType+ , toHSWrappers+ )+import Data.Morpheus.Types.Internal.Resolving+ ( Validation )+import Data.Morpheus.Types.IO ( JSONResponse(..) )+import Data.Semigroup ( (<>) )+import Data.Text ( Text+ , pack+ ) decodeIntrospection :: ByteString -> Validation DataTypeLib-decodeIntrospection jsonDoc =- case jsonSchema of- Left errors -> internalError $ pack errors- Right JSONResponse {responseData = Just Introspection {__schema = Schema {types}}} ->- traverse parse types >>= createDataTypeLib- Right res -> internalError (pack $ show res)- where- jsonSchema :: Either String (JSONResponse Introspection)- jsonSchema = eitherDecode jsonDoc+decodeIntrospection jsonDoc = case jsonSchema of+ Left errors -> internalError $ pack errors+ Right JSONResponse { responseData = Just Introspection { __schema = Schema { types } } }+ -> traverse parse types >>= createDataTypeLib . concat+ Right res -> internalError (pack $ show res)+ where+ jsonSchema :: Either String (JSONResponse Introspection)+ jsonSchema = eitherDecode jsonDoc class ParseJSONSchema a b where- parse :: a -> Validation (Key, b)+ parse :: a -> Validation b -instance ParseJSONSchema Type DataType 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+instance ParseJSONSchema Type [(Key,DataType)] 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 $ createUnionType typeName uni- parse Type {name = Just typeName, kind = INPUT_OBJECT, inputFields = Just iFields} = do- fields <- traverse parse iFields- pure (typeName, DataInputObject $ createType typeName fields)- parse Type {name = Just typeName, kind = OBJECT, fields = Just oFields} = do- fields <- traverse parse oFields- pure (typeName, DataObject $ createType typeName fields)- parse x = internalError $ "Unsuported type" <> pack (show x)+ Just uni -> pure [createUnionType typeName uni]+ parse Type { name = Just typeName, kind = INPUT_OBJECT, inputFields = Just iFields }+ = do+ fields <- traverse parse iFields+ pure [(typeName, DataInputObject $ createType typeName fields)]+ parse Type { name = Just typeName, kind = OBJECT, fields = Just oFields } =+ do+ fields <- traverse parse oFields+ pure [(typeName, DataObject $ createType typeName fields)]+ parse _ = pure [] -instance ParseJSONSchema Field DataField where- parse Field {fieldName, fieldArgs, fieldType} = do+instance ParseJSONSchema Field (Key,DataField) where+ parse Field { fieldName, fieldArgs, fieldType } = do fType <- fieldTypeFromJSON fieldType- args <- traverse genArg fieldArgs+ args <- traverse genArg fieldArgs pure (fieldName, createField args fieldName fType)- where- genArg InputValue {inputName = argName, inputType = argType} =- createArgument argName <$> fieldTypeFromJSON argType+ where+ genArg InputValue { inputName = argName, inputType = argType } =+ createArgument argName <$> fieldTypeFromJSON argType -instance ParseJSONSchema InputValue DataField where- parse InputValue {inputName, inputType} = do+instance ParseJSONSchema InputValue (Key,DataField) where+ parse InputValue { inputName, inputType } = do fieldType <- fieldTypeFromJSON inputType pure (inputName, createField [] inputName fieldType) -fieldTypeFromJSON :: Type -> Validation ([WrapperD], Text)+fieldTypeFromJSON :: Type -> Validation ([TypeWrapper], Text) fieldTypeFromJSON = fmap toHs . fieldTypeRec []- where- toHs (w, t) = (toHSWrappers w, t)- fieldTypeRec :: [DataTypeWrapper] -> Type -> Validation ([DataTypeWrapper], Text)- 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" <> pack (show x)+ where+ toHs (w, t) = (toHSWrappers w, t)+ fieldTypeRec+ :: [DataTypeWrapper] -> Type -> Validation ([DataTypeWrapper], Text)+ 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" <> pack (show x)
src/Data/Morpheus/Parsing/Request/Arguments.hs view
@@ -2,17 +2,35 @@ module Data.Morpheus.Parsing.Request.Arguments ( maybeArguments- ) where+ )+where -import Text.Megaparsec (label, (<|>))+import Text.Megaparsec ( label+ , (<|>)+ ) -- MORPHEUS-import Data.Morpheus.Parsing.Internal.Internal (Parser, getLocation)-import Data.Morpheus.Parsing.Internal.Terms (parseAssignment, parseMaybeTuple, token, variable)-import Data.Morpheus.Parsing.Internal.Value (enumValue, parseValue)-import Data.Morpheus.Types.Internal.AST.RawSelection (Argument (..), RawArgument (..), RawArguments)-import Data.Morpheus.Types.Internal.AST.Selection (ArgumentOrigin (..))-import Data.Morpheus.Types.Internal.Base (Reference (..))+import Data.Morpheus.Parsing.Internal.Internal+ ( Parser+ , getLocation+ )+import Data.Morpheus.Parsing.Internal.Terms+ ( parseAssignment+ , parseMaybeTuple+ , token+ , variable+ )+import Data.Morpheus.Parsing.Internal.Value+ ( enumValue+ , parseValue+ )+import Data.Morpheus.Types.Internal.AST+ ( ValueOrigin(..)+ , Argument(..)+ , RawArgument(..)+ , RawArguments+ , Ref(..)+ ) -- Arguments : https://graphql.github.io/graphql-spec/June2018/#sec-Language.Arguments@@ -24,19 +42,19 @@ -- Name : Value[Const] -- TODO: move variable to Value valueArgument :: Parser RawArgument-valueArgument =- label "valueArgument" $ do- argumentPosition <- getLocation- argumentValue <- parseValue <|> enumValue- pure $ RawArgument $ Argument {argumentValue, argumentOrigin = INLINE, argumentPosition}+valueArgument = label "valueArgument" $ do+ argumentPosition <- getLocation+ argumentValue <- parseValue <|> enumValue+ pure $ RawArgument $ Argument { argumentValue+ , argumentOrigin = INLINE+ , argumentPosition+ } variableArgument :: Parser RawArgument-variableArgument =- label "variableArgument" $ do- (referenceName, referencePosition) <- variable- pure $ VariableReference $ Reference {referenceName, referencePosition}+variableArgument = label "variableArgument" $ do+ (refName, refPosition) <- variable+ pure $ VariableRef $ Ref { refName, refPosition } maybeArguments :: Parser RawArguments maybeArguments = label "maybeArguments" $ parseMaybeTuple argument- where- argument = parseAssignment token (valueArgument <|> variableArgument)+ where argument = parseAssignment token (valueArgument <|> variableArgument)
src/Data/Morpheus/Parsing/Request/Operation.hs view
@@ -2,24 +2,47 @@ {-# LANGUAGE OverloadedStrings #-} module Data.Morpheus.Parsing.Request.Operation- (parseOperation- ) where+ ( parseOperation+ )+where -import Data.Functor (($>))-import Data.Text (Text)-import Text.Megaparsec (label, optional, (<?>), (<|>))-import Text.Megaparsec.Char (string)+import Data.Functor ( ($>) )+import Data.Text ( Text )+import Text.Megaparsec ( label+ , optional+ , (<?>)+ , (<|>)+ )+import Text.Megaparsec.Char ( string ) -- -- MORPHEUS-import Data.Morpheus.Parsing.Internal.Internal (Parser, getLocation)-import Data.Morpheus.Parsing.Internal.Pattern (optionalDirectives)-import Data.Morpheus.Parsing.Internal.Terms (operator, parseMaybeTuple, parseName, parseType,- spaceAndComments1, variable)-import Data.Morpheus.Parsing.Internal.Value (parseDefaultValue)-import Data.Morpheus.Parsing.Request.Selection (parseSelectionSet)-import Data.Morpheus.Types.Internal.AST.Operation (DefaultValue, Operation (..), RawOperation, Variable (..))-import Data.Morpheus.Types.Internal.Data (OperationType (..), isNullable)+import Data.Morpheus.Parsing.Internal.Internal+ ( Parser+ , getLocation+ )+import Data.Morpheus.Parsing.Internal.Pattern+ ( optionalDirectives )+import Data.Morpheus.Parsing.Internal.Terms+ ( operator+ , parseMaybeTuple+ , parseName+ , parseType+ , spaceAndComments1+ , variable+ )+import Data.Morpheus.Parsing.Internal.Value+ ( parseDefaultValue )+import Data.Morpheus.Parsing.Request.Selection+ ( parseSelectionSet )+import Data.Morpheus.Types.Internal.AST+ ( DefaultValue+ , Operation(..)+ , RawOperation+ , Variable(..)+ , OperationType(..)+ , isNullable+ ) -- Variables : https://graphql.github.io/graphql-spec/June2018/#VariableDefinition@@ -28,21 +51,20 @@ -- Variable : Type DefaultValue(opt) -- variableDefinition :: Parser (Text, Variable DefaultValue)-variableDefinition =- label "VariableDefinition" $ do- (name, variablePosition) <- variable- operator ':'- (variableTypeWrappers, variableType) <- parseType- defaultValue <- parseDefaultValue- pure- ( name- , Variable- { variableType- , isVariableRequired = not (isNullable variableTypeWrappers)- , variableTypeWrappers- , variablePosition- , variableValue = defaultValue- })+variableDefinition = label "VariableDefinition" $ do+ (name, variablePosition) <- variable+ operator ':'+ (variableTypeWrappers, variableType) <- parseType+ defaultValue <- parseDefaultValue+ pure+ ( name+ , Variable { variableType+ , isVariableRequired = not (isNullable variableTypeWrappers)+ , variableTypeWrappers+ , variablePosition+ , variableValue = defaultValue+ }+ ) -- Operations : https://graphql.github.io/graphql-spec/June2018/#sec-Language.Operations --@@ -52,38 +74,45 @@ -- OperationType: one of -- query, mutation, subscription parseOperationDefinition :: Parser RawOperation-parseOperationDefinition =- label "OperationDefinition" $ do- operationPosition <- getLocation- operationType <- parseOperationType- operationName <- optional parseName- operationArgs <- parseMaybeTuple variableDefinition- -- TODO: handle directives- _directives <- optionalDirectives- operationSelection <- parseSelectionSet- pure (Operation {operationName, operationType, operationArgs, operationSelection, operationPosition})+parseOperationDefinition = label "OperationDefinition" $ do+ operationPosition <- getLocation+ operationType <- parseOperationType+ operationName <- optional parseName+ operationArgs <- parseMaybeTuple variableDefinition+ -- TODO: handle directives+ _directives <- optionalDirectives+ operationSelection <- parseSelectionSet+ pure+ (Operation { operationName+ , operationType+ , operationArgs+ , operationSelection+ , operationPosition+ }+ ) parseOperationType :: Parser OperationType-parseOperationType =- label "OperationType" $ do- kind <- (string "query" $> Query) <|> (string "mutation" $> Mutation) <|> (string "subscription" $> Subscription)- spaceAndComments1- return kind+parseOperationType = label "OperationType" $ do+ kind <-+ (string "query" $> Query)+ <|> (string "mutation" $> Mutation)+ <|> (string "subscription" $> Subscription)+ spaceAndComments1+ return kind parseAnonymousQuery :: Parser RawOperation-parseAnonymousQuery =- label "AnonymousQuery" $ do- operationPosition <- getLocation- operationSelection <- parseSelectionSet- pure- (Operation- { operationName = Nothing- , operationType = Query- , operationArgs = []- , operationSelection- , operationPosition- }) <?>- "can't parse AnonymousQuery"+parseAnonymousQuery = label "AnonymousQuery" $ do+ operationPosition <- getLocation+ operationSelection <- parseSelectionSet+ pure+ (Operation { operationName = Nothing+ , operationType = Query+ , operationArgs = []+ , operationSelection+ , operationPosition+ }+ )+ <?> "can't parse AnonymousQuery" parseOperation :: Parser RawOperation parseOperation = parseAnonymousQuery <|> parseOperationDefinition
src/Data/Morpheus/Parsing/Request/Parser.hs view
@@ -3,44 +3,61 @@ module Data.Morpheus.Parsing.Request.Parser ( parseGQL- ) where+ )+where -import qualified Data.Aeson as Aeson (Value (..))-import Data.HashMap.Lazy (toList)-import Data.Text (Text)-import Data.Void (Void)-import Text.Megaparsec (ParseErrorBundle, eof, label, manyTill, runParser)+import qualified Data.Aeson as Aeson+ ( Value(..) )+import Data.HashMap.Lazy ( toList )+import Data.Text ( Text )+import Data.Void ( Void )+import Text.Megaparsec ( ParseErrorBundle+ , eof+ , label+ , manyTill+ , runParser+ ) -- -- MORPHEUS-import Data.Morpheus.Parsing.Internal.Internal (Parser, processErrorBundle)-import Data.Morpheus.Parsing.Internal.Terms (spaceAndComments)-import Data.Morpheus.Parsing.Request.Operation (parseOperation)-import Data.Morpheus.Parsing.Request.Selection (parseFragmentDefinition)-import Data.Morpheus.Types.Internal.Validation (Validation)-import Data.Morpheus.Types.Internal.Value (Value (..), replaceValue)-import Data.Morpheus.Types.IO (GQLRequest (..))-import Data.Morpheus.Types.Types (GQLQueryRoot (..))+import Data.Morpheus.Parsing.Internal.Internal+ ( Parser+ , processErrorBundle+ )+import Data.Morpheus.Parsing.Internal.Terms+ ( spaceAndComments )+import Data.Morpheus.Parsing.Request.Operation+ ( parseOperation )+import Data.Morpheus.Parsing.Request.Selection+ ( parseFragmentDefinition )+import Data.Morpheus.Types.Internal.Resolving+ ( Validation+ , Failure(..)+ )+import Data.Morpheus.Types.Internal.AST+ ( Value(..)+ , replaceValue+ , GQLQuery(..)+ )+import Data.Morpheus.Types.IO ( GQLRequest(..) ) -parseGQLSyntax :: Text -> Either (ParseErrorBundle Text Void) GQLQueryRoot++parseGQLSyntax :: Text -> Either (ParseErrorBundle Text Void) GQLQuery parseGQLSyntax = runParser request "<input>"- where- request :: Parser GQLQueryRoot- request =- label "GQLQueryRoot" $ do- spaceAndComments- operation <- parseOperation- fragments <- manyTill parseFragmentDefinition eof- pure GQLQueryRoot {operation, fragments, inputVariables = []}+ where+ request :: Parser GQLQuery+ request = label "GQLQuery" $ do+ spaceAndComments+ operation <- parseOperation+ fragments <- manyTill parseFragmentDefinition eof+ pure GQLQuery { operation, fragments, inputVariables = [] } -parseGQL :: GQLRequest -> Validation GQLQueryRoot-parseGQL GQLRequest {query, variables} =- case parseGQLSyntax query of- Right root -> Right $ root {inputVariables = toVariableMap variables}- Left parseError -> Left $ processErrorBundle parseError- where- toVariableMap :: Maybe Aeson.Value -> [(Text, Value)]- toVariableMap (Just (Aeson.Object x)) = map toMorpheusValue (toList x)- where- toMorpheusValue (key, value) = (key, replaceValue value)- toVariableMap _ = []+parseGQL :: GQLRequest -> Validation GQLQuery+parseGQL GQLRequest { query, variables } = case parseGQLSyntax query of+ Right root -> pure $ root { inputVariables = toVariableMap variables }+ Left parseError -> failure $ processErrorBundle parseError+ where+ toVariableMap :: Maybe Aeson.Value -> [(Text, Value)]+ toVariableMap (Just (Aeson.Object x)) = map toMorpheusValue (toList x)+ where toMorpheusValue (key, value) = (key, replaceValue value)+ toVariableMap _ = []
src/Data/Morpheus/Parsing/Request/Selection.hs view
@@ -4,21 +4,42 @@ module Data.Morpheus.Parsing.Request.Selection ( parseSelectionSet , parseFragmentDefinition- ) where+ )+where -import Data.Text (Text)-import Text.Megaparsec (label, try, (<|>))+import Data.Text ( Text )+import Text.Megaparsec ( label+ , try+ , (<|>)+ ) -- -- MORPHEUS-import Data.Morpheus.Parsing.Internal.Internal (Parser, getLocation)-import Data.Morpheus.Parsing.Internal.Pattern (optionalDirectives)-import Data.Morpheus.Parsing.Internal.Terms (keyword, parseAlias, parseName, parseTypeCondition,- setOf, spreadLiteral, token)-import Data.Morpheus.Parsing.Request.Arguments (maybeArguments)-import Data.Morpheus.Types.Internal.AST.RawSelection (Fragment (..), RawArguments, RawSelection (..),- RawSelectionSet, Reference (..))-import Data.Morpheus.Types.Internal.AST.Selection (Selection (..))+import Data.Morpheus.Parsing.Internal.Internal+ ( Parser+ , getLocation+ )+import Data.Morpheus.Parsing.Internal.Pattern+ ( optionalDirectives )+import Data.Morpheus.Parsing.Internal.Terms+ ( keyword+ , parseAlias+ , parseName+ , parseTypeCondition+ , setOf+ , spreadLiteral+ , token+ )+import Data.Morpheus.Parsing.Request.Arguments+ ( maybeArguments )+import Data.Morpheus.Types.Internal.AST+ ( Selection(..)+ , Fragment(..)+ , RawArguments+ , RawSelection(..)+ , RawSelectionSet+ , Ref(..)+ ) -- Selection Sets : https://graphql.github.io/graphql-spec/June2018/#sec-Selection-Sets@@ -33,11 +54,12 @@ -- parseSelectionSet :: Parser RawSelectionSet parseSelectionSet = label "SelectionSet" $ setOf parseSelection- where- parseSelection = label "Selection" $- try inlineFragment- <|> try spread- <|> parseSelectionField+ where+ parseSelection =+ label "Selection"+ $ try inlineFragment+ <|> try spread+ <|> parseSelectionField -- Fields: https://graphql.github.io/graphql-spec/June2018/#sec-Language.Fields --@@ -45,27 +67,37 @@ -- Alias(opt) Name Arguments(opt) Directives(opt) SelectionSet(opt) -- parseSelectionField :: Parser (Text, RawSelection)-parseSelectionField =- label "SelectionField" $ do- position <- getLocation- aliasName <- parseAlias- name <- parseName- arguments <- maybeArguments- -- TODO: handle Directives- _directives <- optionalDirectives- value <- selSet aliasName arguments <|> buildField aliasName arguments position- return (name, value)- where+parseSelectionField = label "SelectionField" $ do+ position <- getLocation+ aliasName <- parseAlias+ name <- parseName+ arguments <- maybeArguments+ -- TODO: handle Directives+ _directives <- optionalDirectives+ value <-+ selSet aliasName arguments <|> buildField aliasName arguments position+ return (name, value)+ where ----------------------------------------- buildField selectionAlias selectionArguments selectionPosition =- pure (RawSelectionField $ Selection { selectionAlias , selectionArguments, selectionRec = (), selectionPosition})- ------------------------------------------ selSet :: Maybe Text -> RawArguments -> Parser RawSelection- selSet selectionAlias selectionArguments =- label "body" $ do- selectionPosition <- getLocation- selectionRec <- parseSelectionSet- return (RawSelectionSet $ Selection {selectionAlias , selectionArguments, selectionRec, selectionPosition})+ buildField selectionAlias selectionArguments selectionPosition = pure+ (RawSelectionField $ Selection { selectionAlias+ , selectionArguments+ , selectionRec = ()+ , selectionPosition+ }+ )+ -----------------------------------------+ selSet :: Maybe Text -> RawArguments -> Parser RawSelection+ selSet selectionAlias selectionArguments = label "body" $ do+ selectionPosition <- getLocation+ selectionRec <- parseSelectionSet+ return+ (RawSelectionSet $ Selection { selectionAlias+ , selectionArguments+ , selectionRec+ , selectionPosition+ }+ ) --@@ -78,13 +110,12 @@ -- ...FragmentName Directives(opt) -- spread :: Parser (Text, RawSelection)-spread =- label "FragmentSpread" $ do- referencePosition <- spreadLiteral- referenceName <- token- -- TODO: handle Directives- _directives <- optionalDirectives- return (referenceName, Spread $ Reference {referenceName, referencePosition})+spread = label "FragmentSpread" $ do+ refPosition <- spreadLiteral+ refName <- token+ -- TODO: handle Directives+ _directives <- optionalDirectives+ return (refName, Spread $ Ref { refName, refPosition }) -- FragmentDefinition : https://graphql.github.io/graphql-spec/June2018/#FragmentDefinition --@@ -92,16 +123,15 @@ -- fragment FragmentName TypeCondition Directives(opt) SelectionSet -- parseFragmentDefinition :: Parser (Text, Fragment)-parseFragmentDefinition =- label "FragmentDefinition" $ do- keyword "fragment"- fragmentPosition <- getLocation- name <- parseName- fragmentType <- parseTypeCondition- -- TODO: handle Directives- _directives <- optionalDirectives- fragmentSelection <- parseSelectionSet- pure (name, Fragment {fragmentType, fragmentSelection, fragmentPosition})+parseFragmentDefinition = label "FragmentDefinition" $ do+ keyword "fragment"+ fragmentPosition <- getLocation+ name <- parseName+ fragmentType <- parseTypeCondition+ -- TODO: handle Directives+ _directives <- optionalDirectives+ fragmentSelection <- parseSelectionSet+ pure (name, Fragment { fragmentType, fragmentSelection, fragmentPosition }) -- Inline Fragments : https://graphql.github.io/graphql-spec/June2018/#sec-Inline-Fragments --@@ -109,12 +139,15 @@ -- ... TypeCondition(opt) Directives(opt) SelectionSet -- inlineFragment :: Parser (Text, RawSelection)-inlineFragment =- label "InlineFragment" $ do- fragmentPosition <- spreadLiteral- -- TODO: optional- fragmentType <- parseTypeCondition- -- TODO: handle Directives- _directives <- optionalDirectives- fragmentSelection <- parseSelectionSet- pure ("INLINE_FRAGMENT", InlineFragment $ Fragment {fragmentType, fragmentSelection, fragmentPosition})+inlineFragment = label "InlineFragment" $ do+ fragmentPosition <- spreadLiteral+ -- TODO: optional+ fragmentType <- parseTypeCondition+ -- TODO: handle Directives+ _directives <- optionalDirectives+ fragmentSelection <- parseSelectionSet+ pure+ ( "INLINE_FRAGMENT"+ , InlineFragment+ $ Fragment { fragmentType, fragmentSelection, fragmentPosition }+ )
− src/Data/Morpheus/Rendering/Haskell/Render.hs
@@ -1,87 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}--module Data.Morpheus.Rendering.Haskell.Render- ( renderHaskellDocument- ) where--import Data.ByteString.Lazy.Char8 (ByteString)-import Data.Semigroup ((<>))-import Data.Text (Text, intercalate, pack)-import qualified Data.Text as T (concat)-import qualified Data.Text.Lazy as LT (fromStrict)-import Data.Text.Lazy.Encoding (encodeUtf8)---- MORPHEUS-import Data.Morpheus.Rendering.Haskell.Terms (Context (..), renderExtension)-import Data.Morpheus.Rendering.Haskell.Types (renderType)-import Data.Morpheus.Rendering.Haskell.Values (Scope (..), renderResolver, renderRootResolver)-import Data.Morpheus.Types.Internal.Data (DataTypeLib (..), allDataTypes)--renderHaskellDocument :: String -> DataTypeLib -> ByteString-renderHaskellDocument modName lib =- encodeText $- renderLanguageExtensions context <> renderExports context <>- renderImports context <>- onSub renderApiEvents "" <>- renderRootResolver context lib <>- types- where- encodeText = encodeUtf8 . LT.fromStrict- onSub onS els =- case subscription lib of- Nothing -> els- _ -> onS- renderApiEvents =- "data Channel = Channel -- ChannelA | ChannelB" <> "\n\n" <>- "data Content = Content -- ContentA Int | ContentB String" <>- "\n\n"- types = intercalate "\n\n" $ map renderFullType (allDataTypes lib)- where- renderFullType x = renderType cont x <> "\n\n" <> renderResolver cont x- where- cont = context {scope = getScope $ fst x}- getScope "Mutation" = Mutation- getScope "Subscription" = Subscription- getScope _ = Query- context =- Context- { moduleName = pack modName- , imports =- [ ("GHC.Generics", ["Generic"])- , ( "Data.Morpheus.Kind"- , ["SCALAR", "ENUM", "INPUT_OBJECT", "OBJECT", "UNION"])- , ( "Data.Morpheus.Types"- , [ "GQLRootResolver(..)"- , "toMutResolver"- , "IORes"- , "IOMutRes"- , "IOSubRes"- , "Event(..)"- , "SubRootRes"- , "GQLType(..)"- , "GQLScalar(..)"- , "ScalarValue(..)"- ])- , ("Data.Text", ["Text"])- ]- , extensions = ["OverloadedStrings", "DeriveGeneric", "TypeFamilies"]- , scope = Query- , pubSub = onSub ("Channel", "Content") ("()", "()")- }--renderLanguageExtensions :: Context -> Text-renderLanguageExtensions Context {extensions} =- T.concat (map renderExtension extensions) <> "\n"--renderExports :: Context -> Text-renderExports Context {moduleName} =- "-- generated by 'Morpheus' CLI\n" <> "module " <> moduleName <>- " (rootResolver) where\n\n"--renderImports :: Context -> Text-renderImports Context {imports} = T.concat (map renderImport imports) <> "\n"- where- renderImport (src, list) =- "import " <> src <> " (" <> intercalate ", " list <> ")\n"
− src/Data/Morpheus/Rendering/Haskell/Terms.hs
@@ -1,85 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Data.Morpheus.Rendering.Haskell.Terms- ( indent- , renderReturn- , renderData- , renderCon- , renderMaybe- , renderList- , renderTuple- , renderAssignment- , renderExtension- , renderWrapped- , renderSet- , renderUnionCon- , renderEqual- , Scope(..)- , Context(..)- ) where--import Data.Semigroup ((<>))-import Data.Text (Text, intercalate, toUpper)---- MORPHEUS-import Data.Morpheus.Types.Internal.Data (WrapperD (..))--indent :: Text-indent = " "--renderEqual :: Text -> Text -> Text-renderEqual key value = key <> " = " <> value--renderReturn :: Text-renderReturn = "return "--renderData :: Text -> Text-renderData name = "data " <> name <> " = "--renderCon :: Text -> Text-renderCon name = name <> " "--renderMaybe :: Text -> Text-renderMaybe typeName = "Maybe " <> typeName--renderList :: Text -> Text-renderList typeName = "[" <> typeName <> "]"--renderTuple :: Text -> Text-renderTuple typeName = "(" <> typeName <> ")"--renderSet :: [Text] -> Text-renderSet fields = bracket "{ " <> intercalate ("\n ," <> indent) fields <> bracket "}\n"- where- bracket x = "\n " <> x--renderAssignment :: Text -> Text -> Text-renderAssignment key value = key <> " :: " <> value--renderExtension :: Text -> Text-renderExtension name = "{-# LANGUAGE " <> name <> " #-}\n"--renderWrapped :: [WrapperD] -> Text -> Text-renderWrapped (ListD:xs) = renderList . renderWrapped xs-renderWrapped (MaybeD:xs) = renderMaybe . renderWrapped xs-renderWrapped [] = strToText--strToText :: Text -> Text-strToText "String" = "Text"-strToText x = x--renderUnionCon :: Text -> Text -> Text-renderUnionCon typeName conName = renderCon (typeName <> "_" <> toUpper conName)--data Scope- = Mutation- | Subscription- | Query--data Context = Context- { moduleName :: Text- , imports :: [(Text, [Text])]- , extensions :: [Text]- , scope :: Scope- , pubSub :: (Text, Text)- }
− src/Data/Morpheus/Rendering/Haskell/Types.hs
@@ -1,93 +0,0 @@-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}--module Data.Morpheus.Rendering.Haskell.Types- ( renderType- ) where--import Data.Maybe (catMaybes)-import Data.Semigroup ((<>))-import Data.Text (Text, intercalate, pack, toUpper)-import qualified Data.Text as T (head, tail)---- MORPHEUS-import Data.Morpheus.Rendering.Haskell.Terms (Context (..), Scope (..), indent, renderAssignment, renderCon,- renderData, renderSet, renderTuple, renderUnionCon,- renderWrapped)-import Data.Morpheus.Types.Internal.Data (DataArgument, DataField (..), DataType (..), DataTyCon (..),- TypeAlias (..), isNullable)--renderType :: Context -> (Text, DataType) -> Text-renderType context (name, dataType) = typeIntro <> renderData name <> renderT dataType- where- renderT (DataScalar _) = renderCon name <> "Int Int" <> defineTypeClass "SCALAR" <> renderGQLScalar name- renderT (DataEnum DataTyCon {typeData}) = unionType typeData <> defineTypeClass "ENUM"- renderT (DataUnion DataTyCon {typeData}) = renderUnion name typeData <> defineTypeClass "UNION"- renderT (DataInputObject DataTyCon {typeData}) =- renderCon name <> renderObject renderInputField typeData <> defineTypeClass "INPUT_OBJECT"- renderT (DataInputUnion _) = "\n -- Error: Input Union Not Supported"- renderT (DataObject DataTyCon {typeData}) =- renderCon name <> renderObject (renderField context) typeData <> defineTypeClass "OBJECT"- ----------------------------------------------------------------------------------------------------------- typeIntro = "\n\n---- GQL " <> name <> " ------------------------------- \n"- ----------------------------------------------------------------------------------------------------------- defineTypeClass kind =- "\n\n" <> renderTypeInstanceHead "GQLType" name <> indent <> "type KIND " <> name <> " = " <> kind <> "\n\n"- ------------------------------------------------------------------------------------------------------------renderTypeInstanceHead :: Text -> Text -> Text-renderTypeInstanceHead className name = "instance " <> className <> " " <> name <> " where\n"--renderGQLScalar :: Text -> Text-renderGQLScalar name = renderTypeInstanceHead "GQLScalar " name <> renderParse <> renderSerialize <> "\n\n"- where- renderParse = indent <> "parseValue _ = pure (" <> name <> " 0 0 )" <> "\n"- renderSerialize = indent <> "serialize (" <> name <> " x y ) = Int (x + y)"--renderUnion :: Text -> [DataField] -> Text-renderUnion typeName = unionType . map renderElem- where- renderElem DataField {fieldType = TypeAlias {aliasTyCon}} = renderUnionCon typeName aliasTyCon <> aliasTyCon--unionType :: [Text] -> Text-unionType ls = "\n" <> indent <> intercalate ("\n" <> indent <> "| ") ls <> " deriving (Generic)"--renderObject :: (a -> (Text, Maybe Text)) -> [a] -> Text-renderObject f list = intercalate "\n\n" $ renderMainType : catMaybes types- where- renderMainType = renderSet fields <> " deriving (Generic)"- (fields, types) = unzip (map f list)--renderInputField :: (Text, DataField) -> (Text, Maybe Text)-renderInputField (key, DataField {fieldType = TypeAlias {aliasTyCon, aliasWrappers}}) =- (key `renderAssignment` renderWrapped aliasWrappers aliasTyCon, Nothing)--renderField :: Context -> (Text, DataField) -> (Text, Maybe Text)-renderField Context {scope, pubSub = (channel, content)} (key, DataField { fieldType = TypeAlias { aliasWrappers- , aliasTyCon- }- , fieldArgs- }) =- (key `renderAssignment` argTypeName <> " -> " <> renderMonad scope <> result aliasWrappers, argTypes)- where- renderMonad Subscription = "IOSubRes " <> channel <> " " <> content <> " "- renderMonad Mutation =- case channel of- "()" -> "IORes "- _ -> "IOMutRes " <> channel <> " " <> content <> " "- renderMonad _ = "IORes "- ------------------------------------------------------------------ result wrappers- | isNullable wrappers = renderTuple (renderWrapped wrappers aliasTyCon)- | otherwise = renderWrapped wrappers aliasTyCon- (argTypeName, argTypes) = renderArguments fieldArgs- renderArguments :: [(Text, DataArgument)] -> (Text, Maybe Text)- renderArguments [] = ("()", Nothing)- renderArguments list =- ( fieldArgTypeName- , Just (renderData fieldArgTypeName <> renderCon fieldArgTypeName <> renderObject renderInputField list))- where- fieldArgTypeName = "Arg" <> camelCase key- camelCase :: Text -> Text- camelCase "" = ""- camelCase text = toUpper (pack [T.head text]) <> T.tail text
− src/Data/Morpheus/Rendering/Haskell/Values.hs
@@ -1,84 +0,0 @@-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}--module Data.Morpheus.Rendering.Haskell.Values- ( renderRootResolver- , renderResolver- , Scope(..)- ) where--import Data.Semigroup ((<>))-import Data.Text (Text)---- MORPHEUS-import Data.Morpheus.Rendering.Haskell.Terms (Context (..), Scope (..), renderAssignment, renderCon,- renderEqual, renderReturn, renderSet, renderUnionCon)-import Data.Morpheus.Types.Internal.Data (DataField (..), DataType (..), DataTyCon (..),- DataTypeLib (..), TypeAlias (..), WrapperD (..))--renderRootResolver :: Context -> DataTypeLib -> Text-renderRootResolver _ DataTypeLib {mutation, subscription} = renderSignature <> renderBody <> "\n\n"- where- renderSignature = "rootResolver :: " <> renderRootSig (fst <$> subscription) <> "\n"- where- renderRootSig (Just sub) = "GQLRootResolver IO Channel Content Query " <> maybeOperator mutation <> " " <> sub- renderRootSig Nothing = "GQLRootResolver IO () () Query " <> maybeOperator mutation <> " ()"- ----------------------- maybeOperator (Just (name, _)) = name- maybeOperator Nothing = "()"- renderBody = "rootResolver =\n GQLRootResolver" <> renderResObject fields- where- fields =- [ ("queryResolver", "resolveQuery")- , ("mutationResolver", maybeRes mutation)- , ("subscriptionResolver", maybeRes subscription)- ]- ---------------------------------------------- maybeRes (Just (name, _)) = "resolve" <> name- maybeRes Nothing = "return ()"--renderResolver :: Context -> (Text, DataType) -> Text-renderResolver Context {scope, pubSub = (channel, content)} (name, dataType) = renderSig dataType- where- renderSig DataScalar {} = defFunc <> renderReturn <> "$ " <> renderCon name <> "0 0"- renderSig (DataEnum DataTyCon {typeData}) = defFunc <> renderReturn <> renderCon (head typeData)- renderSig (DataUnion DataTyCon {typeData}) = defFunc <> renderUnionCon name typeCon <> " <$> " <> "resolve" <> typeCon- where- typeCon = aliasTyCon $ fieldType $ head typeData- renderSig (DataObject DataTyCon {typeData}) = defFunc <> renderReturn <> renderCon name <> renderObjFields- where- renderObjFields = renderResObject (map renderFieldRes typeData)- renderFieldRes (key, DataField {fieldType = TypeAlias {aliasWrappers, aliasTyCon}}) =- (key, "const " <> withScope scope (renderValue aliasWrappers aliasTyCon))- where- renderValue (MaybeD:_) = const $ "$ " <> renderReturn <> "Nothing"- renderValue (ListD:_) = const $ "$ " <> renderReturn <> "[]"- renderValue [] = fieldValue- ----------------------------------------------------------------------------- fieldValue "String" = "$ return \"\""- fieldValue "Int" = "$ return 0"- fieldValue fName = "resolve" <> fName- -------------------------------------------- withScope Subscription x = "$ Event { channels = [Channel], content = const " <> x <> " }"- withScope Mutation x =- case (channel, content) of- ("()", "()") -> x- _ -> "$ toMutResolver [Event {channels = [Channel], content = Content}] " <> x- withScope _ x = x- renderSig _ = "" -- INPUT Types Does not Need Resolvers- --------------------------------- defFunc = renderSignature <> renderFunc- ----------------------------------------------------------------------------------------------------------- renderSignature = renderAssignment ("resolve" <> name) (renderMonad name) <> "\n"- ---------------------------------------------------------------------------------- renderMonad "Mutation" = "IOMutRes " <> channel <> " " <> content <> " Mutation"- renderMonad "Subscription" = "SubRootRes IO " <> channel <> " Subscription"- renderMonad tName = "IORes " <> tName- ----------------------------------------------------------------------------------------------------------- renderFunc = "resolve" <> name <> " = "- -----------------------------------------renderResObject :: [(Text, Text)] -> Text-renderResObject = renderSet . map renderEntry- where- renderEntry (key, value) = renderEqual key value
src/Data/Morpheus/Rendering/RenderGQL.hs view
@@ -2,34 +2,51 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeSynonymInstances #-} module Data.Morpheus.Rendering.RenderGQL ( RenderGQL(..) , renderGraphQLDocument- ) where+ )+where -import Data.ByteString.Lazy.Char8 (ByteString)-import Data.Semigroup ((<>))-import Data.Text (Text, intercalate)-import qualified Data.Text.Lazy as LT (fromStrict)-import Data.Text.Lazy.Encoding (encodeUtf8)+import Data.ByteString.Lazy.Char8 ( ByteString )+import Data.Semigroup ( (<>) )+import Data.Text ( Text+ , intercalate+ )+import qualified Data.Text.Lazy as LT+ ( fromStrict )+import Data.Text.Lazy.Encoding ( encodeUtf8 ) -- MORPHEUS-import Data.Morpheus.Types.Internal.Data (DataField (..), DataTyCon (..), DataType (..), DataTypeLib,- DataTypeWrapper (..), Key, TypeAlias (..), WrapperD (..),- allDataTypes, isDefaultTypeName, toGQLWrapper)-import Data.Morpheus.Types.Internal.Value (convertToJSONName)+import Data.Morpheus.Types.Internal.AST+ ( DataField(..)+ , DataTyCon(..)+ , DataType(..)+ , DataTypeLib+ , DataTypeWrapper(..)+ , Key+ , TypeAlias(..)+ , TypeWrapper(..)+ , allDataTypes+ , createInputUnionFields+ , fieldVisibility+ , isDefaultTypeName+ , toGQLWrapper+ , DataEnumValue(..)+ , convertToJSONName ) + renderGraphQLDocument :: DataTypeLib -> ByteString-renderGraphQLDocument lib = encodeUtf8 $ LT.fromStrict $ intercalate "\n\n" $ map render visibleTypes- where- visibleTypes = filter (not . isDefaultTypeName . fst) (allDataTypes lib)+renderGraphQLDocument lib =+ encodeUtf8 $ LT.fromStrict $ intercalate "\n\n" $ map render visibleTypes+ where+ visibleTypes = filter (not . isDefaultTypeName . fst) (allDataTypes lib) class RenderGQL a where render :: a -> Key- renderWrapped :: a -> [WrapperD] -> Key- default renderWrapped :: a -> [WrapperD] -> Key+ renderWrapped :: a -> [TypeWrapper] -> Key+ default renderWrapped :: a -> [TypeWrapper] -> Key renderWrapped x wrappers = showGQLWrapper (toGQLWrapper wrappers) where showGQLWrapper [] = render x@@ -40,44 +57,56 @@ render = id instance RenderGQL TypeAlias where- render TypeAlias {aliasTyCon, aliasWrappers} = renderWrapped aliasTyCon aliasWrappers+ render TypeAlias { aliasTyCon, aliasWrappers } =+ renderWrapped aliasTyCon aliasWrappers instance RenderGQL DataType where- render (DataScalar x) = typeName x- render (DataEnum x) = typeName x- render (DataUnion x) = typeName x+ render (DataScalar x) = typeName x+ render (DataEnum x) = typeName x+ render (DataUnion x) = typeName x render (DataInputObject x) = typeName x- render (DataInputUnion x) = typeName x- render ( DataObject x) = typeName x+ render (DataInputUnion x) = typeName x+ render (DataObject x) = typeName x +instance RenderGQL DataEnumValue where+ render DataEnumValue { enumName } = enumName+ instance RenderGQL (Key, DataType) where- render (name, DataScalar {}) = "scalar " <> name- render (name, DataEnum DataTyCon {typeData}) = "enum " <> name <> renderObject id typeData- render (name, DataUnion DataTyCon {typeData}) =- "union " <> name <> " =\n " <> intercalate ("\n" <> renderIndent <> "| ") (map (render . fieldType) typeData)- render (name, DataInputObject DataTyCon {typeData}) = "input " <> name <> render typeData- render (name, DataInputUnion DataTyCon {typeData}) = "input " <> name <> render (mapKeys typeData)- render (name, DataObject DataTyCon {typeData}) = "type " <> name <> render typeData+ render (name, DataScalar{}) = "scalar " <> name+ render (name, DataEnum DataTyCon { typeData }) =+ "enum " <> name <> renderObject render typeData+ render (name, DataUnion DataTyCon { typeData }) =+ "union "+ <> name+ <> " =\n "+ <> intercalate ("\n" <> renderIndent <> "| ") typeData+ render (name, DataInputObject DataTyCon { typeData }) =+ "input " <> name <> render typeData+ render (name, DataInputUnion DataTyCon { typeData }) =+ "input " <> name <> render fields+ where fields = createInputUnionFields name typeData+ render (name, DataObject DataTyCon { typeData }) =+ "type " <> name <> render typeData -mapKeys :: [DataField] -> [(Text, DataField)]-mapKeys = map (\x -> (fieldName x, x)) + -- OBJECT instance RenderGQL [(Text, DataField)] where render = renderObject renderField . ignoreHidden- where- renderField :: (Text, DataField) -> Text- renderField (key, DataField {fieldType, fieldArgs}) =- convertToJSONName key <> renderArgs fieldArgs <> ": " <> render fieldType- where- renderArgs [] = ""- renderArgs list = "(" <> intercalate ", " (map renderField list) <> ")"- ------------------------------------------------------------ ignoreHidden :: [(Text, DataField)] -> [(Text, DataField)]- ignoreHidden = filter (not . fieldHidden . snd)+ where+ renderField :: (Text, DataField) -> Text+ renderField (key, DataField { fieldType, fieldArgs }) =+ convertToJSONName key <> renderArgs fieldArgs <> ": " <> render fieldType+ where+ renderArgs [] = ""+ renderArgs list = "(" <> intercalate ", " (map renderField list) <> ")"+ -----------------------------------------------------------+ ignoreHidden :: [(Text, DataField)] -> [(Text, DataField)]+ ignoreHidden = filter fieldVisibility renderIndent :: Text renderIndent = " " renderObject :: (a -> Text) -> [a] -> Text-renderObject f list = " { \n " <> intercalate ("\n" <> renderIndent) (map f list) <> "\n}"+renderObject f list =+ " { \n " <> intercalate ("\n" <> renderIndent) (map f list) <> "\n}"
src/Data/Morpheus/Rendering/RenderIntrospection.hs view
@@ -2,24 +2,43 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleContexts #-} module Data.Morpheus.Rendering.RenderIntrospection ( render , createObjectType- ) where+ )+where -import Control.Monad.Fail (MonadFail)-import Data.Semigroup ((<>))-import Data.Text (Text, unpack)+import Data.Semigroup ( (<>) )+import Data.Text ( Text )+import Data.Maybe ( isJust ) -import Data.Morpheus.Schema.Schema -- Morpheus-import Data.Morpheus.Schema.TypeKind (TypeKind (..))-import Data.Morpheus.Types.Internal.Data (DataField (..), DataObject, DataTyCon (..), DataType (..),- DataTypeKind (..), DataTypeLib, DataTypeWrapper (..), DataUnion,- TypeAlias (..), kindOf, lookupDataType, toGQLWrapper)-import Data.Morpheus.Types.Internal.Value (convertToJSONName)+import Data.Morpheus.Schema.Schema+import Data.Morpheus.Schema.TypeKind ( TypeKind(..) )+import Data.Morpheus.Types.Internal.AST+ ( DataField(..)+ , DataTyCon(..)+ , DataType(..)+ , DataTypeKind(..)+ , DataTypeLib+ , DataTypeWrapper(..)+ , DataUnion+ , Meta(..)+ , TypeAlias(..)+ , createInputUnionFields+ , fieldVisibility+ , kindOf+ , lookupDataType+ , toGQLWrapper+ , DataEnumValue(..)+ , lookupDeprecated+ , lookupDeprecatedReason+ , convertToJSONName )+import Data.Morpheus.Types.Internal.Resolving+ ( Failure(..) ) constRes :: Applicative m => a -> b -> m a constRes = const . pure@@ -27,27 +46,54 @@ type Result m a = DataTypeLib -> m a class RenderSchema a b where- render :: (Monad m, MonadFail m) => (Text, a) -> DataTypeLib -> m (b m)+ render :: (Monad m, Failure Text m) => (Text, a) -> DataTypeLib -> m (b m) instance RenderSchema DataType S__Type where- render (key, DataScalar DataTyCon {typeDescription}) =- constRes $ createLeafType SCALAR key typeDescription Nothing- render (key, DataEnum DataTyCon {typeDescription, typeData}) =- constRes $ createLeafType ENUM key typeDescription (Just $ map createEnumValue typeData)- render (name, DataInputObject iObject) = renderInputObject (name, iObject)+ render (key, DataScalar DataTyCon { typeMeta }) =+ constRes $ createLeafType SCALAR key typeMeta Nothing+ render (key, DataEnum DataTyCon { typeMeta, typeData }) =+ constRes+ $ createLeafType ENUM key typeMeta (Just $ map createEnumValue typeData)+ render (name, DataInputObject DataTyCon { typeData, typeMeta }) =+ renderInputObject+ where+ renderInputObject lib = do+ fields <- traverse (`renderinputValue` lib) typeData+ pure $ createInputObject name typeMeta fields render (name, DataObject object') = typeFromObject (name, object')- where- typeFromObject (key, DataTyCon {typeData, typeDescription}) lib =- createObjectType key typeDescription <$>- (Just <$> traverse (`render` lib) (filter (not . fieldHidden . snd) typeData))- render (name, DataUnion union') = const $ pure $ typeFromUnion (name, union')+ where+ typeFromObject (key, DataTyCon { typeData, typeMeta }) lib =+ createObjectType key (typeMeta >>= metaDescription)+ <$> (Just <$> traverse (`render` lib) (filter fieldVisibility typeData))+ render (name, DataUnion union) = constRes $ typeFromUnion (name, union) render (name, DataInputUnion inpUnion') = renderInputUnion (name, inpUnion') +createEnumValue :: Monad m => DataEnumValue -> S__EnumValue m+createEnumValue DataEnumValue { enumName, enumMeta } = S__EnumValue+ { s__EnumValueName = constRes enumName+ , s__EnumValueDescription = constRes (enumMeta >>= metaDescription)+ , s__EnumValueIsDeprecated = constRes (isJust deprecated)+ , s__EnumValueDeprecationReason = constRes+ (deprecated >>= lookupDeprecatedReason)+ }+ where deprecated = enumMeta >>= lookupDeprecated+ instance RenderSchema DataField S__Field where- render (key, field@DataField {fieldType = TypeAlias {aliasTyCon}, fieldArgs}) lib = do- kind <- renderTypeKind <$> lookupKind aliasTyCon lib- createFieldWith key (wrap field $ createType kind aliasTyCon Nothing $ Just []) <$>- traverse (`inputValueFromArg` lib) fieldArgs+ render (name, field@DataField { fieldType = TypeAlias { aliasTyCon }, fieldArgs, fieldMeta }) lib+ = do+ kind <- renderTypeKind <$> lookupKind aliasTyCon lib+ args <- traverse (`renderinputValue` lib) fieldArgs+ pure S__Field+ { s__FieldName = constRes (convertToJSONName name)+ , s__FieldDescription = constRes (fieldMeta >>= metaDescription)+ , s__FieldArgs = constRes args+ , s__FieldType' =+ constRes (wrap field $ createType kind aliasTyCon Nothing $ Just [])+ , s__FieldIsDeprecated = constRes (isJust deprecated)+ , s__FieldDeprecationReason = constRes+ (deprecated >>= lookupDeprecatedReason)+ }+ where deprecated = fieldMeta >>= lookupDeprecated renderTypeKind :: DataTypeKind -> TypeKind renderTypeKind KindScalar = SCALAR@@ -60,147 +106,140 @@ renderTypeKind KindNonNull = NON_NULL wrap :: Monad m => DataField -> S__Type m -> S__Type m-wrap DataField {fieldType = TypeAlias {aliasWrappers}} typ = foldr wrapByTypeWrapper typ (toGQLWrapper aliasWrappers)+wrap DataField { fieldType = TypeAlias { aliasWrappers } } typ =+ foldr wrapByTypeWrapper typ (toGQLWrapper aliasWrappers) wrapByTypeWrapper :: Monad m => DataTypeWrapper -> S__Type m -> S__Type m wrapByTypeWrapper ListType = wrapAs LIST wrapByTypeWrapper NonNullType = wrapAs NON_NULL -lookupKind :: (Monad m, MonadFail m) => Text -> Result m DataTypeKind-lookupKind name lib =- case lookupDataType name lib of- Nothing -> fail $ unpack ("Kind Not Found: " <> name)- Just value -> pure (kindOf value)+lookupKind :: (Monad m, Failure Text m) => Text -> Result m DataTypeKind+lookupKind name lib = case lookupDataType name lib of+ Nothing -> failure $ "Kind Not Found: " <> name+ Just value -> pure (kindOf value) -inputValueFromArg :: (Monad m,MonadFail m) => (Text, DataField) -> Result m (S__InputValue m)-inputValueFromArg (key, input) = fmap (createInputValueWith key) . createInputObjectType input+renderinputValue+ :: (Monad m, Failure Text m)+ => (Text, DataField)+ -> Result m (S__InputValue m)+renderinputValue (key, input) =+ fmap (createInputValueWith key (fieldMeta input))+ . createInputObjectType input -createInputObjectType :: (Monad m, MonadFail m) => DataField -> Result m (S__Type m)-createInputObjectType field@DataField {fieldType = TypeAlias {aliasTyCon}} lib = do- kind <- renderTypeKind <$> lookupKind aliasTyCon lib- pure $ wrap field $ createType kind aliasTyCon Nothing $ Just []+createInputObjectType+ :: (Monad m, Failure Text m) => DataField -> Result m (S__Type m)+createInputObjectType field@DataField { fieldType = TypeAlias { aliasTyCon } } lib+ = do+ kind <- renderTypeKind <$> lookupKind aliasTyCon lib+ pure $ wrap field $ createType kind aliasTyCon Nothing $ Just [] -renderInputObject :: (Monad m, MonadFail m) => (Text, DataObject) -> Result m (S__Type m)-renderInputObject (key, DataTyCon {typeData, typeDescription}) lib = do- fields <- traverse (`inputValueFromArg` lib) typeData- pure $ createInputObject key typeDescription fields -renderInputUnion :: (Monad m, MonadFail m) => (Text, DataUnion) -> Result m (S__Type m)-renderInputUnion (key', DataTyCon {typeData, typeDescription}) lib =- createInputObject key' typeDescription <$> traverse createField typeData- where- createField field = createInputValueWith (fieldName field) <$> createInputObjectType field lib+renderInputUnion+ :: (Monad m, Failure Text m) => (Text, DataUnion) -> Result m (S__Type m)+renderInputUnion (key, DataTyCon { typeData, typeMeta }) lib =+ createInputObject key typeMeta+ <$> traverse createField (createInputUnionFields key typeData)+ where+ createField (name, field) =+ createInputValueWith name Nothing <$> createInputObjectType field lib -createLeafType :: Monad m => TypeKind -> Text -> Maybe Text -> Maybe [S__EnumValue m] -> S__Type m-createLeafType kind name description enums =- S__Type- { s__TypeKind = constRes kind- , s__TypeName = constRes $ Just name- , s__TypeDescription = constRes description- , s__TypeFields = constRes Nothing- , s__TypeOfType = constRes Nothing- , s__TypeInterfaces = constRes Nothing- , s__TypePossibleTypes = constRes Nothing- , s__TypeEnumValues = constRes enums- , s__TypeInputFields = constRes Nothing- }+createLeafType+ :: Monad m+ => TypeKind+ -> Text+ -> Maybe Meta+ -> Maybe [S__EnumValue m]+ -> S__Type m+createLeafType kind name meta enums = S__Type+ { s__TypeKind = constRes kind+ , s__TypeName = constRes $ Just name+ , s__TypeDescription = constRes (meta >>= metaDescription)+ , s__TypeFields = constRes Nothing+ , s__TypeOfType = constRes Nothing+ , s__TypeInterfaces = constRes Nothing+ , s__TypePossibleTypes = constRes Nothing+ , s__TypeEnumValues = constRes enums+ , s__TypeInputFields = constRes Nothing+ } typeFromUnion :: Monad m => (Text, DataUnion) -> S__Type m-typeFromUnion (name, DataTyCon {typeData, typeDescription}) =- S__Type- { s__TypeKind = constRes UNION- , s__TypeName = constRes $ Just name- , s__TypeDescription = constRes typeDescription- , s__TypeFields = constRes Nothing- , s__TypeOfType = constRes Nothing- , s__TypeInterfaces = constRes Nothing- , s__TypePossibleTypes =- constRes $ Just (map (\x -> createObjectType (aliasTyCon $ fieldType x) Nothing $ Just []) typeData)- , s__TypeEnumValues = constRes Nothing- , s__TypeInputFields = constRes Nothing- }+typeFromUnion (name, DataTyCon { typeData, typeMeta }) = S__Type+ { s__TypeKind = constRes UNION+ , s__TypeName = constRes $ Just name+ , s__TypeDescription = constRes (typeMeta >>= metaDescription)+ , s__TypeFields = constRes Nothing+ , s__TypeOfType = constRes Nothing+ , s__TypeInterfaces = constRes Nothing+ , s__TypePossibleTypes =+ constRes $ Just (map (\x -> createObjectType x Nothing $ Just []) typeData)+ , s__TypeEnumValues = constRes Nothing+ , s__TypeInputFields = constRes Nothing+ } -createObjectType :: Monad m => Text -> Maybe Text -> Maybe [S__Field m] -> S__Type m-createObjectType name description fields =- S__Type- { s__TypeKind = constRes OBJECT- , s__TypeName = constRes $ Just name- , s__TypeDescription = constRes description- , s__TypeFields = constRes fields- , s__TypeOfType = constRes Nothing- , s__TypeInterfaces = constRes $ Just []- , s__TypePossibleTypes = constRes Nothing- , s__TypeEnumValues = constRes Nothing- , s__TypeInputFields = constRes Nothing- }+createObjectType+ :: Monad m => Text -> Maybe Text -> Maybe [S__Field m] -> S__Type m+createObjectType name description fields = S__Type+ { s__TypeKind = constRes OBJECT+ , s__TypeName = constRes $ Just name+ , s__TypeDescription = constRes description+ , s__TypeFields = constRes fields+ , s__TypeOfType = constRes Nothing+ , s__TypeInterfaces = constRes $ Just []+ , s__TypePossibleTypes = constRes Nothing+ , s__TypeEnumValues = constRes Nothing+ , s__TypeInputFields = constRes Nothing+ } -createInputObject :: Monad m => Text -> Maybe Text -> [S__InputValue m] -> S__Type m-createInputObject name description fields =- S__Type- { s__TypeKind = constRes INPUT_OBJECT- , s__TypeName = constRes $ Just name- , s__TypeDescription = constRes description- , s__TypeFields = constRes Nothing- , s__TypeOfType = constRes Nothing- , s__TypeInterfaces = constRes Nothing- , s__TypePossibleTypes = constRes Nothing- , s__TypeEnumValues = constRes Nothing- , s__TypeInputFields = constRes $ Just fields- }+createInputObject+ :: Monad m => Text -> Maybe Meta -> [S__InputValue m] -> S__Type m+createInputObject name meta fields = S__Type+ { s__TypeKind = constRes INPUT_OBJECT+ , s__TypeName = constRes $ Just name+ , s__TypeDescription = constRes (meta >>= metaDescription)+ , s__TypeFields = constRes Nothing+ , s__TypeOfType = constRes Nothing+ , s__TypeInterfaces = constRes Nothing+ , s__TypePossibleTypes = constRes Nothing+ , s__TypeEnumValues = constRes Nothing+ , s__TypeInputFields = constRes $ Just fields+ } -createType :: Monad m => TypeKind -> Text -> Maybe Text -> Maybe [S__Field m] -> S__Type m-createType kind name description fields' =- S__Type- { s__TypeKind = constRes kind- , s__TypeName = constRes $ Just name- , s__TypeDescription = constRes description- , s__TypeFields = constRes fields'- , s__TypeOfType = constRes Nothing- , s__TypeInterfaces = constRes Nothing- , s__TypePossibleTypes = constRes Nothing- , s__TypeEnumValues = constRes $ Just []- , s__TypeInputFields = constRes Nothing- }+createType+ :: Monad m+ => TypeKind+ -> Text+ -> Maybe Text+ -> Maybe [S__Field m]+ -> S__Type m+createType kind name description fields = S__Type+ { s__TypeKind = constRes kind+ , s__TypeName = constRes $ Just name+ , s__TypeDescription = constRes description+ , s__TypeFields = constRes fields+ , s__TypeOfType = constRes Nothing+ , s__TypeInterfaces = constRes Nothing+ , s__TypePossibleTypes = constRes Nothing+ , s__TypeEnumValues = constRes $ Just []+ , s__TypeInputFields = constRes Nothing+ } wrapAs :: Monad m => TypeKind -> S__Type m -> S__Type m-wrapAs kind contentType =- S__Type- { s__TypeKind = constRes kind- , s__TypeName = constRes Nothing- , s__TypeDescription = constRes Nothing- , s__TypeFields = constRes Nothing- , s__TypeOfType = constRes $ Just contentType- , s__TypeInterfaces = constRes Nothing- , s__TypePossibleTypes = constRes Nothing- , s__TypeEnumValues = constRes Nothing- , s__TypeInputFields = constRes Nothing- }--createFieldWith :: Monad m => Text -> S__Type m -> [S__InputValue m] -> S__Field m-createFieldWith _name fieldType fieldArgs =- S__Field- { s__FieldName = constRes (convertToJSONName _name)- , s__FieldDescription = constRes Nothing- , s__FieldArgs = constRes fieldArgs- , s__FieldType' = constRes fieldType- , s__FieldIsDeprecated = constRes False- , s__FieldDeprecationReason = constRes Nothing- }--createInputValueWith :: Monad m => Text -> S__Type m -> S__InputValue m-createInputValueWith name ivType =- S__InputValue- { s__InputValueName = constRes (convertToJSONName name)- , s__InputValueDescription = constRes Nothing- , s__InputValueType' = constRes ivType- , s__InputValueDefaultValue = constRes Nothing- }+wrapAs kind contentType = S__Type { s__TypeKind = constRes kind+ , s__TypeName = constRes Nothing+ , s__TypeDescription = constRes Nothing+ , s__TypeFields = constRes Nothing+ , s__TypeOfType = constRes $ Just contentType+ , s__TypeInterfaces = constRes Nothing+ , s__TypePossibleTypes = constRes Nothing+ , s__TypeEnumValues = constRes Nothing+ , s__TypeInputFields = constRes Nothing+ } -createEnumValue :: Monad m => Text -> S__EnumValue m-createEnumValue name =- S__EnumValue- { s__EnumValueName = constRes name- , s__EnumValueDescription = constRes Nothing- , s__EnumValueIsDeprecated = constRes False- , s__EnumValueDeprecationReason = constRes Nothing- }+createInputValueWith+ :: Monad m => Text -> Maybe Meta -> S__Type m -> S__InputValue m+createInputValueWith name meta ivType = S__InputValue+ { s__InputValueName = constRes (convertToJSONName name)+ , s__InputValueDescription = constRes (meta >>= metaDescription)+ , s__InputValueType' = constRes ivType+ , s__InputValueDefaultValue = constRes Nothing+ }
src/Data/Morpheus/Schema/Schema.hs view
@@ -8,8 +8,9 @@ {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-}-{-# LANGUAGE TypeSynonymInstances , FlexibleContexts #-}+{-# LANGUAGE FlexibleContexts #-} + module Data.Morpheus.Schema.Schema ( Root(..) , Root__typeArgs(..)@@ -18,13 +19,15 @@ , S__Field(..) , S__EnumValue(..) , S__InputValue(..)- ) where+ )+where -import Data.Text (Text)+import Data.Text ( Text ) -- MORPHEUS-import Data.Morpheus.Execution.Document.Compile (gqlDocumentNamespace)-import Data.Morpheus.Schema.TypeKind (TypeKind)+import Data.Morpheus.Execution.Document.Compile+ ( gqlDocumentNamespace )+import Data.Morpheus.Schema.TypeKind ( TypeKind ) type S__TypeKind = TypeKind
src/Data/Morpheus/Schema/SchemaAPI.hs view
@@ -7,66 +7,93 @@ ( hiddenRootFields , defaultTypes , schemaAPI- ) where+ )+where import Data.Proxy-import Data.Text (Text)+import Data.Text ( Text ) -- MORPHEUS-import Data.Morpheus.Execution.Internal.GraphScanner (resolveUpdates)-import Data.Morpheus.Execution.Server.Introspect (ObjectFields (..), TypeUpdater, introspect)-import Data.Morpheus.Rendering.RenderIntrospection (createObjectType, render)-import Data.Morpheus.Schema.Schema (Root (..), Root__typeArgs (..), S__Schema (..), S__Type)-import Data.Morpheus.Types (constRes)-import Data.Morpheus.Types.GQLType (CUSTOM)-import Data.Morpheus.Types.ID (ID)-import Data.Morpheus.Types.Internal.Data (DataField (..), DataObject, DataTypeLib (..), QUERY,- allDataTypes)-import Data.Morpheus.Types.Internal.Resolver (Resolver (..))+import Data.Morpheus.Execution.Server.Introspect+ ( ObjectFields(..)+ , TypeUpdater+ , introspect+ )+import Data.Morpheus.Rendering.RenderIntrospection+ ( createObjectType+ , render+ )+import Data.Morpheus.Schema.Schema ( Root(..)+ , Root__typeArgs(..)+ , S__Schema(..)+ , S__Type+ )+import Data.Morpheus.Types ( constRes )+import Data.Morpheus.Types.GQLType ( CUSTOM )+import Data.Morpheus.Types.ID ( ID )+import Data.Morpheus.Types.Internal.AST+ ( DataField(..)+ , DataObject+ , DataTypeLib(..)+ , QUERY+ , allDataTypes+ , lookupDataType+ )+import Data.Morpheus.Types.Internal.Resolving+ ( Resolver(..)+ , resolveUpdates+ ) -convertTypes :: Monad m => DataTypeLib -> Resolver QUERY m e [S__Type (Resolver QUERY m e )]++convertTypes+ :: Monad m => DataTypeLib -> Resolver QUERY e m [S__Type (Resolver QUERY e m)] convertTypes lib = traverse (`render` lib) (allDataTypes lib) -buildSchemaLinkType :: Monad m => (Text, DataObject) -> S__Type (Resolver QUERY m e )+buildSchemaLinkType+ :: Monad m => (Text, DataObject) -> S__Type (Resolver QUERY e m) buildSchemaLinkType (key', _) = createObjectType key' Nothing $ Just [] -findType :: Monad m => Text -> DataTypeLib -> Resolver QUERY m e (Maybe (S__Type (Resolver QUERY m e )))-findType name lib = renderT (lookup name (allDataTypes lib))- where- renderT (Just datatype) = Just <$> render (name,datatype) lib- renderT Nothing = pure Nothing--initSchema :: Monad m => DataTypeLib -> Resolver QUERY m e (S__Schema (Resolver QUERY m e ))-initSchema lib =- pure- S__Schema- { s__SchemaTypes = const $ convertTypes lib- , s__SchemaQueryType = constRes $ buildSchemaLinkType $ query lib- , s__SchemaMutationType = constRes $ buildSchemaLinkType <$> mutation lib- , s__SchemaSubscriptionType = constRes $ buildSchemaLinkType <$> subscription lib- , s__SchemaDirectives = constRes []- }+findType+ :: Monad m+ => Text+ -> DataTypeLib+ -> Resolver QUERY e m (Maybe (S__Type (Resolver QUERY e m)))+findType name lib = renderT (lookupDataType name lib)+ where+ renderT (Just datatype) = Just <$> render (name, datatype) lib+ renderT Nothing = pure Nothing -hideFields :: (Text, DataField) -> (Text, DataField)-hideFields (key', field) = (key', field {fieldHidden = True})+initSchema+ :: Monad m+ => DataTypeLib+ -> Resolver QUERY e m (S__Schema (Resolver QUERY e m))+initSchema lib = pure S__Schema+ { s__SchemaTypes = const $ convertTypes lib+ , s__SchemaQueryType = constRes $ buildSchemaLinkType $ query lib+ , s__SchemaMutationType = constRes $ buildSchemaLinkType <$> mutation lib+ , s__SchemaSubscriptionType = constRes+ $ buildSchemaLinkType+ <$> subscription lib+ , s__SchemaDirectives = constRes []+ } hiddenRootFields :: [(Text, DataField)]-hiddenRootFields = map hideFields $ fst $ objectFields (Proxy :: Proxy (CUSTOM (Root Maybe))) (Proxy @(Root Maybe))+hiddenRootFields = fst+ $ objectFields (Proxy :: Proxy (CUSTOM (Root Maybe))) (Proxy @(Root Maybe)) defaultTypes :: TypeUpdater-defaultTypes =- flip- resolveUpdates- [ introspect (Proxy @Bool)- , introspect (Proxy @Int)- , introspect (Proxy @Float)- , introspect (Proxy @Text)- , introspect (Proxy @ID)- , introspect (Proxy @(S__Schema Maybe))- ]+defaultTypes = flip+ resolveUpdates+ [ introspect (Proxy @Bool)+ , introspect (Proxy @Int)+ , introspect (Proxy @Float)+ , introspect (Proxy @Text)+ , introspect (Proxy @ID)+ , introspect (Proxy @(S__Schema Maybe))+ ] -schemaAPI :: Monad m => DataTypeLib -> Root (Resolver QUERY m e)-schemaAPI lib = Root {root__type, root__schema}- where- root__type (Root__typeArgs name) = findType name lib- root__schema _ = initSchema lib+schemaAPI :: Monad m => DataTypeLib -> Root (Resolver QUERY e m)+schemaAPI lib = Root { root__type, root__schema }+ where+ root__type (Root__typeArgs name) = findType name lib+ root__schema _ = initSchema lib
src/Data/Morpheus/Schema/TypeKind.hs view
@@ -5,11 +5,12 @@ module Data.Morpheus.Schema.TypeKind ( TypeKind(..)- ) where+ )+where -import Data.Aeson (FromJSON (..))-import Data.Morpheus.Kind (ENUM)-import Data.Morpheus.Types.GQLType (GQLType (KIND, __typeName))+import Data.Aeson ( FromJSON(..) )+import Data.Morpheus.Kind ( ENUM )+import Data.Morpheus.Types.GQLType ( GQLType(KIND, __typeName) ) import GHC.Generics instance GQLType TypeKind where
src/Data/Morpheus/Server.hs view
@@ -9,67 +9,93 @@ ( gqlSocketApp , initGQLState , GQLState- ) where+ )+where -import Control.Exception (finally)-import Control.Monad (forever)-import Data.Text (Text)-import Network.WebSockets (ServerApp, acceptRequestWith, forkPingThread,- pendingRequest, receiveData, sendTextData)+import Control.Exception ( finally )+import Control.Monad ( forever )+import Data.Text ( Text )+import Network.WebSockets ( ServerApp+ , acceptRequestWith+ , forkPingThread+ , pendingRequest+ , receiveData+ , sendTextData+ ) -- MORPHEUS-import Data.Morpheus.Execution.Server.Resolve (RootResCon, streamResolver)-import Data.Morpheus.Execution.Subscription.Apollo (SubAction (..), acceptApolloSubProtocol,- apolloFormat, toApolloResponse)-import Data.Morpheus.Execution.Subscription.ClientRegister (GQLState, addClientSubscription, connectClient,- disconnectClient, initGQLState, publishUpdates,- removeClientSubscription)-import Data.Morpheus.Types.Internal.Resolver (GQLRootResolver (..))-import Data.Morpheus.Types.Internal.Stream (GQLChannel (..), ResponseEvent (..),- ResponseStream, closeStream)-import Data.Morpheus.Types.Internal.WebSocket (GQLClient (..))-import Data.Morpheus.Types.IO (GQLResponse (..))+import Data.Morpheus.Execution.Server.Resolve+ ( RootResCon+ , coreResolver+ )+import Data.Morpheus.Execution.Subscription.Apollo+ ( SubAction(..)+ , acceptApolloSubProtocol+ , apolloFormat+ , toApolloResponse+ )+import Data.Morpheus.Execution.Subscription.ClientRegister+ ( GQLState+ , addClientSubscription+ , connectClient+ , disconnectClient+ , initGQLState+ , publishUpdates+ , removeClientSubscription+ )+import Data.Morpheus.Types.Internal.Resolving+ ( GQLRootResolver(..)+ , GQLChannel(..)+ , ResponseEvent(..)+ , ResponseStream+ , runResultT+ , Result(..)+ )+import Data.Morpheus.Types.Internal.WebSocket+ ( GQLClient(..) )+import Data.Morpheus.Types.IO ( GQLResponse(..) )+import Data.Morpheus.Types.Internal.AST+ ( Value ) -handleSubscription :: (Eq (StreamChannel e), GQLChannel e) => GQLClient IO e+handleSubscription+ :: (Eq (StreamChannel e), GQLChannel e)+ => GQLClient IO e -> GQLState IO e -> Text- -> ResponseStream IO e GQLResponse+ -> ResponseStream e IO Value -> IO ()-handleSubscription GQLClient {clientConnection, clientID} state sessionId stream = do- (actions, response) <- closeStream stream- case response of- Data _ -> mapM_ execute actions- Errors _ ->- sendTextData clientConnection (toApolloResponse sessionId response)- where- execute (Publish pub) = publishUpdates state pub- execute (Subscribe sub) = addClientSubscription clientID sub sessionId state+handleSubscription GQLClient { clientConnection, clientID } state sessionId stream+ = do+ response <- runResultT stream+ case response of+ Success { events } -> mapM_ execute events+ Failure errors -> sendTextData+ clientConnection+ (toApolloResponse sessionId $ Errors errors)+ where+ execute (Publish pub) = publishUpdates state pub+ execute (Subscribe sub) = addClientSubscription clientID sub sessionId state -- | Wai WebSocket Server App for GraphQL subscriptions-gqlSocketApp ::- RootResCon IO e que mut sub- => GQLRootResolver IO e que mut sub+gqlSocketApp+ :: RootResCon IO e que mut sub+ => GQLRootResolver IO e que mut sub -> GQLState IO e -> ServerApp gqlSocketApp gqlRoot state pending = do- connection <-- acceptRequestWith pending $ acceptApolloSubProtocol (pendingRequest pending)+ connection <- acceptRequestWith pending+ $ acceptApolloSubProtocol (pendingRequest pending) forkPingThread connection 30 client <- connectClient connection state finally (queryHandler client) (disconnectClient client state)- where- queryHandler client = forever handleRequest- where- handleRequest =- receiveData (clientConnection client) >>=- resolveMessage . apolloFormat- where- resolveMessage (SubError x) = print x- resolveMessage (AddSub sessionId request) =- handleSubscription- client- state- sessionId- (streamResolver gqlRoot request)- resolveMessage (RemoveSub sessionId) =- removeClientSubscription (clientID client) sessionId state+ where+ queryHandler client = forever handleRequest+ where+ handleRequest =+ receiveData (clientConnection client) >>= resolveMessage . apolloFormat+ where+ resolveMessage (SubError x) = print x+ resolveMessage (AddSub sessionId request) =+ handleSubscription client state sessionId (coreResolver gqlRoot request)+ resolveMessage (RemoveSub sessionId) =+ removeClientSubscription (clientID client) sessionId state
src/Data/Morpheus/Types.hs view
@@ -1,11 +1,11 @@-{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE DataKinds #-} -- | GQL Types module Data.Morpheus.Types ( Event(..) -- Type Classes , GQLType(KIND, description) , GQLScalar(parseValue, serialize)- -- Values , GQLRequest(..) , GQLResponse(..) , ID(..)@@ -24,31 +24,55 @@ , QUERY , MUTATION , SUBSCRIPTION- , liftEitherM- , liftM- ) where+ , liftEither+ , lift+ , ResolveQ+ , ResolveM+ , ResolveS+ )+where -import Data.Morpheus.Types.GQLScalar (GQLScalar (parseValue, serialize))-import Data.Morpheus.Types.GQLType (GQLType (KIND, description))-import Data.Morpheus.Types.ID (ID (..))-import Data.Morpheus.Types.Internal.Data (MUTATION, QUERY, SUBSCRIPTION)-import Data.Morpheus.Types.Internal.Resolver (Event (..), GQLRootResolver (..), PureOperation, Resolver (..),- liftEitherM, liftM)-import Data.Morpheus.Types.Internal.Value (ScalarValue (..))-import Data.Morpheus.Types.IO (GQLRequest (..), GQLResponse (..))-import Data.Morpheus.Types.Types (Undefined (..))+import Data.Morpheus.Types.GQLScalar ( GQLScalar+ ( parseValue+ , serialize+ )+ )+import Data.Morpheus.Types.GQLType ( GQLType(KIND, description) )+import Data.Morpheus.Types.ID ( ID(..) )+import Data.Morpheus.Types.Internal.AST+ ( MUTATION+ , QUERY+ , SUBSCRIPTION+ , ScalarValue(..)+ )+import Data.Morpheus.Types.Internal.Resolving+ ( Event(..)+ , GQLRootResolver(..)+ , Resolver(..)+ , LiftEither(..)+ , lift+ )+import Data.Morpheus.Types.IO ( GQLRequest(..)+ , GQLResponse(..)+ )+import Data.Morpheus.Types.Types ( Undefined(..) ) type Res = Resolver QUERY type MutRes = Resolver MUTATION type SubRes = Resolver SUBSCRIPTION -type IORes = Res IO-type IOMutRes = MutRes IO-type IOSubRes = SubRes IO+type IORes e = Res e IO+type IOMutRes e = MutRes e IO+type IOSubRes e = SubRes e IO +-- Recursive Resolvers+type ResolveQ e m a = Res e m (a (Res e m))+type ResolveM e m a = MutRes e m (a (MutRes e m))+type ResolveS e m a = SubRes e m (a (Res e m))+ -- resolves constant value on any argument-constRes :: (PureOperation o ,Monad m) => b -> a -> Resolver o m e b+constRes :: (LiftEither o Resolver, Monad m) => b -> a -> Resolver o e m b constRes = const . pure -constMutRes :: Monad m => [e] -> a -> args -> MutRes m e a-constMutRes list value = const $ MutResolver list $ pure value+constMutRes :: Monad m => [e] -> a -> args -> MutRes e m a+constMutRes events value = const $ MutResolver $ pure (events, value)
− src/Data/Morpheus/Types/Custom.hs
@@ -1,44 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}--module Data.Morpheus.Types.Custom- ( Pair(..)- , MapKind(..)- , MapArgs(..)- , mapKindFromList- ) where--import GHC.Generics (Generic)--data Pair k v =- Pair- { key :: k- , value :: v- }- deriving (Generic)--newtype MapArgs k =- MapArgs- { oneOf :: Maybe [k]- }- deriving (Generic)--data MapKind k v m =- MapKind- { size :: Int- , pairs :: MapArgs k -> m [Pair k v]- }- deriving (Generic)--mapKindFromList :: (Eq k, Applicative m) => [(k, v)] -> MapKind k v m-mapKindFromList inputPairs =- MapKind {size = length inputPairs, pairs = resolvePairs}- where- filterBy MapArgs {oneOf = Just list} =- filter ((`elem` list) . fst) inputPairs- filterBy _ = inputPairs- resolvePairs = pure . (map toGQLTuple . filterBy)- toGQLTuple (x, y) = Pair x y
src/Data/Morpheus/Types/GQLScalar.hs view
@@ -5,12 +5,17 @@ module Data.Morpheus.Types.GQLScalar ( GQLScalar(..) , toScalar- ) where+ )+where -import Data.Morpheus.Types.Internal.Data (DataValidator (..))-import Data.Morpheus.Types.Internal.Value (ScalarValue (..), Value (..))-import Data.Proxy (Proxy (..))-import Data.Text (Text)+import Data.Morpheus.Types.Internal.AST.Data+ ( DataValidator(..) )+import Data.Morpheus.Types.Internal.AST.Value+ ( ScalarValue(..)+ , Value(..)+ )+import Data.Proxy ( Proxy(..) )+import Data.Text ( Text ) toScalar :: Value -> Either Text ScalarValue toScalar (Scalar x) = pure x
src/Data/Morpheus/Types/GQLType.hs view
@@ -2,55 +2,69 @@ {-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-}-{-# LANGUAGE TypeSynonymInstances #-} module Data.Morpheus.Types.GQLType ( GQLType(..) , TRUE , FALSE- ) where+ )+where -import Data.Map (Map)-import Data.Proxy (Proxy (..))-import Data.Set (Set)-import Data.Text (Text, intercalate, pack)-import Data.Typeable (TyCon, TypeRep, Typeable, splitTyConApp, tyConFingerprint,- tyConName, typeRep, typeRepTyCon)+import Data.Map ( Map )+import Data.Proxy ( Proxy(..) )+import Data.Set ( Set )+import Data.Text ( Text+ , intercalate+ , pack+ )+import Data.Typeable ( TyCon+ , TypeRep+ , Typeable+ , splitTyConApp+ , tyConFingerprint+ , tyConName+ , typeRep+ , typeRepTyCon+ ) -- MORPHEUS import Data.Morpheus.Kind-import Data.Morpheus.Types.Custom (MapKind, Pair)-import Data.Morpheus.Types.Internal.Data (DataFingerprint (..), QUERY)-import Data.Morpheus.Types.Internal.Resolver (Resolver (..))-import Data.Morpheus.Types.Types (Undefined (..))+import Data.Morpheus.Types.Types ( MapKind+ , Pair+ , Undefined(..)+ )+import Data.Morpheus.Types.Internal.AST.Data+ ( DataFingerprint(..)+ , QUERY+ )+import Data.Morpheus.Types.Internal.Resolving+ ( Resolver(..) ) type TRUE = 'True type FALSE = 'False resolverCon :: TyCon-resolverCon = typeRepTyCon $ typeRep $ Proxy @(Resolver QUERY Maybe)+resolverCon = typeRepTyCon $ typeRep $ Proxy @(Resolver QUERY () Maybe) -- | replaces typeName (A,B) with Pair_A_B replacePairCon :: TyCon -> TyCon-replacePairCon x- | hsPair == x = gqlPair- where- hsPair = typeRepTyCon $ typeRep $ Proxy @(Int, Int)- gqlPair = typeRepTyCon $ typeRep $ Proxy @(Pair Int Int)+replacePairCon x | hsPair == x = gqlPair+ where+ hsPair = typeRepTyCon $ typeRep $ Proxy @(Int, Int)+ gqlPair = typeRepTyCon $ typeRep $ Proxy @(Pair Int Int) replacePairCon x = x -- Ignores Resolver name from typeName ignoreResolver :: (TyCon, [TypeRep]) -> [TyCon]-ignoreResolver (con, _)- | con == resolverCon = []-ignoreResolver (con, args) = con : concatMap (ignoreResolver . splitTyConApp) args+ignoreResolver (con, _) | con == resolverCon = []+ignoreResolver (con, args) =+ con : concatMap (ignoreResolver . splitTyConApp) args -- | GraphQL type, every graphQL type should have an instance of 'GHC.Generics.Generic' and 'GQLType'. --@@ -82,7 +96,7 @@ __typeFingerprint :: Proxy a -> DataFingerprint default __typeFingerprint :: (Typeable a) => Proxy a -> DataFingerprint- __typeFingerprint _ = TypeableFingerprint $ conFingerprints (Proxy @a)+ __typeFingerprint _ = TypeableFingerprint $ map show $ conFingerprints (Proxy @a) where conFingerprints = fmap (map tyConFingerprint) (ignoreResolver . splitTyConApp . typeRep) @@ -144,10 +158,10 @@ __typeName _ = __typeName (Proxy @a) __typeFingerprint _ = __typeFingerprint (Proxy @a) -instance GQLType a => GQLType (Resolver o m e a) where- type KIND (Resolver o m e a) = WRAPPER- __typeName _ = __typeName (Proxy @a)- __typeFingerprint _ = __typeFingerprint (Proxy @a)+instance GQLType a => GQLType (Resolver o e m a) where+ type KIND (Resolver o e m a) = WRAPPER+ __typeName _ = __typeName (Proxy @a)+ __typeFingerprint _ = __typeFingerprint (Proxy @a) instance GQLType b => GQLType (a -> b) where type KIND (a -> b) = WRAPPER
src/Data/Morpheus/Types/ID.hs view
@@ -4,14 +4,18 @@ module Data.Morpheus.Types.ID ( ID(..)- ) where+ )+where -import Data.Morpheus.Kind (SCALAR)-import Data.Morpheus.Types.GQLScalar (GQLScalar (..))-import Data.Morpheus.Types.GQLType (GQLType (KIND))-import Data.Morpheus.Types.Internal.Value (ScalarValue (..))-import Data.Text (Text, pack)-import GHC.Generics (Generic)+import Data.Morpheus.Kind ( SCALAR )+import Data.Morpheus.Types.GQLScalar ( GQLScalar(..) )+import Data.Morpheus.Types.GQLType ( GQLType(KIND) )+import Data.Morpheus.Types.Internal.AST.Value+ ( ScalarValue(..) )+import Data.Text ( Text+ , pack+ )+import GHC.Generics ( Generic ) -- | default GraphQL type, -- parses only 'String' and 'Int' values,@@ -24,7 +28,7 @@ type KIND ID = SCALAR instance GQLScalar ID where- parseValue (Int x) = return (ID $ pack $ show x)+ parseValue (Int x) = return (ID $ pack $ show x) parseValue (String x) = return (ID x) parseValue _ = Left "" serialize (ID x) = String x
src/Data/Morpheus/Types/IO.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE OverloadedStrings #-}@@ -7,31 +8,44 @@ , GQLResponse(..) , JSONResponse(..) , renderResponse- ) where+ )+where -import Data.Aeson (FromJSON (..), ToJSON (..), pairs, withObject, (.:?), (.=))-import qualified Data.Aeson as Aeson (Value (..))-import qualified Data.HashMap.Lazy as LH (toList)-import GHC.Generics (Generic)+import Data.Aeson ( FromJSON(..)+ , ToJSON(..)+ , pairs+ , withObject+ , (.:?)+ , (.=)+ )+import qualified Data.Aeson as Aeson+ ( Value(..) )+import qualified Data.HashMap.Lazy as LH+ ( toList )+import GHC.Generics ( Generic ) -- MORPHEUS-import Data.Morpheus.Types.Internal.Base (Key)-import Data.Morpheus.Types.Internal.Validation (JSONError (..),Validation)-import Data.Morpheus.Types.Internal.Value (Value)-import Data.Morpheus.Error.Utils ( renderErrors)+import Data.Morpheus.Types.Internal.AST.Base+ ( Key )+import Data.Morpheus.Types.Internal.Resolving.Core+ ( GQLError(..)+ , Result(..)+ )+import Data.Morpheus.Types.Internal.AST.Value+ ( Value ) -renderResponse :: Validation Value -> GQLResponse -renderResponse (Left errors) = Errors $ renderErrors errors-renderResponse (Right value) = Data value +renderResponse :: Result e GQLError con Value -> GQLResponse+renderResponse (Failure errors) = Errors errors+renderResponse Success { result } = Data result+ instance FromJSON a => FromJSON (JSONResponse a) where parseJSON = withObject "JSONResponse" objectParser- where- objectParser o = JSONResponse <$> o .:? "data" <*> o .:? "errors"+ where objectParser o = JSONResponse <$> o .:? "data" <*> o .:? "errors" data JSONResponse a = JSONResponse { responseData :: Maybe a- , responseErrors :: Maybe [JSONError]+ , responseErrors :: Maybe [GQLError] } deriving (Generic, Show, ToJSON) -- | GraphQL HTTP Request Body@@ -44,17 +58,16 @@ -- | GraphQL Response data GQLResponse = Data Value- | Errors [JSONError]+ | Errors [GQLError] deriving (Show, Generic) instance FromJSON GQLResponse where- parseJSON (Aeson.Object hm) =- case LH.toList hm of- [("data", value)] -> Data <$> parseJSON value- [("errors", value)] -> Errors <$> parseJSON value- _ -> fail "Invalid GraphQL Response"+ parseJSON (Aeson.Object hm) = case LH.toList hm of+ [("data" , value)] -> Data <$> parseJSON value+ [("errors", value)] -> Errors <$> parseJSON value+ _ -> fail "Invalid GraphQL Response" parseJSON _ = fail "Invalid GraphQL Response" instance ToJSON GQLResponse where- toEncoding (Data _data) = pairs $ "data" .= _data+ toEncoding (Data _data ) = pairs $ "data" .= _data toEncoding (Errors _errors) = pairs $ "errors" .= _errors
+ src/Data/Morpheus/Types/Internal/AST.hs view
@@ -0,0 +1,162 @@+{-# LANGUAGE DeriveLift #-}++module Data.Morpheus.Types.Internal.AST+ (+ -- BASE+ Key+ , Collection+ , Ref(..)+ , Position(..)+ , Message+ , anonymousRef+ , Name+ , Description++ -- VALUE+ , Value(..)+ , ScalarValue(..)+ , Object+ , GQLValue(..)+ , replaceValue+ , decodeScientific+ , convertToJSONName+ , convertToHaskellName++ -- Selection+ , Argument(..)+ , Arguments+ , SelectionSet+ , SelectionRec(..)+ , ValueOrigin(..)+ , ValidSelection+ , Selection(..)+ , RawSelection'+ , FragmentLib+ , RawArguments+ , RawSelectionSet+ , Fragment(..)+ , RawArgument(..)+ , RawSelection(..)++ -- OPERATION+ , Operation(..)+ , Variable(..)+ , ValidOperation+ , RawOperation+ , VariableDefinitions+ , ValidVariables+ , DefaultValue+ , getOperationName+ , getOperationDataType+++ -- DSL+ , DataScalar+ , DataEnum+ , DataObject+ , DataArgument+ , DataUnion+ , DataArguments+ , DataField(..)+ , DataTyCon(..)+ , DataType(..)+ , DataTypeLib(..)+ , DataTypeWrapper(..)+ , DataValidator(..)+ , DataTypeKind(..)+ , DataFingerprint(..)+ , RawDataType(..)+ , ResolverKind(..)+ , TypeWrapper(..)+ , TypeAlias(..)+ , ArgsType(..)+ , DataEnumValue(..)+ , isTypeDefined+ , initTypeLib+ , defineType+ , isFieldNullable+ , allDataTypes+ , lookupDataType+ , kindOf+ , toNullableField+ , toListField+ , isObject+ , isInput+ , toHSWrappers+ , isNullable+ , toGQLWrapper+ , isWeaker+ , isSubscription+ , isOutputObject+ , sysTypes+ , isDefaultTypeName+ , isSchemaTypeName+ , isPrimitiveTypeName+ , OperationType(..)+ , QUERY+ , MUTATION+ , SUBSCRIPTION+ , isEntNode+ , lookupInputType+ , coerceDataObject+ , getDataType+ , lookupDataObject+ , lookupDataUnion+ , lookupType+ , lookupField+ , lookupUnionTypes+ , lookupSelectionField+ , lookupFieldAsSelectionSet+ , createField+ , createArgument+ , createDataTypeLib+ , createEnumType+ , createScalarType+ , createType+ , createUnionType+ , createAlias+ , createInputUnionFields+ , fieldVisibility+ , Meta(..)+ , Directive(..)+ , createEnumValue+ , fromDataType+ , insertType+ , TypeUpdater+ , lookupDeprecated+ , lookupDeprecatedReason+ , TypeD(..)+ , ConsD(..)+ , ClientQuery(..)+ , GQLTypeD(..)+ , ClientType(..)+ -- LOCAL+ , GQLQuery(..)+ , Variables+ )+where++import Data.Map ( Map )+import Language.Haskell.TH.Syntax ( Lift )+import Instances.TH.Lift ( )++-- Morpheus+import Data.Morpheus.Types.Internal.AST.Data+++import Data.Morpheus.Types.Internal.AST.Operation++import Data.Morpheus.Types.Internal.AST.Selection++import Data.Morpheus.Types.Internal.AST.Base++import Data.Morpheus.Types.Internal.AST.Value+++type Variables = Map Key Value++data GQLQuery = GQLQuery+ { fragments :: FragmentLib+ , operation :: RawOperation+ , inputVariables :: [(Key, Value)]+ } deriving (Show,Lift)
+ src/Data/Morpheus/Types/Internal/AST/Base.hs view
@@ -0,0 +1,55 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveLift #-}+{-# LANGUAGE NamedFieldPuns #-}++module Data.Morpheus.Types.Internal.AST.Base+ ( Key+ , Collection+ , Ref(..)+ , Position(..)+ , Message+ , anonymousRef+ , Name+ , Description+ )+where++import Data.Aeson ( FromJSON+ , ToJSON+ )+import Data.Text ( Text )+import GHC.Generics ( Generic )+import Language.Haskell.TH.Syntax ( Lift )+import Instances.TH.Lift ( )+++type Key = Text+type Message = Text+type Name = Key+type Description = Key++type Collection a = [(Key, a)]++data Position = Position+ { line :: Int+ , column :: Int+ } deriving (Show, Generic, FromJSON, ToJSON, Lift)++-- Refference with Position information +--+-- includes position for debugging, where Ref "a" 1 === Ref "a" 3+--+data Ref = Ref+ { refName :: Key+ , refPosition :: Position+ } deriving (Show,Lift)++instance Eq Ref where+ (Ref id1 _) == (Ref id2 _) = id1 == id2++instance Ord Ref where+ compare (Ref x _) (Ref y _) = compare x y++anonymousRef :: Key -> Ref+anonymousRef refName = Ref { refName, refPosition = Position 0 0 }
+ src/Data/Morpheus/Types/Internal/AST/Data.hs view
@@ -0,0 +1,659 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveLift #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}++module Data.Morpheus.Types.Internal.AST.Data+ ( + DataScalar+ , DataEnum+ , DataObject+ , DataArgument+ , DataUnion+ , DataArguments+ , DataField(..)+ , DataTyCon(..)+ , DataType(..)+ , DataTypeLib(..)+ , DataTypeWrapper(..)+ , DataValidator(..)+ , DataTypeKind(..)+ , DataFingerprint(..)+ , RawDataType(..)+ , ResolverKind(..)+ , TypeWrapper(..)+ , TypeAlias(..)+ , ArgsType(..)+ , DataEnumValue(..)+ , isTypeDefined+ , initTypeLib+ , defineType+ , isFieldNullable+ , allDataTypes+ , lookupDataType+ , kindOf+ , toNullableField+ , toListField+ , isObject+ , isInput+ , toHSWrappers+ , isNullable+ , toGQLWrapper+ , isWeaker+ , isSubscription+ , isOutputObject+ , sysTypes+ , isDefaultTypeName+ , isSchemaTypeName+ , isPrimitiveTypeName+ , OperationType(..)+ , QUERY+ , MUTATION+ , SUBSCRIPTION+ , isEntNode+ , lookupInputType+ , coerceDataObject+ , getDataType+ , lookupDataObject+ , lookupDataUnion+ , lookupType+ , lookupField+ , lookupUnionTypes+ , lookupSelectionField+ , lookupFieldAsSelectionSet+ , createField+ , createArgument+ , createDataTypeLib+ , createEnumType+ , createScalarType+ , createType+ , createUnionType+ , createAlias+ , createInputUnionFields+ , fieldVisibility+ , Meta(..)+ , Directive(..)+ , createEnumValue+ , fromDataType+ , insertType+ , TypeUpdater+ , lookupDeprecated+ , lookupDeprecatedReason+ , TypeD(..)+ , ConsD(..)+ , ClientQuery(..)+ , GQLTypeD(..)+ , ClientType(..)+ )+where++import Data.HashMap.Lazy ( HashMap+ , empty+ , fromList+ , insert+ , toList+ , union+ )+import qualified Data.HashMap.Lazy as HM+ ( lookup )+import Data.Semigroup ( (<>) )+import Language.Haskell.TH.Syntax ( Lift )+import Instances.TH.Lift ( )+import Data.List ( find )++-- MORPHEUS+import Data.Morpheus.Error.Internal ( internalError )+import Data.Morpheus.Error.Selection ( cannotQueryField+ , hasNoSubfields+ )+import Data.Morpheus.Types.Internal.AST.Base+ ( Key+ , Position+ , Name+ , Description+ )+import Data.Morpheus.Types.Internal.Resolving.Core+ ( Validation+ , Failure(..)+ , GQLErrors+ , LibUpdater+ , resolveUpdates+ )+import Data.Morpheus.Types.Internal.AST.Value+ ( Value(..)+ , ScalarValue(..)+ )+import Data.Morpheus.Error.Schema ( nameCollisionError )++type QUERY = 'Query+type MUTATION = 'Mutation+type SUBSCRIPTION = 'Subscription++isDefaultTypeName :: Key -> Bool+isDefaultTypeName x = isSchemaTypeName x || isPrimitiveTypeName x++isSchemaTypeName :: Key -> Bool+isSchemaTypeName = (`elem` sysTypes)++isPrimitiveTypeName :: Key -> Bool+isPrimitiveTypeName = (`elem` ["String", "Float", "Int", "Boolean", "ID"])++sysTypes :: [Key]+sysTypes =+ [ "__Schema"+ , "__Type"+ , "__Directive"+ , "__TypeKind"+ , "__Field"+ , "__DirectiveLocation"+ , "__InputValue"+ , "__EnumValue"+ ]++data OperationType+ = Query+ | Subscription+ | Mutation+ deriving (Show, Eq, Lift)++isSubscription :: DataTypeKind -> Bool+isSubscription (KindObject (Just Subscription)) = True+isSubscription _ = False++isOutputObject :: DataTypeKind -> Bool+isOutputObject (KindObject _) = True+isOutputObject _ = False++isObject :: DataTypeKind -> Bool+isObject (KindObject _) = True+isObject KindInputObject = True+isObject _ = False++isInput :: DataTypeKind -> Bool+isInput KindInputObject = True+isInput _ = False++data DataTypeKind+ = KindScalar+ | KindObject (Maybe OperationType)+ | KindUnion+ | KindEnum+ | KindInputObject+ | KindList+ | KindNonNull+ | KindInputUnion+ deriving (Eq, Show, Lift)++data ResolverKind+ = PlainResolver+ | TypeVarResolver+ | ExternalResolver+ deriving (Show, Eq, Lift)++data TypeWrapper+ = TypeList+ | TypeMaybe+ deriving (Show, Lift)++isFieldNullable :: DataField -> Bool+isFieldNullable = isNullable . aliasWrappers . fieldType++isNullable :: [TypeWrapper] -> Bool+isNullable (TypeMaybe : _) = True+isNullable _ = False++isWeaker :: [TypeWrapper] -> [TypeWrapper] -> Bool+isWeaker (TypeMaybe : xs1) (TypeMaybe : xs2) = isWeaker xs1 xs2+isWeaker (TypeMaybe : _ ) _ = True+isWeaker (_ : xs1) (_ : xs2) = isWeaker xs1 xs2+isWeaker _ _ = False++toGQLWrapper :: [TypeWrapper] -> [DataTypeWrapper]+toGQLWrapper (TypeMaybe : (TypeMaybe : tw)) = toGQLWrapper (TypeMaybe : tw)+toGQLWrapper (TypeMaybe : (TypeList : tw)) = ListType : toGQLWrapper tw+toGQLWrapper (TypeList : tw) = [NonNullType, ListType] <> toGQLWrapper tw+toGQLWrapper [TypeMaybe ] = []+toGQLWrapper [] = [NonNullType]++toHSWrappers :: [DataTypeWrapper] -> [TypeWrapper]+toHSWrappers (NonNullType : (NonNullType : xs)) =+ toHSWrappers (NonNullType : xs)+toHSWrappers (NonNullType : (ListType : xs)) = TypeList : toHSWrappers xs+toHSWrappers (ListType : xs) = [TypeMaybe, TypeList] <> toHSWrappers xs+toHSWrappers [] = [TypeMaybe]+toHSWrappers [NonNullType] = []++data DataFingerprint = SystemFingerprint Key | TypeableFingerprint [String]+ deriving (Show, Eq, Ord, Lift)+++newtype DataValidator = DataValidator+ { validateValue :: Value -> Either Key Value+ }++instance Show DataValidator where+ show _ = "DataValidator"++type DataScalar = DataTyCon DataValidator++type DataEnum = DataTyCon [DataEnumValue]++type DataObject = DataTyCon [(Key, DataField)]++type DataArgument = DataField++type DataUnion = DataTyCon [Key]++type DataArguments = [(Key, DataArgument)]++data DataTypeWrapper+ = ListType+ | NonNullType+ deriving (Show, Lift)++data TypeAlias = TypeAlias+ { aliasTyCon :: Key+ , aliasArgs :: Maybe Key+ , aliasWrappers :: [TypeWrapper]+ } deriving (Show,Lift)++data ArgsType = ArgsType+ { argsTypeName :: Key+ , resKind :: ResolverKind+ } deriving (Show,Lift)++data Directive = Directive {+ directiveName :: Name,+ directiveArgs :: [(Name,Value)]+} deriving (Show,Lift)++-- META+data Meta = Meta {+ metaDescription:: Maybe Description,+ metaDirectives :: [Directive]+} deriving (Show,Lift)++lookupDeprecated :: Meta -> Maybe Directive+lookupDeprecated Meta { metaDirectives } = find isDeprecation metaDirectives+ where+ isDeprecation Directive { directiveName = "deprecated" } = True+ isDeprecation _ = False++lookupDeprecatedReason :: Directive -> Maybe Key+lookupDeprecatedReason Directive { directiveArgs } =+ maybeString . snd <$> find isReason directiveArgs+ where+ maybeString (Scalar (String x)) = x+ maybeString _ = "can't read deprecated Reason Value"+ isReason ("reason", _) = True+ isReason _ = False++-- ENUM VALUE+data DataEnumValue = DataEnumValue{+ enumName :: Name,+ enumMeta :: Maybe Meta+} deriving (Show, Lift)++--------------------------------------------------------------------------------------------------+data DataField = DataField+ { fieldName :: Key+ , fieldArgs :: [(Key, DataArgument)]+ , fieldArgsType :: Maybe ArgsType+ , fieldType :: TypeAlias+ , fieldMeta :: Maybe Meta+ } deriving (Show,Lift)++fieldVisibility :: (Key, DataField) -> Bool+fieldVisibility ("__typename", _) = False+fieldVisibility ("__schema" , _) = False+fieldVisibility ("__type" , _) = False+fieldVisibility _ = True++createField :: DataArguments -> Key -> ([TypeWrapper], Key) -> DataField+createField fieldArgs fieldName (aliasWrappers, aliasTyCon) = DataField+ { fieldArgs+ , fieldArgsType = Nothing+ , fieldName+ , fieldType = TypeAlias { aliasTyCon, aliasWrappers, aliasArgs = Nothing }+ , fieldMeta = Nothing+ }++createArgument :: Key -> ([TypeWrapper], Key) -> (Key, DataField)+createArgument fieldName x = (fieldName, createField [] fieldName x)+++toNullableField :: DataField -> DataField+toNullableField dataField+ | isNullable (aliasWrappers $ fieldType dataField) = dataField+ | otherwise = dataField { fieldType = nullable (fieldType dataField) }+ where+ nullable alias@TypeAlias { aliasWrappers } =+ alias { aliasWrappers = TypeMaybe : aliasWrappers }++toListField :: DataField -> DataField+toListField dataField = dataField { fieldType = listW (fieldType dataField) }+ where+ listW alias@TypeAlias { aliasWrappers } =+ alias { aliasWrappers = TypeList : aliasWrappers }++lookupField :: Failure error m => Key -> [(Key, field)] -> error -> m field+lookupField key fields gqlError = case lookup key fields of+ Nothing -> failure gqlError+ Just field -> pure field++lookupSelectionField+ :: Failure GQLErrors Validation+ => Position+ -> Key+ -> DataObject+ -> Validation DataField+lookupSelectionField position fieldName DataTyCon { typeData, typeName } =+ lookupField fieldName typeData gqlError+ where gqlError = cannotQueryField fieldName typeName position++--+-- TYPE CONSTRUCTOR+--------------------------------------------------------------------------------------------------+data DataTyCon a = DataTyCon+ { typeName :: Key+ , typeFingerprint :: DataFingerprint+ , typeMeta :: Maybe Meta+ , typeData :: a+ } deriving (Show, Lift)+++data RawDataType+ = FinalDataType DataType+ | Interface DataObject+ | Implements { implementsInterfaces :: [Key]+ , unImplements :: DataObject }+ deriving (Show)++--+-- DATA TYPE+--------------------------------------------------------------------------------------------------+data DataType+ = DataScalar DataScalar+ | DataEnum DataEnum+ | DataInputObject DataObject+ | DataObject DataObject+ | DataUnion DataUnion+ | DataInputUnion DataUnion+ deriving (Show)++createType :: Key -> a -> DataTyCon a+createType typeName typeData = DataTyCon+ { typeName+ , typeMeta = Nothing+ , typeFingerprint = SystemFingerprint ""+ , typeData+ }++createScalarType :: Key -> (Key, DataType)+createScalarType typeName =+ (typeName, DataScalar $ createType typeName (DataValidator pure))++createEnumType :: Key -> [Key] -> (Key, DataType)+createEnumType typeName typeData =+ (typeName, DataEnum $ createType typeName enumValues)+ where enumValues = map createEnumValue typeData++createEnumValue :: Key -> DataEnumValue+createEnumValue enumName = DataEnumValue { enumName, enumMeta = Nothing }++createUnionType :: Key -> [Key] -> (Key, DataType)+createUnionType typeName typeData =+ (typeName, DataUnion $ createType typeName typeData)+++isEntNode :: DataType -> Bool+isEntNode DataScalar{} = True+isEntNode DataEnum{} = True+isEntNode _ = False++isInputDataType :: DataType -> Bool+isInputDataType DataScalar{} = True+isInputDataType DataEnum{} = True+isInputDataType DataInputObject{} = True+isInputDataType DataInputUnion{} = True+isInputDataType _ = False++coerceDataObject :: Failure error m => error -> DataType -> m DataObject+coerceDataObject _ (DataObject object) = pure object+coerceDataObject gqlError _ = failure gqlError++coerceDataUnion :: Failure error m => error -> DataType -> m DataUnion+coerceDataUnion _ (DataUnion object) = pure object+coerceDataUnion gqlError _ = failure gqlError++kindOf :: DataType -> DataTypeKind+kindOf (DataScalar _) = KindScalar+kindOf (DataEnum _) = KindEnum+kindOf (DataInputObject _) = KindInputObject+kindOf (DataObject _) = KindObject Nothing+kindOf (DataUnion _) = KindUnion+kindOf (DataInputUnion _) = KindInputUnion++fromDataType :: (DataTyCon () -> v) -> DataType -> v+fromDataType f (DataScalar dt) = f dt { typeData = () }+fromDataType f (DataEnum dt) = f dt { typeData = () }+fromDataType f (DataUnion dt) = f dt { typeData = () }+fromDataType f (DataInputObject dt) = f dt { typeData = () }+fromDataType f (DataInputUnion dt) = f dt { typeData = () }+fromDataType f (DataObject dt) = f dt { typeData = () }++--+-- Type Register+--------------------------------------------------------------------------------------------------+data DataTypeLib = DataTypeLib+ { types :: HashMap Key DataType+ , query :: (Key, DataObject)+ , mutation :: Maybe (Key, DataObject)+ , subscription :: Maybe (Key, DataObject)+ } deriving (Show)++type TypeRegister = HashMap Key DataType++initTypeLib :: (Key, DataObject) -> DataTypeLib+initTypeLib query = DataTypeLib { types = empty+ , query = query+ , mutation = Nothing+ , subscription = Nothing+ }++allDataTypes :: DataTypeLib -> [(Key, DataType)]+allDataTypes DataTypeLib { types, query, mutation, subscription } =+ concatMap fromOperation [Just query, mutation, subscription] <> toList types++typeRegister :: DataTypeLib -> TypeRegister+typeRegister DataTypeLib { types, query, mutation, subscription } =+ types `union` fromList+ (concatMap fromOperation [Just query, mutation, subscription])++fromOperation :: Maybe (Key, DataObject) -> [(Key, DataType)]+fromOperation (Just (key', dataType')) = [(key', DataObject dataType')]+fromOperation Nothing = []++lookupDataType :: Key -> DataTypeLib -> Maybe DataType+lookupDataType name lib = name `HM.lookup` typeRegister lib++getDataType :: Failure error m => Key -> DataTypeLib -> error -> m DataType+getDataType name lib gqlError = case lookupDataType name lib of+ Just x -> pure x+ _ -> failure gqlError++lookupDataObject+ :: (Monad m, Failure e m) => e -> Key -> DataTypeLib -> m DataObject+lookupDataObject validationError name lib =+ getDataType name lib validationError >>= coerceDataObject validationError++lookupDataUnion+ :: (Monad m, Failure e m) => e -> Key -> DataTypeLib -> m DataUnion+lookupDataUnion validationError name lib =+ getDataType name lib validationError >>= coerceDataUnion validationError++lookupUnionTypes+ :: (Monad m, Failure GQLErrors m)+ => Position+ -> Key+ -> DataTypeLib+ -> DataField+ -> m [DataObject]+lookupUnionTypes position key lib DataField { fieldType = TypeAlias { aliasTyCon = typeName } }+ = lookupDataUnion gqlError typeName lib+ >>= mapM (flip (lookupDataObject gqlError) lib)+ . typeData+ where gqlError = hasNoSubfields key typeName position++lookupFieldAsSelectionSet+ :: (Monad m, Failure GQLErrors m)+ => Position+ -> Key+ -> DataTypeLib+ -> DataField+ -> m DataObject+lookupFieldAsSelectionSet position key lib DataField { fieldType = TypeAlias { aliasTyCon } }+ = lookupDataObject gqlError aliasTyCon lib+ where gqlError = hasNoSubfields key aliasTyCon position++lookupInputType :: Failure e m => Key -> DataTypeLib -> e -> m DataType+lookupInputType name lib errors = case lookupDataType name lib of+ Just x | isInputDataType x -> pure x+ _ -> failure errors++isTypeDefined :: Key -> DataTypeLib -> Maybe DataFingerprint+isTypeDefined name lib =+ fromDataType typeFingerprint <$> lookupDataType name lib++defineType :: (Key, DataType) -> DataTypeLib -> DataTypeLib+defineType (key, datatype@(DataInputUnion DataTyCon { typeName, typeData = enumKeys, typeFingerprint })) lib+ = lib { types = insert name unionTags (insert key datatype (types lib)) }+ where+ name = typeName <> "Tags"+ unionTags = DataEnum DataTyCon { typeName = name+ , typeFingerprint+ , typeMeta = Nothing+ , typeData = map createEnumValue enumKeys+ }+defineType (key, datatype) lib =+ lib { types = insert key datatype (types lib) }++lookupType :: Failure e m => e -> [(Key, a)] -> Key -> m a+lookupType err lib typeName = case lookup typeName lib of+ Nothing -> failure err+ Just x -> pure x++createDataTypeLib :: [(Key, DataType)] -> Validation DataTypeLib+createDataTypeLib types = case takeByKey "Query" types of+ (Just query, lib1) -> case takeByKey "Mutation" lib1 of+ (mutation, lib2) -> case takeByKey "Subscription" lib2 of+ (subscription, lib3) ->+ pure+ ((foldr defineType (initTypeLib query) lib3) { mutation+ , subscription+ }+ )+ _ -> internalError "Query Not Defined"+ ----------------------------------------------------------------------------+++++++++++++++ where+ takeByKey key lib = case lookup key lib of+ Just (DataObject value) -> (Just (key, value), filter ((/= key) . fst) lib)+ _ -> (Nothing, lib)+++createInputUnionFields :: Key -> [Key] -> [(Key, DataField)]+createInputUnionFields name members = fieldTag : map unionField members+ where+ fieldTag =+ ( "tag"+ , DataField { fieldName = "tag"+ , fieldArgs = []+ , fieldArgsType = Nothing+ , fieldType = createAlias (name <> "Tags")+ , fieldMeta = Nothing+ }+ )+ unionField memberName =+ ( memberName+ , DataField+ { fieldArgs = []+ , fieldArgsType = Nothing+ , fieldName = memberName+ , fieldType = TypeAlias { aliasTyCon = memberName+ , aliasWrappers = [TypeMaybe]+ , aliasArgs = Nothing+ }+ , fieldMeta = Nothing+ }+ )++createAlias :: Key -> TypeAlias+createAlias aliasTyCon =+ TypeAlias { aliasTyCon, aliasWrappers = [], aliasArgs = Nothing }+++type TypeUpdater = LibUpdater DataTypeLib++insertType :: (Key, DataType) -> TypeUpdater+insertType nextType@(name, datatype) lib = case isTypeDefined name lib of+ Nothing -> resolveUpdates (defineType nextType lib) []+ Just fingerprint+ | fingerprint == fromDataType typeFingerprint datatype -> return lib+ |+ -- throw error if 2 different types has same name+ otherwise -> failure $ nameCollisionError name+++-- TEMPLATE HASKELL DATA TYPES++-- CLIENT +data ClientQuery = ClientQuery+ { queryText :: String+ , queryTypes :: [ClientType]+ , queryArgsType :: Maybe TypeD+ } deriving (Show)++data ClientType = ClientType {+ clientType :: TypeD,+ clientKind :: DataTypeKind+} deriving (Show)++-- Document+data GQLTypeD = GQLTypeD+ { typeD :: TypeD+ , typeKindD :: DataTypeKind+ , typeArgD :: [TypeD]+ , typeOriginal:: (Name,DataType)+ } deriving (Show)++data TypeD = TypeD+ { tName :: String+ , tNamespace :: [String]+ , tCons :: [ConsD]+ , tMeta :: Maybe Meta+ } deriving (Show)++data ConsD = ConsD+ { cName :: String+ , cFields :: [DataField]+ } deriving (Show)
src/Data/Morpheus/Types/Internal/AST/Operation.hs view
@@ -1,7 +1,7 @@-{-# LANGUAGE DeriveLift #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE DeriveLift #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-} module Data.Morpheus.Types.Internal.AST.Operation ( Operation(..)@@ -12,19 +12,39 @@ , ValidVariables , DefaultValue , getOperationName- ) where+ , getOperationDataType+ )+where -import Data.Maybe (fromMaybe)-import Language.Haskell.TH.Syntax (Lift (..))+import Data.Maybe ( fromMaybe )+import Language.Haskell.TH.Syntax ( Lift(..) ) -- MORPHEUS-import Data.Morpheus.Types.Internal.AST.RawSelection (RawSelectionSet)-import Data.Morpheus.Types.Internal.AST.Selection (Arguments, SelectionSet)-import Data.Morpheus.Types.Internal.Base (Collection, Key, Position)-import Data.Morpheus.Types.Internal.Data (OperationType, WrapperD)-import Data.Morpheus.Types.Internal.TH (apply, liftMaybeText, liftText, liftTextMap)-import Data.Morpheus.Types.Internal.Value (Value)+import Data.Morpheus.Error.Mutation ( mutationIsNotDefined )+import Data.Morpheus.Error.Subscription+ ( subscriptionIsNotDefined )+import Data.Morpheus.Types.Internal.AST.Selection+ ( Arguments+ , SelectionSet+ , RawSelectionSet+ ) +import Data.Morpheus.Types.Internal.Resolving.Core+ ( Validation ,Failure(..) )+import Data.Morpheus.Types.Internal.AST.Base+ ( Collection+ , Key+ , Position+ )+import Data.Morpheus.Types.Internal.AST.Data+ ( OperationType(..)+ , TypeWrapper+ , DataTypeLib(..)+ , DataObject+ )+import Data.Morpheus.Types.Internal.AST.Value+ ( Value )+ type DefaultValue = Maybe Value type VariableDefinitions = Collection (Variable DefaultValue)@@ -44,19 +64,25 @@ , operationArgs :: args , operationSelection :: sel , operationPosition :: Position- } deriving (Show)--instance Lift (Operation VariableDefinitions RawSelectionSet) where- lift (Operation name kind args sel pos) =- apply 'Operation [liftMaybeText name, lift kind, liftTextMap args, liftTextMap sel, lift pos]+ } deriving (Show,Lift) data Variable a = Variable { variableType :: Key , isVariableRequired :: Bool- , variableTypeWrappers :: [WrapperD]+ , variableTypeWrappers :: [TypeWrapper] , variablePosition :: Position , variableValue :: a- } deriving (Show)+ } deriving (Show,Lift) -instance Lift a => Lift (Variable a) where- lift (Variable t ir w p v) = apply 'Variable [liftText t, lift ir, lift w, lift p, lift v]+getOperationDataType :: Operation a b -> DataTypeLib -> Validation DataObject+getOperationDataType Operation { operationType = Query } lib =+ pure $ snd $ query lib+getOperationDataType Operation { operationType = Mutation, operationPosition } lib+ = case mutation lib of+ Just (_, mutation') -> pure mutation'+ Nothing -> failure $ mutationIsNotDefined operationPosition+getOperationDataType Operation { operationType = Subscription, operationPosition } lib+ = case subscription lib of+ Just (_, subscription') -> pure subscription'+ Nothing -> failure $ subscriptionIsNotDefined operationPosition+
− src/Data/Morpheus/Types/Internal/AST/RawSelection.hs
@@ -1,61 +0,0 @@-{-# LANGUAGE DeriveLift #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE TemplateHaskell #-}--module Data.Morpheus.Types.Internal.AST.RawSelection- ( Reference(..)- , Argument(..)- , RawArgument(..)- , RawSelection(..)- , Fragment(..)- , FragmentLib- , RawArguments- , RawSelectionSet- , Selection (..)- ) where--import Language.Haskell.TH.Syntax (Lift (..))---- MORPHEUS-import Data.Morpheus.Types.Internal.AST.Selection (Argument (..), Selection (..))-import Data.Morpheus.Types.Internal.Base (Collection, Key, Position, Reference (..))-import Data.Morpheus.Types.Internal.TH (apply, liftMaybeText, liftText, liftTextMap)--data Fragment = Fragment- { fragmentType :: Key- , fragmentPosition :: Position- , fragmentSelection :: RawSelectionSet- } deriving (Show)--instance Lift Fragment where- lift (Fragment t p sel) = apply 'Fragment [liftText t, lift p, liftTextMap sel]--type RawSelection' a = Selection RawArguments a--instance Lift a => Lift (RawSelection' a) where- lift (Selection t p alias sel) = apply 'Selection [liftTextMap t, lift p, liftMaybeText alias , lift sel]--type FragmentLib = [(Key, Fragment)]--data RawArgument- = VariableReference Reference- | RawArgument Argument- deriving (Show, Lift)--type RawArguments = Collection RawArgument--type RawSelectionSet = Collection RawSelection--instance Lift RawSelection where- lift (RawSelectionSet (Selection t p alias sel)) =- apply 'RawSelectionSet [apply 'Selection [liftTextMap t, lift p, liftMaybeText alias,liftTextMap sel]]- lift (RawSelectionField p) = apply 'RawSelectionField [lift p]- lift (InlineFragment f) = apply 'InlineFragment [lift f]- lift (Spread f) = apply 'Spread [lift f]--data RawSelection- = RawSelectionSet (RawSelection' RawSelectionSet)- | RawSelectionField (RawSelection' ())- | InlineFragment Fragment- | Spread Reference- deriving (Show)
src/Data/Morpheus/Types/Internal/AST/Selection.hs view
@@ -5,26 +5,63 @@ , Arguments , SelectionSet , SelectionRec(..)- , ArgumentOrigin(..)+ , ValueOrigin(..) , ValidSelection , Selection(..)- ) where+ , RawSelection'+ , FragmentLib+ , RawArguments+ , RawSelectionSet+ , Fragment(..)+ , RawArgument(..)+ , RawSelection(..)+ )+where -import Data.Morpheus.Types.Internal.Base (Collection, Key, Position)-import Data.Morpheus.Types.Internal.Value (Value)-import Language.Haskell.TH.Syntax (Lift (..))+import Language.Haskell.TH.Syntax ( Lift ) -data ArgumentOrigin- = VARIABLE- | INLINE- deriving (Show, Lift) -data Argument = Argument- { argumentValue :: Value- , argumentOrigin :: ArgumentOrigin- , argumentPosition :: Position+-- MORPHEUS+import Data.Morpheus.Types.Internal.AST.Base+ ( Collection+ , Key+ , Position+ , Ref(..)+ )++import Data.Morpheus.Types.Internal.AST.Value+ ( Value )+++-- RAW SELECTION++type RawSelection' a = Selection RawArguments a++type FragmentLib = [(Key, Fragment)]++type RawArguments = Collection RawArgument++type RawSelectionSet = Collection RawSelection++data Fragment = Fragment+ { fragmentType :: Key+ , fragmentPosition :: Position+ , fragmentSelection :: RawSelectionSet } deriving (Show, Lift) +data RawArgument+ = VariableRef Ref+ | RawArgument Argument+ deriving (Show, Lift)++data RawSelection+ = RawSelectionSet (RawSelection' RawSelectionSet)+ | RawSelectionField (RawSelection' ())+ | InlineFragment Fragment+ | Spread Ref+ deriving (Show,Lift)++-- VALID SELECTION type Arguments = Collection Argument type SelectionSet = Collection ValidSelection@@ -33,12 +70,23 @@ type UnionSelection = Collection SelectionSet +data ValueOrigin+ = VARIABLE+ | INLINE+ deriving (Show, Lift)++data Argument = Argument+ { argumentValue :: Value+ , argumentOrigin :: ValueOrigin+ , argumentPosition :: Position+ } deriving (Show, Lift)+ data Selection args rec = Selection { selectionArguments :: args , selectionPosition :: Position , selectionAlias :: Maybe Key , selectionRec :: rec- } deriving (Show)+ } deriving (Show,Lift) data SelectionRec = SelectionSet SelectionSet
+ src/Data/Morpheus/Types/Internal/AST/Value.hs view
@@ -0,0 +1,156 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveLift #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Data.Morpheus.Types.Internal.AST.Value+ ( Value(..)+ , ScalarValue(..)+ , Object+ , GQLValue(..)+ , replaceValue+ , decodeScientific+ , convertToJSONName+ , convertToHaskellName+ )+where++import qualified Data.Aeson as A+ ( FromJSON(..)+ , ToJSON(..)+ , Value(..)+ , object+ , pairs+ , (.=)+ )+import qualified Data.HashMap.Strict as M+ ( toList )+import Data.Scientific ( Scientific+ , floatingOrInteger+ )+import Data.Semigroup ( (<>) )+import Data.Text ( Text )+import qualified Data.Text as T+import qualified Data.Vector as V+ ( toList )+import GHC.Generics ( Generic )+import Instances.TH.Lift ( )+import Language.Haskell.TH.Syntax ( Lift )+++isReserved :: Text -> Bool+isReserved "case" = True+isReserved "class" = True+isReserved "data" = True+isReserved "default" = True+isReserved "deriving" = True+isReserved "do" = True+isReserved "else" = True+isReserved "foreign" = True+isReserved "if" = True+isReserved "import" = True+isReserved "in" = True+isReserved "infix" = True+isReserved "infixl" = True+isReserved "infixr" = True+isReserved "instance" = True+isReserved "let" = True+isReserved "module" = True+isReserved "newtype" = True+isReserved "of" = True+isReserved "then" = True+isReserved "type" = True+isReserved "where" = True+isReserved "_" = True+isReserved _ = False++{-# INLINE isReserved #-}+convertToJSONName :: Text -> Text+convertToJSONName hsName+ | not (T.null hsName) && isReserved name && (T.last hsName == '\'') = name+ | otherwise = hsName+ where name = T.init hsName++convertToHaskellName :: Text -> Text+convertToHaskellName name | isReserved name = name <> "'"+ | otherwise = name++-- | Primitive Values for GQLScalar: 'Int', 'Float', 'String', 'Boolean'.+-- for performance reason type 'Text' represents GraphQl 'String' value+data ScalarValue+ = Int Int+ | Float Float+ | String Text+ | Boolean Bool+ deriving (Show, Generic,Lift)+++instance A.ToJSON ScalarValue where+ toJSON (Float x) = A.toJSON x+ toJSON (Int x) = A.toJSON x+ toJSON (Boolean x) = A.toJSON x+ toJSON (String x) = A.toJSON x++instance A.FromJSON ScalarValue where+ parseJSON (A.Bool v) = pure $ Boolean v+ parseJSON (A.Number v) = pure $ decodeScientific v+ parseJSON (A.String v) = pure $ String v+ parseJSON notScalar = fail $ "Expected Scalar got :" <> show notScalar+++type Object = [(Text, Value)]++data Value+ = Object Object+ | List [Value]+ | Enum Text+ | Scalar ScalarValue+ | Null+ deriving (Show, Generic,Lift)++instance A.ToJSON Value where+ toEncoding Null = A.toEncoding A.Null+ toEncoding (Enum x ) = A.toEncoding x+ toEncoding (List x ) = A.toEncoding x+ toEncoding (Scalar x ) = A.toEncoding x+ toEncoding (Object []) = A.toEncoding $ A.object []+ toEncoding (Object x ) = A.pairs $ foldl1 (<>) $ map encodeField x+ where encodeField (key, value) = convertToJSONName key A..= value++decodeScientific :: Scientific -> ScalarValue+decodeScientific v = case floatingOrInteger v of+ Left float -> Float float+ Right int -> Int int++replaceValue :: A.Value -> Value+replaceValue (A.Bool v) = gqlBoolean v+replaceValue (A.Number v) = Scalar $ decodeScientific v+replaceValue (A.String v) = gqlString v+replaceValue (A.Object v) = gqlObject $ map replace (M.toList v)+ where+ replace :: (a, A.Value) -> (a, Value)+ replace (key, val) = (key, replaceValue val)+replaceValue (A.Array li) = gqlList (map replaceValue (V.toList li))+replaceValue A.Null = gqlNull++instance A.FromJSON Value where+ parseJSON = pure . replaceValue++-- DEFAULT VALUES+class GQLValue a where+ gqlNull :: a+ gqlScalar :: ScalarValue -> a+ gqlBoolean :: Bool -> a+ gqlString :: Text -> a+ gqlList :: [a] -> a+ gqlObject :: [(Text, a)] -> a++-- build GQL Values for Subscription Resolver+instance GQLValue Value where+ gqlNull = Null+ gqlScalar = Scalar+ gqlBoolean = Scalar . Boolean+ gqlString = Scalar . String+ gqlList = List+ gqlObject = Object
− src/Data/Morpheus/Types/Internal/Base.hs
@@ -1,58 +0,0 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DeriveLift #-}-{-# LANGUAGE TemplateHaskell #-}--module Data.Morpheus.Types.Internal.Base- ( Key- , Collection- , Reference (..)- , Position- , EnhancedKey(..)- , Location(..)- , Message- , enhanceKeyWithNull- ) where--import Data.Aeson (FromJSON, ToJSON)-import Data.Text (Text)-import GHC.Generics (Generic)-import Language.Haskell.TH.Syntax (Lift (..))---- MORPHEUS-import Data.Morpheus.Types.Internal.TH (apply, liftText)--type Position = Location--data Location = Location- { line :: Int- , column :: Int- } deriving (Show, Generic, FromJSON, ToJSON, Lift)--type Key = Text-type Message = Text--type Collection a = [(Key, a)]--data Reference = Reference- { referenceName :: Key- , referencePosition :: Position- } deriving (Show)--instance Lift Reference where- lift (Reference name pos) = apply 'Reference [liftText name, lift pos]---- Text value that includes position for debugging, where EnhancedKey "a" 1 === EnhancedKey "a" 3-data EnhancedKey = EnhancedKey- { uid :: Text- , location :: Position- } deriving (Show)--instance Eq EnhancedKey where- (EnhancedKey id1 _) == (EnhancedKey id2 _) = id1 == id2--instance Ord EnhancedKey where- compare (EnhancedKey x _) (EnhancedKey y _) = compare x y--enhanceKeyWithNull :: Key -> EnhancedKey-enhanceKeyWithNull text = EnhancedKey {uid = text, location = Location 0 0}
− src/Data/Morpheus/Types/Internal/Data.hs
@@ -1,338 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DefaultSignatures #-}-{-# LANGUAGE DeriveLift #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TypeSynonymInstances #-}--module Data.Morpheus.Types.Internal.Data- ( Key- , DataScalar- , DataEnum- , DataObject- , DataArgument- , DataUnion- , DataArguments- , DataField(..)- , DataTyCon(..)- , DataType(..)- , DataTypeLib(..)- , DataTypeWrapper(..)- , DataValidator(..)- , DataTypeKind(..)- , DataFingerprint(..)- , RawDataType(..)- , ResolverKind(..)- , WrapperD(..)- , TypeAlias(..)- , ArgsType(..)- , isTypeDefined- , initTypeLib- , defineType- , isFieldNullable- , allDataTypes- , lookupDataType- , kindOf- , toNullableField- , toListField- , isObject- , isInput- , toHSWrappers- , isNullable- , toGQLWrapper- , isWeaker- , isSubscription- , isOutputObject- , sysTypes- , isDefaultTypeName- , isSchemaTypeName- , isPrimitiveTypeName- , OperationType(..)- , QUERY- , MUTATION- , SUBSCRIPTION- , Name- , Description- , isEntNode- ) where--import Data.Semigroup ((<>))-import qualified Data.Text as T (pack, unpack)-import GHC.Fingerprint.Type (Fingerprint)-import Language.Haskell.TH.Syntax (Lift (..))---- MORPHEUS-import Data.Morpheus.Types.Internal.Base (Key)-import Data.Morpheus.Types.Internal.TH (apply, liftText, liftTextMap)-import Data.Morpheus.Types.Internal.Value (Value (..))--type Name = Key-type Description = Key--type QUERY = 'Query-type MUTATION = 'Mutation-type SUBSCRIPTION = 'Subscription--isDefaultTypeName :: Key -> Bool-isDefaultTypeName x = isSchemaTypeName x || isPrimitiveTypeName x--isSchemaTypeName :: Key -> Bool-isSchemaTypeName = (`elem` sysTypes)--isPrimitiveTypeName :: Key -> Bool-isPrimitiveTypeName = (`elem` ["String", "Float", "Int", "Boolean", "ID"])--sysTypes :: [Key]-sysTypes =- ["__Schema", "__Type", "__Directive", "__TypeKind", "__Field", "__DirectiveLocation", "__InputValue", "__EnumValue"]--data OperationType- = Query- | Subscription- | Mutation- deriving (Show, Eq, Lift)--isSubscription :: DataTypeKind -> Bool-isSubscription (KindObject (Just Subscription)) = True-isSubscription _ = False--isOutputObject :: DataTypeKind -> Bool-isOutputObject (KindObject _) = True-isOutputObject _ = False--isObject :: DataTypeKind -> Bool-isObject (KindObject _) = True-isObject KindInputObject = True-isObject _ = False--isInput :: DataTypeKind -> Bool-isInput KindInputObject = True-isInput _ = False--data DataTypeKind- = KindScalar- | KindObject (Maybe OperationType)- | KindUnion- | KindEnum- | KindInputObject- | KindList- | KindNonNull- | KindInputUnion- deriving (Eq, Show, Lift)--data ResolverKind- = PlainResolver- | TypeVarResolver- | ExternalResolver- deriving (Show, Eq, Lift)--data WrapperD- = ListD- | MaybeD- deriving (Show, Lift)--isFieldNullable :: DataField -> Bool-isFieldNullable = isNullable . aliasWrappers . fieldType--isNullable :: [WrapperD] -> Bool-isNullable (MaybeD:_) = True-isNullable _ = False--isWeaker :: [WrapperD] -> [WrapperD] -> Bool-isWeaker (MaybeD:xs1) (MaybeD:xs2) = isWeaker xs1 xs2-isWeaker (MaybeD:_) _ = True-isWeaker (_:xs1) (_:xs2) = isWeaker xs1 xs2-isWeaker _ _ = False--toGQLWrapper :: [WrapperD] -> [DataTypeWrapper]-toGQLWrapper (MaybeD:(MaybeD:tw)) = toGQLWrapper (MaybeD : tw)-toGQLWrapper (MaybeD:(ListD:tw)) = ListType : toGQLWrapper tw-toGQLWrapper (ListD:tw) = [NonNullType, ListType] <> toGQLWrapper tw-toGQLWrapper [MaybeD] = []-toGQLWrapper [] = [NonNullType]--toHSWrappers :: [DataTypeWrapper] -> [WrapperD]-toHSWrappers (NonNullType:(NonNullType:xs)) = toHSWrappers (NonNullType : xs)-toHSWrappers (NonNullType:(ListType:xs)) = ListD : toHSWrappers xs-toHSWrappers (ListType:xs) = [MaybeD, ListD] <> toHSWrappers xs-toHSWrappers [] = [MaybeD]-toHSWrappers [NonNullType] = []--data DataFingerprint- = SystemFingerprint Key- | TypeableFingerprint [Fingerprint]- deriving (Show, Eq, Ord)--newtype DataValidator = DataValidator- { validateValue :: Value -> Either Key Value- }--instance Show DataValidator where- show _ = "DataValidator"--type DataScalar = DataTyCon DataValidator--type DataEnum = DataTyCon [Key]--type DataObject = DataTyCon [(Key, DataField)]--type DataArgument = DataField--type DataUnion = DataTyCon [DataField]--type DataArguments = [(Key, DataArgument)]--data DataTypeWrapper- = ListType- | NonNullType- deriving (Show, Lift)--data TypeAlias = TypeAlias- { aliasTyCon :: Key- , aliasArgs :: Maybe Key- , aliasWrappers :: [WrapperD]- } deriving (Show)--instance Lift TypeAlias where- lift TypeAlias {aliasTyCon = x, aliasArgs, aliasWrappers} =- [|TypeAlias {aliasTyCon = name, aliasArgs = T.pack <$> args, aliasWrappers}|]- where- name = T.unpack x- args = T.unpack <$> aliasArgs--data ArgsType = ArgsType- { argsTypeName :: Key- , resKind :: ResolverKind- } deriving (Show)--instance Lift ArgsType where- lift (ArgsType argT kind) = apply 'ArgsType [liftText argT, lift kind]--data DataField = DataField- { fieldName :: Key- , fieldArgs :: [(Key, DataArgument)]- , fieldArgsType :: Maybe ArgsType- , fieldType :: TypeAlias- , fieldHidden :: Bool- } deriving (Show)--instance Lift DataField where- lift (DataField name args argsT ft hid) =- apply 'DataField [liftText name, liftTextMap args, lift argsT, lift ft, lift hid]--data DataTyCon a = DataTyCon- { typeName :: Key- , typeFingerprint :: DataFingerprint- , typeDescription :: Maybe Key- , typeData :: a- } deriving (Show)--data RawDataType- = FinalDataType DataType- | Interface DataObject- | Implements { implementsInterfaces :: [Key]- , unImplements :: DataObject }- deriving (Show)--isEntNode :: DataType -> Bool-isEntNode DataScalar {} = True-isEntNode DataEnum {} = True-isEntNode _ = False--data DataType- = DataScalar DataScalar- | DataEnum DataEnum- | DataInputObject DataObject- | DataObject DataObject- | DataUnion DataUnion- | DataInputUnion DataUnion- deriving (Show)--data DataTypeLib = DataTypeLib- { scalar :: [(Key, DataScalar)]- , enum :: [(Key, DataEnum)]- , inputObject :: [(Key, DataObject)]- , object :: [(Key, DataObject)]- , union :: [(Key, DataUnion)]- , inputUnion :: [(Key, DataUnion)]- , query :: (Key, DataObject)- , mutation :: Maybe (Key, DataObject)- , subscription :: Maybe (Key, DataObject)- } deriving (Show)--initTypeLib :: (Key, DataObject) -> DataTypeLib-initTypeLib query =- DataTypeLib- { scalar = []- , enum = []- , inputObject = []- , query = query- , object = []- , union = []- , inputUnion = []- , mutation = Nothing- , subscription = Nothing- }--allDataTypes :: DataTypeLib -> [(Key, DataType)]-allDataTypes DataTypeLib { scalar, enum , inputObject, object, union, inputUnion, query, mutation, subscription } =- packType DataObject query :- fromMaybeType mutation ++- fromMaybeType subscription ++- map (packType DataScalar) scalar ++- map (packType DataEnum) enum ++- map (packType DataInputObject) inputObject ++- map (packType DataInputUnion) inputUnion ++ map (packType DataObject) object ++ map (packType DataUnion) union- where- packType f (x, y) = (x, f y)- fromMaybeType :: Maybe (Key, DataObject) -> [(Key, DataType)]- fromMaybeType (Just (key', dataType')) = [(key', DataObject dataType')]- fromMaybeType Nothing = []--lookupDataType :: Key -> DataTypeLib -> Maybe DataType-lookupDataType name lib = name `lookup` allDataTypes lib--kindOf :: DataType -> DataTypeKind-kindOf (DataScalar _) = KindScalar-kindOf (DataEnum _) = KindEnum-kindOf (DataInputObject _) = KindInputObject-kindOf (DataObject _) = KindObject Nothing-kindOf (DataUnion _) = KindUnion-kindOf (DataInputUnion _) = KindInputUnion--fromDataType :: (DataTyCon () -> v) -> DataType -> v-fromDataType f (DataScalar dt) = f dt {typeData = ()}-fromDataType f (DataEnum dt) = f dt {typeData = ()}-fromDataType f (DataUnion dt) = f dt {typeData = ()}-fromDataType f (DataInputObject dt) = f dt {typeData = ()}-fromDataType f (DataInputUnion dt) = f dt {typeData = ()}-fromDataType f (DataObject dt) = f dt {typeData = ()}--isTypeDefined :: Key -> DataTypeLib -> Maybe DataFingerprint-isTypeDefined name lib = fromDataType typeFingerprint <$> lookupDataType name lib--defineType :: (Key, DataType) -> DataTypeLib -> DataTypeLib-defineType (key', DataScalar type') lib = lib {scalar = (key', type') : scalar lib}-defineType (key', DataEnum type') lib = lib {enum = (key', type') : enum lib}-defineType (key', DataInputObject type') lib = lib {inputObject = (key', type') : inputObject lib}-defineType (key', DataObject type') lib = lib {object = (key', type') : object lib}-defineType (key', DataUnion type') lib = lib {union = (key', type') : union lib}-defineType (key', DataInputUnion type') lib = lib {inputUnion = (key', type') : inputUnion lib}--toNullableField :: DataField -> DataField-toNullableField dataField- | isNullable (aliasWrappers $ fieldType dataField) = dataField- | otherwise = dataField {fieldType = nullable (fieldType dataField)}- where- nullable alias@TypeAlias {aliasWrappers} = alias {aliasWrappers = MaybeD : aliasWrappers}--toListField :: DataField -> DataField-toListField dataField = dataField {fieldType = listW (fieldType dataField)}- where- listW alias@TypeAlias {aliasWrappers} = alias {aliasWrappers = ListD : aliasWrappers}
− src/Data/Morpheus/Types/Internal/DataD.hs
@@ -1,37 +0,0 @@-{-# LANGUAGE DeriveLift #-}--module Data.Morpheus.Types.Internal.DataD- ( TypeD(..)- , ConsD(..)- , QueryD(..)- , GQLTypeD(..)- ) where--import Language.Haskell.TH.Syntax (Lift (..))------- MORPHEUS-import Data.Morpheus.Types.Internal.Data (DataField, DataTypeKind)--data QueryD = QueryD- { queryText :: String- , queryTypes :: [GQLTypeD]- , queryArgsType :: Maybe TypeD- } deriving (Show, Lift)--data GQLTypeD = GQLTypeD- { typeD :: TypeD- , typeKindD :: DataTypeKind- , typeArgD :: [TypeD]- } deriving (Show, Lift)--data TypeD = TypeD- { tName :: String- , tNamespace :: [String]- , tCons :: [ConsD]- } deriving (Show, Lift)--data ConsD = ConsD- { cName :: String- , cFields :: [DataField]- } deriving (Show, Lift)
− src/Data/Morpheus/Types/Internal/Resolver.hs
@@ -1,263 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE InstanceSigs #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}--module Data.Morpheus.Types.Internal.Resolver- ( ResolveT- , Event(..)- , GQLRootResolver(..)- , UnSubResolver- , ResponseT- , Resolver(..)- , ResolvingStrategy(..)- , MapGraphQLT(..)- , PureOperation(..)- , resolveObject- , toResponseRes- , withObject- , Resolving(..)- , liftM- , liftEitherM- ) where--import Control.Monad.Fail (MonadFail (..))-import Control.Monad.Trans.Except (ExceptT (..), runExceptT, withExceptT)-import Data.Maybe (fromMaybe)-import Data.Semigroup ((<>))---- MORPHEUS-import Data.Morpheus.Error.Selection (resolverError, subfieldsNotSelected)-import Data.Morpheus.Types.Internal.AST.Selection (Selection (..), SelectionRec (..), SelectionSet,- ValidSelection)-import Data.Morpheus.Types.Internal.Data (Key, MUTATION, OperationType, QUERY, SUBSCRIPTION)-import Data.Morpheus.Types.Internal.Stream (Channel (..), Event (..), ResponseEvent (..),- ResponseStream, StreamChannel, StreamState (..),- StreamT (..), closeStream, injectEvents, mapS, pushEvents)-import Data.Morpheus.Types.Internal.Validation (GQLErrors, Validation)-import Data.Morpheus.Types.Internal.Value (GQLValue (..), Value)-import Data.Morpheus.Types.IO (renderResponse)--withObject :: ( SelectionSet -> ResolvingStrategy o m e value) -> (Key,ValidSelection) -> ResolvingStrategy o m e value-withObject f (_, Selection {selectionRec = SelectionSet selection}) = f selection-withObject _ (key, Selection {selectionPosition}) = Fail $ subfieldsNotSelected key "" selectionPosition--liftM :: (PureOperation o, Monad m) => m a -> Resolver o m e a-liftM = liftEither . fmap pure--liftEitherM :: (PureOperation o, Monad m) => m (Either String a) -> Resolver o m e a-liftEitherM = liftEither------------------------------------------------------------------------------------------type ResolveT = ExceptT GQLErrors-type ResponseT m e = ResolveT (ResponseStream m e)--instance Monad m => MonadFail (Resolver QUERY m e) where- fail = FailedResolver------- Recursive Resolver-newtype RecResolver m a b = RecResolver {- unRecResolver :: a -> ResolveT m b-}--instance Functor m => Functor (RecResolver m a) where- fmap f (RecResolver x) = RecResolver eventFmap- where- eventFmap event = fmap f (x event)--instance Monad m => Applicative (RecResolver m a) where- pure = RecResolver . const . pure- (RecResolver f) <*> (RecResolver res) = RecResolver recX- where- recX event = f event <*> res event--instance Monad m => Monad (RecResolver m a) where- (RecResolver x) >>= next = RecResolver recX- where- recX event = x event >>= (\v-> v event) . unRecResolver . next-------------------------------------------------------------------- GraphQLT--data ResolvingStrategy (o::OperationType) (m:: * -> *) event value where- QueryResolving :: { unQueryT :: ResolveT m value } -> ResolvingStrategy QUERY m event value- MutationResolving :: { unMutationT :: ResolveT (StreamT m event) value } -> ResolvingStrategy MUTATION m event value- SubscriptionResolving :: { unSubscriptionT :: ResolveT (StreamT m (Channel event)) (RecResolver m event value) } -> ResolvingStrategy SUBSCRIPTION m event value- -- SubscriptionRecT :: RecResolver m event value -> GraphQLT SUBSCRIPTION m event value- Fail :: GQLErrors -> ResolvingStrategy o m event value---- GraphQLT Functor-instance Monad m => Functor (ResolvingStrategy o m e) where- fmap _ (Fail mErrors) = Fail mErrors- fmap f (QueryResolving mResolver) = QueryResolving $ f <$> mResolver- fmap f (MutationResolving mResolver) = MutationResolving $ f <$> mResolver- fmap f (SubscriptionResolving mResolver) = SubscriptionResolving $ fmap f <$> mResolver---- GraphQLT Applicative-instance (PureOperation o, Monad m) => Applicative (ResolvingStrategy o m e) where- pure = pureGraphQLT- -------------------------------------- _ <*> (Fail mErrors) = Fail mErrors- (Fail mErrors) <*> _ = Fail mErrors- -------------------------------------- (QueryResolving f) <*> (QueryResolving res) = QueryResolving (f <*> res)- ------------------------------------------------------------------------- (MutationResolving f) <*> (MutationResolving res) = MutationResolving (f <*> res)- --------------------------------------------------------------- (SubscriptionResolving f) <*> (SubscriptionResolving res) = SubscriptionResolving $ do- f1 <- f- res1 <- res- pure (f1 <*> res1)---- GADTResolver-----------------------------------------------------------------data Resolver (o::OperationType) (m :: * -> * ) event value where- FailedResolver :: { unFailedResolver :: String } -> Resolver o m event value- QueryResolver:: { unQueryResolver :: ExceptT String m value } -> Resolver QUERY m event value- MutResolver :: {- mutEvents :: [event] ,- mutResolver :: ExceptT String m value- } -> Resolver MUTATION m event value- SubResolver :: {- subChannels :: [StreamChannel event] ,- subResolver :: event -> Resolver QUERY m event value- } -> Resolver SUBSCRIPTION m event value---- GADTResolver Functor-instance Functor m => Functor (Resolver o m e) where- fmap _ (FailedResolver mErrors) = FailedResolver mErrors- fmap f (QueryResolver mResolver) = QueryResolver $ fmap f mResolver- fmap f (MutResolver events mResolver) = MutResolver events $ fmap f mResolver- fmap f (SubResolver events mResolver) = SubResolver events (eventFmap mResolver)- where- eventFmap res event = fmap f (res event)---- GADTResolver Applicative-instance (PureOperation o ,Monad m) => Applicative (Resolver o m e) where- pure = liftEither . pure . pure- -------------------------------------- _ <*> (FailedResolver mErrors) = FailedResolver mErrors- (FailedResolver mErrors) <*> _ = FailedResolver mErrors- -------------------------------------- (QueryResolver f) <*> (QueryResolver res) = QueryResolver (f <*> res)- ---------------------------------------------------------------------- (MutResolver events1 f) <*> (MutResolver events2 res) = MutResolver (events1 <> events2) (f <*> res)- --------------------------------------------------------------- (SubResolver e1 f) <*> (SubResolver e2 res) = SubResolver (e1<>e2) $- \event -> f event <*> res event--instance (Monad m) => Monad (Resolver QUERY m e) where- return = pure- -------------------------------------- (FailedResolver mErrors) >>= _ = FailedResolver mErrors- -------------------------------------- (QueryResolver f) >>= nextM = QueryResolver (f >>= unQueryResolver. nextM)---- Pure Operation-class PureOperation (o::OperationType) where- liftEither :: Monad m => m (Either String a) -> Resolver o m event a- pureGraphQLT :: Monad m => a -> ResolvingStrategy o m event a- eitherGraphQLT :: Monad m => Validation a -> ResolvingStrategy o m event a--instance PureOperation QUERY where- liftEither = QueryResolver . ExceptT- pureGraphQLT = QueryResolving . pure- eitherGraphQLT = QueryResolving . ExceptT . pure--instance PureOperation MUTATION where- liftEither = MutResolver [] . ExceptT- pureGraphQLT = MutationResolving . pure- eitherGraphQLT = MutationResolving . ExceptT . pure--instance PureOperation SUBSCRIPTION where- liftEither = SubResolver [] . const . liftEither- pureGraphQLT = SubscriptionResolving . pure . pure- eitherGraphQLT = SubscriptionResolving . fmap pure . ExceptT . pure---resolveObject :: (Monad m , PureOperation o ) => SelectionSet -> [FieldRes o m e] -> ResolvingStrategy o m e Value-resolveObject selectionSet fieldResolvers = gqlObject <$> traverse selectResolver selectionSet- where- selectResolver (key, selection@Selection { selectionAlias }) = (fromMaybe key selectionAlias,) <$> lookupRes selection- where- lookupRes sel = (fromMaybe (const $ pure gqlNull) $ lookup key fieldResolvers) (key, sel)--class Resolving o m e where- getArgs :: Validation args -> (args -> Resolver o m e value) -> Resolver o m e value- resolving :: Monad m => (value -> (Key,ValidSelection) -> ResolvingStrategy o m e Value) -> Resolver o m e value -> (Key, ValidSelection) -> ResolvingStrategy o m e Value--type FieldRes o m e = (Key, (Key, ValidSelection) -> ResolvingStrategy o m e Value)--instance Resolving o m e where- getArgs (Right x) f = f x- getArgs (Left _) _ = FailedResolver ""- ---------------------------------------------------------------------------------------------------------------------------------------- resolving encode gResolver selection@(fieldName,Selection { selectionPosition }) = __resolving gResolver- where- __resolving (FailedResolver message) = Fail $ resolverError selectionPosition fieldName message- __resolving (QueryResolver res) =- QueryResolving $ withExceptT (resolverError selectionPosition fieldName) res >>= unQueryT . (`encode` selection)- ---------------------------------------------------------------------------------------------------------------------------------------- __resolving (MutResolver events res) =- MutationResolving $ pushEvents events $ withExceptT (resolverError selectionPosition fieldName) (injectEvents [] res) >>= unMutationT . (`encode` selection)- --------------------------------------------------------------------------------------------------------------------------------- __resolving (SubResolver subChannels res) =- SubscriptionResolving $ ExceptT $ StreamT $ pure $ StreamState { streamEvents , streamValue }- where- streamValue = pure $ RecResolver $ \event -> withExceptT (resolverError selectionPosition fieldName) ( unQueryResolver $ res event) >>= unPub event . (`encode` selection)- streamEvents :: [Channel e]- streamEvents = map Channel subChannels--unPub :: Monad m => event -> ResolvingStrategy SUBSCRIPTION m event a -> ResolveT m a-unPub event x = do- func <- unPureSub x- func event--unPureSub :: Monad m => ResolvingStrategy SUBSCRIPTION m event a -> ResolveT m (event -> ResolveT m a)-unPureSub = ExceptT . fmap (fmap unRecResolver . streamValue) . runStreamT . runExceptT . unSubscriptionT--class MapGraphQLT (fromO :: OperationType) (toO :: OperationType) where- mapGraphQLT :: Monad m => ResolvingStrategy fromO m e a -> ResolvingStrategy toO m e a--instance MapGraphQLT fromO fromO where- mapGraphQLT = id--instance MapGraphQLT QUERY SUBSCRIPTION where- mapGraphQLT (QueryResolving x) = SubscriptionResolving $ injectEvents [] (fmap pure x)- mapGraphQLT (Fail x) = Fail x--toResponseRes :: Monad m => ResolvingStrategy o m event Value -> ResponseT m event Value-toResponseRes (Fail errors) = ExceptT $ StreamT $ pure $ StreamState [] $ Left errors-toResponseRes (QueryResolving resT) = ExceptT $ StreamT $ StreamState [] <$> runExceptT resT-toResponseRes (MutationResolving resT) = ExceptT $ mapS Publish (runExceptT resT)-toResponseRes (SubscriptionResolving resT) =- ExceptT $ StreamT $ handleActions <$> closeStream (runExceptT resT)- where- handleActions (_, Left gqlError) = StreamState [] (Left gqlError)- handleActions (channels, Right subResolver) =- StreamState [Subscribe $ Event channels handleRes] (Right gqlNull)- where- handleRes event = renderResponse <$> runExceptT (unRecResolver subResolver event)--type family UnSubResolver (a :: * -> *) :: (* -> *)--type instance UnSubResolver (Resolver SUBSCRIPTION m e) = Resolver QUERY m e------------------------------------------------------------------------ | GraphQL Root resolver, also the interpreter generates a GQL schema from it.--- 'queryResolver' is required, 'mutationResolver' and 'subscriptionResolver' are optional,--- if your schema does not supports __mutation__ or __subscription__ , you acn use __()__ for it.-data GQLRootResolver (m :: * -> *) event (query :: (* -> *) -> * ) (mut :: (* -> *) -> * ) (sub :: (* -> *) -> * ) = GQLRootResolver- { queryResolver :: query (Resolver QUERY m event)- , mutationResolver :: mut (Resolver MUTATION m event)- , subscriptionResolver :: sub (Resolver SUBSCRIPTION m event)- }
+ src/Data/Morpheus/Types/Internal/Resolving.hs view
@@ -0,0 +1,34 @@+module Data.Morpheus.Types.Internal.Resolving+ ( Event(..)+ , GQLRootResolver(..)+ , UnSubResolver+ , Resolver(..)+ , ResolvingStrategy(..)+ , MapStrategy(..)+ , LiftEither(..)+ , resolveObject+ , toResponseRes+ , withObject+ , resolving+ , toResolver+ , lift+ , SubEvent+ , Validation+ , Failure(..)+ , GQLChannel(..)+ , ResponseEvent(..)+ , ResponseStream+ , cleanEvents+ , Result(..)+ , ResultT(..)+ , unpackEvents+ , LibUpdater+ , resolveUpdates+ , GQLErrors+ , GQLError(..)+ , Position+ )+where++import Data.Morpheus.Types.Internal.Resolving.Resolver+import Data.Morpheus.Types.Internal.Resolving.Core
+ src/Data/Morpheus/Types/Internal/Resolving/Core.hs view
@@ -0,0 +1,160 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE NamedFieldPuns #-}++module Data.Morpheus.Types.Internal.Resolving.Core+ ( GQLError(..)+ , Position(..)+ , GQLErrors+ , Validation+ , Result(..)+ , Failure(..)+ , ResultT(..)+ , fromEither+ , fromEitherSingle+ , unpackEvents+ , LibUpdater+ , resolveUpdates+ , mapEvent+ , mapFailure+ , cleanEvents+ , StatelessResT+ )+where++import Control.Monad ( foldM )+import Data.Function ( (&) )+import Control.Monad.Trans.Class ( MonadTrans(..) )+import Control.Applicative ( liftA2 )+import Data.Aeson ( FromJSON+ , ToJSON+ )+import Data.Morpheus.Types.Internal.AST.Base+ ( Position(..) )+import Data.Text ( Text+ , pack+ )+import GHC.Generics ( Generic )+import Data.Semigroup ( (<>) )+++class Applicative f => Failure error (f :: * -> *) where+ failure :: error -> f v++instance Failure error (Either error) where+ failure = Left++data GQLError = GQLError+ { message :: Text+ , locations :: [Position]+ } deriving (Show, Generic, FromJSON, ToJSON)++type GQLErrors = [GQLError]++type StatelessResT = ResultT () GQLError 'True+type Validation = Result () GQLError 'True++--+-- Result+--+--+data Result events error (concurency :: Bool) a =+ Success { result :: a , warnings :: [error] , events:: [events] }+ | Failure [error] deriving (Functor)++instance Applicative (Result e cocnurency error) where+ pure x = Success x [] []+ Success f w1 e1 <*> Success x w2 e2 = Success (f x) (w1 <> w2) (e1 <> e2)+ Failure e1 <*> Failure e2 = Failure (e1 <> e2)+ Failure e <*> Success _ w _ = Failure (e <> w)+ Success _ w _ <*> Failure e = Failure (e <> w)++instance Monad (Result e cocnurency error) where+ return = pure+ Success v w1 e1 >>= fm = case fm v of+ (Success x w2 e2) -> Success x (w1 <> w2) (e1 <> e2)+ (Failure e ) -> Failure (e <> w1)+ Failure e >>= _ = Failure e++instance Failure [error] (Result ev error con) where+ failure = Failure++unpackEvents :: Result event c e a -> [event]+unpackEvents Success { events } = events+unpackEvents _ = []++fromEither :: Either [er] a -> Result ev er co a+fromEither (Left e) = Failure e+fromEither (Right a) = Success a [] []++fromEitherSingle :: Either er a -> Result ev er co a+fromEitherSingle (Left e) = Failure [e]+fromEitherSingle (Right a) = Success a [] []++-- ResultT+newtype ResultT event error (concurency :: Bool) (m :: * -> * ) a = ResultT { runResultT :: m (Result event error concurency a ) }+ deriving (Functor)++instance Applicative m => Applicative (ResultT event error concurency m) where+ pure = ResultT . pure . pure+ ResultT app1 <*> ResultT app2 = ResultT $ liftA2 (<*>) app1 app2++instance Monad m => Monad (ResultT event error concurency m) where+ return = pure+ (ResultT m1) >>= mFunc = ResultT $ do+ result1 <- m1+ case result1 of+ Failure errors -> pure $ Failure errors+ Success value1 w1 e1 -> do+ result2 <- runResultT (mFunc value1)+ case result2 of+ Failure errors -> pure $ Failure (errors <> w1)+ Success v2 w2 e2 -> return $ Success v2 (w1 <> w2) (e1 <> e2)++instance MonadTrans (ResultT event error concurency) where+ lift = ResultT . fmap pure++instance Applicative m => Failure String (ResultT ev GQLError con m) where+ failure x =+ ResultT $ pure $ Failure [GQLError { message = pack x, locations = [] }]++cleanEvents+ :: Functor m+ => ResultT e1 error concurency m a+ -> ResultT e2 error concurency m a+cleanEvents resT = ResultT $ replace <$> runResultT resT+ where+ replace (Success v w _) = Success v w []+ replace (Failure e ) = Failure e++mapEvent+ :: Monad m+ => (ea -> eb)+ -> ResultT ea er con m value+ -> ResultT eb er con m value+mapEvent func (ResultT ma) = ResultT $ do+ state <- ma+ return $ state { events = map func (events state) }++mapFailure+ :: Monad m+ => (er1 -> er2)+ -> ResultT ev er1 con m value+ -> ResultT ev er2 con m value+mapFailure f (ResultT ma) = ResultT $ do+ state <- ma+ case state of+ Failure x -> pure $ Failure (map f x)+ Success x w e -> pure $ Success x (map f w) e+++-- Helper Functions+type LibUpdater lib = lib -> Validation lib++resolveUpdates :: lib -> [LibUpdater lib] -> Validation lib+resolveUpdates = foldM (&)
+ src/Data/Morpheus/Types/Internal/Resolving/Resolver.hs view
@@ -0,0 +1,382 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}++module Data.Morpheus.Types.Internal.Resolving.Resolver+ ( + Event(..)+ , GQLRootResolver(..)+ , UnSubResolver+ , Resolver(..)+ , ResolvingStrategy(..)+ , MapStrategy(..)+ , LiftEither(..)+ , resolveObject+ , toResponseRes+ , withObject+ , resolving+ , toResolver+ , lift+ , SubEvent+ , GQLChannel(..)+ , ResponseEvent(..)+ , ResponseStream+ )+where++import Control.Monad.Trans.Class ( MonadTrans(..) )+import Data.Maybe ( fromMaybe )+import Data.Semigroup ( (<>) )+import Data.Text ( unpack+ , pack+ )++-- MORPHEUS+import Data.Morpheus.Error.Selection ( resolvingFailedError+ , subfieldsNotSelected+ )+import Data.Morpheus.Types.Internal.AST.Selection+ ( Selection(..)+ , SelectionRec(..)+ , SelectionSet+ , ValidSelection+ )+import Data.Morpheus.Types.Internal.AST.Base+ ( Message , Key )+import Data.Morpheus.Types.Internal.AST.Data+ ( + MUTATION+ , OperationType+ , QUERY+ , SUBSCRIPTION+ )+import Data.Morpheus.Types.Internal.Resolving.Core+ ( GQLErrors+ , GQLError+ , Validation+ , Result(..)+ , Failure(..)+ , ResultT(..)+ , fromEither+ , fromEitherSingle+ , cleanEvents+ , mapFailure+ , mapEvent+ , StatelessResT+ )+import Data.Morpheus.Types.Internal.AST.Value+ ( GQLValue(..)+ , Value+ )+import Data.Morpheus.Types.IO (renderResponse, GQLResponse)+-- MORPHEUS++class LiftEither (o::OperationType) res where+ type ResError res :: *+ liftEither :: Monad m => m (Either (ResError res) a) -> res o event m a++type ResponseStream event m = ResultT (ResponseEvent m event) GQLError 'True m++data ResponseEvent m event+ = Publish event+ | Subscribe (SubEvent m event)++type SubEvent m event = Event (Channel event) (event -> m GQLResponse)++----------------------------------------------------------------------------------------+-- Recursive Resolver+newtype RecResolver m a b = RecResolver {+ unRecResolver :: a -> StatelessResT m b+}++instance Functor m => Functor (RecResolver m a) where+ fmap f (RecResolver x) = RecResolver recX where recX event = f <$> x event++instance Monad m => Applicative (RecResolver m a) where+ pure = RecResolver . const . pure+ (RecResolver f) <*> (RecResolver res) = RecResolver recX+ where recX event = f event <*> res event++instance Monad m => Monad (RecResolver m a) where+ (RecResolver x) >>= next = RecResolver recX+ where recX event = x event >>= (\v -> v event) . unRecResolver . next+------------------------------------------------------------++--- GraphQLT+data ResolvingStrategy (o::OperationType) event (m:: * -> *) value where+ ResolveQ ::{ unResolveQ :: ResultT () GQLError 'True m value } -> ResolvingStrategy QUERY event m value+ ResolveM ::{ unResolveM :: ResultT event GQLError 'True m value } -> ResolvingStrategy MUTATION event m value+ ResolveS ::{ unResolveS :: ResultT (Channel event) GQLError 'True m (RecResolver m event value) } -> ResolvingStrategy SUBSCRIPTION event m value++-- Functor+instance Monad m => Functor (ResolvingStrategy o e m) where+ fmap f (ResolveQ res) = ResolveQ $ f <$> res+ fmap f (ResolveM res) = ResolveM $ f <$> res+ fmap f (ResolveS res) = ResolveS $ (<$>) f <$> res++-- Applicative+instance (LiftEither o ResolvingStrategy, Monad m) => Applicative (ResolvingStrategy o e m) where+ pure = liftEither . pure . pure+ -------------------------------------+ (ResolveQ f) <*> (ResolveQ res) = ResolveQ (f <*> res)+ ------------------------------------------------------------------------+ (ResolveM f) <*> (ResolveM res) = ResolveM (f <*> res)+ --------------------------------------------------------------+ (ResolveS f) <*> (ResolveS res) = ResolveS $ (<*>) <$> f <*> res++-- LiftEither+instance LiftEither QUERY ResolvingStrategy where+ type ResError ResolvingStrategy = GQLErrors+ liftEither = ResolveQ . ResultT . fmap fromEither++instance LiftEither MUTATION ResolvingStrategy where+ type ResError ResolvingStrategy = GQLErrors+ liftEither = ResolveM . ResultT . fmap fromEither++instance LiftEither SUBSCRIPTION ResolvingStrategy where+ type ResError ResolvingStrategy = GQLErrors+ liftEither = ResolveS . ResultT . fmap (fromEither . fmap pure)++ -- Failure+instance (LiftEither o ResolvingStrategy, Monad m) => Failure GQLErrors (ResolvingStrategy o e m) where+ failure = liftEither . pure . Left++-- helper functins+withObject+ :: (LiftEither o ResolvingStrategy, Monad m)+ => (SelectionSet -> ResolvingStrategy o e m value)+ -> (Key, ValidSelection)+ -> ResolvingStrategy o e m value+withObject f (_, Selection { selectionRec = SelectionSet selection }) =+ f selection+withObject _ (key, Selection { selectionPosition }) =+ failure (subfieldsNotSelected key "" selectionPosition)++resolveObject+ :: (Monad m, LiftEither o ResolvingStrategy)+ => SelectionSet+ -> [FieldRes o e m]+ -> ResolvingStrategy o e m Value+resolveObject selectionSet fieldResolvers =+ gqlObject <$> traverse selectResolver selectionSet+ where+ selectResolver (key, selection@Selection { selectionAlias }) =+ (fromMaybe key selectionAlias, ) <$> lookupRes selection+ where+ lookupRes sel =+ (fromMaybe (const $ pure gqlNull) $ lookup key fieldResolvers) (key, sel)++toResponseRes+ :: Monad m+ => ResolvingStrategy o event m Value+ -> ResponseStream event m Value+toResponseRes (ResolveQ resT) = cleanEvents resT+toResponseRes (ResolveM resT) = mapEvent Publish resT+toResponseRes (ResolveS resT) = ResultT $ handleActions <$> runResultT resT+ where+ handleActions (Failure gqlError ) = Failure gqlError+ handleActions (Success subRes warnings channels) = Success+ { result = gqlNull+ , warnings+ , events = [Subscribe $ Event channels eventResolver]+ }+ where+ eventResolver event = renderResponse <$> runResultT (unRecResolver subRes event) ++--+-- +-- GraphQL Field Resolver+--+--+--ResultT GQLError 'True m (RecResolver m event value)+---------------------------------------------------------------+data Resolver (o::OperationType) event (m :: * -> * ) value where+ QueryResolver::{ unQueryResolver :: ResultT () String 'True m value } -> Resolver QUERY event m value+ MutResolver ::{ unMutResolver :: ResultT event String 'True m ([event],value) } -> Resolver MUTATION event m value+ SubResolver ::{+ subChannels :: [StreamChannel event] ,+ subResolver :: event -> Resolver QUERY event m value+ } -> Resolver SUBSCRIPTION event m value++-- Functor+instance Functor m => Functor (Resolver o e m) where+ fmap f (QueryResolver res) = QueryResolver $ fmap f res+ fmap f (MutResolver res) = MutResolver $ tupleMap <$> res+ where tupleMap (e, v) = (e, f v)+ fmap f (SubResolver events mResolver) = SubResolver events+ (eventFmap mResolver)+ where eventFmap res event = fmap f (res event)++-- Applicative+instance (LiftEither o Resolver ,Monad m) => Applicative (Resolver o e m) where+ pure = liftEither . pure . pure+ -------------------------------------+ (QueryResolver f) <*> (QueryResolver res) = QueryResolver (f <*> res)+ ---------------------------------------------------------------------+ MutResolver res1 <*> MutResolver res2 = MutResolver $ join <$> res1 <*> res2+ where join (e1, f) (e2, v) = (e1 <> e2, f v)+ --------------------------------------------------------------+ (SubResolver e1 f) <*> (SubResolver e2 res) = SubResolver (e1 <> e2) subRes+ where subRes event = f event <*> res event++-- Monad +instance (Monad m) => Monad (Resolver QUERY e m) where+ return = pure+ -----------------------------------------------------+ (QueryResolver f) >>= nextM = QueryResolver (f >>= unQueryResolver . nextM)++instance (Monad m) => Monad (Resolver MUTATION e m) where+ return = pure+ -----------------------------------------------------+ (MutResolver m1) >>= mFunc = MutResolver $ do+ (e1, v1) <- m1+ (e2, v2) <- unMutResolver $ mFunc v1+ pure (e1 <> e2, v2)++-- Monad Transformers +instance MonadTrans (Resolver QUERY e) where+ lift = liftEither . fmap pure++instance MonadTrans (Resolver MUTATION e) where+ lift = liftEither . fmap pure++-- LiftEither+instance LiftEither QUERY Resolver where+ type ResError Resolver = String+ liftEither = QueryResolver . ResultT . fmap fromEitherSingle++instance LiftEither MUTATION Resolver where+ type ResError Resolver = String+ liftEither = MutResolver . ResultT . fmap (fromEitherSingle . fmap ([], ))++instance LiftEither SUBSCRIPTION Resolver where+ type ResError Resolver = String+ liftEither = SubResolver [] . const . liftEither++-- Failure+instance (LiftEither o Resolver, Monad m) => Failure Message (Resolver o e m) where+ failure = liftEither . pure . Left . unpack++-- Type Helpers +type family UnSubResolver (a :: * -> *) :: (* -> *)++type instance UnSubResolver (Resolver SUBSCRIPTION m e) = Resolver QUERY m e+++-- RESOLVING+type FieldRes o e m+ = (Key, (Key, ValidSelection) -> ResolvingStrategy o e m Value)++toResolver+ :: (LiftEither o Resolver, Monad m)+ => Validation a+ -> (a -> Resolver o e m b)+ -> Resolver o e m b+toResolver Success { result } f = f result+toResolver (Failure errors) _ =+ failure ("TODO: errors" <> pack (show errors) :: Message)++resolving+ :: forall o e m value+ . Monad m+ => (value -> (Key, ValidSelection) -> ResolvingStrategy o e m Value)+ -> Resolver o e m value+ -> (Key, ValidSelection)+ -> ResolvingStrategy o e m Value+resolving encode gResolver selection@(fieldName, Selection { selectionPosition })+ = _resolve gResolver+ where+ convert :: Monad m => ResultT ev String con m a -> ResultT ev GQLError con m a+ convert =+ mapFailure (resolvingFailedError selectionPosition fieldName . pack)+ ------------------------------+ _encode = (`encode` selection)+ -------------------------------------------------------------------+ _resolve (QueryResolver res) =+ ResolveQ $ convert res >>= unResolveQ . _encode+ ---------------------------------------------------------------------------------------------------------------------------------------+ _resolve (MutResolver res) =+ ResolveM $ replace (convert res) >>= unResolveM . _encode+ where+ replace (ResultT mx) = ResultT $ do+ value <- mx+ pure $ case value of+ Success { warnings, events, result = (events2, result) } ->+ Success { result, warnings, events = events <> events2 }+ Failure x -> Failure x+ --------------------------------------------------------------------------------------------------------------------------------+ _resolve (SubResolver subChannels res) = ResolveS $ ResultT $ pure $ Success+ { events = map Channel subChannels+ , result = RecResolver eventResolver+ , warnings = []+ }+ where+ eventResolver :: e -> StatelessResT m Value+ eventResolver event =+ convert (unQueryResolver $ res event) >>= unPureSub . _encode+ where+ unPureSub+ :: Monad m+ => ResolvingStrategy SUBSCRIPTION e m Value+ -> StatelessResT m Value+ unPureSub (ResolveS x) = cleanEvents x >>= passEvent+ where+ passEvent (RecResolver f) = f event++-- map Resolving strategies +class MapStrategy (from :: OperationType) (to :: OperationType) where+ mapStrategy :: Monad m => ResolvingStrategy from e m a -> ResolvingStrategy to e m a++instance MapStrategy o o where+ mapStrategy = id++instance MapStrategy QUERY SUBSCRIPTION where+ mapStrategy (ResolveQ x) = ResolveS $ pure <$> cleanEvents x++-------------------------------------------------------------------+-- | GraphQL Root resolver, also the interpreter generates a GQL schema from it.+-- 'queryResolver' is required, 'mutationResolver' and 'subscriptionResolver' are optional,+-- if your schema does not supports __mutation__ or __subscription__ , you acn use __()__ for it.+data GQLRootResolver (m :: * -> *) event (query :: (* -> *) -> * ) (mut :: (* -> *) -> * ) (sub :: (* -> *) -> * ) = GQLRootResolver+ { queryResolver :: query (Resolver QUERY event m)+ , mutationResolver :: mut (Resolver MUTATION event m)+ , subscriptionResolver :: sub (Resolver SUBSCRIPTION event m)+ }++ -- EVENTS+++-- 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}
− src/Data/Morpheus/Types/Internal/Stream.hs
@@ -1,117 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE UndecidableInstances #-}--module Data.Morpheus.Types.Internal.Stream- ( StreamState(..)- , ResponseEvent(..)- , SubEvent- , Event(..)- -- STREAMS- , StreamT(..)- , ResponseStream- , closeStream- , mapS- , injectEvents- , initExceptStream- , pushEvents- -- , GQLMonad(..)- , GQLChannel(..)- , Channel(..)- ) where--import Control.Monad.Trans.Except (ExceptT (..), runExceptT)-import Data.Semigroup ((<>))---- MORPHEUS-import Data.Morpheus.Types.IO (GQLResponse)---- EVENTS-data ResponseEvent m event- = Publish event- | Subscribe (SubEvent m event)---- STREAMS-type ResponseStream m event = StreamT m (ResponseEvent m event)--type SubEvent m event = Event (Channel event) (event-> m GQLResponse)--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- }--data StreamState c v = StreamState- { streamEvents :: [c]- , streamValue :: v- } deriving (Functor)---- | Monad Transformer that sums all effect Together-newtype StreamT m s a = StreamT- { runStreamT :: m (StreamState s a)- } deriving (Functor)--instance Monad m => Applicative (StreamT m c) where- pure = StreamT . return . StreamState []- StreamT app1 <*> StreamT app2 =- StreamT $ do- (StreamState effect1 func) <- app1- (StreamState effect2 val) <- app2- return $ StreamState (effect1 ++ effect2) (func val)--instance Monad m => Monad (StreamT m c) where- return = pure- (StreamT m1) >>= mFunc =- StreamT $ do- (StreamState e1 v1) <- m1- (StreamState e2 v2) <- runStreamT $ mFunc v1- return $ StreamState (e1 ++ e2) v2---- Helper Functions-toTuple :: StreamState s a -> ([s], a)-toTuple StreamState {streamEvents, streamValue} = (streamEvents, streamValue)--closeStream :: Monad m => (StreamT m s) v -> m ([s], v)-closeStream resolver = toTuple <$> runStreamT resolver--mapS :: Monad m => (a -> b) -> StreamT m a value -> StreamT m b value-mapS func (StreamT ma) =- StreamT $ do- state <- ma- return $ state {streamEvents = map func (streamEvents state)}--pushEvents :: Functor m => [event] -> ExceptT e (StreamT m event) a -> ExceptT e (StreamT m event) a-pushEvents events = ExceptT . StreamT . fmap updateState . runStreamT . runExceptT- where- updateState x = x { streamEvents = events <> streamEvents x }--injectEvents :: Functor m => [event] -> ExceptT e m a -> ExceptT e (StreamT m event) a-injectEvents states = ExceptT . StreamT . fmap (StreamState states) . runExceptT--initExceptStream :: Applicative m => [event] -> a -> ExceptT e (StreamT m event) a-initExceptStream events = ExceptT . StreamT . pure . StreamState events . Right
src/Data/Morpheus/Types/Internal/TH.hs view
@@ -1,26 +1,10 @@-{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE CPP #-} module Data.Morpheus.Types.Internal.TH where -import Data.Text (Text, pack, unpack) import Language.Haskell.TH-import Language.Haskell.TH.Syntax -liftMaybeText :: Maybe Text -> ExpQ-liftMaybeText (Just x) = appE (conE 'Just) (liftText x)-liftMaybeText Nothing = conE 'Nothing--liftText :: Text -> ExpQ-liftText x = appE (varE 'pack) (lift (unpack x))--liftTextTuple :: Lift a => (Text, a) -> ExpQ-liftTextTuple (name, x) = tupE [liftText name, lift x]--liftTextMap :: Lift a => [(Text, a)] -> ExpQ-liftTextMap = listE . map liftTextTuple- apply :: Name -> [Q Exp] -> Q Exp apply n = foldl appE (conE n) @@ -33,11 +17,15 @@ instanceHeadT :: Name -> String -> [String] -> Q Type instanceHeadT cName iType tArgs = applyT cName [applyT (mkName iType) (map (varT . mkName) tArgs)] -instanceFunD :: Name -> [String] -> Q Exp -> Q Dec+instanceProxyFunD :: (Name,ExpQ) -> DecQ+instanceProxyFunD (name,body) = instanceFunD name ["_"] body++instanceFunD :: Name -> [String] -> ExpQ -> Q Dec instanceFunD name args body = funD name [clause (map (varP . mkName) args) (normalB body) []] instanceHeadMultiT :: Name -> Q Type -> [Q Type] -> Q Type instanceHeadMultiT className iType li = applyT className (iType : li)+ -- "User" -> ["name","id"] -> (User name id) destructRecord :: String -> [String] -> PatQ
− src/Data/Morpheus/Types/Internal/Validation.hs
@@ -1,34 +0,0 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}--module Data.Morpheus.Types.Internal.Validation- ( GQLError(..)- , Location(..)- , GQLErrors- , JSONError(..)- , Validation- , ResolveValue- ) where--import Control.Monad.Trans.Except (ExceptT (..))-import Data.Aeson (FromJSON, ToJSON)-import Data.Morpheus.Types.Internal.Base (Location (..))-import Data.Morpheus.Types.Internal.Value (Value)-import Data.Text (Text)-import GHC.Generics (Generic)--data GQLError = GQLError- { desc :: Text- , positions :: [Location]- } deriving (Show)--type GQLErrors = [GQLError]--data JSONError = JSONError- { message :: Text- , locations :: [Location]- } deriving (Show, Generic, FromJSON, ToJSON)--type Validation = Either GQLErrors--type ResolveValue m = ExceptT GQLErrors m Value
− src/Data/Morpheus/Types/Internal/Value.hs
@@ -1,195 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DeriveLift #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TupleSections #-}--module Data.Morpheus.Types.Internal.Value- ( Value(..)- , ScalarValue(..)- , Object- , GQLValue(..)- , replaceValue- , decodeScientific- , convertToJSONName- , convertToHaskellName- ) where--import qualified Data.Aeson as A (FromJSON (..), ToJSON (..), Value (..), object, pairs, (.=))-import Data.Function ((&))-import qualified Data.HashMap.Strict as M (toList)-import Data.Scientific (Scientific, floatingOrInteger)-import Data.Semigroup ((<>))-import Data.Text (Text)-import qualified Data.Text as T-import qualified Data.Vector as V (toList)-import GHC.Generics (Generic)-import Language.Haskell.TH-import Language.Haskell.TH.Syntax---- MORPHEUS-import Data.Morpheus.Types.Internal.TH (apply, liftText, liftTextMap)--isReserved :: Text -> Bool-isReserved "case" = True-isReserved "class" = True-isReserved "data" = True-isReserved "default" = True-isReserved "deriving" = True-isReserved "do" = True-isReserved "else" = True-isReserved "foreign" = True-isReserved "if" = True-isReserved "import" = True-isReserved "in" = True-isReserved "infix" = True-isReserved "infixl" = True-isReserved "infixr" = True-isReserved "instance" = True-isReserved "let" = True-isReserved "module" = True-isReserved "newtype" = True-isReserved "of" = True-isReserved "then" = True-isReserved "type" = True-isReserved "where" = True-isReserved "_" = True-isReserved _ = False--{-# INLINE isReserved #-}-convertToJSONName :: Text -> Text-convertToJSONName hsName- | not (T.null hsName) && isReserved name && (T.last hsName == '\'') = name- | otherwise = hsName- where- name = T.init hsName--convertToHaskellName :: Text -> Text-convertToHaskellName name- | isReserved name = name <> "'"- | otherwise = name---- | Primitive Values for GQLScalar: 'Int', 'Float', 'String', 'Boolean'.--- for performance reason type 'Text' represents GraphQl 'String' value-data ScalarValue- = Int Int- | Float Float- | String Text- | Boolean Bool- deriving (Show, Generic)--instance Lift ScalarValue where- lift (String n) = apply 'String [liftText n]- lift (Int n) = apply 'Int [lift n]- lift (Float n) = apply 'Float [lift n]- lift (Boolean n) = apply 'Boolean [lift n]--instance A.ToJSON ScalarValue where- toJSON (Float x) = A.toJSON x- toJSON (Int x) = A.toJSON x- toJSON (Boolean x) = A.toJSON x- toJSON (String x) = A.toJSON x--instance A.FromJSON ScalarValue where- parseJSON (A.Bool v) = pure $ Boolean v- parseJSON (A.Number v) = pure $ decodeScientific v- parseJSON (A.String v) = pure $ String v- parseJSON notScalar = fail $ "Expected Scalar got :" <> show notScalar--instance Lift Value where- lift (Object ls) = apply 'Object [liftTextMap ls]- lift (List n) = apply 'List [lift n]- lift (Enum n) = apply 'Enum [liftText n]- lift (Scalar n) = apply 'Scalar [lift n]- lift Null = varE 'Null--type Object = [(Text, Value)]--data Value- = Object Object- | List [Value]- | Enum Text- | Scalar ScalarValue- | Null- deriving (Show, Generic)--instance A.ToJSON Value where- toEncoding Null = A.toEncoding A.Null- toEncoding (Enum x) = A.toEncoding x- toEncoding (List x) = A.toEncoding x- toEncoding (Scalar x) = A.toEncoding x- toEncoding (Object []) = A.toEncoding $ A.object []- toEncoding (Object x) = A.pairs $ foldl1 (<>) $ map encodeField x- where- encodeField (key, value) = convertToJSONName key A..= value--decodeScientific :: Scientific -> ScalarValue-decodeScientific v =- case floatingOrInteger v of- Left float -> Float float- Right int -> Int int--replaceValue :: A.Value -> Value-replaceValue (A.Bool v) = gqlBoolean v-replaceValue (A.Number v) = Scalar $ decodeScientific v-replaceValue (A.String v) = gqlString v-replaceValue (A.Object v) = gqlObject $ map replace (M.toList v)- where- replace :: (a, A.Value) -> (a, Value)- replace (key, val) = (key, replaceValue val)-replaceValue (A.Array li) = gqlList (map replaceValue (V.toList li))-replaceValue A.Null = gqlNull--instance A.FromJSON Value where- parseJSON = pure . replaceValue---- DEFAULT VALUES-class GQLValue a where- gqlNull :: a- gqlScalar :: ScalarValue -> a- gqlBoolean :: Bool -> a- gqlString :: Text -> a- gqlList :: [a] -> a- gqlObject :: [(Text, a)] -> a---- build GQL Values for Subscription Resolver-instance GQLValue Value where- gqlNull = Null- gqlScalar = Scalar- gqlBoolean = Scalar . Boolean- gqlString = Scalar . String- gqlList = List- gqlObject = Object--instance Monad m => GQLValue (m Value) where- gqlNull = pure gqlNull- gqlScalar = pure . gqlScalar- gqlBoolean = pure . gqlBoolean- gqlString = pure . gqlString- ------------------------------------------ -- listValue :: [m Value] -> m Value- gqlList = fmap gqlList . sequence- ------------------------------------------ -- objectValue :: [(Text, m Value )] -> m Value- gqlObject = fmap gqlObject . traverse keyVal- where- keyVal :: Monad m => (Text, m Value) -> m (Text, Value)- keyVal (key, valFunc) = (key, ) <$> valFunc---- build GQL Values for Subscription Resolver-instance Monad m => GQLValue (args -> m Value) where- gqlNull = const gqlNull- gqlScalar = const . gqlScalar- gqlBoolean = pure . gqlBoolean- gqlString = const . gqlString- ----------------------------------------- -- listValue :: [args -> m Value] -> ( args -> m Value )- gqlList res args = gqlList <$> traverse (args &) res- ----------------------------------------- -- objectValue :: [(Text, args -> m Value )] -> ( args -> m Value )- gqlObject res args = gqlObject <$> traverse keyVal res- where- keyVal :: Monad m => (Text, args -> m Value) -> m (Text, Value)- keyVal (key, valFunc) = (key, ) <$> valFunc args
src/Data/Morpheus/Types/Internal/WebSocket.hs view
@@ -4,15 +4,17 @@ ( GQLClient(..) , ClientID , ClientSession(..)- ) where+ )+where -import Data.Semigroup ((<>))-import Data.Text (Text)-import Data.UUID (UUID)-import Network.WebSockets (Connection)+import Data.Semigroup ( (<>) )+import Data.Text ( Text )+import Data.UUID ( UUID )+import Network.WebSockets ( Connection ) -- MORPHEUS-import Data.Morpheus.Types.Internal.Stream (SubEvent)+import Data.Morpheus.Types.Internal.Resolving+ ( SubEvent ) type ClientID = UUID @@ -23,7 +25,7 @@ } instance (Show e) => Show (ClientSession m e ) where- show ClientSession {sessionId} =+ show ClientSession { sessionId } = "GQLSession { id: " <> show sessionId <> ", sessions: " <> "" <> " }" data GQLClient m e =@@ -34,6 +36,9 @@ } instance (Show e) => Show (GQLClient m e) where- show GQLClient {clientID, clientSessions} =- "GQLClient {id:" <> show clientID <> ", sessions:" <> show clientSessions <>- "}"+ show GQLClient { clientID, clientSessions } =+ "GQLClient {id:"+ <> show clientID+ <> ", sessions:"+ <> show clientSessions+ <> "}"
src/Data/Morpheus/Types/Types.hs view
@@ -1,33 +1,46 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE KindSignatures #-}-{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE KindSignatures #-} module Data.Morpheus.Types.Types- ( GQLQueryRoot(..)- , Variables- , Undefined(..)- ) where--import Data.Map (Map)-import GHC.Generics (Generic)-import Language.Haskell.TH.Syntax (Lift (..))+ ( Undefined(..)+ , Pair(..)+ , MapKind(..)+ , MapArgs(..)+ , mapKindFromList+ )+where --- Morpheus-import Data.Morpheus.Types.Internal.AST.Operation (RawOperation)-import Data.Morpheus.Types.Internal.AST.RawSelection (FragmentLib)-import Data.Morpheus.Types.Internal.Base (Key)-import Data.Morpheus.Types.Internal.TH (apply, liftTextMap)-import Data.Morpheus.Types.Internal.Value (Value)+import GHC.Generics ( Generic ) data Undefined (m :: * -> *) = Undefined deriving (Show, Generic) -type Variables = Map Key Value+data Pair k v =+ Pair+ { key :: k+ , value :: v+ }+ deriving (Generic) -data GQLQueryRoot = GQLQueryRoot- { fragments :: FragmentLib- , operation :: RawOperation- , inputVariables :: [(Key, Value)]- } deriving (Show)+newtype MapArgs k =+ MapArgs+ { oneOf :: Maybe [k]+ }+ deriving (Generic) -instance Lift GQLQueryRoot where- lift (GQLQueryRoot f o v) = apply 'GQLQueryRoot [liftTextMap f, lift o, liftTextMap v]+data MapKind k v m =+ MapKind+ { size :: Int+ , pairs :: MapArgs k -> m [Pair k v]+ }+ deriving (Generic)++mapKindFromList :: (Eq k, Applicative m) => [(k, v)] -> MapKind k v m+mapKindFromList inputPairs = MapKind { size = length inputPairs+ , pairs = resolvePairs+ }+ where+ filterBy MapArgs { oneOf = Just list } =+ filter ((`elem` list) . fst) inputPairs+ filterBy _ = inputPairs+ resolvePairs = pure . (map toGQLTuple . filterBy)+ toGQLTuple (x, y) = Pair x y
src/Data/Morpheus/Validation/Document/Validation.hs view
@@ -2,52 +2,76 @@ module Data.Morpheus.Validation.Document.Validation ( validatePartialDocument- ) where+ )+where import Data.Maybe -- -- Morpheus-import Data.Morpheus.Error.Document.Interface (ImplementsError (..), partialImplements, unknownInterface)-import Data.Morpheus.Rendering.RenderGQL (RenderGQL (..))-import Data.Morpheus.Types.Internal.Data (DataField (..), DataType (..), DataObject, DataTyCon (..),- Key, RawDataType (..), TypeAlias (..), isWeaker, isWeaker)-import Data.Morpheus.Types.Internal.Validation (Validation)+import Data.Morpheus.Error.Document.Interface+ ( ImplementsError(..)+ , partialImplements+ , unknownInterface+ )+import Data.Morpheus.Rendering.RenderGQL+ ( RenderGQL(..) )+import Data.Morpheus.Types.Internal.AST+ ( DataField(..)+ , DataType(..)+ , DataObject+ , DataTyCon(..)+ , Key+ , RawDataType(..)+ , TypeAlias(..)+ , isWeaker+ , isWeaker+ )+import Data.Morpheus.Types.Internal.Resolving+ ( Validation+ , Failure(..)+ ) validatePartialDocument :: [(Key, RawDataType)] -> Validation [(Key, DataType)] validatePartialDocument lib = catMaybes <$> traverse validateType lib- where- validateType :: (Key, RawDataType) -> Validation (Maybe (Key, DataType))- validateType (name, FinalDataType x) = pure $ Just (name, x)- validateType (name, Implements interfaces object) = asTuple name <$> object `mustImplement` interfaces- validateType _ = pure Nothing- ------------------------------------ asTuple name x = Just (name, x)- ------------------------------------ mustImplement :: DataObject -> [Key] -> Validation DataType- mustImplement object interfaceKey = do- interface <- traverse getInterfaceByKey interfaceKey- case concatMap (mustBeSubset object) interface of- [] -> pure $ DataObject object- errors -> Left $ partialImplements (typeName object) errors- -------------------------------- mustBeSubset :: DataObject -> DataObject -> [(Key, Key, ImplementsError)]- mustBeSubset DataTyCon {typeData = objFields} DataTyCon {typeName, typeData = interfaceFields} =- concatMap checkField interfaceFields- where- checkField :: (Key, DataField) -> [(Key, Key, ImplementsError)]- checkField (key, DataField {fieldType = interfaceT@TypeAlias { aliasTyCon = interfaceTypeName- , aliasWrappers = interfaceWrappers- }}) =- case lookup key objFields of- Just DataField {fieldType = objT@TypeAlias {aliasTyCon, aliasWrappers}}- | aliasTyCon == interfaceTypeName && not (isWeaker aliasWrappers interfaceWrappers) -> []- | otherwise ->- [(typeName, key, UnexpectedType {expectedType = render interfaceT, foundType = render objT})]- Nothing -> [(typeName, key, UndefinedField)]- -------------------------------- getInterfaceByKey :: Key -> Validation DataObject- getInterfaceByKey key =- case lookup key lib of- Just (Interface x) -> pure x- _ -> Left $ unknownInterface key+ where+ validateType :: (Key, RawDataType) -> Validation (Maybe (Key, DataType))+ validateType (name, FinalDataType x) = pure $ Just (name, x)+ validateType (name, Implements interfaces object) =+ asTuple name <$> object `mustImplement` interfaces+ validateType _ = pure Nothing+ -----------------------------------+ asTuple name x = Just (name, x)+ -----------------------------------+ mustImplement :: DataObject -> [Key] -> Validation DataType+ mustImplement object interfaceKey = do+ interface <- traverse getInterfaceByKey interfaceKey+ case concatMap (mustBeSubset object) interface of+ [] -> pure $ DataObject object+ errors -> failure $ partialImplements (typeName object) errors+ -------------------------------+ mustBeSubset :: DataObject -> DataObject -> [(Key, Key, ImplementsError)]+ mustBeSubset DataTyCon { typeData = objFields } DataTyCon { typeName, typeData = interfaceFields }+ = concatMap checkField interfaceFields+ where+ checkField :: (Key, DataField) -> [(Key, Key, ImplementsError)]+ checkField (key, DataField { fieldType = interfaceT@TypeAlias { aliasTyCon = interfaceTypeName, aliasWrappers = interfaceWrappers } })+ = case lookup key objFields of+ Just DataField { fieldType = objT@TypeAlias { aliasTyCon, aliasWrappers } }+ | aliasTyCon == interfaceTypeName && not+ (isWeaker aliasWrappers interfaceWrappers)+ -> []+ | otherwise+ -> [ ( typeName+ , key+ , UnexpectedType { expectedType = render interfaceT+ , foundType = render objT+ }+ )+ ]+ Nothing -> [(typeName, key, UndefinedField)]+ -------------------------------+ getInterfaceByKey :: Key -> Validation DataObject+ getInterfaceByKey key = case lookup key lib of+ Just (Interface x) -> pure x+ _ -> failure $ unknownInterface key
src/Data/Morpheus/Validation/Internal/Utils.hs view
@@ -1,77 +1,47 @@+{-# LANGUAGE NamedFieldPuns #-}++ module Data.Morpheus.Validation.Internal.Utils ( differKeys- , existsObjectType- , lookupType- , getInputType- , lookupField , checkNameCollision , checkForUnknownKeys , VALIDATION_MODE(..)- ) where--import Data.List ((\\))-import Data.Morpheus.Error.Variable (unknownType)-import Data.Morpheus.Types.Internal.Base (EnhancedKey (..), Key, Position, enhanceKeyWithNull)-import Data.Morpheus.Types.Internal.Data (DataObject, DataType (..), DataTypeLib (..))-import Data.Morpheus.Types.Internal.Validation (Validation)-import qualified Data.Set as S-import Data.Text (Text)+ )+where -type GenError error a = error -> Either error a+import Data.List ( (\\) )+import Data.Morpheus.Types.Internal.AST+ ( Ref(..)+ , Key+ , anonymousRef+ )+import qualified Data.Set as S+import Data.Text ( Text )+import Data.Morpheus.Types.Internal.Resolving+ ( Failure(..) ) data VALIDATION_MODE = WITHOUT_VARIABLES | FULL_VALIDATION deriving (Eq, Show) -lookupType :: error -> [(Text, a)] -> Text -> Either error a-lookupType error' lib' typeName' =- case lookup typeName' lib' of- Nothing -> Left error'- Just x -> pure x--lookupField :: Text -> [(Text, fType)] -> GenError error fType-lookupField id' lib' error' =- case lookup id' lib' of- Nothing -> Left error'- Just field -> pure field--getInputType :: Text -> DataTypeLib -> GenError error DataType-getInputType name lib gqlError =- case lookup name (inputObject lib) of- Just x -> pure (DataInputObject x)- Nothing ->- case lookup name (inputUnion lib) of- Just x -> pure (DataInputUnion x)- Nothing ->- case lookup name (scalar lib) of- Just x -> pure (DataScalar x)- Nothing -> case lookup name (enum lib) of- Just x -> pure (DataEnum x)- Nothing -> Left gqlError--existsObjectType :: Position -> Text -> DataTypeLib -> Validation DataObject-existsObjectType position' typeName' lib = lookupType error' (object lib) typeName'- where- error' = unknownType typeName' position'--differKeys :: [EnhancedKey] -> [Key] -> [EnhancedKey]-differKeys enhanced keys = enhanced \\ map enhanceKeyWithNull keys+differKeys :: [Ref] -> [Key] -> [Ref]+differKeys enhanced keys = enhanced \\ map anonymousRef keys removeDuplicates :: Ord a => [a] -> [a] removeDuplicates = S.toList . S.fromList -elementOfKeys :: [Text] -> EnhancedKey -> Bool-elementOfKeys keys' EnhancedKey {uid = id'} = id' `elem` keys'+elementOfKeys :: [Text] -> Ref -> Bool+elementOfKeys keys Ref { refName } = refName `elem` keys -checkNameCollision :: [EnhancedKey] -> ([EnhancedKey] -> error) -> Either error [EnhancedKey]+checkNameCollision :: Failure e m => [Ref] -> ([Ref] -> e) -> m [Ref] checkNameCollision enhancedKeys errorGenerator = case enhancedKeys \\ removeDuplicates enhancedKeys of [] -> pure enhancedKeys- duplicates -> Left $ errorGenerator duplicates+ duplicates -> failure $ errorGenerator duplicates -checkForUnknownKeys :: [EnhancedKey] -> [Text] -> ([EnhancedKey] -> error) -> Either error [EnhancedKey]+checkForUnknownKeys :: Failure e m => [Ref] -> [Text] -> ([Ref] -> e) -> m [Ref] checkForUnknownKeys enhancedKeys' keys' errorGenerator' = case filter (not . elementOfKeys keys') enhancedKeys' of [] -> pure enhancedKeys'- unknownKeys' -> Left $ errorGenerator' unknownKeys'+ unknownKeys' -> failure $ errorGenerator' unknownKeys'
src/Data/Morpheus/Validation/Internal/Value.hs view
@@ -4,74 +4,110 @@ module Data.Morpheus.Validation.Internal.Value ( validateInputValue , validateEnum- ) where+ )+where -import Data.List (elem)+import Data.List ( elem ) -- MORPHEUS-import Data.Morpheus.Error.Input (InputError (..), InputValidation, Prop (..))-import Data.Morpheus.Rendering.RenderGQL (renderWrapped)-import Data.Morpheus.Types.Internal.Data (DataField (..), DataTyCon (..), DataType (..),- DataTypeLib (..), DataValidator (..), Key, TypeAlias (..),- WrapperD (..), isNullable)-import Data.Morpheus.Types.Internal.Value (Value (..))-import Data.Morpheus.Validation.Internal.Utils (getInputType, lookupField)+import Data.Morpheus.Error.Input ( InputError(..)+ , InputValidation+ , Prop(..)+ )+import Data.Morpheus.Rendering.RenderGQL+ ( renderWrapped )+import Data.Morpheus.Types.Internal.AST+ ( DataField(..)+ , DataTyCon(..)+ , DataType(..)+ , DataTypeLib(..)+ , DataValidator(..)+ , Key+ , TypeAlias(..)+ , TypeWrapper(..)+ , DataEnumValue(..)+ , isNullable+ , lookupField+ , lookupInputType+ , Value(..)+ ) +import Data.Morpheus.Types.Internal.Resolving+ ( Failure(..) )+ -- Validate Variable Argument or all Possible input Values-validateInputValue :: DataTypeLib -> [Prop] -> [WrapperD] -> DataType -> (Key, Value) -> InputValidation Value+validateInputValue+ :: DataTypeLib+ -> [Prop]+ -> [TypeWrapper]+ -> DataType+ -> (Key, Value)+ -> InputValidation Value validateInputValue lib prop' = validate- where- throwError :: [WrapperD] -> DataType -> Value -> InputValidation Value- throwError wrappers datatype value = Left $ UnexpectedType prop' (renderWrapped datatype wrappers) value Nothing- -- VALIDATION- validate :: [WrapperD] -> DataType -> (Key, Value) -> InputValidation Value- -- Validate Null. value = null ?- validate wrappers tName (_, Null)- | isNullable wrappers = return Null- | otherwise = throwError wrappers tName Null- -- Validate LIST- validate (MaybeD:wrappers) type' value' = validateInputValue lib prop' wrappers type' value'- validate (ListD:wrappers) type' (key', List list') = List <$> mapM validateElement list'- where- validateElement element' = validateInputValue lib prop' wrappers type' (key', element')- {-- 2. VALIDATE TYPES, all wrappers are already Processed --}- {-- VALIDATE OBJECT--}- validate [] (DataInputObject DataTyCon {typeData = parentFields'}) (_, Object fields) =- Object <$> mapM validateField fields- where- validateField (_name, value) = do- (type', currentProp') <- validationData value- wrappers' <- aliasWrappers . fieldType <$> getField- value'' <- validateInputValue lib currentProp' wrappers' type' (_name, value)- return (_name, value'')- where- validationData x = do- fieldTypeName' <- aliasTyCon . fieldType <$> getField- let currentProp = prop' ++ [Prop _name fieldTypeName']- type' <- getInputType fieldTypeName' lib (typeMismatch x fieldTypeName' currentProp)- return (type', currentProp)- getField = lookupField _name parentFields' (UnknownField prop' _name)- -- VALIDATE INPUT UNION- -- TODO: Validate Union- validate [] (DataInputUnion DataTyCon {typeData}) (_, Object fields) = return (Object fields)- {-- VALIDATE SCALAR --}- validate [] (DataEnum DataTyCon {typeData = tags', typeName = name'}) (_, value') =- validateEnum (UnexpectedType prop' name' value' Nothing) tags' value'- {-- VALIDATE ENUM --}- validate [] (DataScalar DataTyCon {typeName = name', typeData = DataValidator {validateValue = validator'}}) (_, value') =- case validator' value' of- Right _ -> return value'- Left "" -> Left $ UnexpectedType prop' name' value' Nothing- Left errorMessage -> Left $ UnexpectedType prop' name' value' (Just errorMessage)- {-- 3. THROW ERROR: on invalid values --}- validate wrappers datatype (_, value) = throwError wrappers datatype value+ where+ throwError :: [TypeWrapper] -> DataType -> Value -> InputValidation Value+ throwError wrappers datatype value =+ Left $ UnexpectedType prop' (renderWrapped datatype wrappers) value Nothing+ -- VALIDATION+ validate :: [TypeWrapper] -> DataType -> (Key, Value) -> InputValidation Value+ -- Validate Null. value = null ?+ validate wrappers tName (_, Null) | isNullable wrappers = return Null+ | otherwise = throwError wrappers tName Null+ -- Validate LIST+ validate (TypeMaybe : wrappers) type' value' =+ validateInputValue lib prop' wrappers type' value'+ validate (TypeList : wrappers) type' (key', List list') =+ List <$> mapM validateElement list'+ where+ validateElement element' =+ validateInputValue lib prop' wrappers type' (key', element')+ {-- 2. VALIDATE TYPES, all wrappers are already Processed --}+ {-- VALIDATE OBJECT--}+ validate [] (DataInputObject DataTyCon { typeData = parentFields' }) (_, Object fields)+ = Object <$> mapM validateField fields+ where+ validateField (_name, value) = do+ (type', currentProp') <- validationData value+ wrappers' <- aliasWrappers . fieldType <$> getField+ value'' <- validateInputValue lib+ currentProp'+ wrappers'+ type'+ (_name, value)+ return (_name, value'')+ where+ validationData x = do+ fieldTypeName' <- aliasTyCon . fieldType <$> getField+ let currentProp = prop' ++ [Prop _name fieldTypeName']+ type' <- lookupInputType fieldTypeName'+ lib+ (typeMismatch x fieldTypeName' currentProp)+ return (type', currentProp)+ getField = lookupField _name parentFields' (UnknownField prop' _name)+ -- VALIDATE INPUT UNION+ -- TODO: Validate Union+ validate [] (DataInputUnion DataTyCon { typeData }) (_, Object fields) =+ return (Object fields)+ {-- VALIDATE SCALAR --}+ validate [] (DataEnum DataTyCon { typeData = tags, typeName = name' }) (_, value')+ = validateEnum (UnexpectedType prop' name' value' Nothing) tags value'+ {-- VALIDATE ENUM --}+ validate [] (DataScalar DataTyCon { typeName = name', typeData = DataValidator { validateValue = validator' } }) (_, value')+ = case validator' value' of+ Right _ -> return value'+ Left "" -> failure (UnexpectedType prop' name' value' Nothing)+ Left errorMessage ->+ Left $ UnexpectedType prop' name' value' (Just errorMessage)+ {-- 3. THROW ERROR: on invalid values --}+ validate wrappers datatype (_, value) = throwError wrappers datatype value -validateEnum :: error -> [Key] -> Value -> Either error Value-validateEnum error' tags' (Enum enumValue) =- if enumValue `elem` tags'- then pure (Enum enumValue)- else Left error'-validateEnum error' _ _ = Left error'+validateEnum :: error -> [DataEnumValue] -> Value -> Either error Value+validateEnum gqlError enumValues (Enum enumValue)+ | enumValue `elem` tags = pure (Enum enumValue)+ | otherwise = Left gqlError+ where tags = map enumName enumValues+validateEnum gqlError _ _ = Left gqlError typeMismatch :: Value -> Key -> [Prop] -> InputError-typeMismatch jsType expected' path' = UnexpectedType path' expected' jsType Nothing+typeMismatch jsType expected' path' =+ UnexpectedType path' expected' jsType Nothing
src/Data/Morpheus/Validation/Query/Arguments.hs view
@@ -3,87 +3,144 @@ module Data.Morpheus.Validation.Query.Arguments ( validateArguments- ) where+ )+where -import Data.Morpheus.Error.Arguments (argumentGotInvalidValue, argumentNameCollision,- undefinedArgument, unknownArguments)-import Data.Morpheus.Error.Input (InputValidation, inputErrorMessage)-import Data.Morpheus.Error.Internal (internalUnknownTypeMessage)-import Data.Morpheus.Error.Variable (incompatibleVariableType, undefinedVariable)-import Data.Morpheus.Rendering.RenderGQL (RenderGQL (..))-import Data.Morpheus.Types.Internal.AST.Operation (ValidVariables, Variable (..))-import Data.Morpheus.Types.Internal.AST.RawSelection (RawArgument (..), RawArguments, Reference (..))-import Data.Morpheus.Types.Internal.AST.Selection (Argument (..), ArgumentOrigin (..), Arguments)-import Data.Morpheus.Types.Internal.Base (EnhancedKey (..), Position)-import Data.Morpheus.Types.Internal.Data (DataArgument, DataField (..), DataField, DataTypeLib,- TypeAlias (..), isFieldNullable, isWeaker)-import Data.Morpheus.Types.Internal.Validation (Validation)-import Data.Morpheus.Types.Internal.Value (Value (Null))-import Data.Morpheus.Validation.Internal.Utils (checkForUnknownKeys, checkNameCollision, getInputType)-import Data.Morpheus.Validation.Internal.Value (validateInputValue)-import Data.Text (Text)+import Data.Morpheus.Error.Arguments ( argumentGotInvalidValue+ , argumentNameCollision+ , undefinedArgument+ , unknownArguments+ )+import Data.Morpheus.Error.Input ( InputValidation+ , inputErrorMessage+ )+import Data.Morpheus.Error.Internal ( internalUnknownTypeMessage )+import Data.Morpheus.Error.Variable ( incompatibleVariableType+ , undefinedVariable+ )+import Data.Morpheus.Rendering.RenderGQL+ ( RenderGQL(..) )+import Data.Morpheus.Types.Internal.AST+ ( ValidVariables+ , Variable(..)+ , Argument(..)+ , ValueOrigin(..)+ , Arguments+ , RawArgument(..)+ , RawArguments+ , Ref(..)+ , Position+ , DataArgument+ , DataField(..)+ , DataTypeLib+ , TypeAlias(..)+ , isFieldNullable+ , isWeaker+ , lookupInputType+ , Value(Null)+ )+import Data.Morpheus.Types.Internal.Resolving+ ( Validation+ , Failure(..)+ )+import Data.Morpheus.Validation.Internal.Utils+ ( checkForUnknownKeys+ , checkNameCollision+ )+import Data.Morpheus.Validation.Internal.Value+ ( validateInputValue )+import Data.Text ( Text ) -resolveArgumentVariables :: Text -> ValidVariables -> DataField -> RawArguments -> Validation Arguments-resolveArgumentVariables operatorName variables DataField {fieldName, fieldArgs} = mapM resolveVariable- where- resolveVariable :: (Text, RawArgument) -> Validation (Text, Argument)- resolveVariable (key, RawArgument argument) = pure (key, argument)- resolveVariable (key, VariableReference Reference {referenceName, referencePosition}) =- (key, ) . toArgument <$> lookupVar- where- toArgument argumentValue =- Argument {argumentValue, argumentOrigin = VARIABLE, argumentPosition = referencePosition}- lookupVar =- case lookup referenceName variables of- Nothing -> Left $ undefinedVariable operatorName referencePosition referenceName- Just Variable {variableValue, variableType, variableTypeWrappers} ->- case lookup key fieldArgs of- Nothing -> Left $ unknownArguments fieldName [EnhancedKey key referencePosition]- Just DataField {fieldType = fieldT@TypeAlias {aliasTyCon, aliasWrappers}} ->- if variableType == aliasTyCon && not (isWeaker variableTypeWrappers aliasWrappers)- then return variableValue- else Left $ incompatibleVariableType referenceName varSignature fieldSignature referencePosition- where varSignature = renderWrapped variableType variableTypeWrappers- fieldSignature = render fieldT+resolveArgumentVariables+ :: Text -> ValidVariables -> DataField -> RawArguments -> Validation Arguments+resolveArgumentVariables operatorName variables DataField { fieldName, fieldArgs }+ = mapM resolveVariable+ where+ resolveVariable :: (Text, RawArgument) -> Validation (Text, Argument)+ resolveVariable (key, RawArgument argument) = pure (key, argument)+ resolveVariable (key, VariableRef Ref { refName, refPosition }) =+ (key, ) . toArgument <$> lookupVar+ where+ toArgument argumentValue = Argument { argumentValue+ , argumentOrigin = VARIABLE+ , argumentPosition = refPosition+ }+ lookupVar = case lookup refName variables of+ Nothing -> failure $ undefinedVariable operatorName refPosition refName+ Just Variable { variableValue, variableType, variableTypeWrappers } ->+ case lookup key fieldArgs of+ Nothing -> failure $ unknownArguments fieldName [Ref key refPosition]+ Just DataField { fieldType = fieldT@TypeAlias { aliasTyCon, aliasWrappers } }+ -> if variableType == aliasTyCon && not+ (isWeaker variableTypeWrappers aliasWrappers)+ then return variableValue+ else failure $ incompatibleVariableType refName+ varSignature+ fieldSignature+ refPosition+ where+ varSignature = renderWrapped variableType variableTypeWrappers+ fieldSignature = render fieldT -validateArgument :: DataTypeLib -> Position -> Arguments -> (Text, DataArgument) -> Validation (Text, Argument)-validateArgument lib fieldPosition requestArgs (key, argType@DataField {fieldType = TypeAlias { aliasTyCon- , aliasWrappers- }}) =- case lookup key requestArgs of- Nothing -> handleNullable- Just argument@Argument {argumentOrigin = VARIABLE} -> pure (key, argument) -- Variables are already checked in Variable Validation- Just Argument {argumentValue = Null} -> handleNullable- Just argument -> validateArgumentValue argument- where- handleNullable- | isFieldNullable argType =- pure (key, Argument {argumentValue = Null, argumentOrigin = INLINE, argumentPosition = fieldPosition})- | otherwise = Left $ undefinedArgument (EnhancedKey key fieldPosition)- -------------------------------------------------------------------------- validateArgumentValue :: Argument -> Validation (Text, Argument)- validateArgumentValue arg@Argument {argumentValue, argumentPosition} =- getInputType aliasTyCon lib (internalUnknownTypeMessage aliasTyCon) >>= checkType >> pure (key, arg)- where- checkType type' = handleInputError (validateInputValue lib [] aliasWrappers type' (key, argumentValue))- ---------- handleInputError :: InputValidation a -> Validation ()- handleInputError (Left err) = Left $ argumentGotInvalidValue key (inputErrorMessage err) argumentPosition- handleInputError _ = pure ()+validateArgument+ :: DataTypeLib+ -> Position+ -> Arguments+ -> (Text, DataArgument)+ -> Validation (Text, Argument)+validateArgument lib fieldPosition requestArgs (key, argType@DataField { fieldType = TypeAlias { aliasTyCon, aliasWrappers } })+ = case lookup key requestArgs of+ Nothing -> handleNullable+ Just argument@Argument { argumentOrigin = VARIABLE } ->+ pure (key, argument) -- Variables are already checked in Variable Validation+ Just Argument { argumentValue = Null } -> handleNullable+ Just argument -> validateArgumentValue argument+ where+ handleNullable+ | isFieldNullable argType = pure+ ( key+ , Argument { argumentValue = Null+ , argumentOrigin = INLINE+ , argumentPosition = fieldPosition+ }+ )+ | otherwise = failure $ undefinedArgument (Ref key fieldPosition)+ -------------------------------------------------------------------------+ validateArgumentValue :: Argument -> Validation (Text, Argument)+ validateArgumentValue arg@Argument { argumentValue, argumentPosition } =+ lookupInputType aliasTyCon lib (internalUnknownTypeMessage aliasTyCon)+ >>= checkType+ >> pure (key, arg)+ where+ checkType type' = handleInputError+ (validateInputValue lib [] aliasWrappers type' (key, argumentValue))+ ---------+ handleInputError :: InputValidation a -> Validation ()+ handleInputError (Left err) = failure+ $ argumentGotInvalidValue key (inputErrorMessage err) argumentPosition+ handleInputError _ = pure () -validateArguments ::- DataTypeLib -> Text -> ValidVariables -> (Text, DataField) -> Position -> RawArguments -> Validation Arguments-validateArguments typeLib operatorName variables (key, field@DataField {fieldArgs}) pos rawArgs = do- args <- resolveArgumentVariables operatorName variables field rawArgs- dataArgs <- checkForUnknownArguments args- mapM (validateArgument typeLib pos args) dataArgs- where- checkForUnknownArguments :: Arguments -> Validation [(Text, DataField)]- checkForUnknownArguments args =- checkForUnknownKeys enhancedKeys fieldKeys argError >> checkNameCollision enhancedKeys argumentNameCollision >>- pure fieldArgs- where- argError = unknownArguments key- enhancedKeys = map argToKey args- argToKey (key', Argument {argumentPosition}) = EnhancedKey key' argumentPosition- fieldKeys = map fst fieldArgs+validateArguments+ :: DataTypeLib+ -> Text+ -> ValidVariables+ -> (Text, DataField)+ -> Position+ -> RawArguments+ -> Validation Arguments+validateArguments typeLib operatorName variables (key, field@DataField { fieldArgs }) pos rawArgs+ = do+ args <- resolveArgumentVariables operatorName variables field rawArgs+ dataArgs <- checkForUnknownArguments args+ mapM (validateArgument typeLib pos args) dataArgs+ where+ checkForUnknownArguments :: Arguments -> Validation [(Text, DataField)]+ checkForUnknownArguments args =+ checkForUnknownKeys enhancedKeys fieldKeys argError+ >> checkNameCollision enhancedKeys argumentNameCollision+ >> pure fieldArgs+ where+ argError = unknownArguments key+ enhancedKeys = map argToKey args+ argToKey (key', Argument { argumentPosition }) = Ref key' argumentPosition+ fieldKeys = map fst fieldArgs
src/Data/Morpheus/Validation/Query/Fragment.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NamedFieldPuns #-} module Data.Morpheus.Validation.Query.Fragment@@ -5,97 +6,121 @@ , castFragmentType , resolveSpread , getFragment- ) where+ )+where -import Data.List ((\\))-import Data.Semigroup ((<>))-import Data.Text (Text)-import qualified Data.Text as T (concat)+import Data.List ( (\\) )+import Data.Semigroup ( (<>) )+import Data.Text ( Text )+import qualified Data.Text as T+ ( concat ) -- MORPHEUS-import Data.Morpheus.Error.Fragment (cannotBeSpreadOnType, cannotSpreadWithinItself,- fragmentNameCollision, unknownFragment, unusedFragment)-import Data.Morpheus.Types.Internal.AST.RawSelection (Fragment (..), FragmentLib, RawSelection (..),- Reference (..), Selection (..))-import Data.Morpheus.Types.Internal.Base (EnhancedKey (..), Position)-import Data.Morpheus.Types.Internal.Data (DataTypeLib)-import Data.Morpheus.Types.Internal.Validation (Validation)-import Data.Morpheus.Validation.Internal.Utils (checkNameCollision, existsObjectType)+import Data.Morpheus.Error.Fragment ( cannotBeSpreadOnType+ , cannotSpreadWithinItself+ , fragmentNameCollision+ , unknownFragment+ , unusedFragment+ )+import Data.Morpheus.Error.Variable ( unknownType )+import Data.Morpheus.Types.Internal.AST+ ( Fragment(..)+ , FragmentLib+ , RawSelection(..)+ , Selection(..)+ , Ref(..)+ , Position+ , DataTypeLib+ , lookupDataObject+ )+import Data.Morpheus.Types.Internal.Resolving+ ( Validation+ , Failure(..)+ )+import Data.Morpheus.Validation.Internal.Utils+ ( checkNameCollision ) -validateFragments :: DataTypeLib -> FragmentLib -> [(Text, RawSelection)] -> Validation ()-validateFragments lib fragments operatorSel = validateNameCollision >> checkLoop >> checkUnusedFragments- where- validateNameCollision = checkNameCollision fragmentsKeys fragmentNameCollision- checkUnusedFragments =- case fragmentsKeys \\ usedFragments fragments operatorSel of- [] -> return ()- unused -> Left $ unusedFragment unused- checkLoop = mapM (validateFragment lib) fragments >>= detectLoopOnFragments- fragmentsKeys = map toEnhancedKey fragments- where- toEnhancedKey (key, Fragment {fragmentPosition}) = EnhancedKey key fragmentPosition+validateFragments+ :: DataTypeLib -> FragmentLib -> [(Text, RawSelection)] -> Validation ()+validateFragments lib fragments operatorSel =+ validateNameCollision >> checkLoop >> checkUnusedFragments+ where+ validateNameCollision =+ checkNameCollision fragmentsKeys fragmentNameCollision+ checkUnusedFragments =+ case fragmentsKeys \\ usedFragments fragments operatorSel of+ [] -> return ()+ unused -> failure (unusedFragment unused)+ checkLoop = mapM (validateFragment lib) fragments >>= detectLoopOnFragments+ fragmentsKeys = map toRef fragments+ where toRef (key, Fragment { fragmentPosition }) = Ref key fragmentPosition -type Node = EnhancedKey+type Node = Ref type NodeEdges = (Node, [Node]) type Graph = [NodeEdges] -getFragment :: Reference -> FragmentLib -> Validation Fragment-getFragment Reference {referenceName, referencePosition} lib =- case lookup referenceName lib of- Nothing -> Left $ unknownFragment referenceName referencePosition- Just fragment -> pure fragment+getFragment :: Ref -> FragmentLib -> Validation Fragment+getFragment Ref { refName, refPosition } lib = case lookup refName lib of+ Nothing -> failure $ unknownFragment refName refPosition+ Just fragment -> pure fragment -castFragmentType :: Maybe Text -> Position -> [Text] -> Fragment -> Validation Fragment-castFragmentType key' position' targets' fragment@Fragment {fragmentType} =+castFragmentType+ :: Maybe Text -> Position -> [Text] -> Fragment -> Validation Fragment+castFragmentType key' position' targets' fragment@Fragment { fragmentType } = if fragmentType `elem` targets' then pure fragment- else Left $ cannotBeSpreadOnType key' fragmentType position' (T.concat targets')+ else failure+ $ cannotBeSpreadOnType key' fragmentType position' (T.concat targets') -resolveSpread :: FragmentLib -> [Text] -> Reference -> Validation Fragment-resolveSpread fragments allowedTargets reference@Reference {referenceName, referencePosition} =- getFragment reference fragments >>= castFragmentType (Just referenceName) referencePosition allowedTargets+resolveSpread :: FragmentLib -> [Text] -> Ref -> Validation Fragment+resolveSpread fragments allowedTargets reference@Ref { refName, refPosition } =+ getFragment reference fragments+ >>= castFragmentType (Just refName) refPosition allowedTargets usedFragments :: FragmentLib -> [(Text, RawSelection)] -> [Node] usedFragments fragments = concatMap findAllUses- where- findAllUses :: (Text, RawSelection) -> [Node]- findAllUses (_, RawSelectionSet Selection {selectionRec}) = concatMap findAllUses selectionRec- findAllUses (_, InlineFragment Fragment {fragmentSelection}) = concatMap findAllUses fragmentSelection- findAllUses (_, RawSelectionField {}) = []- findAllUses (_, Spread Reference {referenceName, referencePosition}) =- [EnhancedKey referenceName referencePosition] <> searchInFragment- where- searchInFragment = maybe [] (concatMap findAllUses . fragmentSelection) (lookup referenceName fragments)+ where+ findAllUses :: (Text, RawSelection) -> [Node]+ findAllUses (_, RawSelectionSet Selection { selectionRec }) =+ concatMap findAllUses selectionRec+ findAllUses (_, InlineFragment Fragment { fragmentSelection }) =+ concatMap findAllUses fragmentSelection+ findAllUses (_, RawSelectionField{}) = []+ findAllUses (_, Spread Ref { refName, refPosition }) =+ [Ref refName refPosition] <> searchInFragment+ where+ searchInFragment = maybe []+ (concatMap findAllUses . fragmentSelection)+ (lookup refName fragments) scanForSpread :: (Text, RawSelection) -> [Node]-scanForSpread (_, RawSelectionSet Selection {selectionRec }) = concatMap scanForSpread selectionRec-scanForSpread (_, InlineFragment Fragment {fragmentSelection = selection'}) = concatMap scanForSpread selection'-scanForSpread (_, RawSelectionField {}) = []-scanForSpread (_, Spread Reference {referenceName = name', referencePosition = position'}) =- [EnhancedKey name' position']+scanForSpread (_, RawSelectionSet Selection { selectionRec }) =+ concatMap scanForSpread selectionRec+scanForSpread (_, InlineFragment Fragment { fragmentSelection = selection' }) =+ concatMap scanForSpread selection'+scanForSpread (_, RawSelectionField{}) = []+scanForSpread (_, Spread Ref { refName = name', refPosition = position' }) =+ [Ref name' position'] validateFragment :: DataTypeLib -> (Text, Fragment) -> Validation NodeEdges-validateFragment lib (fName, Fragment {fragmentSelection, fragmentType, fragmentPosition}) =- existsObjectType fragmentPosition fragmentType lib >>- pure (EnhancedKey fName fragmentPosition, concatMap scanForSpread fragmentSelection)+validateFragment lib (fName, Fragment { fragmentSelection, fragmentType, fragmentPosition })+ = lookupDataObject validationError fragmentType lib >> pure+ (Ref fName fragmentPosition, concatMap scanForSpread fragmentSelection)+ where validationError = unknownType fragmentType fragmentPosition detectLoopOnFragments :: Graph -> Validation () detectLoopOnFragments lib = mapM_ checkFragment lib- where- checkFragment (fragmentID, _) = checkForCycle lib fragmentID [fragmentID]+ where+ checkFragment (fragmentID, _) = checkForCycle lib fragmentID [fragmentID] checkForCycle :: Graph -> Node -> [Node] -> Validation Graph-checkForCycle lib parentNode history =- case lookup parentNode lib of- Just node -> concat <$> mapM 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 = Left $ cannotSpreadWithinItself $ history ++ [n]+checkForCycle lib parentNode history = case lookup parentNode lib of+ Just node -> concat <$> mapM 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)
src/Data/Morpheus/Validation/Query/Selection.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}@@ -6,69 +7,101 @@ module Data.Morpheus.Validation.Query.Selection ( validateSelectionSet- ) where+ )+where -import Data.Maybe (fromMaybe)-import Data.Text (Text)+import Data.Maybe ( fromMaybe )+import Data.Text ( Text ) -- MORPHEUS-import Data.Morpheus.Error.Selection (cannotQueryField, duplicateQuerySelections,- hasNoSubfields, subfieldsNotSelected)-import Data.Morpheus.Error.Variable (unknownType)-import Data.Morpheus.Types.Internal.AST.Operation (ValidVariables)-import Data.Morpheus.Types.Internal.AST.RawSelection (Fragment (..), FragmentLib, RawSelection (..),- RawSelectionSet)-import Data.Morpheus.Types.Internal.AST.Selection (Selection (..), SelectionRec (..), SelectionSet)-import Data.Morpheus.Types.Internal.Base (EnhancedKey (..))-import Data.Morpheus.Types.Internal.Data (DataField (..), DataType (..), DataObject,- DataTyCon (..), DataTypeLib (..), TypeAlias (..),- allDataTypes, isEntNode)-import Data.Morpheus.Types.Internal.Validation (Validation)-import Data.Morpheus.Validation.Internal.Utils (checkNameCollision, lookupType)-import Data.Morpheus.Validation.Query.Arguments (validateArguments)-import Data.Morpheus.Validation.Query.Fragment (castFragmentType, resolveSpread)-import Data.Morpheus.Validation.Query.Utils.Selection (lookupFieldAsSelectionSet, lookupSelectionField,- lookupUnionTypes)+import Data.Morpheus.Error.Selection ( cannotQueryField+ , duplicateQuerySelections+ , hasNoSubfields+ , subfieldsNotSelected+ )+import Data.Morpheus.Error.Variable ( unknownType )+import Data.Morpheus.Types.Internal.AST+ ( ValidVariables + , Selection(..)+ , SelectionRec(..)+ , SelectionSet+ , Fragment(..)+ , FragmentLib+ , RawSelection(..)+ , RawSelectionSet+ , DataField(..)+ , Ref(..)+ , DataObject+ , DataTyCon(..)+ , DataType(..)+ , DataTypeLib(..)+ , TypeAlias(..)+ , allDataTypes+ , isEntNode+ , lookupFieldAsSelectionSet+ , lookupSelectionField+ , lookupType+ , lookupUnionTypes+ )+import Data.Morpheus.Types.Internal.Resolving+ ( Validation+ , Failure(..)+ )+import Data.Morpheus.Validation.Internal.Utils+ ( checkNameCollision )+import Data.Morpheus.Validation.Query.Arguments+ ( validateArguments )+import Data.Morpheus.Validation.Query.Fragment+ ( castFragmentType+ , resolveSpread+ ) checkDuplicatesOn :: DataObject -> SelectionSet -> Validation SelectionSet-checkDuplicatesOn DataTyCon {typeName = name'} keys = checkNameCollision enhancedKeys selError >> pure keys- where- selError = duplicateQuerySelections name'- enhancedKeys = map selToKey keys- selToKey (key, Selection {selectionPosition = position' , selectionAlias }) = EnhancedKey (fromMaybe key selectionAlias) position'+checkDuplicatesOn DataTyCon { typeName = name' } keys =+ checkNameCollision enhancedKeys selError >> pure keys+ where+ selError = duplicateQuerySelections name'+ enhancedKeys = map selToKey keys+ selToKey (key, Selection { selectionPosition = position', selectionAlias }) =+ Ref (fromMaybe key selectionAlias) position' -clusterUnionSelection ::- FragmentLib -> Text -> [DataObject] -> (Text, RawSelection) -> Validation ([Fragment], SelectionSet)+clusterUnionSelection+ :: FragmentLib+ -> Text+ -> [DataObject]+ -> (Text, RawSelection)+ -> Validation ([Fragment], SelectionSet) clusterUnionSelection fragments type' possibleTypes' = splitFrag- where- packFragment fragment = return ([fragment], [])- typeNames = map typeName possibleTypes'- splitFrag :: (Text, RawSelection) -> Validation ([Fragment], SelectionSet)- splitFrag (_, Spread ref) = resolveSpread fragments typeNames ref >>= packFragment- splitFrag ("__typename", RawSelectionField selection ) = pure ([] ,[- ( "__typename",- selection {- selectionArguments = [] ,- selectionRec = SelectionField}+ where+ packFragment fragment = return ([fragment], [])+ typeNames = map typeName possibleTypes'+ splitFrag :: (Text, RawSelection) -> Validation ([Fragment], SelectionSet)+ splitFrag (_, Spread ref) =+ resolveSpread fragments typeNames ref >>= packFragment+ splitFrag ("__typename", RawSelectionField selection) = pure+ ( []+ , [ ( "__typename"+ , selection { selectionArguments = [], selectionRec = SelectionField } )- ])- splitFrag (key, RawSelectionSet Selection {selectionPosition}) =- Left $ cannotQueryField key type' selectionPosition- splitFrag (key, RawSelectionField Selection {selectionPosition}) =- Left $ cannotQueryField key type' selectionPosition- -- splitFrag (key', RawAlias {rawAliasPosition = position'}) = Left $ cannotQueryField key' type' position'- splitFrag (_, InlineFragment fragment') =- castFragmentType Nothing (fragmentPosition fragment') typeNames fragment' >>= packFragment+ ]+ )+ splitFrag (key, RawSelectionSet Selection { selectionPosition }) =+ failure $ cannotQueryField key type' selectionPosition+ splitFrag (key, RawSelectionField Selection { selectionPosition }) =+ failure $ cannotQueryField key type' selectionPosition+-- splitFrag (key', RawAlias {rawAliasPosition = position'}) = failure $ cannotQueryField key' type' position'+ splitFrag (_, InlineFragment fragment') =+ castFragmentType Nothing (fragmentPosition fragment') typeNames fragment'+ >>= packFragment categorizeTypes :: [DataObject] -> [Fragment] -> [(DataObject, [Fragment])] categorizeTypes types fragments = filter notEmpty $ map categorizeType types- where- notEmpty = (0 /=) . length . snd- categorizeType :: DataObject -> (DataObject, [Fragment])- categorizeType datatype = (datatype, filter matches fragments)- where- matches fragment = fragmentType fragment == typeName datatype+ where+ notEmpty = (0 /=) . length . snd+ categorizeType :: DataObject -> (DataObject, [Fragment])+ categorizeType datatype = (datatype, filter matches fragments)+ where matches fragment = fragmentType fragment == typeName datatype flatTuple :: [([a], [b])] -> ([a], [b]) flatTuple list' = (concatMap fst list', concatMap snd list')@@ -84,77 +117,112 @@ ] -} -validateSelectionSet ::- DataTypeLib -> FragmentLib -> Text -> ValidVariables -> DataObject -> RawSelectionSet -> Validation SelectionSet+validateSelectionSet+ :: DataTypeLib+ -> FragmentLib+ -> Text+ -> ValidVariables+ -> DataObject+ -> RawSelectionSet+ -> Validation SelectionSet validateSelectionSet lib fragments' operatorName variables = __validate- where- __validate dataType'@DataTyCon {typeName = typeName'} selectionSet' =- concat <$> mapM validateSelection selectionSet' >>= checkDuplicatesOn dataType'- where- validateFragment Fragment {fragmentSelection = selection'} = __validate dataType' selection'- {-+ where+ __validate dataType'@DataTyCon { typeName = typeName' } selectionSet' =+ concat+ <$> mapM validateSelection selectionSet'+ >>= checkDuplicatesOn dataType'+ where+ validateFragment Fragment { fragmentSelection = selection' } =+ __validate dataType' selection'+ {- get dataField and validated arguments for RawSelection -}- getValidationData key Selection {selectionArguments, selectionPosition} = do- selectionField <- lookupSelectionField selectionPosition key dataType'- -- validate field Argument ------ arguments <-- validateArguments- lib- operatorName- variables- (key, selectionField)- selectionPosition- selectionArguments- -- check field Type existence ------ fieldDataType <-- lookupType- (unknownType (aliasTyCon $fieldType selectionField) selectionPosition)- (allDataTypes lib)- (aliasTyCon $ fieldType selectionField)- return (selectionField, fieldDataType, arguments)- -- validate single selection: InlineFragments and Spreads will Be resolved and included in SelectionSet- --- validateSelection :: (Text, RawSelection) -> Validation SelectionSet- validateSelection (key', RawSelectionSet fullRawSelection@Selection { selectionRec = rawSelection, selectionPosition }) = do- (dataField, dataType, arguments) <- getValidationData key' fullRawSelection- case dataType of- DataUnion _ -> do- (categories, __typename) <- clusterTypes- mapM (validateCluster __typename) categories >>= returnSelection arguments . UnionSelection- where clusterTypes = do- unionTypes <- lookupUnionTypes selectionPosition key' lib dataField- (spreads, __typename) <-- flatTuple <$> mapM (clusterUnionSelection fragments' typeName' unionTypes) rawSelection- return (categorizeTypes unionTypes spreads, __typename)- --- -- second arguments will be added to every selection cluster- validateCluster :: SelectionSet -> (DataObject, [Fragment]) -> Validation (Text, SelectionSet)- validateCluster sysSelection' (type', frags') = do- selection' <- __validate type' (concatMap fragmentSelection frags')- return (typeName type', sysSelection' ++ selection')- DataObject _ -> do- fieldType' <- lookupFieldAsSelectionSet selectionPosition key' lib dataField- __validate fieldType' rawSelection >>= returnSelection arguments . SelectionSet- _ -> Left $ hasNoSubfields key' (aliasTyCon $fieldType dataField) selectionPosition- where- returnSelection selectionArguments selectionRec =- pure- [ ( key'- , fullRawSelection- { selectionArguments,- selectionRec- }- )- ]- validateSelection (key, RawSelectionField rawSelection@Selection{selectionPosition}) = do- (dataField, datatype, selectionArguments) <- getValidationData key rawSelection- isLeaf datatype dataField- pure [( key , rawSelection { selectionArguments , selectionRec = SelectionField })]- where- isLeaf dataType DataField {fieldType = TypeAlias {aliasTyCon}}- | isEntNode dataType = Right ()- | otherwise = Left $ subfieldsNotSelected key aliasTyCon selectionPosition- validateSelection (_, Spread reference') = resolveSpread fragments' [typeName'] reference' >>= validateFragment- validateSelection (_, InlineFragment fragment') =- castFragmentType Nothing (fragmentPosition fragment') [typeName'] fragment' >>= validateFragment+ getValidationData key Selection { selectionArguments, selectionPosition } =+ do+ selectionField <- lookupSelectionField selectionPosition key dataType'+ -- validate field Argument -----+ arguments <- validateArguments lib+ operatorName+ variables+ (key, selectionField)+ selectionPosition+ selectionArguments+ -- check field Type existence -----+ fieldDataType <- lookupType+ (unknownType (aliasTyCon $fieldType selectionField) selectionPosition)+ (allDataTypes lib)+ (aliasTyCon $ fieldType selectionField)+ return (selectionField, fieldDataType, arguments)+ -- validate single selection: InlineFragments and Spreads will Be resolved and included in SelectionSet+ --+ validateSelection :: (Text, RawSelection) -> Validation SelectionSet+ validateSelection (key', RawSelectionSet fullRawSelection@Selection { selectionRec = rawSelection, selectionPosition })+ = do+ (dataField, dataType, arguments) <- getValidationData key'+ fullRawSelection+ case dataType of+ DataUnion _ -> do+ (categories, __typename) <- clusterTypes+ mapM (validateCluster __typename) categories+ >>= returnSelection arguments+ . UnionSelection+ where+ clusterTypes = do+ unionTypes <- lookupUnionTypes selectionPosition+ key'+ lib+ dataField+ (spreads, __typename) <-+ flatTuple+ <$> mapM+ (clusterUnionSelection fragments' typeName' unionTypes)+ rawSelection+ return (categorizeTypes unionTypes spreads, __typename)+ --+ -- second arguments will be added to every selection cluster+ validateCluster+ :: SelectionSet+ -> (DataObject, [Fragment])+ -> Validation (Text, SelectionSet)+ validateCluster sysSelection' (type', frags') = do+ selection' <- __validate type'+ (concatMap fragmentSelection frags')+ return (typeName type', sysSelection' ++ selection')+ DataObject _ -> do+ fieldType' <- lookupFieldAsSelectionSet selectionPosition+ key'+ lib+ dataField+ __validate fieldType' rawSelection+ >>= returnSelection arguments+ . SelectionSet+ _ -> failure $ hasNoSubfields key'+ (aliasTyCon $fieldType dataField)+ selectionPosition+ where+ returnSelection selectionArguments selectionRec =+ pure [(key', fullRawSelection { selectionArguments, selectionRec })]+ validateSelection (key, RawSelectionField rawSelection@Selection { selectionPosition })+ = do+ (dataField, datatype, selectionArguments) <- getValidationData+ key+ rawSelection+ isLeaf datatype dataField+ pure+ [ ( key+ , rawSelection { selectionArguments, selectionRec = SelectionField }+ )+ ]+ where+ isLeaf dataType DataField { fieldType = TypeAlias { aliasTyCon } }+ | isEntNode dataType = pure ()+ | otherwise = failure+ $ subfieldsNotSelected key aliasTyCon selectionPosition+ validateSelection (_, Spread reference') =+ resolveSpread fragments' [typeName'] reference' >>= validateFragment+ validateSelection (_, InlineFragment fragment') =+ castFragmentType Nothing+ (fragmentPosition fragment')+ [typeName']+ fragment'+ >>= validateFragment
− src/Data/Morpheus/Validation/Query/Utils/Selection.hs
@@ -1,32 +0,0 @@-{-# LANGUAGE NamedFieldPuns #-}--module Data.Morpheus.Validation.Query.Utils.Selection- ( lookupFieldAsSelectionSet- , lookupSelectionField- , lookupUnionTypes- ) where--import Data.Morpheus.Error.Selection (cannotQueryField, hasNoSubfields)-import Data.Morpheus.Types.Internal.Base (Position)-import Data.Morpheus.Types.Internal.Data (DataField (..), DataObject, DataTyCon (..), DataTypeLib (..),- TypeAlias (..))-import Data.Morpheus.Types.Internal.Validation (Validation)-import Data.Morpheus.Validation.Internal.Utils (lookupField, lookupType)-import Data.Text (Text)--lookupUnionTypes :: Position -> Text -> DataTypeLib -> DataField -> Validation [DataObject]-lookupUnionTypes position' key' lib DataField {fieldType = TypeAlias {aliasTyCon = type'}} =- lookupType error' (union lib) type' >>= mapM (lookupType error' (object lib) . aliasTyCon . fieldType) . typeData- where- error' = hasNoSubfields key' type' position'--lookupFieldAsSelectionSet :: Position -> Text -> DataTypeLib -> DataField -> Validation DataObject-lookupFieldAsSelectionSet position key' lib DataField {fieldType = TypeAlias {aliasTyCon = type'}} =- lookupType error' (object lib) type'- where- error' = hasNoSubfields key' type' position--lookupSelectionField :: Position -> Text -> DataObject -> Validation DataField-lookupSelectionField position' key' DataTyCon {typeData = fields', typeName = name'} = lookupField key' fields' error'- where- error' = cannotQueryField key' name' position'
src/Data/Morpheus/Validation/Query/Validation.hs view
@@ -5,44 +5,51 @@ module Data.Morpheus.Validation.Query.Validation ( validateRequest- ) where+ )+where -import Data.Map (fromList)-import Data.Morpheus.Error.Mutation (mutationIsNotDefined)-import Data.Morpheus.Error.Subscription (subscriptionIsNotDefined)-import Data.Morpheus.Types.Internal.AST.Operation (Operation (..), RawOperation, ValidOperation,- getOperationName)-import Data.Morpheus.Types.Internal.Data (DataObject, DataTypeLib (..), OperationType (..))-import Data.Morpheus.Types.Internal.Validation (Validation)-import Data.Morpheus.Types.Types (GQLQueryRoot (..))-import Data.Morpheus.Validation.Internal.Utils (VALIDATION_MODE)-import Data.Morpheus.Validation.Query.Fragment (validateFragments)-import Data.Morpheus.Validation.Query.Selection (validateSelectionSet)-import Data.Morpheus.Validation.Query.Variable (resolveOperationVariables)+import Data.Map ( fromList )+import Data.Morpheus.Types.Internal.AST+ ( Operation(..)+ , ValidOperation+ , getOperationName+ , getOperationDataType+ , DataTypeLib(..)+ , GQLQuery(..)+ )+import Data.Morpheus.Types.Internal.Resolving+ ( Validation ) -getOperationDataType :: RawOperation -> DataTypeLib -> Validation DataObject-getOperationDataType Operation {operationType = Query} lib = pure $ snd $ query lib-getOperationDataType Operation {operationType = Mutation, operationPosition} lib =- case mutation lib of- Just (_, mutation') -> pure mutation'- Nothing -> Left $ mutationIsNotDefined operationPosition-getOperationDataType Operation {operationType = Subscription, operationPosition} lib =- case subscription lib of- Just (_, subscription') -> pure subscription'- Nothing -> Left $ subscriptionIsNotDefined operationPosition+import Data.Morpheus.Validation.Internal.Utils+ ( VALIDATION_MODE )+import Data.Morpheus.Validation.Query.Fragment+ ( validateFragments )+import Data.Morpheus.Validation.Query.Selection+ ( validateSelectionSet )+import Data.Morpheus.Validation.Query.Variable+ ( resolveOperationVariables ) -validateRequest :: DataTypeLib -> VALIDATION_MODE -> GQLQueryRoot -> Validation ValidOperation-validateRequest lib validationMode GQLQueryRoot { fragments- , inputVariables- , operation = rawOperation@Operation { operationName- , operationType- , operationSelection- , operationPosition- }- } = do- operationDataType <- getOperationDataType rawOperation lib- variables <- resolveOperationVariables lib fragments (fromList inputVariables) validationMode rawOperation- validateFragments lib fragments operationSelection- selection <-- validateSelectionSet lib fragments (getOperationName operationName) variables operationDataType operationSelection- pure $ Operation {operationName, operationType, operationArgs = [], operationSelection = selection, operationPosition}++validateRequest+ :: DataTypeLib -> VALIDATION_MODE -> GQLQuery -> Validation ValidOperation+validateRequest lib validationMode GQLQuery { fragments, inputVariables, operation = rawOperation@Operation { operationName, operationType, operationSelection, operationPosition } }+ = do+ operationDataType <- getOperationDataType rawOperation lib+ variables <- resolveOperationVariables lib+ fragments+ (fromList inputVariables)+ validationMode+ rawOperation+ validateFragments lib fragments operationSelection+ selection <- validateSelectionSet lib+ fragments+ (getOperationName operationName)+ variables+ operationDataType+ operationSelection+ pure $ Operation { operationName+ , operationType+ , operationArgs = []+ , operationSelection = selection+ , operationPosition+ }
src/Data/Morpheus/Validation/Query/Variable.hs view
@@ -1,100 +1,147 @@+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NamedFieldPuns #-} module Data.Morpheus.Validation.Query.Variable ( resolveOperationVariables- ) where+ )+where -import Data.List ((\\))-import qualified Data.Map as M (lookup)-import Data.Maybe (maybe)-import Data.Semigroup ((<>))-import Data.Text (Text)+import Data.List ( (\\) )+import qualified Data.Map as M+ ( lookup )+import Data.Maybe ( maybe )+import Data.Semigroup ( (<>) )+import Data.Text ( Text ) --- MORPHEUS-import Data.Morpheus.Error.Input (inputErrorMessage)-import Data.Morpheus.Error.Variable (uninitializedVariable, unknownType, unusedVariables,- variableGotInvalidValue)-import Data.Morpheus.Types.Internal.AST.Operation (DefaultValue, Operation (..), RawOperation,- ValidVariables, Variable (..), getOperationName)-import Data.Morpheus.Types.Internal.AST.RawSelection (Fragment (..), FragmentLib, RawArgument (..),- RawSelection (..), RawSelectionSet, Reference (..),- Selection (..))-import Data.Morpheus.Types.Internal.Base (EnhancedKey (..), Position)-import Data.Morpheus.Types.Internal.Data (DataType, DataTypeLib)-import Data.Morpheus.Types.Internal.Validation (Validation)-import Data.Morpheus.Types.Internal.Value (Value (..))-import Data.Morpheus.Types.Types (Variables)-import Data.Morpheus.Validation.Internal.Utils (VALIDATION_MODE (..), getInputType)-import Data.Morpheus.Validation.Internal.Value (validateInputValue)-import Data.Morpheus.Validation.Query.Fragment (getFragment)+import Data.Morpheus.Error.Input ( inputErrorMessage )+import Data.Morpheus.Error.Variable ( uninitializedVariable+ , unknownType+ , unusedVariables+ , variableGotInvalidValue+ )+import Data.Morpheus.Types.Internal.AST+ ( DefaultValue+ , Operation(..)+ , RawOperation+ , ValidVariables+ , Variable(..)+ , getOperationName+ , Fragment(..)+ , FragmentLib+ , RawArgument(..)+ , RawSelection(..)+ , RawSelectionSet+ , Selection(..)+ , Ref(..)+ , Position+ , DataType+ , DataTypeLib+ , lookupInputType+ , Variables+ , Value(..)+ )+import Data.Morpheus.Types.Internal.Resolving+ ( Validation+ , Failure(..)+ )+import Data.Morpheus.Validation.Internal.Utils+ ( VALIDATION_MODE(..) )+import Data.Morpheus.Validation.Internal.Value+ ( validateInputValue )+import Data.Morpheus.Validation.Query.Fragment+ ( getFragment ) getVariableType :: Text -> Position -> DataTypeLib -> Validation DataType-getVariableType type' position' lib' = getInputType type' lib' error'- where- error' = unknownType type' position'+getVariableType type' position' lib' = lookupInputType type' lib' error'+ where error' = unknownType type' position' concatMapM :: Monad m => (a -> m [b]) -> [a] -> m [b] concatMapM f = fmap concat . mapM f -allVariableReferences :: FragmentLib -> [RawSelectionSet] -> Validation [EnhancedKey]-allVariableReferences fragmentLib = concatMapM (concatMapM searchReferences)- where- referencesFromArgument :: (Text, RawArgument) -> [EnhancedKey]- referencesFromArgument (_, RawArgument {}) = []- referencesFromArgument (_, VariableReference Reference {referenceName, referencePosition}) =- [EnhancedKey referenceName referencePosition]- -- | search used variables in every arguments- searchReferences :: (Text, RawSelection) -> Validation [EnhancedKey]- searchReferences (_, RawSelectionSet Selection {selectionArguments, selectionRec}) =- getArgs <$> concatMapM searchReferences selectionRec- where- getArgs :: [EnhancedKey] -> [EnhancedKey]- getArgs x = concatMap referencesFromArgument selectionArguments <> x- searchReferences (_, InlineFragment Fragment {fragmentSelection}) = concatMapM searchReferences fragmentSelection- searchReferences (_, RawSelectionField Selection {selectionArguments}) =- return $ concatMap referencesFromArgument selectionArguments- searchReferences (_, Spread reference) =- getFragment reference fragmentLib >>= concatMapM searchReferences . fragmentSelection+allVariableRefs :: FragmentLib -> [RawSelectionSet] -> Validation [Ref]+allVariableRefs fragmentLib = concatMapM (concatMapM searchRefs)+ where+ referencesFromArgument :: (Text, RawArgument) -> [Ref]+ referencesFromArgument (_, RawArgument{}) = []+ referencesFromArgument (_, VariableRef Ref { refName, refPosition }) =+ [Ref refName refPosition]+ -- | search used variables in every arguments+ searchRefs :: (Text, RawSelection) -> Validation [Ref]+ searchRefs (_, RawSelectionSet Selection { selectionArguments, selectionRec })+ = getArgs <$> concatMapM searchRefs selectionRec+ where+ getArgs :: [Ref] -> [Ref]+ getArgs x = concatMap referencesFromArgument selectionArguments <> x+ searchRefs (_, InlineFragment Fragment { fragmentSelection }) =+ concatMapM searchRefs fragmentSelection+ searchRefs (_, RawSelectionField Selection { selectionArguments }) =+ return $ concatMap referencesFromArgument selectionArguments+ searchRefs (_, Spread reference) =+ getFragment reference fragmentLib+ >>= concatMapM searchRefs+ . fragmentSelection -resolveOperationVariables ::- DataTypeLib -> FragmentLib -> Variables -> VALIDATION_MODE -> RawOperation -> Validation ValidVariables-resolveOperationVariables typeLib lib root validationMode Operation {operationName, operationSelection, operationArgs} = do- allVariableReferences lib [operationSelection] >>= checkUnusedVariables- mapM (lookupAndValidateValueOnBody typeLib root validationMode) operationArgs- where- varToKey :: (Text, Variable a) -> EnhancedKey- varToKey (key', Variable {variablePosition}) = EnhancedKey key' variablePosition- --- checkUnusedVariables :: [EnhancedKey] -> Validation ()- checkUnusedVariables refs =- case map varToKey operationArgs \\ refs of- [] -> pure ()- unused' -> Left $ unusedVariables (getOperationName operationName) unused'+resolveOperationVariables+ :: DataTypeLib+ -> FragmentLib+ -> Variables+ -> VALIDATION_MODE+ -> RawOperation+ -> Validation ValidVariables+resolveOperationVariables typeLib lib root validationMode Operation { operationName, operationSelection, operationArgs }+ = do+ allVariableRefs lib [operationSelection] >>= checkUnusedVariables+ mapM (lookupAndValidateValueOnBody typeLib root validationMode)+ operationArgs+ where+ varToKey :: (Text, Variable a) -> Ref+ varToKey (key', Variable { variablePosition }) = Ref key' variablePosition+ --+ checkUnusedVariables :: [Ref] -> Validation ()+ checkUnusedVariables refs = case map varToKey operationArgs \\ refs of+ [] -> pure ()+ unused' ->+ failure $ unusedVariables (getOperationName operationName) unused' -lookupAndValidateValueOnBody ::- DataTypeLib -> Variables -> VALIDATION_MODE -> (Text, Variable DefaultValue) -> Validation (Text, Variable Value)-lookupAndValidateValueOnBody typeLib bodyVariables validationMode (key, var@Variable { variableType- , variablePosition- , isVariableRequired- , variableTypeWrappers- , variableValue = defaultValue- }) =- toVariable <$> (getVariableType variableType variablePosition typeLib >>= checkType getVariable defaultValue)- where- toVariable (varKey, variableValue) = (varKey, var {variableValue})- getVariable = M.lookup key bodyVariables- ------------------------------------------------------------------- checkType (Just variable) Nothing varType = validator varType variable- checkType (Just variable) (Just defValue) varType = validator varType defValue >> validator varType variable- checkType Nothing (Just defValue) varType = validator varType defValue- checkType Nothing Nothing varType- | validationMode /= WITHOUT_VARIABLES && isVariableRequired =- Left $ uninitializedVariable variablePosition variableType key- | otherwise = returnNull- where- returnNull = maybe (pure (key, Null)) (validator varType) (M.lookup key bodyVariables)- ------------------------------------------------------------------------------------------------ validator varType varValue =- case validateInputValue typeLib [] variableTypeWrappers varType (key, varValue) of- Left message -> Left $ variableGotInvalidValue key (inputErrorMessage message) variablePosition- Right value -> pure (key, value)+lookupAndValidateValueOnBody+ :: DataTypeLib+ -> Variables+ -> VALIDATION_MODE+ -> (Text, Variable DefaultValue)+ -> Validation (Text, Variable Value)+lookupAndValidateValueOnBody typeLib bodyVariables validationMode (key, var@Variable { variableType, variablePosition, isVariableRequired, variableTypeWrappers, variableValue = defaultValue })+ = toVariable+ <$> ( getVariableType variableType variablePosition typeLib+ >>= checkType getVariable defaultValue+ )+ where+ toVariable (varKey, variableValue) = (varKey, var { variableValue })+ getVariable = M.lookup key bodyVariables+ ------------------------------------------------------------------+ checkType (Just variable) Nothing varType = validator varType variable+ checkType (Just variable) (Just defValue) varType =+ validator varType defValue >> validator varType variable+ checkType Nothing (Just defValue) varType = validator varType defValue+ checkType Nothing Nothing varType+ | validationMode /= WITHOUT_VARIABLES && isVariableRequired+ = failure $ uninitializedVariable variablePosition variableType key+ | otherwise+ = returnNull+ where+ returnNull =+ maybe (pure (key, Null)) (validator varType) (M.lookup key bodyVariables)+ -----------------------------------------------------------------------------------------------+ validator varType varValue =+ case+ validateInputValue typeLib+ []+ variableTypeWrappers+ varType+ (key, varValue)+ of+ Left message -> failure $ variableGotInvalidValue+ key+ (inputErrorMessage message)+ variablePosition+ Right value -> pure (key, value)
− test/Feature/Holistic/API.gql
@@ -1,51 +0,0 @@-enum TestEnum {- EnumA- EnumB- EnumC-}--input NestedInputObject {- fieldTestID: ID!-}--input TestInputObject {- fieldTestScalar: TestScalar!- fieldNestedInputObject: [NestedInputObject]!-}--input Coordinates {- latitude: TestScalar!- longitude: Int!-}--type Address {- city: String!- street(- argInputObject: TestInputObject!- argMaybeString: String- ): [[[[String!]!]!]]- houseNumber: Int!-}--union TestUnion = User | Address--type User {- name: String!- email: String!- address(coordinates: Coordinates!, comment: String): Address!- office(zipCode: [Int!], cityID: TestEnum!): Address!- friend: User-}--type Query {- user: User!- testUnion: TestUnion-}--type Mutation {- createUser(userID: String!, userName: String): User!-}--type Subscription {- newUser: User!-}
test/Feature/Holistic/API.hs view
@@ -12,16 +12,27 @@ module Feature.Holistic.API ( api , rootResolver- ) where+ )+where -import Data.Morpheus (interpreter)-import Data.Morpheus.Document (importGQLDocument)-import Data.Morpheus.Kind (SCALAR)-import Data.Morpheus.Types (Event, GQLRequest, GQLResponse, GQLRootResolver (..), GQLScalar (..),- GQLType (..), ID (..), ScalarValue (..), Resolver (..), constRes )-import Data.Text (Text)-import GHC.Generics (Generic)+import Data.Morpheus ( interpreter )+import Data.Morpheus.Document ( importGQLDocument )+import Data.Morpheus.Kind ( SCALAR )+import Data.Morpheus.Types ( Event+ , GQLRequest+ , GQLResponse+ , GQLRootResolver(..)+ , GQLScalar(..)+ , GQLType(..)+ , ID(..)+ , ScalarValue(..)+ , Resolver(..)+ , constRes+ )+import Data.Text ( Text )+import GHC.Generics ( Generic ) + data TestScalar = TestScalar Int Int@@ -40,29 +51,31 @@ type EVENT = Event Channel () -importGQLDocument "test/Feature/Holistic/API.gql"+importGQLDocument "test/Feature/Holistic/schema.gql" -rootResolver ::- GQLRootResolver IO EVENT Query Mutation Subscription-rootResolver =- GQLRootResolver- { queryResolver = Query {user, testUnion = constRes Nothing}- , mutationResolver = Mutation {createUser = user}- , subscriptionResolver = Subscription {newUser = const SubResolver {subChannels = [Channel], subResolver = user}}- }- where- user _ =- pure - User- { name = constRes "testName"- , email = constRes ""- , address = resolveAddress- , office = resolveAddress- , friend = constRes Nothing- }- where- resolveAddress _ =- pure Address {city = constRes "", houseNumber = constRes 0, street = constRes Nothing}+rootResolver :: GQLRootResolver IO EVENT Query Mutation Subscription+rootResolver = GQLRootResolver+ { queryResolver = Query { user, testUnion = constRes Nothing }+ , mutationResolver = Mutation { createUser = user }+ , subscriptionResolver = Subscription+ { newUser = const SubResolver+ { subChannels = [Channel]+ , subResolver = user+ }+ }+ }+ where+ user _ = pure User { name = constRes "testName"+ , email = constRes ""+ , address = resolveAddress+ , office = resolveAddress+ , friend = constRes Nothing+ }+ where+ resolveAddress _ = pure Address { city = constRes ""+ , houseNumber = constRes 0+ , street = constRes Nothing+ } api :: GQLRequest -> IO GQLResponse api = interpreter rootResolver
test/Feature/Holistic/cases.json view
@@ -198,5 +198,29 @@ { "path": "parsing/directive/notOnVariable", "description": "fail when: directive is used in Variable Definition"+ },+ {+ "path": "introspection/description/object",+ "description": "check object descriptions, type, field, args"+ },+ {+ "path": "introspection/description/inputObject",+ "description": "check inputObject descriptions, type, field"+ },+ {+ "path": "introspection/description/union",+ "description": "check union descriptions, type"+ },+ {+ "path": "introspection/description/enum",+ "description": "check enum descriptions, type, enumValue"+ },+ {+ "path": "introspection/deprecated/enumValue",+ "description": "check if deprecated works with enumValue"+ },+ {+ "path": "introspection/deprecated/field",+ "description": "check if deprecated works with object fields" } ]
test/Feature/Holistic/fragment/loopingFragment/response.json view
@@ -1,20 +1,11 @@ { "errors": [ {- "message": "Cannot spread fragment \"A\" within itself via A,C,A.",+ "message": "Cannot spread fragment \"A\" within itself via A,A,C.", "locations": [- {- "line": 7,- "column": 10- },- {- "line": 9,- "column": 3- },- {- "line": 17,- "column": 3- }+ { "line": 17, "column": 3 },+ { "line": 7, "column": 10 },+ { "line": 9, "column": 3 } ] } ]
+ test/Feature/Holistic/introspection/deprecated/enumValue/query.gql view
@@ -0,0 +1,9 @@+{+ __type(name: "TestEnum") {+ enumValues {+ name+ isDeprecated+ deprecationReason+ }+ }+}
+ test/Feature/Holistic/introspection/deprecated/enumValue/response.json view
@@ -0,0 +1,15 @@+{+ "data": {+ "__type": {+ "enumValues": [+ { "name": "EnumA", "isDeprecated": false, "deprecationReason": null },+ {+ "name": "EnumB",+ "isDeprecated": true,+ "deprecationReason": "test deprecation enumValue"+ },+ { "name": "EnumC", "isDeprecated": true, "deprecationReason": null }+ ]+ }+ }+}
+ test/Feature/Holistic/introspection/deprecated/field/query.gql view
@@ -0,0 +1,9 @@+{+ __type(name: "Address") {+ fields {+ name+ isDeprecated+ deprecationReason+ }+ }+}
+ test/Feature/Holistic/introspection/deprecated/field/response.json view
@@ -0,0 +1,19 @@+{+ "data": {+ "__type": {+ "fields": [+ {+ "name": "city",+ "isDeprecated": true,+ "deprecationReason": "test deprecation field with reason"+ },+ { "name": "street", "isDeprecated": true, "deprecationReason": null },+ {+ "name": "houseNumber",+ "isDeprecated": false,+ "deprecationReason": null+ }+ ]+ }+ }+}
+ test/Feature/Holistic/introspection/description/enum/query.gql view
@@ -0,0 +1,9 @@+{+ __type(name: "TestEnum") {+ description+ enumValues {+ name+ description+ }+ }+}
+ test/Feature/Holistic/introspection/description/enum/response.json view
@@ -0,0 +1,12 @@+{+ "data": {+ "__type": {+ "description": "type Description: TestEnum\n\nsome random enums for test",+ "enumValues": [+ { "name": "EnumA", "description": "enumValue Description: EnumA" },+ { "name": "EnumB", "description": null },+ { "name": "EnumC", "description": "enumValue Description: EnumC" }+ ]+ }+ }+}
+ test/Feature/Holistic/introspection/description/inputObject/query.gql view
@@ -0,0 +1,9 @@+{+ __type(name: "Coordinates") {+ description+ inputFields {+ name+ description+ }+ }+}
+ test/Feature/Holistic/introspection/description/inputObject/response.json view
@@ -0,0 +1,17 @@+{+ "data": {+ "__type": {+ "description": "type Description: Coordinates\n\nsome random text",+ "inputFields": [+ {+ "name": "latitude",+ "description": "inputValue Description: latitude"+ },+ {+ "name": "longitude",+ "description": "inputValue Description: longitude\n some random inputValue details"+ }+ ]+ }+ }+}
+ test/Feature/Holistic/introspection/description/object/query.gql view
@@ -0,0 +1,13 @@+{+ __type(name: "Address") {+ description+ fields {+ name+ description+ args {+ name+ description+ }+ }+ }+}
+ test/Feature/Holistic/introspection/description/object/response.json view
@@ -0,0 +1,26 @@+{+ "data": {+ "__type": {+ "description": "type Description:\n\n Address",+ "fields": [+ {+ "name": "city",+ "description": "field Description: city",+ "args": []+ },+ {+ "name": "street",+ "description": null,+ "args": [+ {+ "name": "argInputObject",+ "description": "argument Description: inputObject"+ },+ { "name": "argMaybeString", "description": null }+ ]+ },+ { "name": "houseNumber", "description": null, "args": [] }+ ]+ }+ }+}
+ test/Feature/Holistic/introspection/description/union/query.gql view
@@ -0,0 +1,5 @@+{+ __type(name: "TestUnion") {+ description+ }+}
+ test/Feature/Holistic/introspection/description/union/response.json view
@@ -0,0 +1,7 @@+{+ "data": {+ "__type": {+ "description": "type Description: TestUnion\n\nsome random text for union type"+ }+ }+}
test/Feature/Holistic/introspection/kinds/ENUM/response.json view
@@ -7,21 +7,13 @@ "inputFields": null, "interfaces": null, "enumValues": [- {- "name": "EnumA",- "isDeprecated": false,- "deprecationReason": null- },+ { "name": "EnumA", "isDeprecated": false, "deprecationReason": null }, { "name": "EnumB",- "isDeprecated": false,- "deprecationReason": null+ "isDeprecated": true,+ "deprecationReason": "test deprecation enumValue" },- {- "name": "EnumC",- "isDeprecated": false,- "deprecationReason": null- }+ { "name": "EnumC", "isDeprecated": true, "deprecationReason": null } ], "possibleTypes": null }
test/Feature/Holistic/introspection/kinds/OBJECT/response.json view
@@ -10,14 +10,10 @@ "type": { "kind": "NON_NULL", "name": null,- "ofType": {- "kind": "SCALAR",- "name": "String",- "ofType": null- }+ "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } },- "isDeprecated": false,- "deprecationReason": null+ "isDeprecated": true,+ "deprecationReason": "test deprecation field with reason" }, { "name": "street",@@ -37,11 +33,7 @@ }, { "name": "argMaybeString",- "type": {- "kind": "SCALAR",- "name": "String",- "ofType": null- },+ "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "defaultValue": null } ],@@ -66,10 +58,7 @@ "ofType": { "kind": "NON_NULL", "name": null,- "ofType": {- "kind": "SCALAR",- "name": "String"- }+ "ofType": { "kind": "SCALAR", "name": "String" } } } }@@ -77,7 +66,7 @@ } } },- "isDeprecated": false,+ "isDeprecated": true, "deprecationReason": null }, {@@ -86,11 +75,7 @@ "type": { "kind": "NON_NULL", "name": null,- "ofType": {- "kind": "SCALAR",- "name": "Int",- "ofType": null- }+ "ofType": { "kind": "SCALAR", "name": "Int", "ofType": null } }, "isDeprecated": false, "deprecationReason": null
test/Feature/Holistic/parsing/complex/response.json view
@@ -2,12 +2,27 @@ "errors": [ { "message": "Argument coordinates got invalid value ;on longitude Expected type \"Int\" found [null,[[],[{\"uid\":\"1\"}]]].",- "locations": [- {- "line": 8,- "column": 36- }- ]+ "locations": [{ "line": 8, "column": 36 }]+ },+ {+ "message": "Argument coordinates got invalid value ;on longitude Expected type \"Int\" found [].",+ "locations": [{ "line": 11, "column": 36 }]+ },+ {+ "message": "Variable \"$v\" of type \"[[[ID!]]!]\" used in position expecting type \"[Int!]\".",+ "locations": [{ "line": 15, "column": 21 }]+ },+ {+ "message": "Cannot query field \"home\" on type \"User\".",+ "locations": [{ "line": 25, "column": 5 }]+ },+ {+ "message": "Cannot query field \"myUnion\" on type \"User\".",+ "locations": [{ "line": 26, "column": 18 }]+ },+ {+ "message": "Cannot query field \"myUnion\" on type \"User\".",+ "locations": [{ "line": 32, "column": 18 }] } ] }
test/Feature/Holistic/parsing/extraCommas/response.json view
@@ -2,12 +2,31 @@ "errors": [ { "message": "Required Argument: \"coordinates\" was not Defined",- "locations": [- {- "line": 2,- "column": 18- }- ]+ "locations": [{ "line": 2, "column": 18 }]+ },+ {+ "message": "Required Argument: \"coordinates\" was not Defined",+ "locations": [{ "line": 4, "column": 18 }]+ },+ {+ "message": "Required Argument: \"coordinates\" was not Defined",+ "locations": [{ "line": 6, "column": 18 }]+ },+ {+ "message": "Required Argument: \"coordinates\" was not Defined",+ "locations": [{ "line": 10, "column": 9 }]+ },+ {+ "message": "Required Argument: \"coordinates\" was not Defined",+ "locations": [{ "line": 14, "column": 14 }]+ },+ {+ "message": "Required Argument: \"coordinates\" was not Defined",+ "locations": [{ "line": 18, "column": 15 }]+ },+ {+ "message": "Required Argument: \"coordinates\" was not Defined",+ "locations": [{ "line": 22, "column": 15 }] } ] }
+ test/Feature/Holistic/schema.gql view
@@ -0,0 +1,103 @@+"""+type Description: TestEnum++some random enums for test+"""+enum TestEnum {+ """+ enumValue Description: EnumA+ """+ EnumA+ EnumB @deprecated(reason: "test deprecation enumValue")+ """+ enumValue Description: EnumC+ """+ EnumC @deprecated+}++input NestedInputObject {+ fieldTestID: ID!+}++input TestInputObject {+ fieldTestScalar: TestScalar!+ fieldNestedInputObject: [NestedInputObject]!+}++"""+type Description: Coordinates++some random text+"""+input Coordinates {+ """+ inputValue Description: latitude+ """+ latitude: TestScalar!+ """+ inputValue Description: longitude+ some random inputValue details+ """+ longitude: Int!+}++"""+type Description:++ Address+"""+type Address {+ """+ field Description: city+ """+ city: String! @deprecated(reason: "test deprecation field with reason")+ street(+ """+ argument Description: inputObject+ """+ argInputObject: TestInputObject!+ argMaybeString: String+ ): [[[[String!]!]!]] @deprecated+ houseNumber: Int!+}++"""+type Description: TestUnion++some random text for union type+"""+union TestUnion = User | Address++"""+User++MultilineDescription++Test+"""+type User {+ """+ field description: name+ """+ name: String!+ """+ field description: email+ """+ email: String!+ address(coordinates: Coordinates!, comment: String): Address!+ office(zipCode: [Int!], cityID: TestEnum!): Address!+ friend: User+}++type Query {+ user: User!+ testUnion: TestUnion+}++type Mutation {+ createUser(userID: String!, userName: String): User!+}++type Subscription {+ newUser: User!+}
test/Feature/InputType/API.hs view
@@ -6,14 +6,20 @@ module Feature.InputType.API ( api- ) where+ )+where -import Data.Morpheus (interpreter)-import Data.Morpheus.Kind (OBJECT)-import Data.Morpheus.Types (GQLRequest, GQLResponse, GQLRootResolver (..), GQLType (..), IORes,- Undefined (..))-import Data.Text (Text)-import GHC.Generics (Generic)+import Data.Morpheus ( interpreter )+import Data.Morpheus.Kind ( OBJECT )+import Data.Morpheus.Types ( GQLRequest+ , GQLResponse+ , GQLRootResolver(..)+ , GQLType(..)+ , IORes+ , Undefined(..)+ )+import Data.Text ( Text )+import GHC.Generics ( Generic ) data F1Args = F1Args { arg1 :: Text@@ -38,12 +44,15 @@ } deriving (Generic, GQLType) rootResolver :: GQLRootResolver IO () Query Undefined Undefined-rootResolver =- GQLRootResolver- { queryResolver = Query {q1 = A {a1 = const $ return "a1Test", a2 = const $ return 1}}- , mutationResolver = Undefined- , subscriptionResolver = Undefined- }+rootResolver = GQLRootResolver+ { queryResolver = Query+ { q1 = A { a1 = const $ return "a1Test"+ , a2 = const $ return 1+ }+ }+ , mutationResolver = Undefined+ , subscriptionResolver = Undefined+ } api :: GQLRequest -> IO GQLResponse api = interpreter rootResolver
test/Rendering/TestSchemaRendering.hs view
@@ -16,4 +16,4 @@ where schema = toGraphQLDocument schemaProxy expected =- "type Query { \n user: User!\n testUnion: TestUnion\n}\n\nscalar TestScalar\n\nenum TestEnum { \n EnumA\n EnumB\n EnumC\n}\n\ninput Coordinates { \n latitude: TestScalar!\n longitude: Int!\n}\n\ntype Address { \n street: [[[[String!]!]!]]\n}\n\ntype User { \n type: String!\n address(coordinates: Coordinates!, type: String): Int!\n friend(id: ID!, cityID: TestEnum): User!\n}\n\nunion TestUnion =\n User!\n | Address!"+ "type Query { \n user: User!\n testUnion: TestUnion\n}\n\nenum TestEnum { \n EnumA\n EnumB\n EnumC\n}\n\ntype Address { \n street: [[[[String!]!]!]]\n}\n\ninput Coordinates { \n latitude: TestScalar!\n longitude: Int!\n}\n\ntype User { \n type: String!\n address(coordinates: Coordinates!, type: String): Int!\n friend(id: ID!, cityID: TestEnum): User!\n}\n\nunion TestUnion =\n User\n | Address\n\nscalar TestScalar"