packages feed

graphql 0.6.1.0 → 0.7.0.0

raw patch · 26 files changed

+1631/−660 lines, 26 filesdep +QuickCheckdep +parser-combinatorsdep ~aesondep ~containersdep ~hspec-megaparsecPVP ok

version bump matches the API change (PVP)

Dependencies added: QuickCheck, parser-combinators

Dependency ranges changed: aeson, containers, hspec-megaparsec, megaparsec, text

API changes (from Hackage documentation)

- Language.GraphQL.AST: Argument :: Name -> Value -> Argument
- Language.GraphQL.AST: Boolean :: Bool -> Value
- Language.GraphQL.AST: DefinitionFragment :: FragmentDefinition -> Definition
- Language.GraphQL.AST: DefinitionOperation :: OperationDefinition -> Definition
- Language.GraphQL.AST: Directive :: Name -> [Argument] -> Directive
- Language.GraphQL.AST: Enum :: Name -> Value
- Language.GraphQL.AST: Field :: Maybe Alias -> Name -> [Argument] -> [Directive] -> SelectionSetOpt -> Field
- Language.GraphQL.AST: Float :: Double -> Value
- Language.GraphQL.AST: FragmentDefinition :: Name -> TypeCondition -> [Directive] -> SelectionSet -> FragmentDefinition
- Language.GraphQL.AST: FragmentSpread :: Name -> [Directive] -> FragmentSpread
- Language.GraphQL.AST: InlineFragment :: Maybe TypeCondition -> [Directive] -> SelectionSet -> InlineFragment
- Language.GraphQL.AST: Int :: Int32 -> Value
- Language.GraphQL.AST: List :: [Value] -> Value
- Language.GraphQL.AST: Mutation :: OperationType
- Language.GraphQL.AST: NonNullTypeList :: Type -> NonNullType
- Language.GraphQL.AST: NonNullTypeNamed :: Name -> NonNullType
- Language.GraphQL.AST: Null :: Value
- Language.GraphQL.AST: Object :: [ObjectField] -> Value
- Language.GraphQL.AST: ObjectField :: Name -> Value -> ObjectField
- Language.GraphQL.AST: OperationDefinition :: OperationType -> Maybe Name -> [VariableDefinition] -> [Directive] -> SelectionSet -> OperationDefinition
- Language.GraphQL.AST: OperationSelectionSet :: SelectionSet -> OperationDefinition
- Language.GraphQL.AST: Query :: OperationType
- Language.GraphQL.AST: SelectionField :: Field -> Selection
- Language.GraphQL.AST: SelectionFragmentSpread :: FragmentSpread -> Selection
- Language.GraphQL.AST: SelectionInlineFragment :: InlineFragment -> Selection
- Language.GraphQL.AST: String :: Text -> Value
- Language.GraphQL.AST: TypeList :: Type -> Type
- Language.GraphQL.AST: TypeNamed :: Name -> Type
- Language.GraphQL.AST: TypeNonNull :: NonNullType -> Type
- Language.GraphQL.AST: Variable :: Name -> Value
- Language.GraphQL.AST: VariableDefinition :: Name -> Type -> Maybe Value -> VariableDefinition
- Language.GraphQL.AST: data Argument
- Language.GraphQL.AST: data Definition
- Language.GraphQL.AST: data Directive
- Language.GraphQL.AST: data Field
- Language.GraphQL.AST: data FragmentDefinition
- Language.GraphQL.AST: data FragmentSpread
- Language.GraphQL.AST: data InlineFragment
- Language.GraphQL.AST: data NonNullType
- Language.GraphQL.AST: data ObjectField
- Language.GraphQL.AST: data OperationDefinition
- Language.GraphQL.AST: data OperationType
- Language.GraphQL.AST: data Selection
- Language.GraphQL.AST: data Type
- Language.GraphQL.AST: data Value
- Language.GraphQL.AST: data VariableDefinition
- Language.GraphQL.AST: instance GHC.Classes.Eq Language.GraphQL.AST.Argument
- Language.GraphQL.AST: instance GHC.Classes.Eq Language.GraphQL.AST.Definition
- Language.GraphQL.AST: instance GHC.Classes.Eq Language.GraphQL.AST.Directive
- Language.GraphQL.AST: instance GHC.Classes.Eq Language.GraphQL.AST.Field
- Language.GraphQL.AST: instance GHC.Classes.Eq Language.GraphQL.AST.FragmentDefinition
- Language.GraphQL.AST: instance GHC.Classes.Eq Language.GraphQL.AST.FragmentSpread
- Language.GraphQL.AST: instance GHC.Classes.Eq Language.GraphQL.AST.InlineFragment
- Language.GraphQL.AST: instance GHC.Classes.Eq Language.GraphQL.AST.NonNullType
- Language.GraphQL.AST: instance GHC.Classes.Eq Language.GraphQL.AST.ObjectField
- Language.GraphQL.AST: instance GHC.Classes.Eq Language.GraphQL.AST.OperationDefinition
- Language.GraphQL.AST: instance GHC.Classes.Eq Language.GraphQL.AST.OperationType
- Language.GraphQL.AST: instance GHC.Classes.Eq Language.GraphQL.AST.Selection
- Language.GraphQL.AST: instance GHC.Classes.Eq Language.GraphQL.AST.Type
- Language.GraphQL.AST: instance GHC.Classes.Eq Language.GraphQL.AST.Value
- Language.GraphQL.AST: instance GHC.Classes.Eq Language.GraphQL.AST.VariableDefinition
- Language.GraphQL.AST: instance GHC.Show.Show Language.GraphQL.AST.Argument
- Language.GraphQL.AST: instance GHC.Show.Show Language.GraphQL.AST.Definition
- Language.GraphQL.AST: instance GHC.Show.Show Language.GraphQL.AST.Directive
- Language.GraphQL.AST: instance GHC.Show.Show Language.GraphQL.AST.Field
- Language.GraphQL.AST: instance GHC.Show.Show Language.GraphQL.AST.FragmentDefinition
- Language.GraphQL.AST: instance GHC.Show.Show Language.GraphQL.AST.FragmentSpread
- Language.GraphQL.AST: instance GHC.Show.Show Language.GraphQL.AST.InlineFragment
- Language.GraphQL.AST: instance GHC.Show.Show Language.GraphQL.AST.NonNullType
- Language.GraphQL.AST: instance GHC.Show.Show Language.GraphQL.AST.ObjectField
- Language.GraphQL.AST: instance GHC.Show.Show Language.GraphQL.AST.OperationDefinition
- Language.GraphQL.AST: instance GHC.Show.Show Language.GraphQL.AST.OperationType
- Language.GraphQL.AST: instance GHC.Show.Show Language.GraphQL.AST.Selection
- Language.GraphQL.AST: instance GHC.Show.Show Language.GraphQL.AST.Type
- Language.GraphQL.AST: instance GHC.Show.Show Language.GraphQL.AST.Value
- Language.GraphQL.AST: instance GHC.Show.Show Language.GraphQL.AST.VariableDefinition
- Language.GraphQL.AST: type Alias = Name
- Language.GraphQL.AST: type Document = NonEmpty Definition
- Language.GraphQL.AST: type Name = Text
- Language.GraphQL.AST: type SelectionSet = NonEmpty Selection
- Language.GraphQL.AST: type SelectionSetOpt = [Selection]
- Language.GraphQL.AST: type TypeCondition = Name
- Language.GraphQL.AST.Core: Argument :: Name -> Value -> Argument
- Language.GraphQL.AST.Core: data Argument
- Language.GraphQL.AST.Core: instance GHC.Classes.Eq Language.GraphQL.AST.Core.Argument
- Language.GraphQL.AST.Core: instance GHC.Show.Show Language.GraphQL.AST.Core.Argument
- Language.GraphQL.Schema: Argument :: Name -> Value -> Argument
- Language.GraphQL.Schema: data Argument
- Language.GraphQL.Schema: objectA :: MonadIO m => Name -> ([Argument] -> ActionT m [Resolver m]) -> Resolver m
- Language.GraphQL.Schema: scalarA :: (MonadIO m, ToJSON a) => Name -> ([Argument] -> ActionT m a) -> Resolver m
- Language.GraphQL.Schema: wrappedObjectA :: MonadIO m => Name -> ([Argument] -> ActionT m (Wrapping [Resolver m])) -> Resolver m
- Language.GraphQL.Schema: wrappedScalarA :: (MonadIO m, ToJSON a) => Name -> ([Argument] -> ActionT m (Wrapping a)) -> Resolver m
+ Language.GraphQL.AST.Core: instance GHC.Base.Monoid Language.GraphQL.AST.Core.Arguments
+ Language.GraphQL.AST.Core: instance GHC.Base.Semigroup Language.GraphQL.AST.Core.Arguments
+ Language.GraphQL.AST.DirectiveLocation: ArgumentDefinition :: TypeSystemDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: Enum :: TypeSystemDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: EnumValue :: TypeSystemDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: ExecutableDirectiveLocation :: ExecutableDirectiveLocation -> DirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: Field :: ExecutableDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: FieldDefinition :: TypeSystemDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: FragmentDefinition :: ExecutableDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: FragmentSpread :: ExecutableDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: InlineFragment :: ExecutableDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: InputFieldDefinition :: TypeSystemDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: InputObject :: TypeSystemDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: Interface :: TypeSystemDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: Mutation :: ExecutableDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: Object :: TypeSystemDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: Query :: ExecutableDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: Scalar :: TypeSystemDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: Schema :: TypeSystemDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: Subscription :: ExecutableDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: TypeSystemDirectiveLocation :: TypeSystemDirectiveLocation -> DirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: Union :: TypeSystemDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: data DirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: data ExecutableDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: data TypeSystemDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: instance GHC.Classes.Eq Language.GraphQL.AST.DirectiveLocation.DirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: instance GHC.Classes.Eq Language.GraphQL.AST.DirectiveLocation.ExecutableDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: instance GHC.Classes.Eq Language.GraphQL.AST.DirectiveLocation.TypeSystemDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: instance GHC.Show.Show Language.GraphQL.AST.DirectiveLocation.DirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: instance GHC.Show.Show Language.GraphQL.AST.DirectiveLocation.ExecutableDirectiveLocation
+ Language.GraphQL.AST.DirectiveLocation: instance GHC.Show.Show Language.GraphQL.AST.DirectiveLocation.TypeSystemDirectiveLocation
+ Language.GraphQL.AST.Document: Argument :: Name -> Value -> Argument
+ Language.GraphQL.AST.Document: ArgumentsDefinition :: [InputValueDefinition] -> ArgumentsDefinition
+ Language.GraphQL.AST.Document: Boolean :: Bool -> Value
+ Language.GraphQL.AST.Document: DefinitionFragment :: FragmentDefinition -> ExecutableDefinition
+ Language.GraphQL.AST.Document: DefinitionOperation :: OperationDefinition -> ExecutableDefinition
+ Language.GraphQL.AST.Document: Description :: Maybe Text -> Description
+ Language.GraphQL.AST.Document: Directive :: Name -> [Argument] -> Directive
+ Language.GraphQL.AST.Document: DirectiveDefinition :: Description -> Name -> ArgumentsDefinition -> NonEmpty DirectiveLocation -> TypeSystemDefinition
+ Language.GraphQL.AST.Document: Enum :: Name -> Value
+ Language.GraphQL.AST.Document: EnumTypeDefinition :: Description -> Name -> [Directive] -> [EnumValueDefinition] -> TypeDefinition
+ Language.GraphQL.AST.Document: EnumTypeDirectivesExtension :: Name -> NonEmpty Directive -> TypeExtension
+ Language.GraphQL.AST.Document: EnumTypeEnumValuesDefinitionExtension :: Name -> [Directive] -> NonEmpty EnumValueDefinition -> TypeExtension
+ Language.GraphQL.AST.Document: EnumValueDefinition :: Description -> Name -> [Directive] -> EnumValueDefinition
+ Language.GraphQL.AST.Document: ExecutableDefinition :: ExecutableDefinition -> Definition
+ Language.GraphQL.AST.Document: Field :: Maybe Alias -> Name -> [Argument] -> [Directive] -> SelectionSetOpt -> Selection
+ Language.GraphQL.AST.Document: FieldDefinition :: Description -> Name -> ArgumentsDefinition -> Type -> [Directive] -> FieldDefinition
+ Language.GraphQL.AST.Document: Float :: Double -> Value
+ Language.GraphQL.AST.Document: FragmentDefinition :: Name -> TypeCondition -> [Directive] -> SelectionSet -> FragmentDefinition
+ Language.GraphQL.AST.Document: FragmentSpread :: Name -> [Directive] -> Selection
+ Language.GraphQL.AST.Document: ImplementsInterfaces :: t NamedType -> ImplementsInterfaces t
+ Language.GraphQL.AST.Document: InlineFragment :: Maybe TypeCondition -> [Directive] -> SelectionSet -> Selection
+ Language.GraphQL.AST.Document: InputObjectTypeDefinition :: Description -> Name -> [Directive] -> [InputValueDefinition] -> TypeDefinition
+ Language.GraphQL.AST.Document: InputObjectTypeDirectivesExtension :: Name -> NonEmpty Directive -> TypeExtension
+ Language.GraphQL.AST.Document: InputObjectTypeInputFieldsDefinitionExtension :: Name -> [Directive] -> NonEmpty InputValueDefinition -> TypeExtension
+ Language.GraphQL.AST.Document: InputValueDefinition :: Description -> Name -> Type -> Maybe Value -> [Directive] -> InputValueDefinition
+ Language.GraphQL.AST.Document: Int :: Int32 -> Value
+ Language.GraphQL.AST.Document: InterfaceTypeDefinition :: Description -> Name -> [Directive] -> [FieldDefinition] -> TypeDefinition
+ Language.GraphQL.AST.Document: InterfaceTypeDirectivesExtension :: Name -> NonEmpty Directive -> TypeExtension
+ Language.GraphQL.AST.Document: InterfaceTypeFieldsDefinitionExtension :: Name -> [Directive] -> NonEmpty FieldDefinition -> TypeExtension
+ Language.GraphQL.AST.Document: List :: [Value] -> Value
+ Language.GraphQL.AST.Document: Mutation :: OperationType
+ Language.GraphQL.AST.Document: NonNullTypeList :: Type -> NonNullType
+ Language.GraphQL.AST.Document: NonNullTypeNamed :: Name -> NonNullType
+ Language.GraphQL.AST.Document: Null :: Value
+ Language.GraphQL.AST.Document: Object :: [ObjectField] -> Value
+ Language.GraphQL.AST.Document: ObjectField :: Name -> Value -> ObjectField
+ Language.GraphQL.AST.Document: ObjectTypeDefinition :: Description -> Name -> ImplementsInterfaces [] -> [Directive] -> [FieldDefinition] -> TypeDefinition
+ Language.GraphQL.AST.Document: ObjectTypeDirectivesExtension :: Name -> ImplementsInterfaces [] -> NonEmpty Directive -> TypeExtension
+ Language.GraphQL.AST.Document: ObjectTypeFieldsDefinitionExtension :: Name -> ImplementsInterfaces [] -> [Directive] -> NonEmpty FieldDefinition -> TypeExtension
+ Language.GraphQL.AST.Document: ObjectTypeImplementsInterfacesExtension :: Name -> ImplementsInterfaces NonEmpty -> TypeExtension
+ Language.GraphQL.AST.Document: OperationDefinition :: OperationType -> Maybe Name -> [VariableDefinition] -> [Directive] -> SelectionSet -> OperationDefinition
+ Language.GraphQL.AST.Document: OperationTypeDefinition :: OperationType -> NamedType -> OperationTypeDefinition
+ Language.GraphQL.AST.Document: Query :: OperationType
+ Language.GraphQL.AST.Document: ScalarTypeDefinition :: Description -> Name -> [Directive] -> TypeDefinition
+ Language.GraphQL.AST.Document: ScalarTypeExtension :: Name -> NonEmpty Directive -> TypeExtension
+ Language.GraphQL.AST.Document: SchemaDefinition :: [Directive] -> NonEmpty OperationTypeDefinition -> TypeSystemDefinition
+ Language.GraphQL.AST.Document: SchemaDirectivesExtension :: NonEmpty Directive -> SchemaExtension
+ Language.GraphQL.AST.Document: SchemaExtension :: SchemaExtension -> TypeSystemExtension
+ Language.GraphQL.AST.Document: SchemaOperationExtension :: [Directive] -> NonEmpty OperationTypeDefinition -> SchemaExtension
+ Language.GraphQL.AST.Document: SelectionSet :: SelectionSet -> OperationDefinition
+ Language.GraphQL.AST.Document: String :: Text -> Value
+ Language.GraphQL.AST.Document: TypeDefinition :: TypeDefinition -> TypeSystemDefinition
+ Language.GraphQL.AST.Document: TypeExtension :: TypeExtension -> TypeSystemExtension
+ Language.GraphQL.AST.Document: TypeList :: Type -> Type
+ Language.GraphQL.AST.Document: TypeNamed :: Name -> Type
+ Language.GraphQL.AST.Document: TypeNonNull :: NonNullType -> Type
+ Language.GraphQL.AST.Document: TypeSystemDefinition :: TypeSystemDefinition -> Definition
+ Language.GraphQL.AST.Document: TypeSystemExtension :: TypeSystemExtension -> Definition
+ Language.GraphQL.AST.Document: UnionMemberTypes :: t NamedType -> UnionMemberTypes t
+ Language.GraphQL.AST.Document: UnionTypeDefinition :: Description -> Name -> [Directive] -> UnionMemberTypes [] -> TypeDefinition
+ Language.GraphQL.AST.Document: UnionTypeDirectivesExtension :: Name -> NonEmpty Directive -> TypeExtension
+ Language.GraphQL.AST.Document: UnionTypeUnionMemberTypesExtension :: Name -> [Directive] -> UnionMemberTypes NonEmpty -> TypeExtension
+ Language.GraphQL.AST.Document: Variable :: Name -> Value
+ Language.GraphQL.AST.Document: VariableDefinition :: Name -> Type -> Maybe Value -> VariableDefinition
+ Language.GraphQL.AST.Document: data Argument
+ Language.GraphQL.AST.Document: data Definition
+ Language.GraphQL.AST.Document: data Directive
+ Language.GraphQL.AST.Document: data EnumValueDefinition
+ Language.GraphQL.AST.Document: data ExecutableDefinition
+ Language.GraphQL.AST.Document: data FieldDefinition
+ Language.GraphQL.AST.Document: data FragmentDefinition
+ Language.GraphQL.AST.Document: data InputValueDefinition
+ Language.GraphQL.AST.Document: data NonNullType
+ Language.GraphQL.AST.Document: data ObjectField
+ Language.GraphQL.AST.Document: data OperationDefinition
+ Language.GraphQL.AST.Document: data OperationType
+ Language.GraphQL.AST.Document: data OperationTypeDefinition
+ Language.GraphQL.AST.Document: data SchemaExtension
+ Language.GraphQL.AST.Document: data Selection
+ Language.GraphQL.AST.Document: data Type
+ Language.GraphQL.AST.Document: data TypeDefinition
+ Language.GraphQL.AST.Document: data TypeExtension
+ Language.GraphQL.AST.Document: data TypeSystemDefinition
+ Language.GraphQL.AST.Document: data TypeSystemExtension
+ Language.GraphQL.AST.Document: data Value
+ Language.GraphQL.AST.Document: data VariableDefinition
+ Language.GraphQL.AST.Document: instance Data.Foldable.Foldable t => GHC.Classes.Eq (Language.GraphQL.AST.Document.ImplementsInterfaces t)
+ Language.GraphQL.AST.Document: instance Data.Foldable.Foldable t => GHC.Classes.Eq (Language.GraphQL.AST.Document.UnionMemberTypes t)
+ Language.GraphQL.AST.Document: instance Data.Foldable.Foldable t => GHC.Show.Show (Language.GraphQL.AST.Document.ImplementsInterfaces t)
+ Language.GraphQL.AST.Document: instance Data.Foldable.Foldable t => GHC.Show.Show (Language.GraphQL.AST.Document.UnionMemberTypes t)
+ Language.GraphQL.AST.Document: instance GHC.Base.Monoid Language.GraphQL.AST.Document.ArgumentsDefinition
+ Language.GraphQL.AST.Document: instance GHC.Base.Semigroup Language.GraphQL.AST.Document.ArgumentsDefinition
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.Argument
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.ArgumentsDefinition
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.Definition
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.Description
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.Directive
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.EnumValueDefinition
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.ExecutableDefinition
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.FieldDefinition
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.FragmentDefinition
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.InputValueDefinition
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.NonNullType
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.ObjectField
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.OperationDefinition
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.OperationType
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.OperationTypeDefinition
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.SchemaExtension
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.Selection
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.Type
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.TypeDefinition
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.TypeExtension
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.TypeSystemDefinition
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.TypeSystemExtension
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.Value
+ Language.GraphQL.AST.Document: instance GHC.Classes.Eq Language.GraphQL.AST.Document.VariableDefinition
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.Argument
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.ArgumentsDefinition
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.Definition
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.Description
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.Directive
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.EnumValueDefinition
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.ExecutableDefinition
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.FieldDefinition
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.FragmentDefinition
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.InputValueDefinition
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.NonNullType
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.ObjectField
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.OperationDefinition
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.OperationType
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.OperationTypeDefinition
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.SchemaExtension
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.Selection
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.Type
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.TypeDefinition
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.TypeExtension
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.TypeSystemDefinition
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.TypeSystemExtension
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.Value
+ Language.GraphQL.AST.Document: instance GHC.Show.Show Language.GraphQL.AST.Document.VariableDefinition
+ Language.GraphQL.AST.Document: newtype ArgumentsDefinition
+ Language.GraphQL.AST.Document: newtype Description
+ Language.GraphQL.AST.Document: newtype ImplementsInterfaces t
+ Language.GraphQL.AST.Document: newtype UnionMemberTypes t
+ Language.GraphQL.AST.Document: type Alias = Name
+ Language.GraphQL.AST.Document: type Document = NonEmpty Definition
+ Language.GraphQL.AST.Document: type Name = Text
+ Language.GraphQL.AST.Document: type NamedType = Name
+ Language.GraphQL.AST.Document: type SelectionSet = NonEmpty Selection
+ Language.GraphQL.AST.Document: type SelectionSetOpt = [Selection]
+ Language.GraphQL.AST.Document: type TypeCondition = Name
+ Language.GraphQL.AST.Lexer: extend :: forall a. Text -> String -> NonEmpty (Parser a) -> Parser a
+ Language.GraphQL.Schema: Resolver :: Text -> (Field -> CollectErrsT m Object) -> Resolver m
+ Language.GraphQL.Schema: resolversToMap :: (Foldable f, Functor f) => f (Resolver m) -> HashMap Text (Field -> CollectErrsT m Object)
+ Language.GraphQL.Trans: [arguments] :: Context -> Arguments
+ Language.GraphQL.Trans: argument :: Monad m => Name -> ActionT m Value
- Language.GraphQL: graphql :: MonadIO m => NonEmpty (Resolver m) -> Text -> m Value
+ Language.GraphQL: graphql :: Monad m => HashMap Text (NonEmpty (Resolver m)) -> Text -> m Value
- Language.GraphQL: graphqlSubs :: MonadIO m => NonEmpty (Resolver m) -> Subs -> Text -> m Value
+ Language.GraphQL: graphqlSubs :: Monad m => HashMap Text (NonEmpty (Resolver m)) -> Subs -> Text -> m Value
- Language.GraphQL.AST.Core: Field :: Maybe Alias -> Name -> [Argument] -> Seq Selection -> Field
+ Language.GraphQL.AST.Core: Field :: Maybe Alias -> Name -> Arguments -> Seq Selection -> Field
- Language.GraphQL.AST.Encoder: definition :: Formatter -> Definition -> Text
+ Language.GraphQL.AST.Encoder: definition :: Formatter -> ExecutableDefinition -> Text
- Language.GraphQL.AST.Lexer: at :: Parser Char
+ Language.GraphQL.AST.Lexer: at :: Parser Text
- Language.GraphQL.Execute: execute :: MonadIO m => NonEmpty (Resolver m) -> Subs -> Document -> m Value
+ Language.GraphQL.Execute: execute :: Monad m => HashMap Text (NonEmpty (Resolver m)) -> Subs -> Document -> m Value
- Language.GraphQL.Execute: executeWithName :: MonadIO m => NonEmpty (Resolver m) -> Text -> Subs -> Document -> m Value
+ Language.GraphQL.Execute: executeWithName :: Monad m => HashMap Text (NonEmpty (Resolver m)) -> Text -> Subs -> Document -> m Value
- Language.GraphQL.Schema: object :: MonadIO m => Name -> ActionT m [Resolver m] -> Resolver m
+ Language.GraphQL.Schema: object :: Monad m => Name -> ActionT m [Resolver m] -> Resolver m
- Language.GraphQL.Schema: resolve :: MonadIO m => [Resolver m] -> Seq Selection -> CollectErrsT m Value
+ Language.GraphQL.Schema: resolve :: Monad m => HashMap Text (Field -> CollectErrsT m Object) -> Seq Selection -> CollectErrsT m Value
- Language.GraphQL.Schema: scalar :: (MonadIO m, ToJSON a) => Name -> ActionT m a -> Resolver m
+ Language.GraphQL.Schema: scalar :: (Monad m, ToJSON a) => Name -> ActionT m a -> Resolver m
- Language.GraphQL.Schema: type Subs = Name -> Maybe Value
+ Language.GraphQL.Schema: type Subs = HashMap Name Value
- Language.GraphQL.Schema: wrappedObject :: MonadIO m => Name -> ActionT m (Wrapping [Resolver m]) -> Resolver m
+ Language.GraphQL.Schema: wrappedObject :: Monad m => Name -> ActionT m (Wrapping [Resolver m]) -> Resolver m
- Language.GraphQL.Schema: wrappedScalar :: (MonadIO m, ToJSON a) => Name -> ActionT m (Wrapping a) -> Resolver m
+ Language.GraphQL.Schema: wrappedScalar :: (Monad m, ToJSON a) => Name -> ActionT m (Wrapping a) -> Resolver m
- Language.GraphQL.Trans: Context :: HashMap Name Value -> Context
+ Language.GraphQL.Trans: Context :: Arguments -> Context

