diff --git a/graphql.go b/graphql.go index cb0c9082dd..4b1d85908c 100644 --- a/graphql.go +++ b/graphql.go @@ -12,6 +12,7 @@ import ( "github.com/neelance/graphql-go/internal/exec" "github.com/neelance/graphql-go/internal/query" "github.com/neelance/graphql-go/internal/schema" + "github.com/neelance/graphql-go/introspection" ) const OpenTracingTagQuery = "graphql.query" @@ -111,6 +112,10 @@ func (s *Schema) Exec(ctx context.Context, queryString string, operationName str } } +func (s *Schema) Inspect() *introspection.Schema { + return &introspection.Schema{Schema: s.schema} +} + func (s *Schema) ToJSON() ([]byte, error) { result, err := exec.IntrospectSchema(s.schema) if err != nil { diff --git a/internal/common/types.go b/internal/common/types.go index f69509ee5f..f3d1c0ebd3 100644 --- a/internal/common/types.go +++ b/internal/common/types.go @@ -69,6 +69,6 @@ func ResolveType(t Type, resolver Resolver) (Type, error) { } return refT, nil default: - panic("unreachable") + return t, nil } } diff --git a/internal/exec/introspection.go b/internal/exec/introspection.go index b81d139c13..220a82a934 100644 --- a/internal/exec/introspection.go +++ b/internal/exec/introspection.go @@ -2,30 +2,21 @@ package exec import ( "context" - "fmt" "reflect" - "sort" - "strings" "github.com/neelance/graphql-go/errors" - "github.com/neelance/graphql-go/internal/common" "github.com/neelance/graphql-go/internal/query" "github.com/neelance/graphql-go/internal/schema" + "github.com/neelance/graphql-go/introspection" ) -var metaSchema *schema.Schema var schemaExec iExec var typeExec iExec func init() { - metaSchema = schema.New() - if err := metaSchema.Parse(metaSchemaSrc); err != nil { - panic(err) - } - { var err error - schemaExec, err = makeWithType(metaSchema, metaSchema.Types["__Schema"], &schemaResolver{}) + schemaExec, err = makeWithType(schema.Meta, schema.Meta.Types["__Schema"], &introspection.Schema{}) if err != nil { panic(err) } @@ -33,7 +24,7 @@ func init() { { var err error - typeExec, err = makeWithType(metaSchema, metaSchema.Types["__Type"], &typeResolver{}) + typeExec, err = makeWithType(schema.Meta, schema.Meta.Types["__Type"], &introspection.Type{}) if err != nil { panic(err) } @@ -49,7 +40,7 @@ func IntrospectSchema(s *schema.Schema) (interface{}, error) { } func introspectSchema(ctx context.Context, r *request, selSet *query.SelectionSet) interface{} { - return schemaExec.exec(ctx, r, selSet, reflect.ValueOf(&schemaResolver{r.schema}), false) + return schemaExec.exec(ctx, r, selSet, reflect.ValueOf(&introspection.Schema{Schema: r.schema}), false) } func introspectType(ctx context.Context, r *request, name string, selSet *query.SelectionSet) interface{} { @@ -57,458 +48,14 @@ func introspectType(ctx context.Context, r *request, name string, selSet *query. if !ok { return nil } - return typeExec.exec(ctx, r, selSet, reflect.ValueOf(&typeResolver{t}), false) -} - -var metaSchemaSrc = ` - # A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document. - # - # In some cases, you need to provide options to alter GraphQL's execution behavior - # in ways field arguments will not suffice, such as conditionally including or - # skipping a field. Directives provide this by describing additional information - # to the executor. - type __Directive { - name: String! - description: String - locations: [__DirectiveLocation!]! - args: [__InputValue!]! - } - - # A Directive can be adjacent to many parts of the GraphQL language, a - # __DirectiveLocation describes one such possible adjacencies. - enum __DirectiveLocation { - # Location adjacent to a query operation. - QUERY - # Location adjacent to a mutation operation. - MUTATION - # Location adjacent to a subscription operation. - SUBSCRIPTION - # Location adjacent to a field. - FIELD - # Location adjacent to a fragment definition. - FRAGMENT_DEFINITION - # Location adjacent to a fragment spread. - FRAGMENT_SPREAD - # Location adjacent to an inline fragment. - INLINE_FRAGMENT - # Location adjacent to a schema definition. - SCHEMA - # Location adjacent to a scalar definition. - SCALAR - # Location adjacent to an object type definition. - OBJECT - # Location adjacent to a field definition. - FIELD_DEFINITION - # Location adjacent to an argument definition. - ARGUMENT_DEFINITION - # Location adjacent to an interface definition. - INTERFACE - # Location adjacent to a union definition. - UNION - # Location adjacent to an enum definition. - ENUM - # Location adjacent to an enum value definition. - ENUM_VALUE - # Location adjacent to an input object type definition. - INPUT_OBJECT - # Location adjacent to an input object field definition. - INPUT_FIELD_DEFINITION - } - - # One possible value for a given Enum. Enum values are unique values, not a - # placeholder for a string or numeric value. However an Enum value is returned in - # a JSON response as a string. - type __EnumValue { - name: String! - description: String - isDeprecated: Boolean! - deprecationReason: String - } - - # Object and Interface types are described by a list of Fields, each of which has - # a name, potentially a list of arguments, and a return type. - type __Field { - name: String! - description: String - args: [__InputValue!]! - type: __Type! - isDeprecated: Boolean! - deprecationReason: String - } - - # Arguments provided to Fields or Directives and the input fields of an - # InputObject are represented as Input Values which describe their type and - # optionally a default value. - type __InputValue { - name: String! - description: String - type: __Type! - # A GraphQL-formatted string representing the default value for this input value. - defaultValue: String - } - - # A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all - # available types and directives on the server, as well as the entry points for - # query, mutation, and subscription operations. - type __Schema { - # A list of all types supported by this server. - types: [__Type!]! - # The type that query operations will be rooted at. - queryType: __Type! - # If this server supports mutation, the type that mutation operations will be rooted at. - mutationType: __Type - # If this server support subscription, the type that subscription operations will be rooted at. - subscriptionType: __Type - # A list of all directives supported by this server. - directives: [__Directive!]! - } - - # The fundamental unit of any GraphQL Schema is the type. There are many kinds of - # types in GraphQL as represented by the ` + "`" + `__TypeKind` + "`" + ` enum. - # - # Depending on the kind of a type, certain fields describe information about that - # type. Scalar types provide no information beyond a name and description, while - # Enum types provide their values. Object and Interface types provide the fields - # they describe. Abstract types, Union and Interface, provide the Object types - # possible at runtime. List and NonNull types compose other types. - type __Type { - kind: __TypeKind! - name: String - description: String - fields(includeDeprecated: Boolean = false): [__Field!] - interfaces: [__Type!] - possibleTypes: [__Type!] - enumValues(includeDeprecated: Boolean = false): [__EnumValue!] - inputFields: [__InputValue!] - ofType: __Type - } - - # An enum describing what kind of type a given ` + "`" + `__Type` + "`" + ` is. - enum __TypeKind { - # Indicates this type is a scalar. - SCALAR - # Indicates this type is an object. ` + "`" + `fields` + "`" + ` and ` + "`" + `interfaces` + "`" + ` are valid fields. - OBJECT - # Indicates this type is an interface. ` + "`" + `fields` + "`" + ` and ` + "`" + `possibleTypes` + "`" + ` are valid fields. - INTERFACE - # Indicates this type is a union. ` + "`" + `possibleTypes` + "`" + ` is a valid field. - UNION - # Indicates this type is an enum. ` + "`" + `enumValues` + "`" + ` is a valid field. - ENUM - # Indicates this type is an input object. ` + "`" + `inputFields` + "`" + ` is a valid field. - INPUT_OBJECT - # Indicates this type is a list. ` + "`" + `ofType` + "`" + ` is a valid field. - LIST - # Indicates this type is a non-null. ` + "`" + `ofType` + "`" + ` is a valid field. - NON_NULL - } -` - -type schemaResolver struct { - schema *schema.Schema -} - -func (r *schemaResolver) Types() []*typeResolver { - var l []*typeResolver - addTypes := func(s *schema.Schema, metaOnly bool) { - var names []string - for name := range s.Types { - if !metaOnly || strings.HasPrefix(name, "__") { - names = append(names, name) - } - } - sort.Strings(names) - for _, name := range names { - l = append(l, &typeResolver{s.Types[name]}) - } - } - addTypes(r.schema, false) - addTypes(metaSchema, true) - return l -} - -func (r *schemaResolver) QueryType() *typeResolver { - t, ok := r.schema.EntryPoints["query"] - if !ok { - return nil - } - return &typeResolver{t} -} - -func (r *schemaResolver) MutationType() *typeResolver { - t, ok := r.schema.EntryPoints["mutation"] - if !ok { - return nil - } - return &typeResolver{t} -} - -func (r *schemaResolver) SubscriptionType() *typeResolver { - t, ok := r.schema.EntryPoints["subscription"] - if !ok { - return nil - } - return &typeResolver{t} -} - -func (r *schemaResolver) Directives() []*directiveResolver { - return []*directiveResolver{ - &directiveResolver{ - name: "skip", - description: "Directs the executor to skip this field or fragment when the `if` argument is true.", - locations: []string{"FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"}, - args: []*inputValueResolver{ - &inputValueResolver{&common.InputValue{ - Name: "if", - Desc: "Skipped when true.", - Type: &common.NonNull{OfType: r.schema.Types["Boolean"]}, - }}, - }, - }, - &directiveResolver{ - name: "include", - description: "Directs the executor to include this field or fragment only when the `if` argument is true.", - locations: []string{"FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"}, - args: []*inputValueResolver{ - &inputValueResolver{&common.InputValue{ - Name: "if", - Desc: "Included when true.", - Type: &common.NonNull{OfType: r.schema.Types["Boolean"]}, - }}, - }, - }, - } -} - -type typeResolver struct { - typ common.Type -} - -func (r *typeResolver) Kind() string { - return r.typ.Kind() -} - -func (r *typeResolver) Name() *string { - if named, ok := r.typ.(schema.NamedType); ok { - name := named.TypeName() - return &name - } - return nil -} - -func (r *typeResolver) Description() *string { - if named, ok := r.typ.(schema.NamedType); ok { - desc := named.Description() - if desc == "" { - return nil - } - return &desc - } - return nil -} - -func (r *typeResolver) Fields(args *struct{ IncludeDeprecated bool }) *[]*fieldResolver { - var fields map[string]*schema.Field - var fieldOrder []string - switch t := r.typ.(type) { - case *schema.Object: - fields = t.Fields - fieldOrder = t.FieldOrder - case *schema.Interface: - fields = t.Fields - fieldOrder = t.FieldOrder - default: - return nil - } - - l := make([]*fieldResolver, len(fieldOrder)) - for i, name := range fieldOrder { - l[i] = &fieldResolver{fields[name]} - } - return &l -} - -func (r *typeResolver) Interfaces() *[]*typeResolver { - t, ok := r.typ.(*schema.Object) - if !ok { - return nil - } - - l := make([]*typeResolver, len(t.Interfaces)) - for i, intf := range t.Interfaces { - l[i] = &typeResolver{intf} - } - return &l -} - -func (r *typeResolver) PossibleTypes() *[]*typeResolver { - var possibleTypes []*schema.Object - switch t := r.typ.(type) { - case *schema.Interface: - possibleTypes = t.PossibleTypes - case *schema.Union: - possibleTypes = t.PossibleTypes - default: - return nil - } - - l := make([]*typeResolver, len(possibleTypes)) - for i, intf := range possibleTypes { - l[i] = &typeResolver{intf} - } - return &l -} - -func (r *typeResolver) EnumValues(args *struct{ IncludeDeprecated bool }) *[]*enumValueResolver { - t, ok := r.typ.(*schema.Enum) - if !ok { - return nil - } - - l := make([]*enumValueResolver, len(t.Values)) - for i, v := range t.Values { - l[i] = &enumValueResolver{v} - } - return &l -} - -func (r *typeResolver) InputFields() *[]*inputValueResolver { - t, ok := r.typ.(*schema.InputObject) - if !ok { - return nil - } - - l := make([]*inputValueResolver, len(t.FieldOrder)) - for i, name := range t.FieldOrder { - l[i] = &inputValueResolver{t.Fields[name]} - } - return &l -} - -func (r *typeResolver) OfType() *typeResolver { - switch t := r.typ.(type) { - case *common.List: - return &typeResolver{t.OfType} - case *common.NonNull: - return &typeResolver{t.OfType} - default: - return nil - } -} - -type fieldResolver struct { - field *schema.Field -} - -func (r *fieldResolver) Name() string { - return r.field.Name -} - -func (r *fieldResolver) Description() *string { - if r.field.Desc == "" { - return nil - } - return &r.field.Desc -} - -func (r *fieldResolver) Args() []*inputValueResolver { - l := make([]*inputValueResolver, len(r.field.Args.FieldOrder)) - for i, name := range r.field.Args.FieldOrder { - l[i] = &inputValueResolver{r.field.Args.Fields[name]} - } - return l -} - -func (r *fieldResolver) Type() *typeResolver { - return &typeResolver{r.field.Type} -} - -func (r *fieldResolver) IsDeprecated() bool { - return false -} - -func (r *fieldResolver) DeprecationReason() *string { - return nil -} - -type inputValueResolver struct { - value *common.InputValue -} - -func (r *inputValueResolver) Name() string { - return r.value.Name -} - -func (r *inputValueResolver) Description() *string { - if r.value.Desc == "" { - return nil - } - return &r.value.Desc -} - -func (r *inputValueResolver) Type() *typeResolver { - return &typeResolver{r.value.Type} -} - -func (r *inputValueResolver) DefaultValue() *string { - if r.value.Default == nil { - return nil - } - s := fmt.Sprint(r.value.Default) - return &s -} - -type enumValueResolver struct { - value *schema.EnumValue -} - -func (r *enumValueResolver) Name() string { - return r.value.Name -} - -func (r *enumValueResolver) Description() *string { - if r.value.Desc == "" { - return nil - } - return &r.value.Desc -} - -func (r *enumValueResolver) IsDeprecated() bool { - return false -} - -func (r *enumValueResolver) DeprecationReason() *string { - return nil -} - -type directiveResolver struct { - name string - description string - locations []string - args []*inputValueResolver -} - -func (r *directiveResolver) Name() string { - return r.name -} - -func (r *directiveResolver) Description() *string { - return &r.description -} - -func (r *directiveResolver) Locations() []string { - return r.locations -} - -func (r *directiveResolver) Args() []*inputValueResolver { - return r.args + return typeExec.exec(ctx, r, selSet, reflect.ValueOf(&introspection.Type{t}), false) } var introspectionQuery *query.Document func init() { var err *errors.QueryError - introspectionQuery, err = query.Parse(introspectionQuerySrc, metaSchema.Resolve) + introspectionQuery, err = query.Parse(introspectionQuerySrc, schema.Meta.Resolve) if err != nil { panic(err) } diff --git a/internal/schema/meta.go b/internal/schema/meta.go new file mode 100644 index 0000000000..f456c22450 --- /dev/null +++ b/internal/schema/meta.go @@ -0,0 +1,163 @@ +package schema + +var Meta *Schema + +func init() { + Meta = &Schema{ + entryPointNames: make(map[string]string), + Types: map[string]NamedType{ + "Int": &Scalar{"Int", "The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1."}, + "Float": &Scalar{"Float", "The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point)."}, + "String": &Scalar{"String", "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text."}, + "Boolean": &Scalar{"Boolean", "The `Boolean` scalar type represents `true` or `false`."}, + "ID": &Scalar{"ID", "The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID."}, + }, + } + if err := Meta.Parse(metaSrc); err != nil { + panic(err) + } +} + +var metaSrc = ` + # A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document. + # + # In some cases, you need to provide options to alter GraphQL's execution behavior + # in ways field arguments will not suffice, such as conditionally including or + # skipping a field. Directives provide this by describing additional information + # to the executor. + type __Directive { + name: String! + description: String + locations: [__DirectiveLocation!]! + args: [__InputValue!]! + } + + # A Directive can be adjacent to many parts of the GraphQL language, a + # __DirectiveLocation describes one such possible adjacencies. + enum __DirectiveLocation { + # Location adjacent to a query operation. + QUERY + # Location adjacent to a mutation operation. + MUTATION + # Location adjacent to a subscription operation. + SUBSCRIPTION + # Location adjacent to a field. + FIELD + # Location adjacent to a fragment definition. + FRAGMENT_DEFINITION + # Location adjacent to a fragment spread. + FRAGMENT_SPREAD + # Location adjacent to an inline fragment. + INLINE_FRAGMENT + # Location adjacent to a schema definition. + SCHEMA + # Location adjacent to a scalar definition. + SCALAR + # Location adjacent to an object type definition. + OBJECT + # Location adjacent to a field definition. + FIELD_DEFINITION + # Location adjacent to an argument definition. + ARGUMENT_DEFINITION + # Location adjacent to an interface definition. + INTERFACE + # Location adjacent to a union definition. + UNION + # Location adjacent to an enum definition. + ENUM + # Location adjacent to an enum value definition. + ENUM_VALUE + # Location adjacent to an input object type definition. + INPUT_OBJECT + # Location adjacent to an input object field definition. + INPUT_FIELD_DEFINITION + } + + # One possible value for a given Enum. Enum values are unique values, not a + # placeholder for a string or numeric value. However an Enum value is returned in + # a JSON response as a string. + type __EnumValue { + name: String! + description: String + isDeprecated: Boolean! + deprecationReason: String + } + + # Object and Interface types are described by a list of Fields, each of which has + # a name, potentially a list of arguments, and a return type. + type __Field { + name: String! + description: String + args: [__InputValue!]! + type: __Type! + isDeprecated: Boolean! + deprecationReason: String + } + + # Arguments provided to Fields or Directives and the input fields of an + # InputObject are represented as Input Values which describe their type and + # optionally a default value. + type __InputValue { + name: String! + description: String + type: __Type! + # A GraphQL-formatted string representing the default value for this input value. + defaultValue: String + } + + # A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all + # available types and directives on the server, as well as the entry points for + # query, mutation, and subscription operations. + type __Schema { + # A list of all types supported by this server. + types: [__Type!]! + # The type that query operations will be rooted at. + queryType: __Type! + # If this server supports mutation, the type that mutation operations will be rooted at. + mutationType: __Type + # If this server support subscription, the type that subscription operations will be rooted at. + subscriptionType: __Type + # A list of all directives supported by this server. + directives: [__Directive!]! + } + + # The fundamental unit of any GraphQL Schema is the type. There are many kinds of + # types in GraphQL as represented by the ` + "`" + `__TypeKind` + "`" + ` enum. + # + # Depending on the kind of a type, certain fields describe information about that + # type. Scalar types provide no information beyond a name and description, while + # Enum types provide their values. Object and Interface types provide the fields + # they describe. Abstract types, Union and Interface, provide the Object types + # possible at runtime. List and NonNull types compose other types. + type __Type { + kind: __TypeKind! + name: String + description: String + fields(includeDeprecated: Boolean = false): [__Field!] + interfaces: [__Type!] + possibleTypes: [__Type!] + enumValues(includeDeprecated: Boolean = false): [__EnumValue!] + inputFields: [__InputValue!] + ofType: __Type + } + + # An enum describing what kind of type a given ` + "`" + `__Type` + "`" + ` is. + enum __TypeKind { + # Indicates this type is a scalar. + SCALAR + # Indicates this type is an object. ` + "`" + `fields` + "`" + ` and ` + "`" + `interfaces` + "`" + ` are valid fields. + OBJECT + # Indicates this type is an interface. ` + "`" + `fields` + "`" + ` and ` + "`" + `possibleTypes` + "`" + ` are valid fields. + INTERFACE + # Indicates this type is a union. ` + "`" + `possibleTypes` + "`" + ` is a valid field. + UNION + # Indicates this type is an enum. ` + "`" + `enumValues` + "`" + ` is a valid field. + ENUM + # Indicates this type is an input object. ` + "`" + `inputFields` + "`" + ` is a valid field. + INPUT_OBJECT + # Indicates this type is a list. ` + "`" + `ofType` + "`" + ` is a valid field. + LIST + # Indicates this type is a non-null. ` + "`" + `ofType` + "`" + ` is a valid field. + NON_NULL + } +` diff --git a/internal/schema/schema.go b/internal/schema/schema.go index 7f9de17a38..f2c3cdb1a4 100644 --- a/internal/schema/schema.go +++ b/internal/schema/schema.go @@ -106,16 +106,14 @@ type Field struct { } func New() *Schema { - return &Schema{ + s := &Schema{ entryPointNames: make(map[string]string), - Types: map[string]NamedType{ - "Int": &Scalar{"Int", "The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1."}, - "Float": &Scalar{"Float", "The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point)."}, - "String": &Scalar{"String", "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text."}, - "Boolean": &Scalar{"Boolean", "The `Boolean` scalar type represents `true` or `false`."}, - "ID": &Scalar{"ID", "The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID."}, - }, + Types: make(map[string]NamedType), } + for n, t := range Meta.Types { + s.Types[n] = t + } + return s } func (s *Schema) Parse(schemaString string) error { diff --git a/introspection/introspection.go b/introspection/introspection.go new file mode 100644 index 0000000000..9fbbb2fd6e --- /dev/null +++ b/introspection/introspection.go @@ -0,0 +1,304 @@ +package introspection + +import ( + "fmt" + "sort" + + "github.com/neelance/graphql-go/internal/common" + "github.com/neelance/graphql-go/internal/schema" +) + +type Schema struct { + Schema *schema.Schema +} + +func (r *Schema) Types() []*Type { + var names []string + for name := range r.Schema.Types { + names = append(names, name) + } + sort.Strings(names) + + var l []*Type + for _, name := range names { + l = append(l, &Type{r.Schema.Types[name]}) + } + return l +} + +func (r *Schema) QueryType() *Type { + t, ok := r.Schema.EntryPoints["query"] + if !ok { + return nil + } + return &Type{t} +} + +func (r *Schema) MutationType() *Type { + t, ok := r.Schema.EntryPoints["mutation"] + if !ok { + return nil + } + return &Type{t} +} + +func (r *Schema) SubscriptionType() *Type { + t, ok := r.Schema.EntryPoints["subscription"] + if !ok { + return nil + } + return &Type{t} +} + +func (r *Schema) Directives() []*directiveResolver { + return []*directiveResolver{ + &directiveResolver{ + name: "skip", + description: "Directs the executor to skip this field or fragment when the `if` argument is true.", + locations: []string{"FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"}, + args: []*InputValue{ + &InputValue{&common.InputValue{ + Name: "if", + Desc: "Skipped when true.", + Type: &common.NonNull{OfType: r.Schema.Types["Boolean"]}, + }}, + }, + }, + &directiveResolver{ + name: "include", + description: "Directs the executor to include this field or fragment only when the `if` argument is true.", + locations: []string{"FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"}, + args: []*InputValue{ + &InputValue{&common.InputValue{ + Name: "if", + Desc: "Included when true.", + Type: &common.NonNull{OfType: r.Schema.Types["Boolean"]}, + }}, + }, + }, + } +} + +type Type struct { + Typ common.Type +} + +func (r *Type) Kind() string { + return r.Typ.Kind() +} + +func (r *Type) Name() *string { + if named, ok := r.Typ.(schema.NamedType); ok { + name := named.TypeName() + return &name + } + return nil +} + +func (r *Type) Description() *string { + if named, ok := r.Typ.(schema.NamedType); ok { + desc := named.Description() + if desc == "" { + return nil + } + return &desc + } + return nil +} + +func (r *Type) Fields(args *struct{ IncludeDeprecated bool }) *[]*Field { + var fields map[string]*schema.Field + var fieldOrder []string + switch t := r.Typ.(type) { + case *schema.Object: + fields = t.Fields + fieldOrder = t.FieldOrder + case *schema.Interface: + fields = t.Fields + fieldOrder = t.FieldOrder + default: + return nil + } + + l := make([]*Field, len(fieldOrder)) + for i, name := range fieldOrder { + l[i] = &Field{fields[name]} + } + return &l +} + +func (r *Type) Interfaces() *[]*Type { + t, ok := r.Typ.(*schema.Object) + if !ok { + return nil + } + + l := make([]*Type, len(t.Interfaces)) + for i, intf := range t.Interfaces { + l[i] = &Type{intf} + } + return &l +} + +func (r *Type) PossibleTypes() *[]*Type { + var possibleTypes []*schema.Object + switch t := r.Typ.(type) { + case *schema.Interface: + possibleTypes = t.PossibleTypes + case *schema.Union: + possibleTypes = t.PossibleTypes + default: + return nil + } + + l := make([]*Type, len(possibleTypes)) + for i, intf := range possibleTypes { + l[i] = &Type{intf} + } + return &l +} + +func (r *Type) EnumValues(args *struct{ IncludeDeprecated bool }) *[]*EnumValue { + t, ok := r.Typ.(*schema.Enum) + if !ok { + return nil + } + + l := make([]*EnumValue, len(t.Values)) + for i, v := range t.Values { + l[i] = &EnumValue{v} + } + return &l +} + +func (r *Type) InputFields() *[]*InputValue { + t, ok := r.Typ.(*schema.InputObject) + if !ok { + return nil + } + + l := make([]*InputValue, len(t.FieldOrder)) + for i, name := range t.FieldOrder { + l[i] = &InputValue{t.Fields[name]} + } + return &l +} + +func (r *Type) OfType() *Type { + switch t := r.Typ.(type) { + case *common.List: + return &Type{t.OfType} + case *common.NonNull: + return &Type{t.OfType} + default: + return nil + } +} + +type Field struct { + field *schema.Field +} + +func (r *Field) Name() string { + return r.field.Name +} + +func (r *Field) Description() *string { + if r.field.Desc == "" { + return nil + } + return &r.field.Desc +} + +func (r *Field) Args() []*InputValue { + l := make([]*InputValue, len(r.field.Args.FieldOrder)) + for i, name := range r.field.Args.FieldOrder { + l[i] = &InputValue{r.field.Args.Fields[name]} + } + return l +} + +func (r *Field) Type() *Type { + return &Type{r.field.Type} +} + +func (r *Field) IsDeprecated() bool { + return false +} + +func (r *Field) DeprecationReason() *string { + return nil +} + +type InputValue struct { + value *common.InputValue +} + +func (r *InputValue) Name() string { + return r.value.Name +} + +func (r *InputValue) Description() *string { + if r.value.Desc == "" { + return nil + } + return &r.value.Desc +} + +func (r *InputValue) Type() *Type { + return &Type{r.value.Type} +} + +func (r *InputValue) DefaultValue() *string { + if r.value.Default == nil { + return nil + } + s := fmt.Sprint(r.value.Default) + return &s +} + +type EnumValue struct { + value *schema.EnumValue +} + +func (r *EnumValue) Name() string { + return r.value.Name +} + +func (r *EnumValue) Description() *string { + if r.value.Desc == "" { + return nil + } + return &r.value.Desc +} + +func (r *EnumValue) IsDeprecated() bool { + return false +} + +func (r *EnumValue) DeprecationReason() *string { + return nil +} + +type directiveResolver struct { + name string + description string + locations []string + args []*InputValue +} + +func (r *directiveResolver) Name() string { + return r.name +} + +func (r *directiveResolver) Description() *string { + return &r.description +} + +func (r *directiveResolver) Locations() []string { + return r.locations +} + +func (r *directiveResolver) Args() []*InputValue { + return r.args +}