예제 #1
0
// Given a variable definition, and any value of input, return a value which
// adheres to the variable definition, or throw an error.
func getVariableValue(schema Schema, definitionAST *ast.VariableDefinition, input interface{}) (interface{}, error) {
	ttype, err := typeFromAST(schema, definitionAST.Type)
	if err != nil {
		return nil, err
	}
	variable := definitionAST.Variable

	if ttype == nil || !IsInputType(ttype) {
		return "", gqlerrors.NewError(
			fmt.Sprintf(`Variable "$%v" expected value of type `+
				`"%v" which cannot be used as an input type.`, variable.Name.Value, printer.Print(definitionAST.Type)),
			[]ast.Node{definitionAST},
			"",
			nil,
			[]int{},
		)
	}

	if isValidInputValue(input, ttype) {
		if isNullish(input) {
			defaultValue := definitionAST.DefaultValue
			if defaultValue != nil {
				variables := map[string]interface{}{}
				val := valueFromAST(defaultValue, ttype, variables)
				return val, nil
			}
		}
		return coerceValue(ttype, input), nil
	}
	if isNullish(input) {
		return "", gqlerrors.NewError(
			fmt.Sprintf(`Variable "$%v" of required type `+
				`"%v" was not provided.`, variable.Name.Value, printer.Print(definitionAST.Type)),
			[]ast.Node{definitionAST},
			"",
			nil,
			[]int{},
		)
	}
	inputStr := ""
	b, err := json.Marshal(input)
	if err == nil {
		inputStr = string(b)
	}
	return "", gqlerrors.NewError(
		fmt.Sprintf(`Variable "$%v" expected value of type `+
			`"%v" but got: %v.`, variable.Name.Value, printer.Print(definitionAST.Type), inputStr),
		[]ast.Node{definitionAST},
		"",
		nil,
		[]int{},
	)
}
예제 #2
0
func TestPrinter_PrintsMinimalAST(t *testing.T) {
	astDoc := ast.NewField(&ast.Field{
		Name: ast.NewName(&ast.Name{
			Value: "foo",
		}),
	})
	results := printer.Print(astDoc)
	expected := "foo"
	if !reflect.DeepEqual(results, expected) {
		t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(expected, results))
	}
}
예제 #3
0
func TestSchemaPrinter_PrintsMinimalAST(t *testing.T) {
	astDoc := ast.NewScalarDefinition(&ast.ScalarDefinition{
		Name: ast.NewName(&ast.Name{
			Value: "foo",
		}),
	})
	results := printer.Print(astDoc)
	expected := "scalar foo"
	if !reflect.DeepEqual(results, expected) {
		t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(expected, results))
	}
}
예제 #4
0
func TestPrinter_PrintsKitchenSink(t *testing.T) {
	b, err := ioutil.ReadFile("../../kitchen-sink.graphql")
	if err != nil {
		t.Fatalf("unable to load kitchen-sink.graphql")
	}

	query := string(b)
	astDoc := parse(t, query)
	expected := `query namedQuery($foo: ComplexFooType, $bar: Bar = DefaultBarValue) {
  customUser: user(id: [987, 654]) {
    id
    ... on User @defer {
      field2 {
        id
        alias: field1(first: 10, after: $foo) @include(if: $foo) {
          id
          ...frag
        }
      }
    }
  }
}

mutation favPost {
  fav(post: 123) @defer {
    post {
      id
    }
  }
}

fragment frag on Follower {
  foo(size: $size, bar: $b, obj: {key: "value"})
}

{
  unnamed(truthyVal: true, falseyVal: false)
  query
}
`
	results := printer.Print(astDoc)

	if !reflect.DeepEqual(expected, results) {
		t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(results, expected))
	}
}
예제 #5
0
func TestSchemaPrinter_PrintsKitchenSink(t *testing.T) {
	b, err := ioutil.ReadFile("../../schema-kitchen-sink.graphql")
	if err != nil {
		t.Fatalf("unable to load schema-kitchen-sink.graphql")
	}

	query := string(b)
	astDoc := parse(t, query)
	expected := `type Foo implements Bar {
  one: Type
  two(argument: InputType!): Type
  three(argument: InputType, other: String): Int
  four(argument: String = "string"): String
  five(argument: [String] = ["string", "string"]): String
  six(argument: InputType = {key: "value"}): Type
}

interface Bar {
  one: Type
  four(argument: String = "string"): String
}

union Feed = Story | Article | Advert

scalar CustomScalar

enum Site {
  DESKTOP
  MOBILE
}

input InputType {
  key: String!
  answer: Int = 42
}

extend type Foo {
  seven(argument: [String]): Type
}
`
	results := printer.Print(astDoc)
	if !reflect.DeepEqual(expected, results) {
		t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(results, expected))
	}
}
예제 #6
0
func TestPrinter_DoesNotAlterAST(t *testing.T) {
	b, err := ioutil.ReadFile("../../kitchen-sink.graphql")
	if err != nil {
		t.Fatalf("unable to load kitchen-sink.graphql")
	}

	query := string(b)
	astDoc := parse(t, query)

	astDocBefore := testutil.ASTToJSON(t, astDoc)

	_ = printer.Print(astDoc)

	astDocAfter := testutil.ASTToJSON(t, astDoc)

	_ = testutil.ASTToJSON(t, astDoc)

	if !reflect.DeepEqual(astDocAfter, astDocBefore) {
		t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(astDocAfter, astDocBefore))
	}
}
예제 #7
0
func init() {

	__TypeKind = NewEnum(EnumConfig{
		Name:        "__TypeKind",
		Description: "An enum describing what kind of type a given __Type is",
		Values: EnumValueConfigMap{
			"SCALAR": &EnumValueConfig{
				Value:       TypeKindScalar,
				Description: "Indicates this type is a scalar.",
			},
			"OBJECT": &EnumValueConfig{
				Value: TypeKindObject,
				Description: "Indicates this type is an object. " +
					"`fields` and `interfaces` are valid fields.",
			},
			"INTERFACE": &EnumValueConfig{
				Value: TypeKindInterface,
				Description: "Indicates this type is an interface. " +
					"`fields` and `possibleTypes` are valid fields.",
			},
			"UNION": &EnumValueConfig{
				Value: TypeKindUnion,
				Description: "Indicates this type is a union. " +
					"`possibleTypes` is a valid field.",
			},
			"ENUM": &EnumValueConfig{
				Value: TypeKindEnum,
				Description: "Indicates this type is an enum. " +
					"`enumValues` is a valid field.",
			},
			"INPUT_OBJECT": &EnumValueConfig{
				Value: TypeKindInputObject,
				Description: "Indicates this type is an input object. " +
					"`inputFields` is a valid field.",
			},
			"LIST": &EnumValueConfig{
				Value: TypeKindList,
				Description: "Indicates this type is a list. " +
					"`ofType` is a valid field.",
			},
			"NON_NULL": &EnumValueConfig{
				Value: TypeKindNonNull,
				Description: "Indicates this type is a non-null. " +
					"`ofType` is a valid field.",
			},
		},
	})

	// Note: some fields (for e.g "fields", "interfaces") are defined later due to cyclic reference
	__Type = NewObject(ObjectConfig{
		Name: "__Type",
		Fields: Fields{
			"kind": &Field{
				Type: NewNonNull(__TypeKind),
				Resolve: func(p ResolveParams) interface{} {
					switch p.Source.(type) {
					case *Scalar:
						return TypeKindScalar
					case *Object:
						return TypeKindObject
					case *Interface:
						return TypeKindInterface
					case *Union:
						return TypeKindUnion
					case *Enum:
						return TypeKindEnum
					case *InputObject:
						return TypeKindInputObject
					case *List:
						return TypeKindList
					case *NonNull:
						return TypeKindNonNull
					}
					panic(fmt.Sprintf("Unknown kind of type: %v", p.Source))
				},
			},
			"name": &Field{
				Type: String,
			},
			"description": &Field{
				Type: String,
			},
			"fields":        &Field{},
			"interfaces":    &Field{},
			"possibleTypes": &Field{},
			"enumValues":    &Field{},
			"inputFields":   &Field{},
			"ofType":        &Field{},
		},
	})

	__InputValue = NewObject(ObjectConfig{
		Name: "__InputValue",
		Fields: Fields{
			"name": &Field{
				Type: NewNonNull(String),
			},
			"description": &Field{
				Type: String,
			},
			"type": &Field{
				Type: NewNonNull(__Type),
			},
			"defaultValue": &Field{
				Type: String,
				Resolve: func(p ResolveParams) interface{} {
					if inputVal, ok := p.Source.(*Argument); ok {
						if inputVal.DefaultValue == nil {
							return nil
						}
						astVal := astFromValue(inputVal.DefaultValue, inputVal)
						return printer.Print(astVal)
					}
					if inputVal, ok := p.Source.(*InputObjectField); ok {
						if inputVal.DefaultValue == nil {
							return nil
						}
						astVal := astFromValue(inputVal.DefaultValue, inputVal)
						return printer.Print(astVal)
					}
					return nil
				},
			},
		},
	})

	__Field = NewObject(ObjectConfig{
		Name: "__Field",
		Fields: Fields{
			"name": &Field{
				Type: NewNonNull(String),
			},
			"description": &Field{
				Type: String,
			},
			"args": &Field{
				Type: NewNonNull(NewList(NewNonNull(__InputValue))),
				Resolve: func(p ResolveParams) interface{} {
					if field, ok := p.Source.(*FieldDefinition); ok {
						return field.Args
					}
					return []interface{}{}
				},
			},
			"type": &Field{
				Type: NewNonNull(__Type),
			},
			"isDeprecated": &Field{
				Type: NewNonNull(Boolean),
				Resolve: func(p ResolveParams) interface{} {
					if field, ok := p.Source.(*FieldDefinition); ok {
						return (field.DeprecationReason != "")
					}
					return false
				},
			},
			"deprecationReason": &Field{
				Type: String,
			},
		},
	})

	__Directive = NewObject(ObjectConfig{
		Name: "__Directive",
		Fields: Fields{
			"name": &Field{
				Type: NewNonNull(String),
			},
			"description": &Field{
				Type: String,
			},
			"args": &Field{
				Type: NewNonNull(NewList(
					NewNonNull(__InputValue),
				)),
			},
			"onOperation": &Field{
				Type: NewNonNull(Boolean),
			},
			"onFragment": &Field{
				Type: NewNonNull(Boolean),
			},
			"onField": &Field{
				Type: NewNonNull(Boolean),
			},
		},
	})

	__Schema = NewObject(ObjectConfig{
		Name: "__Schema",
		Description: `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 and
mutation operations.`,
		Fields: Fields{
			"types": &Field{
				Description: "A list of all types supported by this server.",
				Type: NewNonNull(NewList(
					NewNonNull(__Type),
				)),
				Resolve: func(p ResolveParams) interface{} {
					if schema, ok := p.Source.(Schema); ok {
						results := []Type{}
						for _, ttype := range schema.TypeMap() {
							results = append(results, ttype)
						}
						return results
					}
					return []Type{}
				},
			},
			"queryType": &Field{
				Description: "The type that query operations will be rooted at.",
				Type:        NewNonNull(__Type),
				Resolve: func(p ResolveParams) interface{} {
					if schema, ok := p.Source.(Schema); ok {
						return schema.QueryType()
					}
					return nil
				},
			},
			"mutationType": &Field{
				Description: `If this server supports mutation, the type that ` +
					`mutation operations will be rooted at.`,
				Type: __Type,
				Resolve: func(p ResolveParams) interface{} {
					if schema, ok := p.Source.(Schema); ok {
						if schema.MutationType() != nil {
							return schema.MutationType()
						}
					}
					return nil
				},
			},
			"directives": &Field{
				Description: `A list of all directives supported by this server.`,
				Type: NewNonNull(NewList(
					NewNonNull(__Directive),
				)),
				Resolve: func(p ResolveParams) interface{} {
					if schema, ok := p.Source.(Schema); ok {
						return schema.Directives()
					}
					return nil
				},
			},
		},
	})

	__EnumValue = NewObject(ObjectConfig{
		Name: "__EnumValue",
		Fields: Fields{
			"name": &Field{
				Type: NewNonNull(String),
			},
			"description": &Field{
				Type: String,
			},
			"isDeprecated": &Field{
				Type: NewNonNull(Boolean),
				Resolve: func(p ResolveParams) interface{} {
					if field, ok := p.Source.(*EnumValueDefinition); ok {
						return (field.DeprecationReason != "")
					}
					return false
				},
			},
			"deprecationReason": &Field{
				Type: String,
			},
		},
	})

	// Again, adding field configs to __Type that have cyclic reference here
	// because golang don't like them too much during init/compile-time
	__Type.AddFieldConfig("fields", &Field{
		Type: NewList(NewNonNull(__Field)),
		Args: FieldConfigArgument{
			"includeDeprecated": &ArgumentConfig{
				Type:         Boolean,
				DefaultValue: false,
			},
		},
		Resolve: func(p ResolveParams) interface{} {
			includeDeprecated, _ := p.Args["includeDeprecated"].(bool)
			switch ttype := p.Source.(type) {
			case *Object:
				if ttype == nil {
					return nil
				}
				fields := []*FieldDefinition{}
				for _, field := range ttype.Fields() {
					if !includeDeprecated && field.DeprecationReason != "" {
						continue
					}
					fields = append(fields, field)
				}
				return fields
			case *Interface:
				if ttype == nil {
					return nil
				}
				fields := []*FieldDefinition{}
				for _, field := range ttype.Fields() {
					if !includeDeprecated && field.DeprecationReason != "" {
						continue
					}
					fields = append(fields, field)
				}
				return fields
			}
			return nil
		},
	})
	__Type.AddFieldConfig("interfaces", &Field{
		Type: NewList(NewNonNull(__Type)),
		Resolve: func(p ResolveParams) interface{} {
			switch ttype := p.Source.(type) {
			case *Object:
				return ttype.Interfaces()
			}
			return nil
		},
	})
	__Type.AddFieldConfig("possibleTypes", &Field{
		Type: NewList(NewNonNull(__Type)),
		Resolve: func(p ResolveParams) interface{} {
			switch ttype := p.Source.(type) {
			case *Interface:
				return ttype.PossibleTypes()
			case *Union:
				return ttype.PossibleTypes()
			}
			return nil
		},
	})
	__Type.AddFieldConfig("enumValues", &Field{
		Type: NewList(NewNonNull(__EnumValue)),
		Args: FieldConfigArgument{
			"includeDeprecated": &ArgumentConfig{
				Type:         Boolean,
				DefaultValue: false,
			},
		},
		Resolve: func(p ResolveParams) interface{} {
			includeDeprecated, _ := p.Args["includeDeprecated"].(bool)
			switch ttype := p.Source.(type) {
			case *Enum:
				if includeDeprecated {
					return ttype.Values()
				}
				values := []*EnumValueDefinition{}
				for _, value := range ttype.Values() {
					if value.DeprecationReason != "" {
						continue
					}
					values = append(values, value)
				}
				return values
			}
			return nil
		},
	})
	__Type.AddFieldConfig("inputFields", &Field{
		Type: NewList(NewNonNull(__InputValue)),
		Resolve: func(p ResolveParams) interface{} {
			switch ttype := p.Source.(type) {
			case *InputObject:
				fields := []*InputObjectField{}
				for _, field := range ttype.Fields() {
					fields = append(fields, field)
				}
				return fields
			}
			return nil
		},
	})
	__Type.AddFieldConfig("ofType", &Field{
		Type: __Type,
	})

	/**
	 * Note that these are FieldDefinition and not FieldConfig,
	 * so the format for args is different.
	 */

	SchemaMetaFieldDef = &FieldDefinition{
		Name:        "__schema",
		Type:        NewNonNull(__Schema),
		Description: "Access the current type schema of this server.",
		Args:        []*Argument{},
		Resolve: func(p ResolveParams) interface{} {
			return p.Info.Schema
		},
	}
	TypeMetaFieldDef = &FieldDefinition{
		Name:        "__type",
		Type:        __Type,
		Description: "Request the type information of a single type.",
		Args: []*Argument{
			&Argument{
				PrivateName: "name",
				Type:        NewNonNull(String),
			},
		},
		Resolve: func(p ResolveParams) interface{} {
			name, ok := p.Args["name"].(string)
			if !ok {
				return nil
			}
			return p.Info.Schema.Type(name)
		},
	}

	TypeNameMetaFieldDef = &FieldDefinition{
		Name:        "__typename",
		Type:        NewNonNull(String),
		Description: "The name of the current Object type at runtime.",
		Args:        []*Argument{},
		Resolve: func(p ResolveParams) interface{} {
			return p.Info.ParentType.Name()
		},
	}

}