func TestSchemaParser_SimpleFieldWithArgWithDefaultValue(t *testing.T) { body := ` type Hello { world(flag: Boolean = true): String }` astDoc := parse(t, body) expected := ast.NewDocument(&ast.Document{ Loc: testLoc(1, 53), Definitions: []ast.Node{ ast.NewObjectDefinition(&ast.ObjectDefinition{ Loc: testLoc(1, 53), Name: ast.NewName(&ast.Name{ Value: "Hello", Loc: testLoc(6, 11), }), Interfaces: []*ast.Named{}, Fields: []*ast.FieldDefinition{ ast.NewFieldDefinition(&ast.FieldDefinition{ Loc: testLoc(16, 51), Name: ast.NewName(&ast.Name{ Value: "world", Loc: testLoc(16, 21), }), Arguments: []*ast.InputValueDefinition{ ast.NewInputValueDefinition(&ast.InputValueDefinition{ Loc: testLoc(22, 42), Name: ast.NewName(&ast.Name{ Value: "flag", Loc: testLoc(22, 26), }), Type: ast.NewNamed(&ast.Named{ Loc: testLoc(28, 35), Name: ast.NewName(&ast.Name{ Value: "Boolean", Loc: testLoc(28, 35), }), }), DefaultValue: ast.NewBooleanValue(&ast.BooleanValue{ Value: true, Loc: testLoc(38, 42), }), }), }, Type: ast.NewNamed(&ast.Named{ Loc: testLoc(45, 51), Name: ast.NewName(&ast.Name{ Value: "String", Loc: testLoc(45, 51), }), }), }), }, }), }, }) if !reflect.DeepEqual(astDoc, expected) { t.Fatalf("unexpected document, expected: %v, got: %v", expected, astDoc) } }
func parseValueLiteral(parser *Parser, isConst bool) (ast.Value, error) { token := parser.Token switch token.Kind { case lexer.TokenKind[lexer.BRACKET_L]: return parseList(parser, isConst) case lexer.TokenKind[lexer.BRACE_L]: return parseObject(parser, isConst) case lexer.TokenKind[lexer.INT]: advance(parser) return ast.NewIntValue(&ast.IntValue{ Value: token.Value, Loc: loc(parser, token.Start), }), nil case lexer.TokenKind[lexer.FLOAT]: advance(parser) return ast.NewFloatValue(&ast.FloatValue{ Value: token.Value, Loc: loc(parser, token.Start), }), nil case lexer.TokenKind[lexer.STRING]: advance(parser) return ast.NewStringValue(&ast.StringValue{ Value: token.Value, Loc: loc(parser, token.Start), }), nil case lexer.TokenKind[lexer.NAME]: if token.Value == "true" || token.Value == "false" { advance(parser) value := true if token.Value == "false" { value = false } return ast.NewBooleanValue(&ast.BooleanValue{ Value: value, Loc: loc(parser, token.Start), }), nil } else if token.Value != "null" { advance(parser) return ast.NewEnumValue(&ast.EnumValue{ Value: token.Value, Loc: loc(parser, token.Start), }), nil } case lexer.TokenKind[lexer.DOLLAR]: if !isConst { return parseVariable(parser) } } if err := unexpected(parser, lexer.Token{}); err != nil { return nil, err } return nil, nil }
/** * Produces a GraphQL Value AST given a Golang value. * * Optionally, a GraphQL type may be provided, which will be used to * disambiguate between value primitives. * * | JSON Value | GraphQL Value | * | ------------- | -------------------- | * | Object | Input Object | * | Array | List | * | Boolean | Boolean | * | String | String / Enum Value | * | Number | Int / Float | * */ func astFromValue(value interface{}, ttype Type) ast.Value { if ttype, ok := ttype.(*NonNull); ok { // Note: we're not checking that the result is non-null. // This function is not responsible for validating the input value. val := astFromValue(value, ttype.OfType) return val } if isNullish(value) { return nil } valueVal := reflect.ValueOf(value) if !valueVal.IsValid() { return nil } if valueVal.Type().Kind() == reflect.Ptr { valueVal = valueVal.Elem() } if !valueVal.IsValid() { return nil } // Convert Golang slice to GraphQL list. If the Type is a list, but // the value is not an array, convert the value using the list's item type. if ttype, ok := ttype.(*List); ok { if valueVal.Type().Kind() == reflect.Slice { itemType := ttype.OfType values := []ast.Value{} for i := 0; i < valueVal.Len(); i++ { item := valueVal.Index(i).Interface() itemAST := astFromValue(item, itemType) if itemAST != nil { values = append(values, itemAST) } } return ast.NewListValue(&ast.ListValue{ Values: values, }) } else { // Because GraphQL will accept single values as a "list of one" when // expecting a list, if there's a non-array value and an expected list type, // create an AST using the list's item type. val := astFromValue(value, ttype.OfType) return val } } if valueVal.Type().Kind() == reflect.Map { // TODO: implement astFromValue from Map to Value } if value, ok := value.(bool); ok { return ast.NewBooleanValue(&ast.BooleanValue{ Value: value, }) } if value, ok := value.(int); ok { if ttype == Float { return ast.NewIntValue(&ast.IntValue{ Value: fmt.Sprintf("%v.0", value), }) } return ast.NewIntValue(&ast.IntValue{ Value: fmt.Sprintf("%v", value), }) } if value, ok := value.(float32); ok { return ast.NewFloatValue(&ast.FloatValue{ Value: fmt.Sprintf("%v", value), }) } if value, ok := value.(float64); ok { return ast.NewFloatValue(&ast.FloatValue{ Value: fmt.Sprintf("%v", value), }) } if value, ok := value.(string); ok { if _, ok := ttype.(*Enum); ok { return ast.NewEnumValue(&ast.EnumValue{ Value: fmt.Sprintf("%v", value), }) } return ast.NewStringValue(&ast.StringValue{ Value: fmt.Sprintf("%v", value), }) } // fallback, treat as string return ast.NewStringValue(&ast.StringValue{ Value: fmt.Sprintf("%v", value), }) }