From a52369bf4d25029cd226224c1a6947f3bbad447d Mon Sep 17 00:00:00 2001 From: mununki <woonki.moon@gmail.com> Date: Tue, 2 Apr 2024 16:16:24 +0900 Subject: [PATCH 01/10] add test --- test/default_value/schema/a.graphql | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 test/default_value/schema/a.graphql diff --git a/test/default_value/schema/a.graphql b/test/default_value/schema/a.graphql new file mode 100644 index 0000000..d3ddf10 --- /dev/null +++ b/test/default_value/schema/a.graphql @@ -0,0 +1,5 @@ +type Query { + test(X: Int = 20): Int + test1(X: Int = ADMIN): Int + test2(X: String = "user"): Int +} From f83c8a226e09ea85130b4acf39e161307593545c Mon Sep 17 00:00:00 2001 From: mununki <woonki.moon@gmail.com> Date: Tue, 2 Apr 2024 16:24:28 +0900 Subject: [PATCH 02/10] fix parse and write the default value in arg definitions --- lib/graphql.go | 2 +- lib/lex.go | 35 ++++++++++++++++++++++++++++ lib/parse.go | 14 +++++------ lib/write.go | 7 +++--- test/default_value/generated.graphql | 12 ++++++++++ test/default_value/schema/a.graphql | 5 +++- 6 files changed, 63 insertions(+), 12 deletions(-) create mode 100644 test/default_value/generated.graphql diff --git a/lib/graphql.go b/lib/graphql.go index 4b09fbe..a7f2ff8 100644 --- a/lib/graphql.go +++ b/lib/graphql.go @@ -66,7 +66,7 @@ type Type struct { type Arg struct { Name string Type string - TypeExt *string // in case of enum e.g. admin(role: Role = ADMIN): Admin! + TypeExt *string // in case of default values e.g. admin(role: Role = ADMIN): Admin! Null bool IsList bool IsListNull bool diff --git a/lib/lex.go b/lib/lex.go index 054290e..6374313 100644 --- a/lib/lex.go +++ b/lib/lex.go @@ -489,3 +489,38 @@ func (l *lexer) consumeIdent(includings ...tokenType) (*token, *[]string) { return tok, &comments } } + +/* +The `consumeIdent` function treats tokString as a comment. +If you want to scan the tokString for ident, you can use the consumeIdentInclString function. + +e.g. scan "woonki" for ident here + +test(name: String = "woonki"): Bool +*/ +func (l *lexer) consumeIdentInclString(includings ...tokenType) (*token, *[]string) { + comments := []string{} + includings = append(includings, tokString) + + for { + tok := l.next() + if tok.typ == tokSingleLineComment || tok.typ == tokBlockString { + comments = append(comments, tok.String()) + continue + } + + isIncluded := false + for _, incl := range includings { + if tok.typ == incl { + isIncluded = true + break + } + } + + if tok.typ != tokIdent && !isIncluded { + errorf(`%s:%d:%d: unexpected "%s"`, l.filename, l.line, l.col, tok.String()) + } + l.skipSpace() + return tok, &comments + } +} diff --git a/lib/parse.go b/lib/parse.go index 7731913..8d84d8b 100644 --- a/lib/parse.go +++ b/lib/parse.go @@ -68,19 +68,19 @@ func (p *Parser) parseArgs() []*Arg { typ, _ := p.lex.consumeIdent() arg.Type = typ.String() - if p.lex.peek() == '=' { - p.lex.consumeToken(tokEqual) - tex, _ := p.lex.consumeIdent() - te := tex.String() - arg.TypeExt = &te - } - if p.lex.peek() == '!' { arg.Null = false p.lex.consumeToken(tokBang) } else { arg.Null = true } + + if p.lex.peek() == '=' { + p.lex.consumeToken(tokEqual) + tex, _ := p.lex.consumeIdentInclString(tokNumber) + te := tex.String() + arg.TypeExt = &te + } } arg.Directives = p.parseDirectives() diff --git a/lib/write.go b/lib/write.go index e95b86b..f5c6c65 100644 --- a/lib/write.go +++ b/lib/write.go @@ -280,12 +280,13 @@ func (ms *MergedSchema) stitchArgument(a *Arg, l int, i int) { } } else { ms.buf.WriteString(a.Type) - if a.TypeExt != nil { - ms.buf.WriteString(" = " + *a.TypeExt) - } if !a.Null { ms.buf.WriteString("!") } + if a.TypeExt != nil { + ms.buf.WriteString(" = " + *a.TypeExt) + } + ms.stitchDirectives(a.Directives) } if l <= 2 && i != l-1 { diff --git a/test/default_value/generated.graphql b/test/default_value/generated.graphql new file mode 100644 index 0000000..ac3064a --- /dev/null +++ b/test/default_value/generated.graphql @@ -0,0 +1,12 @@ +type Query { + test(X: Int = 20): Int + test(X: Int! = 20): Int + test1(X: Int = ADMIN): Int + test2(X: String! = "user"): Int + test3(X: String = "user" @deprecated): Int + test3(X: String = "user" @deprecated, Y: String! = "operator" @unique): Int +} + + + + diff --git a/test/default_value/schema/a.graphql b/test/default_value/schema/a.graphql index d3ddf10..e72845c 100644 --- a/test/default_value/schema/a.graphql +++ b/test/default_value/schema/a.graphql @@ -1,5 +1,8 @@ type Query { test(X: Int = 20): Int + test(X: Int! = 20): Int test1(X: Int = ADMIN): Int - test2(X: String = "user"): Int + test2(X: String! = "user"): Int + test3(X: String = "user" @deprecated): Int + test3(X: String = "user" @deprecated, Y: String! = "operator" @unique): Int } From 5f17c77bb7cb1ade180329ce3e3dc29e9af8d02d Mon Sep 17 00:00:00 2001 From: mununki <woonki.moon@gmail.com> Date: Tue, 2 Apr 2024 16:43:32 +0900 Subject: [PATCH 03/10] add test --- test/default_value/schema/a.graphql | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/default_value/schema/a.graphql b/test/default_value/schema/a.graphql index e72845c..111911e 100644 --- a/test/default_value/schema/a.graphql +++ b/test/default_value/schema/a.graphql @@ -6,3 +6,7 @@ type Query { test3(X: String = "user" @deprecated): Int test3(X: String = "user" @deprecated, Y: String! = "operator" @unique): Int } + +input User { + name: String! = "woonki" +} From 1aeac27fb5ee0031e30c060fae522c1befa61a0a Mon Sep 17 00:00:00 2001 From: mununki <woonki.moon@gmail.com> Date: Tue, 2 Apr 2024 16:44:01 +0900 Subject: [PATCH 04/10] fix handling default value in input object --- lib/graphql.go | 1 + lib/schema.go | 7 +++++++ lib/write.go | 3 +++ test/default_value/generated.graphql | 3 +++ 4 files changed, 14 insertions(+) diff --git a/lib/graphql.go b/lib/graphql.go index a7f2ff8..8d2ce7d 100644 --- a/lib/graphql.go +++ b/lib/graphql.go @@ -82,6 +82,7 @@ type Field struct { Null bool IsList bool IsListNull bool + DefaultValue *string Directives []*Directive Descriptions *[]string Comments *[]string diff --git a/lib/schema.go b/lib/schema.go index 4619c5e..bd2e433 100644 --- a/lib/schema.go +++ b/lib/schema.go @@ -330,6 +330,13 @@ func (s *Schema) Parse(p *Parser) { } else { fd.Null = true } + + if p.lex.peek() == '=' { + p.lex.consumeToken(tokEqual) + tex, _ := p.lex.consumeIdentInclString(tokNumber) + te := tex.String() + fd.DefaultValue = &te + } } fd.Directives = p.parseDirectives() diff --git a/lib/write.go b/lib/write.go index f5c6c65..4fa1c90 100644 --- a/lib/write.go +++ b/lib/write.go @@ -237,6 +237,9 @@ func (ms *MergedSchema) WriteSchema(s *Schema) string { if p.IsList && !p.IsListNull { ms.buf.WriteString("!") } + if p.DefaultValue != nil { + ms.buf.WriteString(" = " + *p.DefaultValue) + } ms.stitchDirectives(p.Directives) diff --git a/test/default_value/generated.graphql b/test/default_value/generated.graphql index ac3064a..34c3fc1 100644 --- a/test/default_value/generated.graphql +++ b/test/default_value/generated.graphql @@ -10,3 +10,6 @@ type Query { +input User { + name: String! = "woonki" +} From 8f5e8e5ed828968d7041af80ead74b5bd60fe1de Mon Sep 17 00:00:00 2001 From: mununki <woonki.moon@gmail.com> Date: Tue, 2 Apr 2024 16:46:09 +0900 Subject: [PATCH 05/10] rename Arg.TypeExt -> Arg.DefaultValue --- lib/graphql.go | 2 +- lib/parse.go | 2 +- lib/write.go | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/graphql.go b/lib/graphql.go index 8d2ce7d..3602e3b 100644 --- a/lib/graphql.go +++ b/lib/graphql.go @@ -66,7 +66,7 @@ type Type struct { type Arg struct { Name string Type string - TypeExt *string // in case of default values e.g. admin(role: Role = ADMIN): Admin! + DefaultValue *string // in case of default values e.g. admin(role: Role = ADMIN): Admin! Null bool IsList bool IsListNull bool diff --git a/lib/parse.go b/lib/parse.go index 8d84d8b..27ba408 100644 --- a/lib/parse.go +++ b/lib/parse.go @@ -79,7 +79,7 @@ func (p *Parser) parseArgs() []*Arg { p.lex.consumeToken(tokEqual) tex, _ := p.lex.consumeIdentInclString(tokNumber) te := tex.String() - arg.TypeExt = &te + arg.DefaultValue = &te } } arg.Directives = p.parseDirectives() diff --git a/lib/write.go b/lib/write.go index 4fa1c90..1e50f31 100644 --- a/lib/write.go +++ b/lib/write.go @@ -286,8 +286,8 @@ func (ms *MergedSchema) stitchArgument(a *Arg, l int, i int) { if !a.Null { ms.buf.WriteString("!") } - if a.TypeExt != nil { - ms.buf.WriteString(" = " + *a.TypeExt) + if a.DefaultValue != nil { + ms.buf.WriteString(" = " + *a.DefaultValue) } ms.stitchDirectives(a.Directives) } From d991ee1527018b7b0f661daa020d1238bf18fc18 Mon Sep 17 00:00:00 2001 From: mununki <woonki.moon@gmail.com> Date: Tue, 2 Apr 2024 17:24:40 +0900 Subject: [PATCH 06/10] fix handling multiple default values in argument definitions --- lib/graphql.go | 16 ++++++++-------- lib/parse.go | 22 +++++++++++++++++++++- lib/write.go | 23 +++++++++++++++++++++-- test/default_value/generated.graphql | 18 ++++++++++++------ test/default_value/schema/a.graphql | 18 ++++++++++++------ 5 files changed, 74 insertions(+), 23 deletions(-) diff --git a/lib/graphql.go b/lib/graphql.go index 3602e3b..14b6c22 100644 --- a/lib/graphql.go +++ b/lib/graphql.go @@ -64,14 +64,14 @@ type Type struct { } type Arg struct { - Name string - Type string - DefaultValue *string // in case of default values e.g. admin(role: Role = ADMIN): Admin! - Null bool - IsList bool - IsListNull bool - Directives []*Directive - Descriptions *[]string + Name string + Type string + DefaultValues *[]string // in case of default values e.g. admin(role: Role = ADMIN): Admin! + Null bool + IsList bool + IsListNull bool + Directives []*Directive + Descriptions *[]string } type Field struct { diff --git a/lib/parse.go b/lib/parse.go index 27ba408..9ce7e2b 100644 --- a/lib/parse.go +++ b/lib/parse.go @@ -64,6 +64,24 @@ func (p *Parser) parseArgs() []*Arg { } else { arg.IsListNull = true } + + if p.lex.peek() == '=' { + p.lex.consumeToken(tokEqual) + defaultValues := []string{} + for p.lex.peek() == '[' { + p.lex.consumeIdent(tokLBracket) + for p.lex.peek() != ']' { + tex, _ := p.lex.consumeIdentInclString(tokNumber) + te := tex.String() + defaultValues = append(defaultValues, te) + if p.lex.peek() == ',' { + p.lex.consumeToken(tokComma) + } + } + p.lex.consumeIdent(tokRBracket) + arg.DefaultValues = &defaultValues + } + } } else { typ, _ := p.lex.consumeIdent() arg.Type = typ.String() @@ -79,7 +97,9 @@ func (p *Parser) parseArgs() []*Arg { p.lex.consumeToken(tokEqual) tex, _ := p.lex.consumeIdentInclString(tokNumber) te := tex.String() - arg.DefaultValue = &te + defaultValues := []string{} + defaultValues = append(defaultValues, te) + arg.DefaultValues = &defaultValues } } arg.Directives = p.parseDirectives() diff --git a/lib/write.go b/lib/write.go index 1e50f31..f56a05d 100644 --- a/lib/write.go +++ b/lib/write.go @@ -281,13 +281,21 @@ func (ms *MergedSchema) stitchArgument(a *Arg, l int, i int) { if !a.IsListNull { ms.buf.WriteString("!") } + if a.DefaultValues != nil { + ms.buf.WriteString(" = ") + ms.buf.WriteString("[") + ms.stitchDefaultValues(a.DefaultValues) + ms.buf.WriteString("]") + } + ms.stitchDirectives(a.Directives) } else { ms.buf.WriteString(a.Type) if !a.Null { ms.buf.WriteString("!") } - if a.DefaultValue != nil { - ms.buf.WriteString(" = " + *a.DefaultValue) + if a.DefaultValues != nil { + ms.buf.WriteString(" = ") + ms.stitchDefaultValues(a.DefaultValues) } ms.stitchDirectives(a.Directives) } @@ -315,6 +323,17 @@ func (ms *MergedSchema) stitchDirectives(a []*Directive) { } } +func (ms *MergedSchema) stitchDefaultValues(a *[]string) { + if l := len(*a); l > 0 { + for i, v := range *a { + ms.buf.WriteString(v) + if i < l-1 { + ms.buf.WriteString(", ") + } + } + } +} + func (ms *MergedSchema) stitchDirectiveArgument(a *DirectiveArg, l int, i int) { if l > 2 { ms.addIndent(2) diff --git a/test/default_value/generated.graphql b/test/default_value/generated.graphql index 34c3fc1..add7fdd 100644 --- a/test/default_value/generated.graphql +++ b/test/default_value/generated.graphql @@ -1,10 +1,16 @@ type Query { - test(X: Int = 20): Int - test(X: Int! = 20): Int - test1(X: Int = ADMIN): Int - test2(X: String! = "user"): Int - test3(X: String = "user" @deprecated): Int - test3(X: String = "user" @deprecated, Y: String! = "operator" @unique): Int + test1(X: Int = 20): Int + test2(X: Int! = 20): Int + test3(X: User = ADMIN): Int + test4(X: String! = "user"): Int + test5(X: String = "user" @deprecated): Int + test6(X: String = "user" @deprecated, Y: String! = "operator" @unique): Int + test7(X: [Int] = [20]): Int + test8(X: [Int] = [20, 30]): Int + test9(X: [User!]! = [ADMIN]): Int + test10(X: [String!] = ["user"]): Int + test11(X: [String] = ["user", "user1"] @deprecated): Int + test12(X: [String] = ["user", "user1"] @deprecated, Y: String! = "operator" @unique): Int } diff --git a/test/default_value/schema/a.graphql b/test/default_value/schema/a.graphql index 111911e..c3cb0b2 100644 --- a/test/default_value/schema/a.graphql +++ b/test/default_value/schema/a.graphql @@ -1,10 +1,16 @@ type Query { - test(X: Int = 20): Int - test(X: Int! = 20): Int - test1(X: Int = ADMIN): Int - test2(X: String! = "user"): Int - test3(X: String = "user" @deprecated): Int - test3(X: String = "user" @deprecated, Y: String! = "operator" @unique): Int + test1(X: Int = 20): Int + test2(X: Int! = 20): Int + test3(X: User = ADMIN): Int + test4(X: String! = "user"): Int + test5(X: String = "user" @deprecated): Int + test6(X: String = "user" @deprecated, Y: String! = "operator" @unique): Int + test7(X: [Int] = [20]): Int + test8(X: [Int] = [20, 30]): Int + test9(X: [User!]! = [ADMIN]): Int + test10(X: [String!] = ["user"]): Int + test11(X: [String] = ["user", "user1"] @deprecated): Int + test12(X: [String] = ["user", "user1"] @deprecated, Y: String! = "operator" @unique): Int } input User { From 3e48ba6e2b186cc5e6697635c79f6dcaf8a332b6 Mon Sep 17 00:00:00 2001 From: mununki <woonki.moon@gmail.com> Date: Tue, 2 Apr 2024 17:26:00 +0900 Subject: [PATCH 07/10] add test --- test/default_value/schema/a.graphql | 1 + 1 file changed, 1 insertion(+) diff --git a/test/default_value/schema/a.graphql b/test/default_value/schema/a.graphql index c3cb0b2..8bb862a 100644 --- a/test/default_value/schema/a.graphql +++ b/test/default_value/schema/a.graphql @@ -15,4 +15,5 @@ type Query { input User { name: String! = "woonki" + nicknames: [String!]! = ["mununki", "arnold"] } From 3e7a171be59bbf0488cb1e14554638a5c944e12b Mon Sep 17 00:00:00 2001 From: mununki <woonki.moon@gmail.com> Date: Tue, 2 Apr 2024 17:37:45 +0900 Subject: [PATCH 08/10] fix handling multiple default values in input object --- lib/graphql.go | 20 ++++++++++---------- lib/schema.go | 21 ++++++++++++++++++++- lib/write.go | 13 ++++++++++--- test/default_value/generated.graphql | 1 + test/object_extension/generated.graphql | 2 +- 5 files changed, 42 insertions(+), 15 deletions(-) diff --git a/lib/graphql.go b/lib/graphql.go index 14b6c22..fbfd473 100644 --- a/lib/graphql.go +++ b/lib/graphql.go @@ -76,16 +76,16 @@ type Arg struct { type Field struct { BaseFileInfo - Name string - Args []*Arg - Type string - Null bool - IsList bool - IsListNull bool - DefaultValue *string - Directives []*Directive - Descriptions *[]string - Comments *[]string + Name string + Args []*Arg + Type string + Null bool + IsList bool + IsListNull bool + DefaultValues *[]string + Directives []*Directive + Descriptions *[]string + Comments *[]string } type Scalar struct { diff --git a/lib/schema.go b/lib/schema.go index bd2e433..3dc4e22 100644 --- a/lib/schema.go +++ b/lib/schema.go @@ -319,6 +319,23 @@ func (s *Schema) Parse(p *Parser) { } else { fd.IsListNull = true } + if p.lex.peek() == '=' { + p.lex.consumeToken(tokEqual) + defaultValues := []string{} + for p.lex.peek() == '[' { + p.lex.consumeIdent(tokLBracket) + for p.lex.peek() != ']' { + tex, _ := p.lex.consumeIdentInclString(tokNumber) + te := tex.String() + defaultValues = append(defaultValues, te) + if p.lex.peek() == ',' { + p.lex.consumeToken(tokComma) + } + } + p.lex.consumeIdent(tokRBracket) + fd.DefaultValues = &defaultValues + } + } } else { fd.IsList = false fd.IsListNull = false @@ -335,7 +352,9 @@ func (s *Schema) Parse(p *Parser) { p.lex.consumeToken(tokEqual) tex, _ := p.lex.consumeIdentInclString(tokNumber) te := tex.String() - fd.DefaultValue = &te + defaultValues := []string{} + defaultValues = append(defaultValues, te) + fd.DefaultValues = &defaultValues } } diff --git a/lib/write.go b/lib/write.go index f56a05d..64008dc 100644 --- a/lib/write.go +++ b/lib/write.go @@ -237,10 +237,17 @@ func (ms *MergedSchema) WriteSchema(s *Schema) string { if p.IsList && !p.IsListNull { ms.buf.WriteString("!") } - if p.DefaultValue != nil { - ms.buf.WriteString(" = " + *p.DefaultValue) + if p.DefaultValues != nil { + if p.IsList { + ms.buf.WriteString(" = ") + ms.buf.WriteString("[") + ms.stitchDefaultValues(p.DefaultValues) + ms.buf.WriteString("]") + } else { + ms.buf.WriteString(" = ") + ms.stitchDefaultValues(p.DefaultValues) + } } - ms.stitchDirectives(p.Directives) ms.buf.WriteString("\n") diff --git a/test/default_value/generated.graphql b/test/default_value/generated.graphql index add7fdd..e947b58 100644 --- a/test/default_value/generated.graphql +++ b/test/default_value/generated.graphql @@ -18,4 +18,5 @@ type Query { input User { name: String! = "woonki" + nicknames: [String!]! = ["mununki", "arnold"] } diff --git a/test/object_extension/generated.graphql b/test/object_extension/generated.graphql index df1238f..5a57a73 100644 --- a/test/object_extension/generated.graphql +++ b/test/object_extension/generated.graphql @@ -1,4 +1,4 @@ -type Person implements Node @talkable @walkable { +type Person implements Node @walkable @talkable { id: ID! createTime: Time! updateTime: Time! From 4b6720b13a293c55c6ef52293b2fb78c85e2d7c2 Mon Sep 17 00:00:00 2001 From: mununki <woonki.moon@gmail.com> Date: Tue, 2 Apr 2024 17:43:30 +0900 Subject: [PATCH 09/10] printing directives in order by name --- lib/write.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/write.go b/lib/write.go index 64008dc..b81c2da 100644 --- a/lib/write.go +++ b/lib/write.go @@ -1,6 +1,7 @@ package lib import ( + "sort" "strings" ) @@ -316,6 +317,9 @@ func (ms *MergedSchema) stitchArgument(a *Arg, l int, i int) { } func (ms *MergedSchema) stitchDirectives(a []*Directive) { + sort.SliceStable(a, func(i, j int) bool { + return a[i].Name > a[j].Name + }) if l := len(a); l > 0 { for _, a := range a { ms.buf.WriteString(" @" + a.Name) From 44d568690e9300ffb4375805faafba98bcb36ce1 Mon Sep 17 00:00:00 2001 From: mununki <woonki.moon@gmail.com> Date: Tue, 2 Apr 2024 17:47:53 +0900 Subject: [PATCH 10/10] clean up --- lib/parse.go | 2 +- lib/schema.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/parse.go b/lib/parse.go index 9ce7e2b..f840ea5 100644 --- a/lib/parse.go +++ b/lib/parse.go @@ -68,7 +68,7 @@ func (p *Parser) parseArgs() []*Arg { if p.lex.peek() == '=' { p.lex.consumeToken(tokEqual) defaultValues := []string{} - for p.lex.peek() == '[' { + if p.lex.peek() == '[' { p.lex.consumeIdent(tokLBracket) for p.lex.peek() != ']' { tex, _ := p.lex.consumeIdentInclString(tokNumber) diff --git a/lib/schema.go b/lib/schema.go index 3dc4e22..47d929a 100644 --- a/lib/schema.go +++ b/lib/schema.go @@ -322,7 +322,7 @@ func (s *Schema) Parse(p *Parser) { if p.lex.peek() == '=' { p.lex.consumeToken(tokEqual) defaultValues := []string{} - for p.lex.peek() == '[' { + if p.lex.peek() == '[' { p.lex.consumeIdent(tokLBracket) for p.lex.peek() != ']' { tex, _ := p.lex.consumeIdentInclString(tokNumber)