Files

CHANGELOG.md view
@@ -1,9 +1,62 @@-# Change Log+# Changelog All notable changes to this project will be documented in this file. +The format is based on+[Keep a Changelog](https://keepachangelog.com/en/1.0.0/),+and this project adheres to+[Haskell Package Versioning Policy](https://pvp.haskell.org/).++## [Unreleased]++## [0.7.0.0] - 2020-05-11+### Fixed+- Result of null encoding+- Block strings encoding+- Result of tab and newline encoding++### Added+- AST for the GraphQL schema.+- Type system definition parser.+- `Trans.argument`.+- Schema extension parser.+- Contributing guidelines.+- `Schema.resolversToMap` (intended for to be used internally).++### Changed+- Rename `AST.Definition` into `AST.Document.ExecutableDefinition`.+  `AST.Document.TypeSystemDefinition` and `AST.Document.TypeSystemExtension`+  can also be definitions.+- Move all AST data to `AST.Document` and reexport them.+- Rename `AST.OperationSelectionSet` to `AST.Document.SelectionSet`.+- Make `Schema.Subs` a `Data.HashMap.Strict` (was a function+  `key -> Maybe value` before).+- Make `AST.Lexer.at` a text (symbol) parser. It was a char before and is+  `symbol "@"` now.+- Replace `MonadIO` with a plain `Monad`. Since the tests don't use IO,+  set the inner monad to `Identity`.+- `NonEmpty (Resolver m)` is now `HashMap Text (NonEmpty (Resolver m))`. Root+  operation type can be any type, therefore a hashmap is needed. Since types+  cannot be empty, we save the list of resolvers in the type as a non-empty+  list. Currently only "Query" and "Mutation" are supported as types. For more+  schema support is required. The executor checks now if the type in the query+  matches the type of the provided root resolvers.++### Removed+- `AST.Field`, `AST.InlineFragment` and `AST.FragmentSpread`.+  These types are only used in `AST.Selection` and `AST.Selection` contains now+  3 corresponding data constructors, `Field`, `InlineFragment` and+  `FragmentSpread`, instead of separate types. It simplifies pattern matching+  and doesn't make the code less typesafe.+- `Schema.scalarA`.+- `Schema.wrappedScalarA`.+- `Schema.wrappedObjectA`.+- `Schema.objectA`.+- `AST.Argument`. Replaced with `AST.Arguments` which holds all arguments as a+  key/value map.+ ## [0.6.1.0] - 2019-12-23 ### Fixed-- Parsing multiple string arguments, such as +- Parsing multiple string arguments, such as   `login(username: "username", password: "password")` would fail on the comma   due to strings not having a space consumer. - Fragment spread is evaluated based on the `__typename` resolver. If the@@ -162,6 +215,8 @@ ### Added - Data types for the GraphQL language. +[Unreleased]: https://github.com/caraus-ecms/graphql/compare/v0.6.1.0...HEAD+[0.7.0.0]: https://github.com/caraus-ecms/graphql/compare/v0.6.1.0...v0.7.0.0 [0.6.1.0]: https://github.com/caraus-ecms/graphql/compare/v0.6.0.0...v0.6.1.0 [0.6.0.0]: https://github.com/caraus-ecms/graphql/compare/v0.5.1.0...v0.6.0.0 [0.5.1.0]: https://github.com/caraus-ecms/graphql/compare/v0.5.0.1...v0.5.1.0
LICENSE view
@@ -1,4 +1,4 @@-Copyright 2019 Eugen Wissner, Germany+Copyright 2019-2020 Eugen Wissner, Germany Copyright 2015-2017 J. Daniel Navarro  All rights reserved.
README.md view
@@ -24,10 +24,17 @@ ## Documentation  API documentation is available through-[hackage](https://hackage.haskell.org/package/graphql).+[Hackage](https://hackage.haskell.org/package/graphql).  You'll also find a small tutorial with some examples under [docs/tutorial](https://github.com/caraus-ecms/graphql/tree/master/docs/tutorial).++## Further information++- [Contributing guidelines](CONTRIBUTING.md).+- [Changelog](CHANGELOG.md) – this one contains the most recent changes; +  individual changelogs for specific versions can be found on+  [Hackage](https://hackage.haskell.org/package/graphql).  ## Contact 
docs/tutorial/tutorial.lhs view
@@ -12,20 +12,19 @@ Since this file is a literate haskell file, we start by importing some dependencies.  > {-# LANGUAGE OverloadedStrings #-}-> {-# LANGUAGE LambdaCase #-} > module Main where > > import Control.Monad.IO.Class (liftIO)-> import Control.Monad.Trans.Except (throwE) > import Data.Aeson (encode) > import Data.ByteString.Lazy.Char8 (putStrLn)+> import Data.HashMap.Strict (HashMap)+> import qualified Data.HashMap.Strict as HashMap > import Data.List.NonEmpty (NonEmpty(..)) > import Data.Text (Text) > import Data.Time (getCurrentTime) > > import Language.GraphQL > import qualified Language.GraphQL.Schema as Schema-> import Language.GraphQL.Trans (ActionT(..)) > > import Prelude hiding (putStrLn) @@ -36,8 +35,8 @@  First we build a GraphQL schema. -> schema1 :: NonEmpty (Schema.Resolver IO)-> schema1 = hello :| []+> schema1 :: HashMap Text (NonEmpty (Schema.Resolver IO))+> schema1 = HashMap.singleton "Query" $ hello :| [] > > hello :: Schema.Resolver IO > hello = Schema.scalar "hello" (return ("it's me" :: Text))@@ -66,14 +65,13 @@  For this example, we're going to be using time. -> schema2 :: NonEmpty (Schema.Resolver IO)-> schema2 = time :| []+> schema2 :: HashMap Text (NonEmpty (Schema.Resolver IO))+> schema2 = HashMap.singleton "Query" $ time :| [] > > time :: Schema.Resolver IO-> time = Schema.scalarA "time" $ \case->   [] -> do t <- liftIO getCurrentTime->            return $ show t->   _ -> ActionT $ throwE "Invalid arguments."+> time = Schema.scalar "time" $ do+>   t <- liftIO getCurrentTime+>   return $ show t  This defines a simple schema with one type and one field, which resolves to the current time.@@ -126,8 +124,8 @@  Now that we have two resolvers, we can define a schema which uses them both. -> schema3 :: NonEmpty (Schema.Resolver IO)-> schema3 = hello :| [time]+> schema3 :: HashMap Text (NonEmpty (Schema.Resolver IO))+> schema3 = HashMap.singleton "Query" $ hello :| [time] > > query3 :: Text > query3 = "query timeAndHello { time hello }"
graphql.cabal view
@@ -1,9 +1,9 @@ cabal-version: 1.12 name: graphql-version: 0.6.1.0+version: 0.7.0.0 license: BSD3 license-file: LICENSE-copyright: (c) 2019 Eugen Wissner,+copyright: (c) 2019-2020 Eugen Wissner,            (c) 2015-2017 J. Daniel Navarro maintainer: belka@caraus.de author: Danny Navarro <j@dannynavarro.net>,@@ -34,6 +34,8 @@         Language.GraphQL         Language.GraphQL.AST         Language.GraphQL.AST.Core+        Language.GraphQL.AST.DirectiveLocation+        Language.GraphQL.AST.Document         Language.GraphQL.AST.Encoder         Language.GraphQL.AST.Lexer         Language.GraphQL.AST.Parser@@ -48,11 +50,12 @@         Language.GraphQL.Type.Directive     default-language: Haskell2010     build-depends:-        aeson >=1.4.6.0 && <1.5,+        aeson >=1.4.7.1 && <1.5,         base >=4.7 && <5,-        containers >=0.6.0.1 && <0.7,-        megaparsec >=7.0.5 && <7.1,-        text >=1.2.3.1 && <1.3,+        containers >=0.6.2.1 && <0.7,+        megaparsec >=8.0.0 && <8.1,+        parser-combinators >=1.2.1 && <1.3,+        text >=1.2.4.0 && <1.3,         transformers >=0.5.6.2 && <0.6,         unordered-containers >=0.2.10.0 && <0.3 @@ -75,15 +78,17 @@     default-language: Haskell2010     ghc-options: -threaded -rtsopts -with-rtsopts=-N     build-depends:-        aeson >=1.4.6.0 && <1.5,+        QuickCheck >=2.13.2 && <2.14,+        aeson >=1.4.7.1 && <1.5,         base >=4.7 && <5,-        containers >=0.6.0.1 && <0.7,+        containers >=0.6.2.1 && <0.7,         graphql -any,         hspec >=2.7.1 && <2.8,         hspec-expectations >=0.8.2 && <0.9,-        hspec-megaparsec >=2.0.1 && <2.1,-        megaparsec >=7.0.5 && <7.1,+        hspec-megaparsec >=2.1.0 && <2.2,+        megaparsec >=8.0.0 && <8.1,+        parser-combinators >=1.2.1 && <1.3,         raw-strings-qq ==1.1.*,-        text >=1.2.3.1 && <1.3,+        text >=1.2.4.0 && <1.3,         transformers >=0.5.6.2 && <0.6,         unordered-containers >=0.2.10.0 && <0.3
src/Language/GraphQL.hs view
@@ -4,10 +4,10 @@     , graphqlSubs     ) where -import Control.Monad.IO.Class (MonadIO) import qualified Data.Aeson as Aeson import Data.List.NonEmpty (NonEmpty)-import qualified Data.Text as T+import Data.HashMap.Strict (HashMap)+import Data.Text (Text) import Language.GraphQL.Error import Language.GraphQL.Execute import Language.GraphQL.AST.Parser@@ -16,19 +16,19 @@  -- | If the text parses correctly as a @GraphQL@ query the query is -- executed using the given 'Schema.Resolver's.-graphql :: MonadIO m-    => NonEmpty (Schema.Resolver m) -- ^ Resolvers.-    -> T.Text -- ^ Text representing a @GraphQL@ request document.+graphql :: Monad m+    => HashMap Text (NonEmpty (Schema.Resolver m)) -- ^ Resolvers.+    -> Text -- ^ Text representing a @GraphQL@ request document.     -> m Aeson.Value -- ^ Response.-graphql = flip graphqlSubs $ const Nothing+graphql = flip graphqlSubs mempty  -- | If the text parses correctly as a @GraphQL@ query the substitution is -- applied to the query and the query is then executed using to the given -- 'Schema.Resolver's.-graphqlSubs :: MonadIO m-    => NonEmpty (Schema.Resolver m) -- ^ Resolvers.+graphqlSubs :: Monad m+    => HashMap Text (NonEmpty (Schema.Resolver m)) -- ^ Resolvers.     -> Schema.Subs -- ^ Variable substitution function.-    -> T.Text -- ^ Text representing a @GraphQL@ request document.+    -> Text -- ^ Text representing a @GraphQL@ request document.     -> m Aeson.Value -- ^ Response. graphqlSubs schema f     = either parseError (execute schema f)
src/Language/GraphQL/AST.hs view
@@ -1,185 +1,6 @@--- | This module defines an abstract syntax tree for the @GraphQL@ language based on---   <https://facebook.github.io/graphql/ Facebook's GraphQL Specification>.------ Target AST for Parser.+-- | Target AST for Parser. module Language.GraphQL.AST-    ( Alias-    , Argument(..)-    , Definition(..)-    , Directive(..)-    , Document-    , Field(..)-    , FragmentDefinition(..)-    , FragmentSpread(..)-    , InlineFragment(..)-    , Name-    , NonNullType(..)-    , ObjectField(..)-    , OperationDefinition(..)-    , OperationType(..)-    , Selection(..)-    , SelectionSet-    , SelectionSetOpt-    , Type(..)-    , TypeCondition-    , Value(..)-    , VariableDefinition(..)+    ( module Language.GraphQL.AST.Document     ) where -import Data.Int (Int32)-import Data.List.NonEmpty (NonEmpty)-import Data.Text (Text)---- * Document---- | GraphQL document.-type Document = NonEmpty Definition---- | Name-type Name = Text---- | Directive.-data Directive = Directive Name [Argument] deriving (Eq, Show)---- * Operations---- | Top-level definition of a document, either an operation or a fragment.-data Definition-    = DefinitionOperation OperationDefinition-    | DefinitionFragment FragmentDefinition-    deriving (Eq, Show)---- | Operation definition.-data OperationDefinition-    = OperationSelectionSet SelectionSet-    | OperationDefinition OperationType-                          (Maybe Name)-                          [VariableDefinition]-                          [Directive]-                          SelectionSet-    deriving (Eq, Show)---- | GraphQL has 3 operation types: queries, mutations and subscribtions.------ Currently only queries and mutations are supported.-data OperationType = Query | Mutation deriving (Eq, Show)---- * Selections---- | "Top-level" selection, selection on an operation or fragment.-type SelectionSet = NonEmpty Selection---- | Field selection.-type SelectionSetOpt = [Selection]---- | Single selection element.-data Selection-    = SelectionField Field-    | SelectionFragmentSpread FragmentSpread-    | SelectionInlineFragment InlineFragment-    deriving (Eq, Show)---- * Field---- | Single GraphQL field.------ The only required property of a field is its name. Optionally it can also--- have an alias, arguments or a list of subfields.------ Given the following query:------ @--- {---   zuck: user(id: 4) {---     id---     name---   }--- }--- @------ * "user", "id" and "name" are field names.--- * "user" has two subfields, "id" and "name".--- * "zuck" is an alias for "user". "id" and "name" have no aliases.--- * "id: 4" is an argument for "user". "id" and "name" don't have any--- arguments.-data Field-    = Field (Maybe Alias) Name [Argument] [Directive] SelectionSetOpt-    deriving (Eq, Show)---- | Alternative field name.------ @--- {---   smallPic: profilePic(size: 64)---   bigPic: profilePic(size: 1024)--- }--- @------ Here "smallPic" and "bigPic" are aliases for the same field, "profilePic",--- used to distinquish between profile pictures with different arguments--- (sizes).-type Alias = Name---- | Single argument.------ @--- {---   user(id: 4) {---     name---   }--- }--- @------  Here "id" is an argument for the field "user" and its value is 4.-data Argument = Argument Name Value deriving (Eq,Show)---- * Fragments---- | Fragment spread.-data FragmentSpread = FragmentSpread Name [Directive] deriving (Eq, Show)---- | Inline fragment.-data InlineFragment = InlineFragment (Maybe TypeCondition) [Directive] SelectionSet-                      deriving (Eq, Show)---- | Fragment definition.-data FragmentDefinition-    = FragmentDefinition Name TypeCondition [Directive] SelectionSet-    deriving (Eq, Show)---- * Inputs---- | Input value.-data Value = Variable Name-           | Int Int32-           | Float Double-           | String Text-           | Boolean Bool-           | Null-           | Enum Name-           | List [Value]-           | Object [ObjectField]-           deriving (Eq, Show)---- | Key-value pair.------ A list of 'ObjectField's represents a GraphQL object type.-data ObjectField = ObjectField Name Value deriving (Eq, Show)---- | Variable definition.-data VariableDefinition = VariableDefinition Name Type (Maybe Value)-                          deriving (Eq, Show)---- | Type condition.-type TypeCondition = Name---- | Type representation.-data Type = TypeNamed   Name-          | TypeList    Type-          | TypeNonNull NonNullType-            deriving (Eq, Show)---- | Helper type to represent Non-Null types and lists of such types.-data NonNullType = NonNullTypeNamed Name-                 | NonNullTypeList  Type-                   deriving (Eq, Show)+import Language.GraphQL.AST.Document
src/Language/GraphQL/AST/Core.hs view
@@ -1,7 +1,6 @@ -- | This is the AST meant to be executed. module Language.GraphQL.AST.Core     ( Alias-    , Argument(..)     , Arguments(..)     , Directive(..)     , Document@@ -35,15 +34,18 @@  -- | Single GraphQL field. data Field-    = Field (Maybe Alias) Name [Argument] (Seq Selection)+    = Field (Maybe Alias) Name Arguments (Seq Selection)     deriving (Eq, Show) --- | Single argument.-data Argument = Argument Name Value deriving (Eq, Show)- -- | Argument list. newtype Arguments = Arguments (HashMap Name Value)     deriving (Eq, Show)++instance Semigroup Arguments where+    (Arguments x) <> (Arguments y) = Arguments $ x <> y++instance Monoid Arguments where+    mempty = Arguments mempty  -- | Directive. data Directive = Directive Name Arguments
+ src/Language/GraphQL/AST/DirectiveLocation.hs view
@@ -0,0 +1,41 @@+-- | Various parts of a GraphQL document can be annotated with directives. +--   This module describes locations in a document where directives can appear.+module Language.GraphQL.AST.DirectiveLocation+    ( DirectiveLocation(..)+    , ExecutableDirectiveLocation(..)+    , TypeSystemDirectiveLocation(..)+    ) where++-- | All directives can be splitted in two groups: directives used to annotate+--   various parts of executable definitions and the ones used in the schema+--   definition.+data DirectiveLocation+    = ExecutableDirectiveLocation ExecutableDirectiveLocation+    | TypeSystemDirectiveLocation TypeSystemDirectiveLocation+    deriving (Eq, Show)++-- | Where directives can appear in an executable definition, like a query.+data ExecutableDirectiveLocation+    = Query+    | Mutation+    | Subscription+    | Field+    | FragmentDefinition+    | FragmentSpread+    | InlineFragment+    deriving (Eq, Show)++-- | Where directives can appear in a type system definition.+data TypeSystemDirectiveLocation+    = Schema+    | Scalar+    | Object+    | FieldDefinition+    | ArgumentDefinition+    | Interface+    | Union+    | Enum+    | EnumValue+    | InputObject+    | InputFieldDefinition+    deriving (Eq, Show)
+ src/Language/GraphQL/AST/Document.hs view
@@ -0,0 +1,486 @@+{-# LANGUAGE OverloadedStrings #-}++-- | This module defines an abstract syntax tree for the @GraphQL@ language. It+-- follows closely the structure given in the specification. Please refer to+-- <https://facebook.github.io/graphql/ Facebook's GraphQL Specification>.+-- for more information.+module Language.GraphQL.AST.Document+    ( Alias+    , Argument(..)+    , ArgumentsDefinition(..)+    , Definition(..)+    , Description(..)+    , Directive(..)+    , Document+    , EnumValueDefinition(..)+    , ExecutableDefinition(..)+    , FieldDefinition(..)+    , FragmentDefinition(..)+    , ImplementsInterfaces(..)+    , InputValueDefinition(..)+    , Name+    , NamedType+    , NonNullType(..)+    , ObjectField(..)+    , OperationDefinition(..)+    , OperationType(..)+    , OperationTypeDefinition(..)+    , SchemaExtension(..)+    , Selection(..)+    , SelectionSet+    , SelectionSetOpt+    , Type(..)+    , TypeCondition+    , TypeDefinition(..)+    , TypeExtension(..)+    , TypeSystemDefinition(..)+    , TypeSystemExtension(..)+    , UnionMemberTypes(..)+    , Value(..)+    , VariableDefinition(..)+    ) where++import Data.Foldable (toList)+import Data.Int (Int32)+import Data.List.NonEmpty (NonEmpty)+import Data.Text (Text)+import qualified Data.Text as Text+import Language.GraphQL.AST.DirectiveLocation++-- * Language++-- ** Source Text++-- | Name.+type Name = Text++-- ** Document++-- | GraphQL document.+type Document = NonEmpty Definition++-- | All kinds of definitions that can occur in a GraphQL document.+data Definition+    = ExecutableDefinition ExecutableDefinition+    | TypeSystemDefinition TypeSystemDefinition+    | TypeSystemExtension TypeSystemExtension+    deriving (Eq, Show)++-- | Top-level definition of a document, either an operation or a fragment.+data ExecutableDefinition+    = DefinitionOperation OperationDefinition+    | DefinitionFragment FragmentDefinition+    deriving (Eq, Show)++-- ** Operations++-- | Operation definition.+data OperationDefinition+    = SelectionSet SelectionSet+    | OperationDefinition+        OperationType+        (Maybe Name)+        [VariableDefinition]+        [Directive]+        SelectionSet+    deriving (Eq, Show)++-- | GraphQL has 3 operation types:+--+-- * query - a read-only fetch.+-- * mutation - a write operation followed by a fetch.+-- * subscription - a long-lived request that fetches data in response to+-- source events.+--+-- Currently only queries and mutations are supported.+data OperationType = Query | Mutation deriving (Eq, Show)++-- ** Selection Sets++-- | "Top-level" selection, selection on an operation or fragment.+type SelectionSet = NonEmpty Selection++-- | Field selection.+type SelectionSetOpt = [Selection]++-- | Selection is a single entry in a selection set. It can be a single field,+-- fragment spread or inline fragment.+--+-- The only required property of a field is its name. Optionally it can also+-- have an alias, arguments, directives and a list of subfields.+--+-- In the following query "user" is a field with two subfields, "id" and "name":+--+-- @+-- {+--   user {+--     id+--     name+--   }+-- }+-- @+--+-- A fragment spread refers to a fragment defined outside the operation and is+-- expanded at the execution time.+--+-- @+-- {+--   user {+--     ...userFragment+--   }+-- }+--+-- fragment userFragment on UserType {+--   id+--   name+-- }+-- @+--+-- Inline fragments are similar but they don't have any name and the type+-- condition ("on UserType") is optional.+--+-- @+-- {+--   user {+--     ... on UserType {+--       id+--       name+--     }+-- }+-- @+data Selection+    = Field (Maybe Alias) Name [Argument] [Directive] SelectionSetOpt+    | FragmentSpread Name [Directive]+    | InlineFragment (Maybe TypeCondition) [Directive] SelectionSet+    deriving (Eq, Show)++-- ** Arguments++-- | Single argument.+--+-- @+-- {+--   user(id: 4) {+--     name+--   }+-- }+-- @+--+--  Here "id" is an argument for the field "user" and its value is 4.+data Argument = Argument Name Value deriving (Eq,Show)++-- ** Field Alias++-- | Alternative field name.+--+-- @+-- {+--   smallPic: profilePic(size: 64)+--   bigPic: profilePic(size: 1024)+-- }+-- @+--+-- Here "smallPic" and "bigPic" are aliases for the same field, "profilePic",+-- used to distinquish between profile pictures with different arguments+-- (sizes).+type Alias = Name++-- ** Fragments++-- | Fragment definition.+data FragmentDefinition+    = FragmentDefinition Name TypeCondition [Directive] SelectionSet+    deriving (Eq, Show)++-- | Type condition.+type TypeCondition = Name++-- ** Input Values++-- | Input value.+data Value+    = Variable Name+    | Int Int32+    | Float Double+    | String Text+    | Boolean Bool+    | Null+    | Enum Name+    | List [Value]+    | Object [ObjectField]+    deriving (Eq, Show)++-- | Key-value pair.+--+--   A list of 'ObjectField's represents a GraphQL object type.+data ObjectField = ObjectField Name Value deriving (Eq, Show)++-- ** Variables++-- | Variable definition.+data VariableDefinition = VariableDefinition Name Type (Maybe Value)+    deriving (Eq, Show)++-- ** Type References++-- | Type representation.+data Type+    = TypeNamed Name+    | TypeList Type+    | TypeNonNull NonNullType+    deriving (Eq, Show)++-- | Represents type names.+type NamedType = Name++-- | Helper type to represent Non-Null types and lists of such types.+data NonNullType+    = NonNullTypeNamed Name+    | NonNullTypeList Type+    deriving (Eq, Show)++-- ** Directives++-- | Directive.+--+-- Directives begin with "@", can accept arguments, and can be applied to the+-- most GraphQL elements, providing additional information.+data Directive = Directive Name [Argument] deriving (Eq, Show)++-- * Type System++-- | Type system can define a schema, a type or a directive.+--+-- @+-- schema {+--   query: Query+-- }+--+-- directive @example on FIELD_DEFINITION+--+-- type Query {+--   field: String @example+-- }+-- @+--+-- This example defines a custom directive "@example", which is applied to a+-- field definition of the type definition "Query". On the top the schema+-- is defined by taking advantage of the type "Query".+data TypeSystemDefinition+    = SchemaDefinition [Directive] (NonEmpty OperationTypeDefinition)+    | TypeDefinition TypeDefinition+    | DirectiveDefinition+        Description Name ArgumentsDefinition (NonEmpty DirectiveLocation)+    deriving (Eq, Show)++-- ** Type System Extensions++-- | Extension for a type system definition. Only schema and type definitions+-- can be extended.+data TypeSystemExtension+    = SchemaExtension SchemaExtension+    | TypeExtension TypeExtension+    deriving (Eq, Show)++-- ** Schema++-- | Root operation type definition.+--+-- Defining root operation types is not required since they have defaults. So+-- the default query root type is "Query", and the default mutation root type+-- is "Mutation". But these defaults can be changed for a specific schema. In+-- the following code the query root type is changed to "MyQueryRootType", and+-- the mutation root type to "MyMutationRootType":+--+-- @+-- schema {+--   query: MyQueryRootType+--   mutation: MyMutationRootType+-- }+-- @+data OperationTypeDefinition+    = OperationTypeDefinition OperationType NamedType+    deriving (Eq, Show)++-- | Extension of the schema definition by further operations or directives.+data SchemaExtension+    = SchemaOperationExtension [Directive] (NonEmpty OperationTypeDefinition)+    | SchemaDirectivesExtension (NonEmpty Directive)+    deriving (Eq, Show)++-- ** Descriptions++-- | GraphQL has built-in capability to document service APIs. Documentation+-- is a GraphQL string that precedes a particular definition and contains+-- Markdown. Any GraphQL definition can be documented this way.+--+-- @+-- """+-- Supported languages.+-- """+-- enum Language {+--   "English"+--   EN+--+--   "Russian"+--   RU+-- }+-- @+newtype Description = Description (Maybe Text)+    deriving (Eq, Show)++-- ** Types++-- | Type definitions describe various user-defined types.+data TypeDefinition+    = ScalarTypeDefinition Description Name [Directive]+    | ObjectTypeDefinition+        Description+        Name+        (ImplementsInterfaces [])+        [Directive]+        [FieldDefinition]+    | InterfaceTypeDefinition Description Name [Directive] [FieldDefinition]+    | UnionTypeDefinition Description Name [Directive] (UnionMemberTypes [])+    | EnumTypeDefinition Description Name [Directive] [EnumValueDefinition]+    | InputObjectTypeDefinition+        Description Name [Directive] [InputValueDefinition]+    deriving (Eq, Show)++-- | Extensions for custom, already defined types.+data TypeExtension+    = ScalarTypeExtension Name (NonEmpty Directive)+    | ObjectTypeFieldsDefinitionExtension+        Name (ImplementsInterfaces []) [Directive] (NonEmpty FieldDefinition)+    | ObjectTypeDirectivesExtension+        Name (ImplementsInterfaces []) (NonEmpty Directive)+    | ObjectTypeImplementsInterfacesExtension+        Name (ImplementsInterfaces NonEmpty)+    | InterfaceTypeFieldsDefinitionExtension+        Name [Directive] (NonEmpty FieldDefinition)+    | InterfaceTypeDirectivesExtension Name (NonEmpty Directive)+    | UnionTypeUnionMemberTypesExtension+        Name [Directive] (UnionMemberTypes NonEmpty)+    | UnionTypeDirectivesExtension Name (NonEmpty Directive)+    | EnumTypeEnumValuesDefinitionExtension+        Name [Directive] (NonEmpty EnumValueDefinition)+    | EnumTypeDirectivesExtension Name (NonEmpty Directive)+    | InputObjectTypeInputFieldsDefinitionExtension+        Name [Directive] (NonEmpty InputValueDefinition)+    | InputObjectTypeDirectivesExtension Name (NonEmpty Directive)+    deriving (Eq, Show)++-- ** Objects++-- | Defines a list of interfaces implemented by the given object type.+--+-- @+-- type Business implements NamedEntity & ValuedEntity {+--   name: String+-- }+-- @+--+-- Here the object type "Business" implements two interfaces: "NamedEntity" and+-- "ValuedEntity".+newtype ImplementsInterfaces t = ImplementsInterfaces (t NamedType)++instance Foldable t => Eq (ImplementsInterfaces t) where+    (ImplementsInterfaces xs) == (ImplementsInterfaces ys)+        = toList xs == toList ys++instance Foldable t => Show (ImplementsInterfaces t) where+    show (ImplementsInterfaces interfaces) = Text.unpack+        $ Text.append "implements"+        $ Text.intercalate " & "+        $ toList interfaces++-- | Definition of a single field in a type.+--+-- @+-- type Person {+--   name: String+--   picture(width: Int, height: Int): Url+-- }+-- @+--+-- "name" and "picture", including their arguments and types, are field+-- definitions.+data FieldDefinition+    = FieldDefinition Description Name ArgumentsDefinition Type [Directive]+    deriving (Eq, Show)++-- | A list of values passed to a field.+--+-- @+-- type Person {+--   name: String+--   picture(width: Int, height: Int): Url+-- }+-- @+--+-- "Person" has two fields, "name" and "picture". "name" doesn't have any+-- arguments, so 'ArgumentsDefinition' contains an empty list. "picture"+-- contains definitions for 2 arguments: "width" and "height".+newtype ArgumentsDefinition = ArgumentsDefinition [InputValueDefinition]+    deriving (Eq, Show)++instance Semigroup ArgumentsDefinition where+    (ArgumentsDefinition xs) <> (ArgumentsDefinition ys) =+        ArgumentsDefinition $ xs <> ys++instance Monoid ArgumentsDefinition where+    mempty = ArgumentsDefinition []++-- | Defines an input value.+--+-- * Input values can define field arguments, see 'ArgumentsDefinition'.+-- * They can also be used as field definitions in an input type.+--+-- @+-- input Point2D {+--   x: Float+--   y: Float+-- }+-- @+--+-- The input type "Point2D" contains two value definitions: "x" and "y".+data InputValueDefinition+    = InputValueDefinition Description Name Type (Maybe Value) [Directive]+    deriving (Eq, Show)++-- ** Unions++-- | List of types forming a union.+--+-- @+-- union SearchResult = Person | Photo+-- @+--+-- "Person" and "Photo" are member types of the union "SearchResult".+newtype UnionMemberTypes t = UnionMemberTypes (t NamedType)++instance Foldable t => Eq (UnionMemberTypes t) where+    (UnionMemberTypes xs) == (UnionMemberTypes ys) = toList xs == toList ys++instance Foldable t => Show (UnionMemberTypes t) where+    show (UnionMemberTypes memberTypes) = Text.unpack+        $ Text.intercalate " | "+        $ toList memberTypes++-- ** Enums++-- | Single value in an enum definition.+--+-- @+-- enum Direction {+--   NORTH+--   EAST+--   SOUTH+--   WEST+-- }+-- @+--+-- "NORTH, "EAST", "SOUTH", and "WEST" are value definitions of an enum type+-- definition "Direction".+data EnumValueDefinition = EnumValueDefinition Description Name [Directive]+    deriving (Eq, Show)
src/Language/GraphQL/AST/Encoder.hs view
@@ -15,7 +15,6 @@  import Data.Char (ord) import Data.Foldable (fold)-import Data.Monoid ((<>)) import qualified Data.List.NonEmpty as NonEmpty import Data.Text (Text) import qualified Data.Text as Text@@ -26,6 +25,7 @@ import Data.Text.Lazy.Builder.Int (decimal, hexadecimal) import Data.Text.Lazy.Builder.RealFloat (realFloat) import qualified Language.GraphQL.AST as Full+import Language.GraphQL.AST.Document  -- | Instructs the encoder whether the GraphQL document should be minified or --   pretty printed.@@ -43,16 +43,18 @@ minified :: Formatter minified = Minified --- | Converts a 'Full.Document' into a string.-document :: Formatter -> Full.Document -> Lazy.Text+-- | Converts a Document' into a string.+document :: Formatter -> Document -> Lazy.Text document formatter defs     | Pretty _ <- formatter = Lazy.Text.intercalate "\n" encodeDocument     | Minified <-formatter = Lazy.Text.snoc (mconcat encodeDocument) '\n'   where-    encodeDocument = NonEmpty.toList $ definition formatter <$> defs+    encodeDocument = foldr executableDefinition [] defs+    executableDefinition (ExecutableDefinition x) acc = definition formatter x : acc+    executableDefinition _ acc = acc --- | Converts a 'Full.Definition' into a string.-definition :: Formatter -> Full.Definition -> Lazy.Text+-- | Converts a t'Full.ExecutableDefinition' into a string.+definition :: Formatter -> ExecutableDefinition -> Lazy.Text definition formatter x     | Pretty _ <- formatter = Lazy.Text.snoc (encodeDefinition x) '\n'     | Minified <- formatter = encodeDefinition x@@ -62,14 +64,16 @@     encodeDefinition (Full.DefinitionFragment fragment)         = fragmentDefinition formatter fragment +-- | Converts a 'Full.OperationDefinition into a string. operationDefinition :: Formatter -> Full.OperationDefinition -> Lazy.Text-operationDefinition formatter (Full.OperationSelectionSet sels)+operationDefinition formatter (Full.SelectionSet sels)     = selectionSet formatter sels operationDefinition formatter (Full.OperationDefinition Full.Query name vars dirs sels)     = "query " <> node formatter name vars dirs sels operationDefinition formatter (Full.OperationDefinition Full.Mutation name vars dirs sels)     = "mutation " <> node formatter name vars dirs sels +-- | Converts a Full.Query or Full.Mutation into a string. node :: Formatter ->     Maybe Full.Name ->     [Full.VariableDefinition] ->@@ -110,17 +114,21 @@ selectionSetOpt :: Formatter -> Full.SelectionSetOpt -> Lazy.Text selectionSetOpt formatter = bracesList formatter $ selection formatter +indentSymbol :: Lazy.Text+indentSymbol = "  "+ indent :: (Integral a) => a -> Lazy.Text-indent indentation = Lazy.Text.replicate (fromIntegral indentation) "  "+indent indentation = Lazy.Text.replicate (fromIntegral indentation) indentSymbol  selection :: Formatter -> Full.Selection -> Lazy.Text selection formatter = Lazy.Text.append indent' . encodeSelection   where-    encodeSelection (Full.SelectionField field') = field incrementIndent field'-    encodeSelection (Full.SelectionInlineFragment fragment) =-        inlineFragment incrementIndent fragment-    encodeSelection (Full.SelectionFragmentSpread spread) =-        fragmentSpread incrementIndent spread+    encodeSelection (Full.Field alias name args directives' selections) =+        field incrementIndent alias name args directives' selections+    encodeSelection (Full.InlineFragment typeCondition directives' selections) =+        inlineFragment incrementIndent typeCondition directives' selections+    encodeSelection (Full.FragmentSpread name directives') =+        fragmentSpread incrementIndent name directives'     incrementIndent         | Pretty indentation <- formatter = Pretty $ indentation + 1         | otherwise = Minified@@ -131,8 +139,15 @@ colon :: Formatter -> Lazy.Text colon formatter = eitherFormat formatter ": " ":" -field :: Formatter -> Full.Field -> Lazy.Text-field formatter (Full.Field alias name args dirs set)+-- | Converts Full.Field into a string+field :: Formatter ->+    Maybe Full.Name ->+    Full.Name ->+    [Full.Argument] ->+    [Full.Directive] ->+    [Full.Selection] ->+    Lazy.Text+field formatter alias name args dirs set     = optempty prependAlias (fold alias)     <> Lazy.Text.fromStrict name     <> optempty (arguments formatter) args@@ -154,13 +169,18 @@  -- * Fragments -fragmentSpread :: Formatter -> Full.FragmentSpread -> Lazy.Text-fragmentSpread formatter (Full.FragmentSpread name ds)-    = "..." <> Lazy.Text.fromStrict name <> optempty (directives formatter) ds+fragmentSpread :: Formatter -> Full.Name -> [Full.Directive] -> Lazy.Text+fragmentSpread formatter name directives'+    = "..." <> Lazy.Text.fromStrict name+    <> optempty (directives formatter) directives' -inlineFragment :: Formatter -> Full.InlineFragment -> Lazy.Text-inlineFragment formatter (Full.InlineFragment tc dirs sels)-    = "... on "+inlineFragment ::+    Formatter ->+    Maybe Full.TypeCondition ->+    [Full.Directive] ->+    Full.SelectionSet ->+    Lazy.Text+inlineFragment formatter tc dirs sels = "... on "     <> Lazy.Text.fromStrict (fold tc)     <> directives formatter dirs     <> eitherFormat formatter " " mempty@@ -191,7 +211,7 @@ value _ (Full.Int x) = Builder.toLazyText $ decimal x value _ (Full.Float x) = Builder.toLazyText $ realFloat x value _ (Full.Boolean  x) = booleanValue x-value _ Full.Null = mempty+value _ Full.Null = "null" value formatter (Full.String string) = stringValue formatter string value _ (Full.Enum x) = Lazy.Text.fromStrict x value formatter (Full.List x) = listValue formatter x@@ -201,34 +221,50 @@ booleanValue True  = "true" booleanValue False = "false" +quote :: Builder.Builder+quote = Builder.singleton '\"'++oneLine :: Text -> Builder+oneLine string = quote <> Text.foldr (mappend . escape) quote string+ stringValue :: Formatter -> Text -> Lazy.Text stringValue Minified string = Builder.toLazyText-    $ quote <> Text.foldr (mappend . escape') quote string-  where-    quote = Builder.singleton '\"'-    escape' '\n' = Builder.fromString "\\n"-    escape' char = escape char-stringValue (Pretty indentation) string = byStringType $ Text.lines string-  where-    byStringType [] = "\"\""-    byStringType [line] = Builder.toLazyText-        $ quote <> Text.foldr (mappend . escape) quote line-    byStringType lines' = "\"\"\"\n"-        <> Lazy.Text.unlines (transformLine <$> lines')-        <> indent indentation-        <> "\"\"\""-    transformLine = (indent (indentation + 1) <>)-        . Lazy.Text.fromStrict-        . Text.replace "\"\"\"" "\\\"\"\""-    quote = Builder.singleton '\"'+    $ quote <> Text.foldr (mappend . escape) quote string+stringValue (Pretty indentation) string =+  if hasEscaped string+  then stringValue Minified string+  else Builder.toLazyText $ encoded lines'+    where+      isWhiteSpace char = char == ' ' || char == '\t'+      isNewline char = char == '\n' || char == '\r'+      hasEscaped = Text.any (not . isAllowed)+      isAllowed char =+          char == '\t' || isNewline char || (char >= '\x0020' && char /= '\x007F') +      tripleQuote = Builder.fromText "\"\"\""+      start = tripleQuote <> Builder.singleton '\n'+      end = Builder.fromLazyText (indent indentation) <> tripleQuote++      strip = Text.dropWhile isWhiteSpace . Text.dropWhileEnd isWhiteSpace+      lines' = map Builder.fromText $ Text.split isNewline (Text.replace "\r\n" "\n" $ strip string)+      encoded [] = oneLine string+      encoded [_] = oneLine string+      encoded lines'' = start <> transformLines lines'' <> end+      transformLines = foldr ((\line acc -> line <> Builder.singleton '\n' <> acc) . transformLine) mempty+      transformLine line =+        if Lazy.Text.null (Builder.toLazyText line)+        then line+        else Builder.fromLazyText (indent (indentation + 1)) <> line+ escape :: Char -> Builder escape char'     | char' == '\\' = Builder.fromString "\\\\"     | char' == '\"' = Builder.fromString "\\\""     | char' == '\b' = Builder.fromString "\\b"     | char' == '\f' = Builder.fromString "\\f"+    | char' == '\n' = Builder.fromString "\\n"     | char' == '\r' = Builder.fromString "\\r"+    | char' == '\t' = Builder.fromString "\\t"     | char' < '\x0010' = unicode  "\\u000" char'     | char' < '\x0020' = unicode "\\u00" char'     | otherwise = Builder.singleton char'
src/Language/GraphQL/AST/Lexer.hs view
@@ -15,6 +15,7 @@     , dollar     , comment     , equals+    , extend     , integer     , float     , lexeme@@ -28,20 +29,16 @@     , unicodeBOM     ) where -import Control.Applicative ( Alternative(..)-                           , liftA2-                           )-import Data.Char ( chr-                 , digitToInt-                 , isAsciiLower-                 , isAsciiUpper-                 , ord-                 )+import Control.Applicative (Alternative(..), liftA2)+import Data.Char (chr, digitToInt, isAsciiLower, isAsciiUpper, ord) import Data.Foldable (foldl') import Data.List (dropWhileEnd)+import qualified Data.List.NonEmpty as NonEmpty+import Data.List.NonEmpty (NonEmpty(..)) import Data.Proxy (Proxy(..)) import Data.Void (Void) import Text.Megaparsec ( Parsec+                       , (<?>)                        , between                        , chunk                        , chunkToTokens@@ -56,11 +53,9 @@                        , takeWhile1P                        , try                        )-import Text.Megaparsec.Char ( char-                            , digitChar-                            , space1-                            )+import Text.Megaparsec.Char (char, digitChar, space1) import qualified Text.Megaparsec.Char.Lexer as Lexer+import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Lazy as TL @@ -97,8 +92,8 @@ dollar = symbol "$"  -- | Parser for "@".-at :: Parser Char-at = char '@'+at :: Parser Text+at = symbol "@"  -- | Parser for "&". amp :: Parser T.Text@@ -134,7 +129,7 @@  -- | Parser for strings. string :: Parser T.Text-string = between "\"" "\"" stringValue <* spaceConsumer +string = between "\"" "\"" stringValue <* spaceConsumer   where     stringValue = T.pack <$> many stringCharacter     stringCharacter = satisfy isStringCharacter1@@ -143,7 +138,7 @@  -- | Parser for block strings. blockString :: Parser T.Text-blockString = between "\"\"\"" "\"\"\"" stringValue <* spaceConsumer +blockString = between "\"\"\"" "\"\"\"" stringValue <* spaceConsumer   where     stringValue = do         byLine <- sepBy (many blockStringCharacter) lineTerminator@@ -226,3 +221,16 @@ -- | Parser for the "Byte Order Mark". unicodeBOM :: Parser () unicodeBOM = optional (char '\xfeff') >> pure ()++-- | Parses "extend" followed by a 'symbol'. It is used by schema extensions.+extend :: forall a. Text -> String -> NonEmpty (Parser a) -> Parser a+extend token extensionLabel parsers+    = foldr combine headParser (NonEmpty.tail parsers)+    <?> extensionLabel+  where+    headParser = tryExtension $ NonEmpty.head parsers+    combine current accumulated = accumulated <|> tryExtension current+    tryExtension extensionParser = try+        $ symbol "extend"+        *> symbol token+        *> extensionParser
src/Language/GraphQL/AST/Parser.hs view
@@ -6,63 +6,358 @@     ( document     ) where -import Control.Applicative ( Alternative(..)-                           , optional-                           )+import Control.Applicative (Alternative(..), optional)+import Control.Applicative.Combinators (sepBy1)+import qualified Control.Applicative.Combinators.NonEmpty as NonEmpty import Data.List.NonEmpty (NonEmpty(..))-import Language.GraphQL.AST+import Data.Text (Text)+import qualified Language.GraphQL.AST.DirectiveLocation as Directive+import Language.GraphQL.AST.DirectiveLocation+    ( DirectiveLocation+    , ExecutableDirectiveLocation+    , TypeSystemDirectiveLocation+    )+import Language.GraphQL.AST.Document import Language.GraphQL.AST.Lexer-import Text.Megaparsec ( lookAhead-                       , option-                       , try-                       , (<?>)-                       )+import Text.Megaparsec (lookAhead, option, try, (<?>))  -- | Parser for the GraphQL documents. document :: Parser Document-document = unicodeBOM >> spaceConsumer >> lexeme (manyNE definition)+document = unicodeBOM+    >> spaceConsumer+    >> lexeme (NonEmpty.some definition)  definition :: Parser Definition-definition = DefinitionOperation <$> operationDefinition-         <|> DefinitionFragment  <$> fragmentDefinition-         <?> "definition error!"+definition = ExecutableDefinition <$> executableDefinition+    <|> TypeSystemDefinition <$> typeSystemDefinition+    <|> TypeSystemExtension <$> typeSystemExtension+    <?> "Definition" +executableDefinition :: Parser ExecutableDefinition+executableDefinition = DefinitionOperation <$> operationDefinition+    <|> DefinitionFragment  <$> fragmentDefinition+    <?> "ExecutableDefinition"++typeSystemDefinition :: Parser TypeSystemDefinition+typeSystemDefinition = schemaDefinition+    <|> TypeDefinition <$> typeDefinition+    <|> directiveDefinition+    <?> "TypeSystemDefinition"++typeSystemExtension :: Parser TypeSystemExtension+typeSystemExtension = SchemaExtension <$> schemaExtension+    <|> TypeExtension <$> typeExtension+    <?> "TypeSystemExtension"++directiveDefinition :: Parser TypeSystemDefinition+directiveDefinition = DirectiveDefinition+    <$> description+    <* symbol "directive"+    <* at+    <*> name+    <*> argumentsDefinition+    <* symbol "on"+    <*> directiveLocations+    <?> "DirectiveDefinition"++directiveLocations :: Parser (NonEmpty DirectiveLocation)+directiveLocations = optional pipe+    *> directiveLocation `NonEmpty.sepBy1` pipe++directiveLocation :: Parser DirectiveLocation+directiveLocation+    = Directive.ExecutableDirectiveLocation <$> executableDirectiveLocation+    <|> Directive.TypeSystemDirectiveLocation <$> typeSystemDirectiveLocation++executableDirectiveLocation :: Parser ExecutableDirectiveLocation+executableDirectiveLocation = Directive.Query <$ symbol "QUERY"+    <|> Directive.Mutation <$ symbol "MUTATION"+    <|> Directive.Subscription <$ symbol "SUBSCRIPTION"+    <|> Directive.Field <$ symbol "FIELD"+    <|> Directive.FragmentDefinition <$ "FRAGMENT_DEFINITION"+    <|> Directive.FragmentSpread <$ "FRAGMENT_SPREAD"+    <|> Directive.InlineFragment <$ "INLINE_FRAGMENT"++typeSystemDirectiveLocation :: Parser TypeSystemDirectiveLocation+typeSystemDirectiveLocation = Directive.Schema <$ symbol "SCHEMA"+    <|> Directive.Scalar <$ symbol "SCALAR"+    <|> Directive.Object <$ symbol "OBJECT"+    <|> Directive.FieldDefinition <$ symbol "FIELD_DEFINITION"+    <|> Directive.ArgumentDefinition <$ symbol "ARGUMENT_DEFINITION"+    <|> Directive.Interface <$ symbol "INTERFACE"+    <|> Directive.Union <$ symbol "UNION"+    <|> Directive.Enum <$ symbol "ENUM"+    <|> Directive.EnumValue <$ symbol "ENUM_VALUE"+    <|> Directive.InputObject <$ symbol "INPUT_OBJECT"+    <|> Directive.InputFieldDefinition <$ symbol "INPUT_FIELD_DEFINITION"++typeDefinition :: Parser TypeDefinition+typeDefinition = scalarTypeDefinition+    <|> objectTypeDefinition+    <|> interfaceTypeDefinition+    <|> unionTypeDefinition+    <|> enumTypeDefinition+    <|> inputObjectTypeDefinition+    <?> "TypeDefinition"++typeExtension :: Parser TypeExtension+typeExtension = scalarTypeExtension+    <|> objectTypeExtension+    <|> interfaceTypeExtension+    <|> unionTypeExtension+    <|> enumTypeExtension+    <|> inputObjectTypeExtension+    <?> "TypeExtension"++scalarTypeDefinition :: Parser TypeDefinition+scalarTypeDefinition = ScalarTypeDefinition+    <$> description+    <* symbol "scalar"+    <*> name+    <*> directives+    <?> "ScalarTypeDefinition"++scalarTypeExtension :: Parser TypeExtension+scalarTypeExtension = extend "scalar" "ScalarTypeExtension"+    $ (ScalarTypeExtension <$> name <*> NonEmpty.some directive) :| []++objectTypeDefinition :: Parser TypeDefinition+objectTypeDefinition = ObjectTypeDefinition+    <$> description+    <* symbol "type"+    <*> name+    <*> option (ImplementsInterfaces []) (implementsInterfaces sepBy1)+    <*> directives+    <*> braces (many fieldDefinition)+    <?> "ObjectTypeDefinition"++objectTypeExtension :: Parser TypeExtension+objectTypeExtension = extend "type" "ObjectTypeExtension"+    $ fieldsDefinitionExtension :|+        [ directivesExtension+        , implementsInterfacesExtension+        ]+  where+    fieldsDefinitionExtension = ObjectTypeFieldsDefinitionExtension+        <$> name+        <*> option (ImplementsInterfaces []) (implementsInterfaces sepBy1)+        <*> directives+        <*> braces (NonEmpty.some fieldDefinition)+    directivesExtension = ObjectTypeDirectivesExtension+        <$> name+        <*> option (ImplementsInterfaces []) (implementsInterfaces sepBy1)+        <*> NonEmpty.some directive+    implementsInterfacesExtension = ObjectTypeImplementsInterfacesExtension+        <$> name+        <*> implementsInterfaces NonEmpty.sepBy1++description :: Parser Description+description = Description+    <$> optional (string <|> blockString)+    <?> "Description"++unionTypeDefinition :: Parser TypeDefinition+unionTypeDefinition = UnionTypeDefinition+    <$> description+    <* symbol "union"+    <*> name+    <*> directives+    <*> option (UnionMemberTypes []) (unionMemberTypes sepBy1)+    <?> "UnionTypeDefinition"++unionTypeExtension :: Parser TypeExtension+unionTypeExtension = extend "union" "UnionTypeExtension"+    $ unionMemberTypesExtension :| [directivesExtension]+  where+    unionMemberTypesExtension = UnionTypeUnionMemberTypesExtension+        <$> name+        <*> directives+        <*> unionMemberTypes NonEmpty.sepBy1+    directivesExtension = UnionTypeDirectivesExtension+        <$> name+        <*> NonEmpty.some directive++unionMemberTypes ::+    Foldable t =>+    (Parser Text -> Parser Text -> Parser (t NamedType)) ->+    Parser (UnionMemberTypes t)+unionMemberTypes sepBy' = UnionMemberTypes+    <$ equals+    <* optional pipe+    <*> name `sepBy'` pipe+    <?> "UnionMemberTypes"++interfaceTypeDefinition :: Parser TypeDefinition+interfaceTypeDefinition = InterfaceTypeDefinition+    <$> description+    <* symbol "interface"+    <*> name+    <*> directives+    <*> braces (many fieldDefinition)+    <?> "InterfaceTypeDefinition"++interfaceTypeExtension :: Parser TypeExtension+interfaceTypeExtension = extend "interface" "InterfaceTypeExtension"+    $ fieldsDefinitionExtension :| [directivesExtension]+  where+    fieldsDefinitionExtension = InterfaceTypeFieldsDefinitionExtension+        <$> name+        <*> directives+        <*> braces (NonEmpty.some fieldDefinition)+    directivesExtension = InterfaceTypeDirectivesExtension+        <$> name+        <*> NonEmpty.some directive++enumTypeDefinition :: Parser TypeDefinition+enumTypeDefinition = EnumTypeDefinition+    <$> description+    <* symbol "enum"+    <*> name+    <*> directives+    <*> listOptIn braces enumValueDefinition+    <?> "EnumTypeDefinition"++enumTypeExtension :: Parser TypeExtension+enumTypeExtension = extend "enum" "EnumTypeExtension"+    $ enumValuesDefinitionExtension :| [directivesExtension]+  where+    enumValuesDefinitionExtension = EnumTypeEnumValuesDefinitionExtension+        <$> name+        <*> directives+        <*> braces (NonEmpty.some enumValueDefinition)+    directivesExtension = EnumTypeDirectivesExtension+        <$> name+        <*> NonEmpty.some directive++inputObjectTypeDefinition :: Parser TypeDefinition+inputObjectTypeDefinition = InputObjectTypeDefinition+    <$> description+    <* symbol "input"+    <*> name+    <*> directives+    <*> listOptIn braces inputValueDefinition+    <?> "InputObjectTypeDefinition"++inputObjectTypeExtension :: Parser TypeExtension+inputObjectTypeExtension = extend "input" "InputObjectTypeExtension"+    $ inputFieldsDefinitionExtension :| [directivesExtension]+  where+    inputFieldsDefinitionExtension = InputObjectTypeInputFieldsDefinitionExtension+        <$> name+        <*> directives+        <*> braces (NonEmpty.some inputValueDefinition)+    directivesExtension = InputObjectTypeDirectivesExtension+        <$> name+        <*> NonEmpty.some directive++enumValueDefinition :: Parser EnumValueDefinition+enumValueDefinition = EnumValueDefinition+    <$> description+    <*> enumValue+    <*> directives+    <?> "EnumValueDefinition"++implementsInterfaces ::+    Foldable t =>+    (Parser Text -> Parser Text -> Parser (t NamedType)) ->+    Parser (ImplementsInterfaces t)+implementsInterfaces sepBy' = ImplementsInterfaces+    <$ symbol "implements"+    <* optional amp+    <*> name `sepBy'` amp+    <?> "ImplementsInterfaces"++inputValueDefinition :: Parser InputValueDefinition+inputValueDefinition = InputValueDefinition+    <$> description+    <*> name+    <* colon+    <*> type'+    <*> defaultValue+    <*> directives+    <?> "InputValueDefinition"++argumentsDefinition :: Parser ArgumentsDefinition+argumentsDefinition = ArgumentsDefinition+    <$> listOptIn parens inputValueDefinition+    <?> "ArgumentsDefinition"++fieldDefinition :: Parser FieldDefinition+fieldDefinition = FieldDefinition+    <$> description+    <*> name+    <*> argumentsDefinition+    <* colon+    <*> type'+    <*> directives+    <?> "FieldDefinition"++schemaDefinition :: Parser TypeSystemDefinition+schemaDefinition = SchemaDefinition+    <$ symbol "schema"+    <*> directives+    <*> operationTypeDefinitions+    <?> "SchemaDefinition"++operationTypeDefinitions :: Parser (NonEmpty OperationTypeDefinition)+operationTypeDefinitions = braces $ NonEmpty.some operationTypeDefinition++schemaExtension :: Parser SchemaExtension+schemaExtension = extend "schema" "SchemaExtension"+    $ schemaOperationExtension :| [directivesExtension]+  where+    directivesExtension = SchemaDirectivesExtension+        <$> NonEmpty.some directive+    schemaOperationExtension = SchemaOperationExtension+        <$> directives+        <*> operationTypeDefinitions++operationTypeDefinition :: Parser OperationTypeDefinition+operationTypeDefinition = OperationTypeDefinition+    <$> operationType <* colon+    <*> name+    <?> "OperationTypeDefinition"+ operationDefinition :: Parser OperationDefinition-operationDefinition = OperationSelectionSet <$> selectionSet-                  <|> OperationDefinition   <$> operationType-                                            <*> optional name-                                            <*> opt variableDefinitions-                                            <*> opt directives-                                            <*> selectionSet-                  <?> "operationDefinition error"+operationDefinition = SelectionSet <$> selectionSet+    <|> operationDefinition'+    <?> "operationDefinition error"+  where+    operationDefinition'+        = OperationDefinition <$> operationType+        <*> optional name+        <*> variableDefinitions+        <*> directives+        <*> selectionSet  operationType :: Parser OperationType operationType = Query <$ symbol "query"     <|> Mutation <$ symbol "mutation"-    <?> "operationType error"+    -- <?> Keep default error message  -- * SelectionSet  selectionSet :: Parser SelectionSet-selectionSet = braces $ manyNE selection+selectionSet = braces $ NonEmpty.some selection  selectionSetOpt :: Parser SelectionSetOpt-selectionSetOpt = braces $ some selection+selectionSetOpt = listOptIn braces selection  selection :: Parser Selection-selection = SelectionField          <$> field-        <|> try (SelectionFragmentSpread <$> fragmentSpread)-        <|> SelectionInlineFragment <$> inlineFragment-        <?> "selection error!"+selection = field+    <|> try fragmentSpread+    <|> inlineFragment+    <?> "selection error!"  -- * Field -field :: Parser Field-field = Field <$> optional alias-              <*> name-              <*> opt arguments-              <*> opt directives-              <*> opt selectionSetOpt+field :: Parser Selection+field = Field+    <$> optional alias+    <*> name+    <*> arguments+    <*> directives+    <*> selectionSetOpt  alias :: Parser Alias alias = try $ name <* colon@@ -70,30 +365,32 @@ -- * Arguments  arguments :: Parser [Argument]-arguments = parens $ some argument+arguments = listOptIn parens argument  argument :: Parser Argument argument = Argument <$> name <* colon <*> value  -- * Fragments -fragmentSpread :: Parser FragmentSpread-fragmentSpread = FragmentSpread <$  spread-                                <*> fragmentName-                                <*> opt directives+fragmentSpread :: Parser Selection+fragmentSpread = FragmentSpread+    <$ spread+    <*> fragmentName+    <*> directives -inlineFragment :: Parser InlineFragment-inlineFragment = InlineFragment <$  spread-                                <*> optional typeCondition-                                <*> opt directives-                                <*> selectionSet+inlineFragment :: Parser Selection+inlineFragment = InlineFragment+    <$ spread+    <*> optional typeCondition+    <*> directives+    <*> selectionSet  fragmentDefinition :: Parser FragmentDefinition fragmentDefinition = FragmentDefinition                  <$  symbol "fragment"                  <*> name                  <*> typeCondition-                 <*> opt directives+                 <*> directives                  <*> selectionSet  fragmentName :: Parser Name@@ -121,68 +418,68 @@     booleanValue = True  <$ symbol "true"                <|> False <$ symbol "false" -    enumValue :: Parser Name-    enumValue = but (symbol "true") *> but (symbol "false") *> but (symbol "null") *> name-     listValue :: Parser [Value]     listValue = brackets $ some value      objectValue :: Parser [ObjectField]     objectValue = braces $ some objectField +enumValue :: Parser Name+enumValue = but (symbol "true") *> but (symbol "false") *> but (symbol "null") *> name+ objectField :: Parser ObjectField-objectField = ObjectField <$> name <* symbol ":" <*> value+objectField = ObjectField <$> name <* colon <*> value  -- * Variables  variableDefinitions :: Parser [VariableDefinition]-variableDefinitions = parens $ some variableDefinition+variableDefinitions = listOptIn parens variableDefinition  variableDefinition :: Parser VariableDefinition-variableDefinition = VariableDefinition <$> variable-                                        <*  colon-                                        <*> type_-                                        <*> optional defaultValue+variableDefinition = VariableDefinition+    <$> variable+    <*  colon+    <*> type'+    <*> defaultValue+    <?> "VariableDefinition"+ variable :: Parser Name variable = dollar *> name -defaultValue :: Parser Value-defaultValue = equals *> value+defaultValue :: Parser (Maybe Value)+defaultValue = optional (equals *> value) <?> "DefaultValue"  -- * Input Types -type_ :: Parser Type-type_ = try (TypeNonNull <$> nonNullType)-    <|> TypeList <$> brackets type_+type' :: Parser Type+type' = try (TypeNonNull <$> nonNullType)+    <|> TypeList <$> brackets type'     <|> TypeNamed <$> name-    <?> "type_ error!"+    <?> "Type"  nonNullType :: Parser NonNullType nonNullType = NonNullTypeNamed <$> name <* bang-          <|> NonNullTypeList  <$> brackets type_  <* bang+          <|> NonNullTypeList  <$> brackets type'  <* bang           <?> "nonNullType error!"  -- * Directives  directives :: Parser [Directive]-directives = some directive+directives = many directive  directive :: Parser Directive directive = Directive-        <$  at-        <*> name-        <*> opt arguments+    <$  at+    <*> name+    <*> arguments  -- * Internal -opt :: Monoid a => Parser a -> Parser a-opt = option mempty+listOptIn :: (Parser [a] -> Parser [a]) -> Parser a -> Parser [a]+listOptIn surround = option [] . surround . some  -- Hack to reverse parser success but :: Parser a -> Parser () but pn = False <$ lookAhead pn <|> pure True >>= \case     False -> empty     True  -> pure ()--manyNE :: Alternative f => f a -> f (NonEmpty a)-manyNE p = (:|) <$> p <*> many p
src/Language/GraphQL/Error.hs view
@@ -20,18 +20,20 @@                                  , modify                                  , runStateT                                  )-import Text.Megaparsec ( ParseErrorBundle(..)-                       , SourcePos(..)-                       , errorOffset-                       , parseErrorTextPretty-                       , reachOffset-                       , unPos-                       )+import Text.Megaparsec+    ( ParseErrorBundle(..)+    , PosState(..)+    , SourcePos(..)+    , errorOffset+    , parseErrorTextPretty+    , reachOffset+    , unPos+    )  -- | Wraps a parse error into a list of errors. parseError :: Applicative f => ParseErrorBundle Text Void -> f Aeson.Value parseError ParseErrorBundle{..}  =-  pure $ Aeson.object [("errors", Aeson.toJSON $ fst $ foldl go ([], bundlePosState) bundleErrors)]+    pure $ Aeson.object [("errors", Aeson.toJSON $ fst $ foldl go ([], bundlePosState) bundleErrors)]   where     errorObject s SourcePos{..} = Aeson.object         [ ("message", Aeson.toJSON $ init $ parseErrorTextPretty s)@@ -39,7 +41,8 @@         , ("column", Aeson.toJSON $ unPos sourceColumn)         ]     go (result, state) x =-        let (sourcePosition, _, newState) = reachOffset (errorOffset x) state+        let (_, newState) = reachOffset (errorOffset x) state+            sourcePosition = pstateSourcePos newState          in (errorObject x sourcePosition : result, newState)  -- | A wrapper to pass error messages around.
src/Language/GraphQL/Execute.hs view
@@ -6,14 +6,14 @@     , executeWithName     ) where -import Control.Monad.IO.Class (MonadIO) import qualified Data.Aeson as Aeson-import Data.Foldable (toList) import Data.List.NonEmpty (NonEmpty(..))-import qualified Data.List.NonEmpty as NE+import qualified Data.List.NonEmpty as NonEmpty+import Data.HashMap.Strict (HashMap)+import qualified Data.HashMap.Strict as HashMap import Data.Text (Text) import qualified Data.Text as Text-import qualified Language.GraphQL.AST as AST+import Language.GraphQL.AST.Document import qualified Language.GraphQL.AST.Core as AST.Core import qualified Language.GraphQL.Execute.Transform as Transform import Language.GraphQL.Error@@ -24,13 +24,14 @@ -- -- Returns the result of the query against the schema wrapped in a /data/ -- field, or errors wrapped in an /errors/ field.-execute :: MonadIO m-    => NonEmpty (Schema.Resolver m) -- ^ Resolvers.+execute :: Monad m+    => HashMap Text (NonEmpty (Schema.Resolver m)) -- ^ Resolvers.     -> Schema.Subs -- ^ Variable substitution function.-    -> AST.Document -- @GraphQL@ document.+    -> Document -- @GraphQL@ document.     -> m Aeson.Value execute schema subs doc =-    maybe transformError (document schema Nothing) $ Transform.document subs doc+    maybe transformError (document schema Nothing)+        $ Transform.document subs doc   where     transformError = return $ singleError "Schema transformation error." @@ -40,24 +41,25 @@ -- -- Returns the result of the query against the schema wrapped in a /data/ -- field, or errors wrapped in an /errors/ field.-executeWithName :: MonadIO m-    => NonEmpty (Schema.Resolver m) -- ^ Resolvers+executeWithName :: Monad m+    => HashMap Text (NonEmpty (Schema.Resolver m)) -- ^ Resolvers     -> Text -- ^ Operation name.     -> Schema.Subs -- ^ Variable substitution function.-    -> AST.Document -- ^ @GraphQL@ Document.+    -> Document -- ^ @GraphQL@ Document.     -> m Aeson.Value executeWithName schema name subs doc =-    maybe transformError (document schema $ Just name) $ Transform.document subs doc+    maybe transformError (document schema $ Just name)+        $ Transform.document subs doc   where     transformError = return $ singleError "Schema transformation error." -document :: MonadIO m-    => NonEmpty (Schema.Resolver m)+document :: Monad m+    => HashMap Text (NonEmpty (Schema.Resolver m))     -> Maybe Text     -> AST.Core.Document     -> m Aeson.Value document schema Nothing (op :| []) = operation schema op-document schema (Just name) operations = case NE.dropWhile matchingName operations of+document schema (Just name) operations = case NonEmpty.dropWhile matchingName operations of     [] -> return $ singleError         $ Text.unwords ["Operation", name, "couldn't be found in the document."]     (op:_)  -> operation schema op@@ -67,11 +69,18 @@     matchingName _ = False document _ _ _ = return $ singleError "Missing operation name." -operation :: MonadIO m-    => NonEmpty (Schema.Resolver m)+operation :: Monad m+    => HashMap Text (NonEmpty (Schema.Resolver m))     -> AST.Core.Operation     -> m Aeson.Value-operation schema (AST.Core.Query _ flds)-    = runCollectErrs (Schema.resolve (toList schema) flds)-operation schema (AST.Core.Mutation _ flds)-    = runCollectErrs (Schema.resolve (toList schema) flds)+operation schema = schemaOperation+  where+    runResolver fields = runCollectErrs+        . flip Schema.resolve fields+        . Schema.resolversToMap+    resolve fields queryType = maybe lookupError (runResolver fields)+        $ HashMap.lookup queryType schema+    lookupError = pure+        $ singleError "Root operation type couldn't be found in the schema."+    schemaOperation (AST.Core.Query _ fields) = resolve fields "Query"+    schemaOperation (AST.Core.Mutation _ fields) = resolve fields "Mutation"
src/Language/GraphQL/Execute/Transform.hs view
@@ -11,7 +11,7 @@ import Control.Arrow (first) import Control.Monad (foldM, unless) import Control.Monad.Trans.Class (lift)-import Control.Monad.Trans.Reader (ReaderT, ask, runReaderT)+import Control.Monad.Trans.Reader (ReaderT, asks, runReaderT) import Control.Monad.Trans.State (StateT, evalStateT, gets, modify) import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HashMap@@ -19,6 +19,7 @@ import Data.Sequence (Seq, (<|), (><)) import qualified Language.GraphQL.AST as Full import qualified Language.GraphQL.AST.Core as Core+import Language.GraphQL.AST.Document (Definition(..), Document) import qualified Language.GraphQL.Schema as Schema import qualified Language.GraphQL.Type.Directive as Directive @@ -35,18 +36,19 @@  -- | Rewrites the original syntax tree into an intermediate representation used -- for query execution.-document :: Schema.Subs -> Full.Document -> Maybe Core.Document+document :: Schema.Subs -> Document -> Maybe Core.Document document subs document' =     flip runReaderT subs         $ evalStateT (collectFragments >> operations operationDefinitions)         $ Replacement HashMap.empty fragmentTable   where     (fragmentTable, operationDefinitions) = foldr defragment mempty document'-    defragment (Full.DefinitionOperation definition) acc =+    defragment (ExecutableDefinition (Full.DefinitionOperation definition)) acc =         (definition :) <$> acc-    defragment (Full.DefinitionFragment definition) acc =+    defragment (ExecutableDefinition (Full.DefinitionFragment definition)) acc =         let (Full.FragmentDefinition name _ _ _) = definition          in first (HashMap.insert name definition) acc+    defragment _ acc = acc  -- * Operation @@ -56,26 +58,47 @@     lift . lift $ NonEmpty.nonEmpty coreOperations  operation :: Full.OperationDefinition -> TransformT Core.Operation-operation (Full.OperationSelectionSet sels) =-    operation $ Full.OperationDefinition Full.Query mempty mempty mempty sels--- TODO: Validate Variable definitions with substituter-operation (Full.OperationDefinition Full.Query name _vars _dirs sels) =-    Core.Query name <$> appendSelection sels-operation (Full.OperationDefinition Full.Mutation name _vars _dirs sels) =-    Core.Mutation name <$> appendSelection sels+operation (Full.SelectionSet sels)+    = operation $ Full.OperationDefinition Full.Query mempty mempty mempty sels+operation (Full.OperationDefinition Full.Query name _vars _dirs sels)+    = Core.Query name <$> appendSelection sels+operation (Full.OperationDefinition Full.Mutation name _vars _dirs sels)+    = Core.Mutation name <$> appendSelection sels  -- * Selection  selection ::     Full.Selection ->     TransformT (Either (Seq Core.Selection) Core.Selection)-selection (Full.SelectionField field') =-    maybe (Left mempty) (Right . Core.SelectionField) <$> field field'-selection (Full.SelectionFragmentSpread fragment) =-    maybe (Left mempty) (Right . Core.SelectionFragment)-    <$> fragmentSpread fragment-selection (Full.SelectionInlineFragment fragment) =-    inlineFragment fragment+selection (Full.Field alias name arguments' directives' selections) =+    maybe (Left mempty) (Right . Core.SelectionField) <$> do+        fieldArguments <- arguments arguments'+        fieldSelections <- appendSelection selections+        fieldDirectives <- Directive.selection <$> directives directives'+        let field' = Core.Field alias name fieldArguments fieldSelections+        pure $ field' <$ fieldDirectives+selection (Full.FragmentSpread name directives') =+    maybe (Left mempty) (Right . Core.SelectionFragment) <$> do+        spreadDirectives <- Directive.selection <$> directives directives'+        fragments' <- gets fragments+        fragment <- maybe lookupDefinition liftJust (HashMap.lookup name fragments')+        pure $ fragment <$ spreadDirectives+  where+    lookupDefinition = do+        fragmentDefinitions' <- gets fragmentDefinitions+        found <- lift . lift $ HashMap.lookup name fragmentDefinitions'+        fragmentDefinition found+selection (Full.InlineFragment type' directives' selections) = do+    fragmentDirectives <- Directive.selection <$> directives directives'+    case fragmentDirectives of+        Nothing -> pure $ Left mempty+        _ -> do+            fragmentSelectionSet <- appendSelection selections+            pure $ maybe Left selectionFragment type' fragmentSelectionSet+  where+    selectionFragment typeName = Right+        . Core.SelectionFragment+        . Core.Fragment typeName  appendSelection ::     Traversable t =>@@ -104,33 +127,6 @@         _ <- fragmentDefinition nextValue         collectFragments -inlineFragment ::-    Full.InlineFragment ->-    TransformT (Either (Seq Core.Selection) Core.Selection)-inlineFragment (Full.InlineFragment type' directives' selectionSet) = do-    fragmentDirectives <- Directive.selection <$> directives directives'-    case fragmentDirectives of-        Nothing -> pure $ Left mempty-        _ -> do-            fragmentSelectionSet <- appendSelection selectionSet-            pure $ maybe Left selectionFragment type' fragmentSelectionSet-  where-    selectionFragment typeName = Right-        . Core.SelectionFragment-        . Core.Fragment typeName--fragmentSpread :: Full.FragmentSpread -> TransformT (Maybe Core.Fragment)-fragmentSpread (Full.FragmentSpread name directives') = do-    spreadDirectives <- Directive.selection <$> directives directives'-    fragments' <- gets fragments-    fragment <- maybe lookupDefinition liftJust (HashMap.lookup name fragments')-    pure $ fragment <$ spreadDirectives -  where-    lookupDefinition = do-        fragmentDefinitions' <- gets fragmentDefinitions-        found <- lift . lift $ HashMap.lookup name fragmentDefinitions'-        fragmentDefinition found- fragmentDefinition ::     Full.FragmentDefinition ->     TransformT Core.Fragment@@ -147,28 +143,15 @@         let newFragments = HashMap.insert name newValue fragments'          in Replacement newFragments fragmentDefinitions' -field :: Full.Field -> TransformT (Maybe Core.Field)-field (Full.Field alias name arguments' directives' selections) = do-    fieldArguments <- traverse argument arguments'-    fieldSelections <- appendSelection selections-    fieldDirectives <- Directive.selection <$> directives directives'-    let field' = Core.Field alias name fieldArguments fieldSelections-    pure $ field' <$ fieldDirectives- arguments :: [Full.Argument] -> TransformT Core.Arguments arguments = fmap Core.Arguments . foldM go HashMap.empty   where-    go arguments' argument' = do-        (Core.Argument name value') <- argument argument'-        return $ HashMap.insert name value' arguments'--argument :: Full.Argument -> TransformT Core.Argument-argument (Full.Argument n v) = Core.Argument n <$> value v+    go arguments' (Full.Argument name value') = do+        substitutedValue <- value value'+        return $ HashMap.insert name substitutedValue arguments'  value :: Full.Value -> TransformT Core.Value-value (Full.Variable n) = do-    substitute' <- lift ask-    lift . lift $ substitute' n+value (Full.Variable name) = lift (asks $ HashMap.lookup name) >>= lift . lift value (Full.Int i) = pure $ Core.Int i value (Full.Float f) = pure $ Core.Float f value (Full.String x) = pure $ Core.String x
src/Language/GraphQL/Schema.hs view
@@ -3,28 +3,23 @@ -- | This module provides a representation of a @GraphQL@ Schema in addition to -- functions for defining and manipulating schemas. module Language.GraphQL.Schema-    ( Resolver+    ( Resolver(..)     , Subs     , object-    , objectA-    , scalar-    , scalarA     , resolve+    , resolversToMap+    , scalar     , wrappedObject-    , wrappedObjectA     , wrappedScalar-    , wrappedScalarA     -- * AST Reexports     , Field-    , Argument(..)     , Value(..)     ) where -import Control.Monad.IO.Class (MonadIO(..)) import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Except (runExceptT) import Control.Monad.Trans.Reader (runReaderT)-import Data.Foldable (find, fold)+import Data.Foldable (fold, toList) import Data.Maybe (fromMaybe) import qualified Data.Aeson as Aeson import Data.HashMap.Strict (HashMap)@@ -38,81 +33,80 @@ import qualified Language.GraphQL.Type as Type  -- | Resolves a 'Field' into an @Aeson.@'Data.Aeson.Types.Object' with error---   information (if an error has occurred). @m@ is usually expected to be an---   instance of 'MonadIO'.+--   information (if an error has occurred). @m@ is an arbitrary monad, usually+--   'IO'. data Resolver m = Resolver     Text -- ^ Name     (Field -> CollectErrsT m Aeson.Object) -- ^ Resolver --- | Variable substitution function.-type Subs = Name -> Maybe Value+-- | Converts resolvers to a map.+resolversToMap+    :: (Foldable f, Functor f)+    => f (Resolver m)+    -> HashMap Text (Field -> CollectErrsT m Aeson.Object)+resolversToMap = HashMap.fromList . toList . fmap toKV+  where+    toKV (Resolver name f) = (name, f) --- | Create a new 'Resolver' with the given 'Name' from the given 'Resolver's.-object :: MonadIO m => Name -> ActionT m [Resolver m] -> Resolver m-object name = objectA name . const+-- | Contains variables for the query. The key of the map is a variable name,+--   and the value is the variable value.+type Subs = HashMap Name Value --- | Like 'object' but also taking 'Argument's.-objectA :: MonadIO m-    => Name -> ([Argument] -> ActionT m [Resolver m]) -> Resolver m-objectA name f = Resolver name $ resolveFieldValue f resolveRight+-- | Create a new 'Resolver' with the given 'Name' from the given 'Resolver's.+object :: Monad m => Name -> ActionT m [Resolver m] -> Resolver m+object name f = Resolver name $ resolveFieldValue f resolveRight   where-    resolveRight fld@(Field _ _ _ flds) resolver = withField (resolve resolver flds) fld+    resolveRight fld@(Field _ _ _ flds) resolver+        = withField (resolve (resolversToMap resolver) flds) fld --- | Like 'object' but also taking 'Argument's and can be null or a list of objects.-wrappedObjectA :: MonadIO m-    => Name -> ([Argument] -> ActionT m (Type.Wrapping [Resolver m])) -> Resolver m-wrappedObjectA name f = Resolver name $ resolveFieldValue f resolveRight+-- | Like 'object' but can be null or a list of objects.+wrappedObject ::+    Monad m =>+    Name ->+    ActionT m (Type.Wrapping [Resolver m]) ->+    Resolver m+wrappedObject name f = Resolver name $ resolveFieldValue f resolveRight   where     resolveRight fld@(Field _ _ _ sels) resolver-        = withField (traverse (`resolve` sels) resolver) fld---- | Like 'object' but can be null or a list of objects.-wrappedObject :: MonadIO m-    => Name -> ActionT m (Type.Wrapping [Resolver m]) -> Resolver m-wrappedObject name = wrappedObjectA name . const+        = withField (traverse (resolveMap sels) resolver) fld+    resolveMap = flip (resolve . resolversToMap)  -- | A scalar represents a primitive value, like a string or an integer.-scalar :: (MonadIO m, Aeson.ToJSON a) => Name -> ActionT m a -> Resolver m-scalar name = scalarA name . const---- | Like 'scalar' but also taking 'Argument's.-scalarA :: (MonadIO m, Aeson.ToJSON a)-    => Name -> ([Argument] -> ActionT m a) -> Resolver m-scalarA name f = Resolver name $ resolveFieldValue f resolveRight+scalar :: (Monad m, Aeson.ToJSON a) => Name -> ActionT m a -> Resolver m+scalar name f = Resolver name $ resolveFieldValue f resolveRight   where     resolveRight fld result = withField (return result) fld --- | Like 'scalar' but also taking 'Argument's and can be null or a list of scalars.-wrappedScalarA :: (MonadIO m, Aeson.ToJSON a)-    => Name -> ([Argument] -> ActionT m (Type.Wrapping a)) -> Resolver m-wrappedScalarA name f = Resolver name $ resolveFieldValue f resolveRight+-- | Like 'scalar' but can be null or a list of scalars.+wrappedScalar ::+    (Monad m, Aeson.ToJSON a) =>+    Name ->+    ActionT m (Type.Wrapping a) ->+    Resolver m+wrappedScalar name f = Resolver name $ resolveFieldValue f resolveRight   where     resolveRight fld (Type.Named result) = withField (return result) fld     resolveRight fld Type.Null         = return $ HashMap.singleton (aliasOrName fld) Aeson.Null     resolveRight fld (Type.List result) = withField (return result) fld --- | Like 'scalar' but can be null or a list of scalars.-wrappedScalar :: (MonadIO m, Aeson.ToJSON a)-    => Name -> ActionT m (Type.Wrapping a) -> Resolver m-wrappedScalar name = wrappedScalarA name . const--resolveFieldValue :: MonadIO m-    => ([Argument] -> ActionT m a)-    -> (Field -> a -> CollectErrsT m (HashMap Text Aeson.Value))-    -> Field-    -> CollectErrsT m (HashMap Text Aeson.Value)+resolveFieldValue ::+    Monad m =>+    ActionT m a ->+    (Field -> a -> CollectErrsT m Aeson.Object) ->+    Field ->+    CollectErrsT m (HashMap Text Aeson.Value) resolveFieldValue f resolveRight fld@(Field _ _ args _) = do-    result <- lift $ reader . runExceptT . runActionT $ f args+    result <- lift $ reader . runExceptT . runActionT $ f     either resolveLeft (resolveRight fld) result       where-        reader = flip runReaderT $ Context mempty+        reader = flip runReaderT $ Context {arguments=args}         resolveLeft err = do             _ <- addErrMsg err             return $ HashMap.singleton (aliasOrName fld) Aeson.Null --- | Helper function to facilitate 'Argument' handling.-withField :: (MonadIO m, Aeson.ToJSON a)+-- | Helper function to facilitate error handling and result emitting.+withField :: (Monad m, Aeson.ToJSON a)     => CollectErrsT m a -> Field -> CollectErrsT m (HashMap Text Aeson.Value) withField v fld     = HashMap.singleton (aliasOrName fld) . Aeson.toJSON <$> runAppendErrs v@@ -120,23 +114,22 @@ -- | Takes a list of 'Resolver's and a list of 'Field's and applies each --   'Resolver' to each 'Field'. Resolves into a value containing the --   resolved 'Field', or a null value and error information.-resolve :: MonadIO m-    => [Resolver m] -> Seq Selection -> CollectErrsT m Aeson.Value+resolve :: Monad m+    => HashMap Text (Field -> CollectErrsT m Aeson.Object)+    -> Seq Selection+    -> CollectErrsT m Aeson.Value resolve resolvers = fmap (Aeson.toJSON . fold) . traverse tryResolvers   where-    resolveTypeName (Resolver "__typename" f) = do+    resolveTypeName f = do         value <- f $ Field Nothing "__typename" mempty mempty         return $ HashMap.lookupDefault "" "__typename" value-    resolveTypeName _ = return ""     tryResolvers (SelectionField fld@(Field _ name _ _))-        = maybe (errmsg fld) (tryResolver fld) $ find (compareResolvers name) resolvers+        = fromMaybe (errmsg fld) $ HashMap.lookup name resolvers <*> Just fld     tryResolvers (SelectionFragment (Fragment typeCondition selections')) = do-        that <- traverse resolveTypeName (find (compareResolvers "__typename") resolvers)+        that <- traverse resolveTypeName $ HashMap.lookup "__typename" resolvers         if maybe True (Aeson.String typeCondition ==) that             then fmap fold . traverse tryResolvers $ selections'             else return mempty-    compareResolvers name (Resolver name' _) = name == name'-    tryResolver fld (Resolver _ resolver)  = resolver fld     errmsg fld@(Field _ name _ _) = do         addErrMsg $ T.unwords ["field", name, "not resolved."]         return $ HashMap.singleton (aliasOrName fld) Aeson.Null
src/Language/GraphQL/Trans.hs view
@@ -1,7 +1,8 @@ -- | Monad transformer stack used by the @GraphQL@ resolvers. module Language.GraphQL.Trans     ( ActionT(..)-    , Context(Context)+    , Context(..)+    , argument     ) where  import Control.Applicative (Alternative(..))@@ -9,13 +10,17 @@ import Control.Monad.IO.Class (MonadIO(..)) import Control.Monad.Trans.Class (MonadTrans(..)) import Control.Monad.Trans.Except (ExceptT)-import Control.Monad.Trans.Reader (ReaderT)-import Data.HashMap.Strict (HashMap)+import Control.Monad.Trans.Reader (ReaderT, asks)+import qualified Data.HashMap.Strict as HashMap+import Data.Maybe (fromMaybe) import Data.Text (Text)-import Language.GraphQL.AST.Core (Name, Value)+import Language.GraphQL.AST.Core+import Prelude hiding (lookup)  -- | Resolution context holds resolver arguments.-newtype Context = Context (HashMap Name Value)+newtype Context = Context+    { arguments :: Arguments+    }  -- | Monad transformer stack used by the resolvers to provide error handling --   and resolution context (resolver arguments).@@ -47,3 +52,13 @@ instance Monad m => MonadPlus (ActionT m) where     mzero = empty     mplus = (<|>)++-- | Retrieves an argument by its name. If the argument with this name couldn't+--   be found, returns 'Value.Null' (i.e. the argument is assumed to+--   be optional then).+argument :: Monad m => Name -> ActionT m Value+argument argumentName = do+    argumentValue <- ActionT $ lift $ asks $ lookup . arguments+    pure $ fromMaybe Null argumentValue+  where+    lookup (Arguments argumentMap) = HashMap.lookup argumentName argumentMap
tests/Language/GraphQL/AST/EncoderSpec.hs view
@@ -6,37 +6,124 @@  import Language.GraphQL.AST import Language.GraphQL.AST.Encoder-import Test.Hspec (Spec, context, describe, it, shouldBe)+import Test.Hspec (Spec, context, describe, it, shouldBe, shouldStartWith, shouldEndWith, shouldNotContain)+import Test.QuickCheck (choose, oneof, forAll) import Text.RawString.QQ (r)+import Data.Text.Lazy (cons, toStrict, unpack)  spec :: Spec spec = do     describe "value" $ do+        context "null value" $ do+            let testNull formatter = value formatter Null `shouldBe` "null"+            it "minified" $ testNull minified+            it "pretty" $ testNull pretty+         context "minified" $ do             it "escapes \\" $                 value minified (String "\\") `shouldBe` "\"\\\\\""-            it "escapes quotes" $+            it "escapes double quotes" $                 value minified (String "\"") `shouldBe` "\"\\\"\""+            it "escapes \\f" $+                value minified (String "\f") `shouldBe` "\"\\f\""+            it "escapes \\n" $+                value minified (String "\n") `shouldBe` "\"\\n\""+            it "escapes \\r" $+                value minified (String "\r") `shouldBe` "\"\\r\""+            it "escapes \\t" $+                value minified (String "\t") `shouldBe` "\"\\t\""             it "escapes backspace" $                 value minified (String "a\bc") `shouldBe` "\"a\\bc\""-            it "escapes Unicode" $-                value minified (String "\0") `shouldBe` "\"\\u0000\""+            context "escapes Unicode for chars less than 0010" $ do+                it "Null" $ value minified (String "\x0000") `shouldBe` "\"\\u0000\""+                it "bell" $ value minified (String "\x0007") `shouldBe` "\"\\u0007\""+            context "escapes Unicode for char less than 0020" $ do+                it "DLE" $ value minified (String "\x0010") `shouldBe` "\"\\u0010\""+                it "EM" $ value minified (String "\x0019") `shouldBe` "\"\\u0019\""+            context "encodes without escape" $ do+                it "space" $ value minified (String "\x0020") `shouldBe` "\" \""+                it "~" $ value minified (String "\x007E") `shouldBe` "\"~\""          context "pretty" $ do             it "uses strings for short string values" $                 value pretty (String "Short text") `shouldBe` "\"Short text\""-            it "uses block strings for text with new lines" $+            it "uses block strings for text with new lines, with newline symbol" $                 value pretty (String "Line 1\nLine 2")-                    `shouldBe` "\"\"\"\n  Line 1\n  Line 2\n\"\"\""-            it "escapes \\ in short strings" $-                value pretty (String "\\") `shouldBe` "\"\\\\\""+                    `shouldBe` [r|"""+  Line 1+  Line 2+"""|]+            it "uses block strings for text with new lines, with CR symbol" $+                value pretty (String "Line 1\rLine 2")+                    `shouldBe` [r|"""+  Line 1+  Line 2+"""|]+            it "uses block strings for text with new lines, with CR symbol followed by newline" $+                value pretty (String "Line 1\r\nLine 2")+                    `shouldBe` [r|"""+  Line 1+  Line 2+"""|]+            it "encodes as one line string if has escaped symbols" $ do+                let+                  genNotAllowedSymbol = oneof+                    [ choose ('\x0000', '\x0008')+                    , choose ('\x000B', '\x000C')+                    , choose ('\x000E', '\x001F')+                    , pure '\x007F'+                    ] +                forAll genNotAllowedSymbol $ \x -> do+                    let+                      rawValue = "Short \n" <> cons x "text"+                      encoded = value pretty (String $ toStrict rawValue)+                    shouldStartWith (unpack encoded) "\""+                    shouldEndWith (unpack encoded) "\""+                    shouldNotContain (unpack encoded) "\"\"\""++            it "Hello world" $ value pretty (String "Hello,\n  World!\n\nYours,\n  GraphQL.")+              `shouldBe` [r|"""+  Hello,+    World!++  Yours,+    GraphQL.+"""|]++            it "has only newlines" $ value pretty (String "\n") `shouldBe` [r|"""+++"""|]+            it "has newlines and one symbol at the begining" $+              value pretty (String "a\n\n") `shouldBe` [r|"""+  a+++"""|]+            it "has newlines and one symbol at the end" $+              value pretty (String "\n\na") `shouldBe` [r|"""+++  a+"""|]+            it "has newlines and one symbol in the middle" $+              value pretty (String "\na\n") `shouldBe` [r|"""++  a++"""|]+            it "skip trailing whitespaces" $ value pretty (String "  Short\ntext    ")+              `shouldBe` [r|"""+  Short+  text+"""|]+     describe "definition" $         it "indents block strings in arguments" $             let arguments = [Argument "message" (String "line1\nline2")]                 field = Field Nothing "field" arguments [] []-                set = OperationSelectionSet $ pure $ SelectionField field-                operation = DefinitionOperation set+                operation = DefinitionOperation $ SelectionSet $ pure field              in definition pretty operation `shouldBe` [r|{   field(message: """     line1
tests/Language/GraphQL/AST/LexerSpec.hs view
@@ -8,7 +8,7 @@ import Data.Void (Void) import Language.GraphQL.AST.Lexer import Test.Hspec (Spec, context, describe, it)-import Test.Hspec.Megaparsec (shouldParse, shouldSucceedOn)+import Test.Hspec.Megaparsec (shouldParse, shouldFailOn, shouldSucceedOn) import Text.Megaparsec (ParseErrorBundle, parse) import Text.RawString.QQ (r) @@ -77,7 +77,7 @@             parse spread "" "..." `shouldParse` "..."             parse colon "" ":" `shouldParse` ":"             parse equals "" "=" `shouldParse` "="-            parse at "" "@" `shouldParse` '@'+            parse at "" "@" `shouldParse` "@"             runBetween brackets `shouldSucceedOn` "[]"             runBetween braces `shouldSucceedOn` "{}"             parse pipe "" "|" `shouldParse` "|"@@ -87,6 +87,13 @@             parse blockString "" [r|""""""|] `shouldParse` ""         it "lexes ampersand" $             parse amp "" "&" `shouldParse` "&"+        it "lexes schema extensions" $+            parseExtend "schema" `shouldSucceedOn` "extend schema"+        it "fails if the given token doesn't match" $+            parseExtend "schema" `shouldFailOn` "extend shema"++parseExtend :: Text -> (Text -> Either (ParseErrorBundle Text Void) ())+parseExtend extension = parse (extend extension "" $ pure $ pure ()) ""  runBetween :: (Parser () -> Parser ()) -> Text -> Either (ParseErrorBundle Text Void) () runBetween parser = parse (parser $ pure ()) ""
tests/Language/GraphQL/AST/ParserSpec.hs view
@@ -4,9 +4,11 @@     ( spec     ) where +import Data.List.NonEmpty (NonEmpty(..))+import Language.GraphQL.AST.Document import Language.GraphQL.AST.Parser import Test.Hspec (Spec, describe, it)-import Test.Hspec.Megaparsec (shouldSucceedOn)+import Test.Hspec.Megaparsec (shouldParse, shouldSucceedOn) import Text.Megaparsec (parse) import Text.RawString.QQ (r) @@ -28,17 +30,115 @@     it "accepts two required arguments" $         parse document "" `shouldSucceedOn` [r|             mutation auth($username: String!, $password: String!){-                test+              test             }|]      it "accepts two string arguments" $         parse document "" `shouldSucceedOn` [r|             mutation auth{-                test(username: "username", password: "password")+              test(username: "username", password: "password")             }|]      it "accepts two block string arguments" $         parse document "" `shouldSucceedOn` [r|             mutation auth{-                test(username: """username""", password: """password""")+              test(username: """username""", password: """password""")             }|]++    it "parses minimal schema definition" $+        parse document "" `shouldSucceedOn` [r|schema { query: Query }|]++    it "parses minimal scalar definition" $+        parse document "" `shouldSucceedOn` [r|scalar Time|]++    it "parses ImplementsInterfaces" $+        parse document "" `shouldSucceedOn` [r|+            type Person implements NamedEntity & ValuedEntity {+              name: String+            }+        |]++    it "parses a  type without ImplementsInterfaces" $+        parse document "" `shouldSucceedOn` [r|+            type Person {+              name: String+            }+        |]++    it "parses ArgumentsDefinition in an ObjectDefinition" $+        parse document "" `shouldSucceedOn` [r|+            type Person {+              name(first: String, last: String): String+            }+        |]++    it "parses minimal union type definition" $+        parse document "" `shouldSucceedOn` [r|+            union SearchResult = Photo | Person+        |]++    it "parses minimal interface type definition" $+        parse document "" `shouldSucceedOn` [r|+            interface NamedEntity {+              name: String+            }+        |]++    it "parses minimal enum type definition" $+        parse document "" `shouldSucceedOn` [r|+            enum Direction {+              NORTH+              EAST+              SOUTH+              WEST+            }+        |]++    it "parses minimal enum type definition" $+        parse document "" `shouldSucceedOn` [r|+            enum Direction {+              NORTH+              EAST+              SOUTH+              WEST+            }+        |]++    it "parses minimal input object type definition" $+        parse document "" `shouldSucceedOn` [r|+            input Point2D {+              x: Float+              y: Float+            }+        |]++    it "parses minimal input enum definition with an optional pipe" $+        parse document "" `shouldSucceedOn` [r|+            directive @example on+              | FIELD+              | FRAGMENT_SPREAD+        |]++    it "parses schema extension with a new directive" $+        parse document "" `shouldSucceedOn`[r|+            extend schema @newDirective+        |]++    it "parses schema extension with an operation type definition" $+        parse document "" `shouldSucceedOn` [r|extend schema { query: Query }|]++    it "parses schema extension with an operation type and directive" $+        let newDirective = Directive "newDirective" []+            testSchemaExtension = TypeSystemExtension+                $ SchemaExtension+                $ SchemaOperationExtension [newDirective]+                $ OperationTypeDefinition Query "Query" :| []+            query = [r|extend schema @newDirective { query: Query }|]+         in parse document "" query `shouldParse` (testSchemaExtension :| [])++    it "parses an object extension" $+        parse document "" `shouldSucceedOn` [r|+            extend type Story {+              isHiddenLocally: Boolean+            }+        |]
tests/Test/DirectiveSpec.hs view
@@ -5,14 +5,18 @@     ) where  import Data.Aeson (Value, object, (.=))+import Data.HashMap.Strict (HashMap)+import qualified Data.HashMap.Strict as HashMap import Data.List.NonEmpty (NonEmpty(..))+import Data.Text (Text) import Language.GraphQL import qualified Language.GraphQL.Schema as Schema import Test.Hspec (Spec, describe, it, shouldBe) import Text.RawString.QQ (r) -experimentalResolver :: Schema.Resolver IO-experimentalResolver = Schema.scalar "experimentalField" $ pure (5 :: Int) +experimentalResolver :: HashMap Text (NonEmpty (Schema.Resolver IO))+experimentalResolver = HashMap.singleton "Query"+    $ Schema.scalar "experimentalField" (pure (5 :: Int)) :| []  emptyObject :: Value emptyObject = object@@ -29,7 +33,7 @@               }             |] -            actual <- graphql (experimentalResolver :| []) query+            actual <- graphql experimentalResolver query             actual `shouldBe` emptyObject          it "should not skip fields if @skip is false" $ do@@ -44,7 +48,7 @@                         ]                     ] -            actual <- graphql (experimentalResolver :| []) query+            actual <- graphql experimentalResolver query             actual `shouldBe` expected          it "should skip fields if @include is false" $ do@@ -54,7 +58,7 @@               }             |] -            actual <- graphql (experimentalResolver :| []) query+            actual <- graphql experimentalResolver query             actual `shouldBe` emptyObject          it "should be able to @skip a fragment spread" $ do@@ -68,7 +72,7 @@               }             |] -            actual <- graphql (experimentalResolver :| []) query+            actual <- graphql experimentalResolver query             actual `shouldBe` emptyObject          it "should be able to @skip an inline fragment" $ do@@ -80,5 +84,5 @@               }             |] -            actual <- graphql (experimentalResolver :| []) query+            actual <- graphql experimentalResolver query             actual `shouldBe` emptyObject
tests/Test/FragmentSpec.hs view
@@ -51,7 +51,7 @@ spec = do     describe "Inline fragment executor" $ do         it "chooses the first selection if the type matches" $ do-            actual <- graphql (garment "Hat" :| []) inlineQuery+            actual <- graphql (HashMap.singleton "Query" $ garment "Hat" :| []) inlineQuery             let expected = object                     [ "data" .= object                         [ "garment" .= object@@ -62,7 +62,7 @@              in actual `shouldBe` expected          it "chooses the last selection if the type matches" $ do-            actual <- graphql (garment "Shirt" :| []) inlineQuery+            actual <- graphql (HashMap.singleton "Query" $ garment "Shirt" :| []) inlineQuery             let expected = object                     [ "data" .= object                         [ "garment" .= object@@ -83,7 +83,7 @@             }|]                 resolvers = Schema.object "garment" $ return [circumference,  size] -            actual <- graphql (resolvers :| []) query+            actual <- graphql (HashMap.singleton "Query" $ resolvers :| []) query             let expected = object                     [ "data" .= object                         [ "garment" .= object@@ -101,7 +101,7 @@               }             }|] -            actual <- graphql (size :| []) query+            actual <- graphql (HashMap.singleton "Query" $ size :| []) query             actual `shouldNotSatisfy` hasErrors      describe "Fragment spread executor" $ do@@ -116,7 +116,7 @@               }             |] -            actual <- graphql (circumference :| []) query+            actual <- graphql (HashMap.singleton "Query" $ circumference :| []) query             let expected = object                     [ "data" .= object                         [ "circumference" .= (60 :: Int)@@ -141,7 +141,7 @@               }             |] -            actual <- graphql (garment "Hat" :| []) query+            actual <- graphql (HashMap.singleton "Query" $ garment "Hat" :| []) query             let expected = object                     [ "data" .= object                         [ "garment" .= object@@ -162,7 +162,7 @@               }             |] -            actual <- graphql (circumference :| []) query+            actual <- graphql (HashMap.singleton "Query" $ circumference :| []) query             actual `shouldSatisfy` hasErrors          it "considers type condition" $ do@@ -187,5 +187,5 @@                             ]                         ]                     ]-            actual <- graphql (garment "Hat" :| []) query+            actual <- graphql (HashMap.singleton "Query" $ garment "Hat" :| []) query             actual `shouldBe` expected
tests/Test/StarWars/Data.hs view
@@ -8,7 +8,6 @@     , getEpisode     , getFriends     , getHero-    , getHeroIO     , getHuman     , id_     , homePlanet@@ -17,11 +16,8 @@     , typeName     ) where -import Data.Monoid (mempty)-import Control.Applicative ( Alternative(..)-                           , liftA2 -                           )-import Control.Monad.IO.Class (MonadIO(..))+import Data.Functor.Identity (Identity)+import Control.Applicative (Alternative(..), liftA2) import Control.Monad.Trans.Except (throwE) import Data.Maybe (catMaybes) import Data.Text (Text)@@ -71,7 +67,7 @@ appearsIn (Left  x) = _appearsIn . _droidChar $ x appearsIn (Right x) = _appearsIn . _humanChar $ x -secretBackstory :: MonadIO m => Character -> ActionT m Text+secretBackstory :: Character -> ActionT Identity Text secretBackstory = const $ ActionT $ throwE "secretBackstory is secret."  typeName :: Character -> Text@@ -165,9 +161,6 @@ getHero :: Int -> Character getHero 5 = luke getHero _ = artoo--getHeroIO :: Int -> IO Character-getHeroIO = pure . getHero  getHuman :: Alternative f => ID -> f Character getHuman = fmap Right . getHuman'
tests/Test/StarWars/QuerySpec.hs view
@@ -5,20 +5,15 @@     ) where  import qualified Data.Aeson as Aeson-import Data.Aeson ( object-                  , (.=)-                  )+import Data.Aeson ((.=))+import Data.Functor.Identity (Identity(..))+import qualified Data.HashMap.Strict as HashMap import Data.Text (Text) import Language.GraphQL import Language.GraphQL.Schema (Subs) import Text.RawString.QQ (r)-import Test.Hspec.Expectations ( Expectation-                               , shouldBe-                               )-import Test.Hspec ( Spec-                  , describe-                  , it-                  )+import Test.Hspec.Expectations (Expectation, shouldBe)+import Test.Hspec (Spec, describe, it) import Test.StarWars.Schema  -- * Test@@ -34,7 +29,11 @@             }             }         |]-        $ object [ "data" .= object ["hero" .= object ["id" .= ("2001" :: Text)]]]+        $ Aeson.object+            [ "data" .= Aeson.object+                [ "hero" .= Aeson.object ["id" .= ("2001" :: Text)]+                ]+            ]       it "R2-D2 ID and friends" $ testQuery         [r| query HeroNameAndFriendsQuery {             hero {@@ -46,14 +45,14 @@             }             }         |]-        $ object [ "data" .= object [-            "hero" .= object+        $ Aeson.object [ "data" .= Aeson.object [+            "hero" .= Aeson.object                 [ "id" .= ("2001" :: Text)                 , r2d2Name                 , "friends" .=-                    [ object [lukeName]-                    , object [hanName]-                    , object [leiaName]+                    [ Aeson.object [lukeName]+                    , Aeson.object [hanName]+                    , Aeson.object [leiaName]                     ]                 ]         ]]@@ -73,37 +72,37 @@               }             }         |]-        $ object [ "data" .= object [-          "hero" .= object [+        $ Aeson.object [ "data" .= Aeson.object [+          "hero" .= Aeson.object [               "name" .= ("R2-D2" :: Text)             , "friends" .= [-                  object [+                  Aeson.object [                       "name" .= ("Luke Skywalker" :: Text)                     , "appearsIn" .= ["NEWHOPE","EMPIRE","JEDI" :: Text]                     , "friends" .= [-                          object [hanName]-                        , object [leiaName]-                        , object [c3poName]-                        , object [r2d2Name]+                          Aeson.object [hanName]+                        , Aeson.object [leiaName]+                        , Aeson.object [c3poName]+                        , Aeson.object [r2d2Name]                         ]                     ]-                , object [+                , Aeson.object [                       hanName                     , "appearsIn" .= [ "NEWHOPE","EMPIRE","JEDI" :: Text]-                    , "friends" .= [-                          object [lukeName]-                        , object [leiaName]-                        , object [r2d2Name]+                    , "friends" .=+                        [ Aeson.object [lukeName]+                        , Aeson.object [leiaName]+                        , Aeson.object [r2d2Name]                         ]                     ]-                , object [+                , Aeson.object [                       leiaName                     , "appearsIn" .= [ "NEWHOPE","EMPIRE","JEDI" :: Text]-                    , "friends" .= [-                          object [lukeName]-                        , object [hanName]-                        , object [c3poName]-                        , object [r2d2Name]+                    , "friends" .=+                        [ Aeson.object [lukeName]+                        , Aeson.object [hanName]+                        , Aeson.object [c3poName]+                        , Aeson.object [r2d2Name]                         ]                     ]                 ]@@ -116,40 +115,40 @@               }             }         |]-        $ object [ "data" .= object [-          "human" .= object [lukeName]-        ]]+        $ Aeson.object [ "data" .= Aeson.object+            [ "human" .= Aeson.object [lukeName]+            ]]      it "Luke ID with variable" $ testQueryParams-      (\v -> if v == "someId" then Just "1000" else Nothing)+      (HashMap.singleton "someId" "1000")       [r| query FetchSomeIDQuery($someId: String!) {             human(id: $someId) {               name             }           }       |]-      $ object [ "data" .= object [-        "human" .= object [lukeName]+      $ Aeson.object [ "data" .= Aeson.object [+        "human" .= Aeson.object [lukeName]       ]]     it "Han ID with variable" $ testQueryParams-      (\v -> if v == "someId" then Just "1002" else Nothing)+      (HashMap.singleton "someId" "1002")       [r| query FetchSomeIDQuery($someId: String!) {             human(id: $someId) {               name             }           }       |]-      $ object [ "data" .= object [-        "human" .= object [hanName]+      $ Aeson.object [ "data" .= Aeson.object [+        "human" .= Aeson.object [hanName]       ]]     it "Invalid ID" $ testQueryParams-      (\v -> if v == "id" then Just "Not a valid ID" else Nothing)+      (HashMap.singleton "id" "Not a valid ID")       [r| query humanQuery($id: String!) {             human(id: $id) {               name             }           }-      |] $ object ["data" .= object ["human" .= Aeson.Null]]+      |] $ Aeson.object ["data" .= Aeson.object ["human" .= Aeson.Null]]     it "Luke aliased" $ testQuery       [r| query FetchLukeAliased {             luke: human(id: "1000") {@@ -157,8 +156,8 @@             }           }       |]-      $ object [ "data" .= object [-       "luke" .= object [lukeName]+      $ Aeson.object [ "data" .= Aeson.object [+       "luke" .= Aeson.object [lukeName]       ]]     it "R2-D2 ID and friends aliased" $ testQuery       [r| query HeroNameAndFriendsQuery {@@ -171,14 +170,14 @@             }           }       |]-      $ object [ "data" .= object [-        "hero" .= object [+      $ Aeson.object [ "data" .= Aeson.object [+        "hero" .= Aeson.object [             "id" .= ("2001" :: Text)           , r2d2Name-          , "friends" .= [-                object ["friendName" .= ("Luke Skywalker" :: Text)]-              , object ["friendName" .= ("Han Solo" :: Text)]-              , object ["friendName" .= ("Leia Organa" :: Text)]+          , "friends" .=+              [ Aeson.object ["friendName" .= ("Luke Skywalker" :: Text)]+              , Aeson.object ["friendName" .= ("Han Solo" :: Text)]+              , Aeson.object ["friendName" .= ("Leia Organa" :: Text)]               ]           ]       ]]@@ -192,9 +191,9 @@             }           }       |]-      $ object [ "data" .= object [-        "luke" .= object [lukeName]-      , "leia" .= object [leiaName]+      $ Aeson.object [ "data" .= Aeson.object+        [ "luke" .= Aeson.object [lukeName]+        , "leia" .= Aeson.object [leiaName]       ]]      describe "Fragments for complex queries" $ do@@ -210,9 +209,9 @@               }             }         |]-        $ object [ "data" .= object [-          "luke" .= object [lukeName, tatooine]-        , "leia" .= object [leiaName, alderaan]+        $ Aeson.object [ "data" .= Aeson.object [+          "luke" .= Aeson.object [lukeName, tatooine]+        , "leia" .= Aeson.object [leiaName, alderaan]         ]]       it "Fragment for duplicate content" $ testQuery         [r|  query UseFragment {@@ -228,9 +227,9 @@               homePlanet             }         |]-        $ object [ "data" .= object [-          "luke" .= object [lukeName, tatooine]-        , "leia" .= object [leiaName, alderaan]+        $ Aeson.object [ "data" .= Aeson.object [+          "luke" .= Aeson.object [lukeName, tatooine]+        , "leia" .= Aeson.object [leiaName, alderaan]         ]]      describe "__typename" $ do@@ -242,8 +241,11 @@               }             }           |]-        $ object ["data" .= object [-            "hero" .= object ["__typename" .= ("Droid" :: Text), r2d2Name]+        $ Aeson.object ["data" .= Aeson.object [+            "hero" .= Aeson.object+                [ "__typename" .= ("Droid" :: Text)+                , r2d2Name+                ]         ]]       it "Luke is a human" $ testQuery         [r| query CheckTypeOfLuke {@@ -253,8 +255,11 @@               }             }           |]-        $ object ["data" .= object [-            "hero" .= object ["__typename" .= ("Human" :: Text), lukeName]+        $ Aeson.object ["data" .= Aeson.object [+            "hero" .= Aeson.object+                [ "__typename" .= ("Human" :: Text)+                , lukeName+                ]         ]]      describe "Errors in resolvers" $ do@@ -267,15 +272,15 @@               }             }           |]-          $ object-              [ "data" .= object-                  [ "hero" .= object+          $ Aeson.object+              [ "data" .= Aeson.object+                  [ "hero" .= Aeson.object                       [ "name" .= ("R2-D2" :: Text)                       , "secretBackstory" .= Aeson.Null                       ]                   ]               , "errors" .=-                  [ object+                  [ Aeson.object                       ["message" .= ("secretBackstory is secret." :: Text)]                   ]               ]@@ -290,19 +295,19 @@                 }               }             |]-          $ object ["data" .= object-            [ "hero" .= object+          $ Aeson.object ["data" .= Aeson.object+            [ "hero" .= Aeson.object                 [ "name" .= ("R2-D2" :: Text)                 , "friends" .=-                    [ object+                    [ Aeson.object                         [ "name" .= ("Luke Skywalker" :: Text)                         , "secretBackstory" .= Aeson.Null                         ]-                    , object+                    , Aeson.object                         [ "name" .= ("Han Solo" :: Text)                         , "secretBackstory" .= Aeson.Null                         ]-                    , object+                    , Aeson.object                         [ "name" .= ("Leia Organa" :: Text)                         , "secretBackstory" .= Aeson.Null                         ]@@ -310,9 +315,15 @@                 ]             ]             , "errors" .=-                [ object ["message" .= ("secretBackstory is secret." :: Text)]-                , object ["message" .= ("secretBackstory is secret." :: Text)]-                , object ["message" .= ("secretBackstory is secret." :: Text)]+                [ Aeson.object+                    [ "message" .= ("secretBackstory is secret." :: Text)+                    ]+                , Aeson.object+                    [ "message" .= ("secretBackstory is secret." :: Text)+                    ]+                , Aeson.object+                    [ "message" .= ("secretBackstory is secret." :: Text)+                    ]                 ]             ]         it "error on secretBackstory with alias" $ testQuery@@ -323,15 +334,17 @@                 }               }             |]-          $ object-              [ "data" .= object-                  [ "mainHero" .= object+          $ Aeson.object+              [ "data" .= Aeson.object+                  [ "mainHero" .= Aeson.object                       [ "name" .= ("R2-D2" :: Text)                       , "story" .= Aeson.Null                       ]                   ]               , "errors" .=-                  [ object ["message" .= ("secretBackstory is secret." :: Text)]+                  [ Aeson.object+                    [ "message" .= ("secretBackstory is secret." :: Text)+                    ]                   ]               ] @@ -345,7 +358,8 @@     alderaan = "homePlanet" .= ("Alderaan" :: Text)  testQuery :: Text -> Aeson.Value -> Expectation-testQuery q expected = graphql schema q >>= flip shouldBe expected+testQuery q expected = runIdentity (graphql schema q) `shouldBe` expected  testQueryParams :: Subs -> Text -> Aeson.Value -> Expectation-testQueryParams f q expected = graphqlSubs schema f q >>= flip shouldBe expected+testQueryParams f q expected =+    runIdentity (graphqlSubs schema f q) `shouldBe` expected
tests/Test/StarWars/Schema.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} module Test.StarWars.Schema     ( character@@ -10,9 +9,12 @@  import Control.Monad.Trans.Except (throwE) import Control.Monad.Trans.Class (lift)-import Control.Monad.IO.Class (MonadIO(..))+import Data.Functor.Identity (Identity)+import Data.HashMap.Strict (HashMap)+import qualified Data.HashMap.Strict as HashMap import Data.List.NonEmpty (NonEmpty(..)) import Data.Maybe (catMaybes)+import Data.Text (Text) import qualified Language.GraphQL.Schema as Schema import Language.GraphQL.Trans import qualified Language.GraphQL.Type as Type@@ -20,32 +22,37 @@  -- See https://github.com/graphql/graphql-js/blob/master/src/__tests__/starWarsSchema.js -schema :: MonadIO m => NonEmpty (Schema.Resolver m)-schema = hero :| [human, droid]+schema :: HashMap Text (NonEmpty (Schema.Resolver Identity))+schema = HashMap.singleton "Query" $ hero :| [human, droid] -hero :: MonadIO m => Schema.Resolver m-hero = Schema.objectA "hero" $ \case-  [] -> character artoo-  [Schema.Argument "episode" (Schema.Enum "NEWHOPE")] -> character $ getHero 4-  [Schema.Argument "episode" (Schema.Enum "EMPIRE" )] -> character $ getHero 5-  [Schema.Argument "episode" (Schema.Enum "JEDI"   )] -> character $ getHero 6-  _ -> ActionT $ throwE "Invalid arguments."+hero :: Schema.Resolver Identity+hero = Schema.object "hero" $ do+  episode <- argument "episode"+  character $ case episode of+      Schema.Enum "NEWHOPE" -> getHero 4+      Schema.Enum "EMPIRE" -> getHero 5+      Schema.Enum "JEDI" -> getHero 6+      _ -> artoo -human :: MonadIO m => Schema.Resolver m-human = Schema.wrappedObjectA "human" $ \case-  [Schema.Argument "id" (Schema.String i)] -> do-      humanCharacter <- lift $ return $ getHuman i >>= Just-      case humanCharacter of-        Nothing -> return Type.Null-        Just e -> Type.Named <$> character e-  _ -> ActionT $ throwE "Invalid arguments."+human :: Schema.Resolver Identity+human = Schema.wrappedObject "human" $ do+    id' <- argument "id"+    case id' of+        Schema.String i -> do+            humanCharacter <- lift $ return $ getHuman i >>= Just+            case humanCharacter of+                Nothing -> return Type.Null+                Just e -> Type.Named <$> character e+        _ -> ActionT $ throwE "Invalid arguments." -droid :: MonadIO m => Schema.Resolver m-droid = Schema.objectA "droid" $ \case-   [Schema.Argument "id" (Schema.String i)] -> character =<< liftIO (getDroid i)-   _ -> ActionT $ throwE "Invalid arguments."+droid :: Schema.Resolver Identity+droid = Schema.object "droid" $ do+    id' <- argument "id"+    case id' of+        Schema.String i -> character =<< getDroid i+        _ -> ActionT $ throwE "Invalid arguments." -character :: MonadIO m => Character -> ActionT m [Schema.Resolver m]+character :: Character -> ActionT Identity [Schema.Resolver Identity] character char = return     [ Schema.scalar "id" $ return $ id_ char     , Schema.scalar "name" $ return $ name char