diff options
Diffstat (limited to 'src/Language/GraphQL/AST/Document.hs')
| -rw-r--r-- | src/Language/GraphQL/AST/Document.hs | 486 |
1 files changed, 486 insertions, 0 deletions
diff --git a/src/Language/GraphQL/AST/Document.hs b/src/Language/GraphQL/AST/Document.hs new file mode 100644 index 0000000..3b13691 --- /dev/null +++ b/src/Language/GraphQL/AST/Document.hs @@ -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) |
