introspection.go

  1package graphql
  2
  3import (
  4	"fmt"
  5	"reflect"
  6	"sort"
  7
  8	"github.com/graphql-go/graphql/language/ast"
  9	"github.com/graphql-go/graphql/language/printer"
 10)
 11
 12const (
 13	TypeKindScalar      = "SCALAR"
 14	TypeKindObject      = "OBJECT"
 15	TypeKindInterface   = "INTERFACE"
 16	TypeKindUnion       = "UNION"
 17	TypeKindEnum        = "ENUM"
 18	TypeKindInputObject = "INPUT_OBJECT"
 19	TypeKindList        = "LIST"
 20	TypeKindNonNull     = "NON_NULL"
 21)
 22
 23// SchemaType is type definition for __Schema
 24var SchemaType *Object
 25
 26// DirectiveType is type definition for __Directive
 27var DirectiveType *Object
 28
 29// TypeType is type definition for __Type
 30var TypeType *Object
 31
 32// FieldType is type definition for __Field
 33var FieldType *Object
 34
 35// InputValueType is type definition for __InputValue
 36var InputValueType *Object
 37
 38// EnumValueType is type definition for __EnumValue
 39var EnumValueType *Object
 40
 41// TypeKindEnumType is type definition for __TypeKind
 42var TypeKindEnumType *Enum
 43
 44// DirectiveLocationEnumType is type definition for __DirectiveLocation
 45var DirectiveLocationEnumType *Enum
 46
 47// Meta-field definitions.
 48
 49// SchemaMetaFieldDef Meta field definition for Schema
 50var SchemaMetaFieldDef *FieldDefinition
 51
 52// TypeMetaFieldDef Meta field definition for types
 53var TypeMetaFieldDef *FieldDefinition
 54
 55// TypeNameMetaFieldDef Meta field definition for type names
 56var TypeNameMetaFieldDef *FieldDefinition
 57
 58func init() {
 59
 60	TypeKindEnumType = NewEnum(EnumConfig{
 61		Name:        "__TypeKind",
 62		Description: "An enum describing what kind of type a given `__Type` is",
 63		Values: EnumValueConfigMap{
 64			"SCALAR": &EnumValueConfig{
 65				Value:       TypeKindScalar,
 66				Description: "Indicates this type is a scalar.",
 67			},
 68			"OBJECT": &EnumValueConfig{
 69				Value: TypeKindObject,
 70				Description: "Indicates this type is an object. " +
 71					"`fields` and `interfaces` are valid fields.",
 72			},
 73			"INTERFACE": &EnumValueConfig{
 74				Value: TypeKindInterface,
 75				Description: "Indicates this type is an interface. " +
 76					"`fields` and `possibleTypes` are valid fields.",
 77			},
 78			"UNION": &EnumValueConfig{
 79				Value: TypeKindUnion,
 80				Description: "Indicates this type is a union. " +
 81					"`possibleTypes` is a valid field.",
 82			},
 83			"ENUM": &EnumValueConfig{
 84				Value: TypeKindEnum,
 85				Description: "Indicates this type is an enum. " +
 86					"`enumValues` is a valid field.",
 87			},
 88			"INPUT_OBJECT": &EnumValueConfig{
 89				Value: TypeKindInputObject,
 90				Description: "Indicates this type is an input object. " +
 91					"`inputFields` is a valid field.",
 92			},
 93			"LIST": &EnumValueConfig{
 94				Value: TypeKindList,
 95				Description: "Indicates this type is a list. " +
 96					"`ofType` is a valid field.",
 97			},
 98			"NON_NULL": &EnumValueConfig{
 99				Value: TypeKindNonNull,
100				Description: "Indicates this type is a non-null. " +
101					"`ofType` is a valid field.",
102			},
103		},
104	})
105
106	DirectiveLocationEnumType = NewEnum(EnumConfig{
107		Name: "__DirectiveLocation",
108		Description: "A Directive can be adjacent to many parts of the GraphQL language, a " +
109			"__DirectiveLocation describes one such possible adjacencies.",
110		Values: EnumValueConfigMap{
111			"QUERY": &EnumValueConfig{
112				Value:       DirectiveLocationQuery,
113				Description: "Location adjacent to a query operation.",
114			},
115			"MUTATION": &EnumValueConfig{
116				Value:       DirectiveLocationMutation,
117				Description: "Location adjacent to a mutation operation.",
118			},
119			"SUBSCRIPTION": &EnumValueConfig{
120				Value:       DirectiveLocationSubscription,
121				Description: "Location adjacent to a subscription operation.",
122			},
123			"FIELD": &EnumValueConfig{
124				Value:       DirectiveLocationField,
125				Description: "Location adjacent to a field.",
126			},
127			"FRAGMENT_DEFINITION": &EnumValueConfig{
128				Value:       DirectiveLocationFragmentDefinition,
129				Description: "Location adjacent to a fragment definition.",
130			},
131			"FRAGMENT_SPREAD": &EnumValueConfig{
132				Value:       DirectiveLocationFragmentSpread,
133				Description: "Location adjacent to a fragment spread.",
134			},
135			"INLINE_FRAGMENT": &EnumValueConfig{
136				Value:       DirectiveLocationInlineFragment,
137				Description: "Location adjacent to an inline fragment.",
138			},
139			"SCHEMA": &EnumValueConfig{
140				Value:       DirectiveLocationSchema,
141				Description: "Location adjacent to a schema definition.",
142			},
143			"SCALAR": &EnumValueConfig{
144				Value:       DirectiveLocationScalar,
145				Description: "Location adjacent to a scalar definition.",
146			},
147			"OBJECT": &EnumValueConfig{
148				Value:       DirectiveLocationObject,
149				Description: "Location adjacent to a object definition.",
150			},
151			"FIELD_DEFINITION": &EnumValueConfig{
152				Value:       DirectiveLocationFieldDefinition,
153				Description: "Location adjacent to a field definition.",
154			},
155			"ARGUMENT_DEFINITION": &EnumValueConfig{
156				Value:       DirectiveLocationArgumentDefinition,
157				Description: "Location adjacent to an argument definition.",
158			},
159			"INTERFACE": &EnumValueConfig{
160				Value:       DirectiveLocationInterface,
161				Description: "Location adjacent to an interface definition.",
162			},
163			"UNION": &EnumValueConfig{
164				Value:       DirectiveLocationUnion,
165				Description: "Location adjacent to a union definition.",
166			},
167			"ENUM": &EnumValueConfig{
168				Value:       DirectiveLocationEnum,
169				Description: "Location adjacent to an enum definition.",
170			},
171			"ENUM_VALUE": &EnumValueConfig{
172				Value:       DirectiveLocationEnumValue,
173				Description: "Location adjacent to an enum value definition.",
174			},
175			"INPUT_OBJECT": &EnumValueConfig{
176				Value:       DirectiveLocationInputObject,
177				Description: "Location adjacent to an input object type definition.",
178			},
179			"INPUT_FIELD_DEFINITION": &EnumValueConfig{
180				Value:       DirectiveLocationInputFieldDefinition,
181				Description: "Location adjacent to an input object field definition.",
182			},
183		},
184	})
185
186	// Note: some fields (for e.g "fields", "interfaces") are defined later due to cyclic reference
187	TypeType = NewObject(ObjectConfig{
188		Name: "__Type",
189		Description: "The fundamental unit of any GraphQL Schema is the type. There are " +
190			"many kinds of types in GraphQL as represented by the `__TypeKind` enum." +
191			"\n\nDepending on the kind of a type, certain fields describe " +
192			"information about that type. Scalar types provide no information " +
193			"beyond a name and description, while Enum types provide their values. " +
194			"Object and Interface types provide the fields they describe. Abstract " +
195			"types, Union and Interface, provide the Object types possible " +
196			"at runtime. List and NonNull types compose other types.",
197
198		Fields: Fields{
199			"kind": &Field{
200				Type: NewNonNull(TypeKindEnumType),
201				Resolve: func(p ResolveParams) (interface{}, error) {
202					switch p.Source.(type) {
203					case *Scalar:
204						return TypeKindScalar, nil
205					case *Object:
206						return TypeKindObject, nil
207					case *Interface:
208						return TypeKindInterface, nil
209					case *Union:
210						return TypeKindUnion, nil
211					case *Enum:
212						return TypeKindEnum, nil
213					case *InputObject:
214						return TypeKindInputObject, nil
215					case *List:
216						return TypeKindList, nil
217					case *NonNull:
218						return TypeKindNonNull, nil
219					}
220					return nil, fmt.Errorf("Unknown kind of type: %v", p.Source)
221				},
222			},
223			"name": &Field{
224				Type: String,
225			},
226			"description": &Field{
227				Type: String,
228			},
229			"fields":        &Field{},
230			"interfaces":    &Field{},
231			"possibleTypes": &Field{},
232			"enumValues":    &Field{},
233			"inputFields":   &Field{},
234			"ofType":        &Field{},
235		},
236	})
237
238	InputValueType = NewObject(ObjectConfig{
239		Name: "__InputValue",
240		Description: "Arguments provided to Fields or Directives and the input fields of an " +
241			"InputObject are represented as Input Values which describe their type " +
242			"and optionally a default value.",
243		Fields: Fields{
244			"name": &Field{
245				Type: NewNonNull(String),
246			},
247			"description": &Field{
248				Type: String,
249			},
250			"type": &Field{
251				Type: NewNonNull(TypeType),
252			},
253			"defaultValue": &Field{
254				Type: String,
255				Description: "A GraphQL-formatted string representing the default value for this " +
256					"input value.",
257				Resolve: func(p ResolveParams) (interface{}, error) {
258					if inputVal, ok := p.Source.(*Argument); ok {
259						if inputVal.DefaultValue == nil {
260							return nil, nil
261						}
262						if isNullish(inputVal.DefaultValue) {
263							return nil, nil
264						}
265						astVal := astFromValue(inputVal.DefaultValue, inputVal)
266						return printer.Print(astVal), nil
267					}
268					if inputVal, ok := p.Source.(*InputObjectField); ok {
269						if inputVal.DefaultValue == nil {
270							return nil, nil
271						}
272						astVal := astFromValue(inputVal.DefaultValue, inputVal)
273						return printer.Print(astVal), nil
274					}
275					return nil, nil
276				},
277			},
278		},
279	})
280
281	FieldType = NewObject(ObjectConfig{
282		Name: "__Field",
283		Description: "Object and Interface types are described by a list of Fields, each of " +
284			"which has a name, potentially a list of arguments, and a return type.",
285		Fields: Fields{
286			"name": &Field{
287				Type: NewNonNull(String),
288			},
289			"description": &Field{
290				Type: String,
291			},
292			"args": &Field{
293				Type: NewNonNull(NewList(NewNonNull(InputValueType))),
294				Resolve: func(p ResolveParams) (interface{}, error) {
295					if field, ok := p.Source.(*FieldDefinition); ok {
296						return field.Args, nil
297					}
298					return []interface{}{}, nil
299				},
300			},
301			"type": &Field{
302				Type: NewNonNull(TypeType),
303			},
304			"isDeprecated": &Field{
305				Type: NewNonNull(Boolean),
306				Resolve: func(p ResolveParams) (interface{}, error) {
307					if field, ok := p.Source.(*FieldDefinition); ok {
308						return (field.DeprecationReason != ""), nil
309					}
310					return false, nil
311				},
312			},
313			"deprecationReason": &Field{
314				Type: String,
315			},
316		},
317	})
318
319	DirectiveType = NewObject(ObjectConfig{
320		Name: "__Directive",
321		Description: "A Directive provides a way to describe alternate runtime execution and " +
322			"type validation behavior in a GraphQL document. " +
323			"\n\nIn some cases, you need to provide options to alter GraphQL's " +
324			"execution behavior in ways field arguments will not suffice, such as " +
325			"conditionally including or skipping a field. Directives provide this by " +
326			"describing additional information to the executor.",
327		Fields: Fields{
328			"name": &Field{
329				Type: NewNonNull(String),
330			},
331			"description": &Field{
332				Type: String,
333			},
334			"locations": &Field{
335				Type: NewNonNull(NewList(
336					NewNonNull(DirectiveLocationEnumType),
337				)),
338			},
339			"args": &Field{
340				Type: NewNonNull(NewList(
341					NewNonNull(InputValueType),
342				)),
343			},
344			// NOTE: the following three fields are deprecated and are no longer part
345			// of the GraphQL specification.
346			"onOperation": &Field{
347				DeprecationReason: "Use `locations`.",
348				Type:              NewNonNull(Boolean),
349				Resolve: func(p ResolveParams) (interface{}, error) {
350					if dir, ok := p.Source.(*Directive); ok {
351						res := false
352						for _, loc := range dir.Locations {
353							if loc == DirectiveLocationQuery ||
354								loc == DirectiveLocationMutation ||
355								loc == DirectiveLocationSubscription {
356								res = true
357								break
358							}
359						}
360						return res, nil
361					}
362					return false, nil
363				},
364			},
365			"onFragment": &Field{
366				DeprecationReason: "Use `locations`.",
367				Type:              NewNonNull(Boolean),
368				Resolve: func(p ResolveParams) (interface{}, error) {
369					if dir, ok := p.Source.(*Directive); ok {
370						res := false
371						for _, loc := range dir.Locations {
372							if loc == DirectiveLocationFragmentSpread ||
373								loc == DirectiveLocationInlineFragment ||
374								loc == DirectiveLocationFragmentDefinition {
375								res = true
376								break
377							}
378						}
379						return res, nil
380					}
381					return false, nil
382				},
383			},
384			"onField": &Field{
385				DeprecationReason: "Use `locations`.",
386				Type:              NewNonNull(Boolean),
387				Resolve: func(p ResolveParams) (interface{}, error) {
388					if dir, ok := p.Source.(*Directive); ok {
389						res := false
390						for _, loc := range dir.Locations {
391							if loc == DirectiveLocationField {
392								res = true
393								break
394							}
395						}
396						return res, nil
397					}
398					return false, nil
399				},
400			},
401		},
402	})
403
404	SchemaType = NewObject(ObjectConfig{
405		Name: "__Schema",
406		Description: `A GraphQL Schema defines the capabilities of a GraphQL server. ` +
407			`It exposes all available types and directives on the server, as well as ` +
408			`the entry points for query, mutation, and subscription operations.`,
409		Fields: Fields{
410			"types": &Field{
411				Description: "A list of all types supported by this server.",
412				Type: NewNonNull(NewList(
413					NewNonNull(TypeType),
414				)),
415				Resolve: func(p ResolveParams) (interface{}, error) {
416					if schema, ok := p.Source.(Schema); ok {
417						results := []Type{}
418						for _, ttype := range schema.TypeMap() {
419							results = append(results, ttype)
420						}
421						return results, nil
422					}
423					return []Type{}, nil
424				},
425			},
426			"queryType": &Field{
427				Description: "The type that query operations will be rooted at.",
428				Type:        NewNonNull(TypeType),
429				Resolve: func(p ResolveParams) (interface{}, error) {
430					if schema, ok := p.Source.(Schema); ok {
431						return schema.QueryType(), nil
432					}
433					return nil, nil
434				},
435			},
436			"mutationType": &Field{
437				Description: `If this server supports mutation, the type that ` +
438					`mutation operations will be rooted at.`,
439				Type: TypeType,
440				Resolve: func(p ResolveParams) (interface{}, error) {
441					if schema, ok := p.Source.(Schema); ok {
442						if schema.MutationType() != nil {
443							return schema.MutationType(), nil
444						}
445					}
446					return nil, nil
447				},
448			},
449			"subscriptionType": &Field{
450				Description: `If this server supports subscription, the type that ` +
451					`subscription operations will be rooted at.`,
452				Type: TypeType,
453				Resolve: func(p ResolveParams) (interface{}, error) {
454					if schema, ok := p.Source.(Schema); ok {
455						if schema.SubscriptionType() != nil {
456							return schema.SubscriptionType(), nil
457						}
458					}
459					return nil, nil
460				},
461			},
462			"directives": &Field{
463				Description: `A list of all directives supported by this server.`,
464				Type: NewNonNull(NewList(
465					NewNonNull(DirectiveType),
466				)),
467				Resolve: func(p ResolveParams) (interface{}, error) {
468					if schema, ok := p.Source.(Schema); ok {
469						return schema.Directives(), nil
470					}
471					return nil, nil
472				},
473			},
474		},
475	})
476
477	EnumValueType = NewObject(ObjectConfig{
478		Name: "__EnumValue",
479		Description: "One possible value for a given Enum. Enum values are unique values, not " +
480			"a placeholder for a string or numeric value. However an Enum value is " +
481			"returned in a JSON response as a string.",
482		Fields: Fields{
483			"name": &Field{
484				Type: NewNonNull(String),
485			},
486			"description": &Field{
487				Type: String,
488			},
489			"isDeprecated": &Field{
490				Type: NewNonNull(Boolean),
491				Resolve: func(p ResolveParams) (interface{}, error) {
492					if field, ok := p.Source.(*EnumValueDefinition); ok {
493						return (field.DeprecationReason != ""), nil
494					}
495					return false, nil
496				},
497			},
498			"deprecationReason": &Field{
499				Type: String,
500			},
501		},
502	})
503
504	// Again, adding field configs to __Type that have cyclic reference here
505	// because golang don't like them too much during init/compile-time
506	TypeType.AddFieldConfig("fields", &Field{
507		Type: NewList(NewNonNull(FieldType)),
508		Args: FieldConfigArgument{
509			"includeDeprecated": &ArgumentConfig{
510				Type:         Boolean,
511				DefaultValue: false,
512			},
513		},
514		Resolve: func(p ResolveParams) (interface{}, error) {
515			includeDeprecated, _ := p.Args["includeDeprecated"].(bool)
516			switch ttype := p.Source.(type) {
517			case *Object:
518				if ttype == nil {
519					return nil, nil
520				}
521				fields := []*FieldDefinition{}
522				var fieldNames sort.StringSlice
523				for name, field := range ttype.Fields() {
524					if !includeDeprecated && field.DeprecationReason != "" {
525						continue
526					}
527					fieldNames = append(fieldNames, name)
528				}
529				sort.Sort(fieldNames)
530				for _, name := range fieldNames {
531					fields = append(fields, ttype.Fields()[name])
532				}
533				return fields, nil
534			case *Interface:
535				if ttype == nil {
536					return nil, nil
537				}
538				fields := []*FieldDefinition{}
539				for _, field := range ttype.Fields() {
540					if !includeDeprecated && field.DeprecationReason != "" {
541						continue
542					}
543					fields = append(fields, field)
544				}
545				return fields, nil
546			}
547			return nil, nil
548		},
549	})
550	TypeType.AddFieldConfig("interfaces", &Field{
551		Type: NewList(NewNonNull(TypeType)),
552		Resolve: func(p ResolveParams) (interface{}, error) {
553			switch ttype := p.Source.(type) {
554			case *Object:
555				return ttype.Interfaces(), nil
556			}
557			return nil, nil
558		},
559	})
560	TypeType.AddFieldConfig("possibleTypes", &Field{
561		Type: NewList(NewNonNull(TypeType)),
562		Resolve: func(p ResolveParams) (interface{}, error) {
563			switch ttype := p.Source.(type) {
564			case *Interface:
565				return p.Info.Schema.PossibleTypes(ttype), nil
566			case *Union:
567				return p.Info.Schema.PossibleTypes(ttype), nil
568			}
569			return nil, nil
570		},
571	})
572	TypeType.AddFieldConfig("enumValues", &Field{
573		Type: NewList(NewNonNull(EnumValueType)),
574		Args: FieldConfigArgument{
575			"includeDeprecated": &ArgumentConfig{
576				Type:         Boolean,
577				DefaultValue: false,
578			},
579		},
580		Resolve: func(p ResolveParams) (interface{}, error) {
581			includeDeprecated, _ := p.Args["includeDeprecated"].(bool)
582			switch ttype := p.Source.(type) {
583			case *Enum:
584				if includeDeprecated {
585					return ttype.Values(), nil
586				}
587				values := []*EnumValueDefinition{}
588				for _, value := range ttype.Values() {
589					if value.DeprecationReason != "" {
590						continue
591					}
592					values = append(values, value)
593				}
594				return values, nil
595			}
596			return nil, nil
597		},
598	})
599	TypeType.AddFieldConfig("inputFields", &Field{
600		Type: NewList(NewNonNull(InputValueType)),
601		Resolve: func(p ResolveParams) (interface{}, error) {
602			switch ttype := p.Source.(type) {
603			case *InputObject:
604				fields := []*InputObjectField{}
605				for _, field := range ttype.Fields() {
606					fields = append(fields, field)
607				}
608				return fields, nil
609			}
610			return nil, nil
611		},
612	})
613	TypeType.AddFieldConfig("ofType", &Field{
614		Type: TypeType,
615	})
616
617	// Note that these are FieldDefinition and not FieldConfig,
618	// so the format for args is different.
619	SchemaMetaFieldDef = &FieldDefinition{
620		Name:        "__schema",
621		Type:        NewNonNull(SchemaType),
622		Description: "Access the current type schema of this server.",
623		Args:        []*Argument{},
624		Resolve: func(p ResolveParams) (interface{}, error) {
625			return p.Info.Schema, nil
626		},
627	}
628	TypeMetaFieldDef = &FieldDefinition{
629		Name:        "__type",
630		Type:        TypeType,
631		Description: "Request the type information of a single type.",
632		Args: []*Argument{
633			{
634				PrivateName: "name",
635				Type:        NewNonNull(String),
636			},
637		},
638		Resolve: func(p ResolveParams) (interface{}, error) {
639			name, ok := p.Args["name"].(string)
640			if !ok {
641				return nil, nil
642			}
643			return p.Info.Schema.Type(name), nil
644		},
645	}
646
647	TypeNameMetaFieldDef = &FieldDefinition{
648		Name:        "__typename",
649		Type:        NewNonNull(String),
650		Description: "The name of the current Object type at runtime.",
651		Args:        []*Argument{},
652		Resolve: func(p ResolveParams) (interface{}, error) {
653			return p.Info.ParentType.Name(), nil
654		},
655	}
656
657}
658
659// Produces a GraphQL Value AST given a Golang value.
660//
661// Optionally, a GraphQL type may be provided, which will be used to
662// disambiguate between value primitives.
663//
664// | JSON Value    | GraphQL Value        |
665// | ------------- | -------------------- |
666// | Object        | Input Object         |
667// | Array         | List                 |
668// | Boolean       | Boolean              |
669// | String        | String / Enum Value  |
670// | Number        | Int / Float          |
671
672func astFromValue(value interface{}, ttype Type) ast.Value {
673
674	if ttype, ok := ttype.(*NonNull); ok {
675		// Note: we're not checking that the result is non-null.
676		// This function is not responsible for validating the input value.
677		val := astFromValue(value, ttype.OfType)
678		return val
679	}
680	if isNullish(value) {
681		return nil
682	}
683	valueVal := reflect.ValueOf(value)
684	if !valueVal.IsValid() {
685		return nil
686	}
687	if valueVal.Type().Kind() == reflect.Ptr {
688		valueVal = valueVal.Elem()
689	}
690	if !valueVal.IsValid() {
691		return nil
692	}
693
694	// Convert Golang slice to GraphQL list. If the Type is a list, but
695	// the value is not an array, convert the value using the list's item type.
696	if ttype, ok := ttype.(*List); ok {
697		if valueVal.Type().Kind() == reflect.Slice {
698			itemType := ttype.OfType
699			values := []ast.Value{}
700			for i := 0; i < valueVal.Len(); i++ {
701				item := valueVal.Index(i).Interface()
702				itemAST := astFromValue(item, itemType)
703				if itemAST != nil {
704					values = append(values, itemAST)
705				}
706			}
707			return ast.NewListValue(&ast.ListValue{
708				Values: values,
709			})
710		}
711		// Because GraphQL will accept single values as a "list of one" when
712		// expecting a list, if there's a non-array value and an expected list type,
713		// create an AST using the list's item type.
714		val := astFromValue(value, ttype.OfType)
715		return val
716	}
717
718	if valueVal.Type().Kind() == reflect.Map {
719		// TODO: implement astFromValue from Map to Value
720	}
721
722	if value, ok := value.(bool); ok {
723		return ast.NewBooleanValue(&ast.BooleanValue{
724			Value: value,
725		})
726	}
727	if value, ok := value.(int); ok {
728		if ttype == Float {
729			return ast.NewIntValue(&ast.IntValue{
730				Value: fmt.Sprintf("%v.0", value),
731			})
732		}
733		return ast.NewIntValue(&ast.IntValue{
734			Value: fmt.Sprintf("%v", value),
735		})
736	}
737	if value, ok := value.(float32); ok {
738		return ast.NewFloatValue(&ast.FloatValue{
739			Value: fmt.Sprintf("%v", value),
740		})
741	}
742	if value, ok := value.(float64); ok {
743		return ast.NewFloatValue(&ast.FloatValue{
744			Value: fmt.Sprintf("%v", value),
745		})
746	}
747
748	if value, ok := value.(string); ok {
749		if _, ok := ttype.(*Enum); ok {
750			return ast.NewEnumValue(&ast.EnumValue{
751				Value: fmt.Sprintf("%v", value),
752			})
753		}
754		return ast.NewStringValue(&ast.StringValue{
755			Value: fmt.Sprintf("%v", value),
756		})
757	}
758
759	// fallback, treat as string
760	return ast.NewStringValue(&ast.StringValue{
761		Value: fmt.Sprintf("%v", value),
762	})
763}