From 10afdbf4814f6004b9b6501df6aa845f9e0d80ba Mon Sep 17 00:00:00 2001 From: bannzai Date: Tue, 21 Dec 2021 16:16:42 +0900 Subject: [PATCH 01/21] Implement node type checker --- core.go | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 core.go diff --git a/core.go b/core.go new file mode 100644 index 0000000..de9daa3 --- /dev/null +++ b/core.go @@ -0,0 +1,70 @@ +package nodecheck + +import ( + "flag" + "fmt" + "regexp" + "strings" + + "github.com/vektah/gqlparser/v2/ast" + + "github.com/gqlgo/gqlanalysis" +) + +var excludes string +var Analyzer = &gqlanalysis.Analyzer{ + Name: "nodecheck", + Doc: "nodecheck finds invalid GraphQL type that type does not conform Node interface", + Flags: func() flag.FlagSet { + f := flag.NewFlagSet("node check", flag.ExitOnError) + f.StringVar(&excludes, "exclude", "", "exclude GraphQL types for node check. it can specify multiple values separated by `,` and it can use regex(e.g *Connection") + return *f + }(), + Run: run, +} + +func run(pass *gqlanalysis.Pass) (interface{}, error) { + allTypes := map[string]*ast.Definition{} + for _, def := range pass.Schema.Types { + if def.Kind == ast.Object { + allTypes[def.Name] = def + } + } + + allNodeImplements := map[string]*ast.Definition{} + for _, t := range pass.Schema.Implements["Node"] { + allNodeImplements[t.Name] = t + } + + unconformedTypes := map[string]*ast.Definition{} + for k, v := range allTypes { + if _, ok := allNodeImplements[k]; !ok { + unconformedTypes[v.Name] = v + } + } + + needToNodeTypes := []*ast.Definition{} + + for k, v := range unconformedTypes { + ok := false + for _, rules := range strings.Split(excludes, ",") { + regex := regexp.MustCompile(rules) + + if ok { + break + } + if regex.MatchString(k) { + ok = true + } + } + if !ok { + needToNodeTypes = append(needToNodeTypes, v) + } + } + + if len(needToNodeTypes) > 0 { + return nil, fmt.Errorf("GraphQL types need to conform to Node type %s", needToNodeTypes) + } + + return nil, nil +} From 1c975605c9cf1407fe073063a46dcbc5da2c38c9 Mon Sep 17 00:00:00 2001 From: bannzai Date: Tue, 21 Dec 2021 16:17:18 +0900 Subject: [PATCH 02/21] Add main.go --- cmd/nodecheck/main.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 cmd/nodecheck/main.go diff --git a/cmd/nodecheck/main.go b/cmd/nodecheck/main.go new file mode 100644 index 0000000..4a96f75 --- /dev/null +++ b/cmd/nodecheck/main.go @@ -0,0 +1,12 @@ +package main + +import ( + "github.com/bannzai/nodecheck" + "github.com/gqlgo/gqlanalysis/multichecker" +) + +func main() { + multichecker.Main( + nodecheck.Analyzer, + ) +} From c62532e6cec1535263be9f23c9c70347693f148f Mon Sep 17 00:00:00 2001 From: bannzai Date: Tue, 21 Dec 2021 16:19:17 +0900 Subject: [PATCH 03/21] :shell: go get github.com/gqlgo/gqlanalysis/internal/checker@v0.2.1 --- go.sum | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/go.sum b/go.sum index 7f538e6..0eb439e 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,10 @@ github.com/99designs/gqlgen v0.13.0/go.mod h1:NV130r6f4tpRWuAI+zsrSdooO/eWUv+Gyyoi3rEfXIk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Yamashou/gqlgenc v0.0.0-20210330020310-5eab2091f840 h1:pLmcSh/7xffZydsct+KAX8nMXYL6b4eRpT5OKCGUGdA= github.com/Yamashou/gqlgenc v0.0.0-20210330020310-5eab2091f840/go.mod h1:BivGCpycfS8C36iVex2xH2j+kJa3/ca/42kdoGO6I6o= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/agnivade/levenshtein v1.0.3/go.mod h1:4SFRZbbXWLF4MU1T9Qg0pGgH3Pjs+t6ie5efyrwRJXs= +github.com/agnivade/levenshtein v1.1.0 h1:n6qGwyHG61v3ABce1rPVZklEYRT8NFpCMrpZdBUbYGM= github.com/agnivade/levenshtein v1.1.0/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= @@ -33,6 +35,7 @@ github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007/go.mod h1:9ELz6aaclSIG github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-zglob v0.0.3 h1:6Ry4EYsScDyt5di4OI6xw1bYhOqfE5S33Z1OPy+d+To= github.com/mattn/go-zglob v0.0.3/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= @@ -60,7 +63,9 @@ github.com/vektah/gqlparser/v2 v2.2.0 h1:bAc3slekAAJW6sZTi07aGq0OrfaCjj4jxARAaC7 github.com/vektah/gqlparser/v2 v2.2.0/go.mod h1:i3mQIGIrbK2PD1RrCeMTlVbkF2FJ6WkU1KJlJlC+3F4= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -94,6 +99,7 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From e510b64ef6ad7299b41e80b98abc28cc1bfca15a Mon Sep 17 00:00:00 2001 From: bannzai Date: Wed, 12 Jan 2022 13:58:24 +0900 Subject: [PATCH 04/21] Fix pick implements --- core.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/core.go b/core.go index de9daa3..abc99aa 100644 --- a/core.go +++ b/core.go @@ -32,8 +32,12 @@ func run(pass *gqlanalysis.Pass) (interface{}, error) { } allNodeImplements := map[string]*ast.Definition{} - for _, t := range pass.Schema.Implements["Node"] { - allNodeImplements[t.Name] = t + for name, t := range allTypes { + for _, typeInterface := range pass.Schema.Implements[name] { + if typeInterface.Kind == ast.Interface && typeInterface.Name == "Node" { + allNodeImplements[name] = t + } + } } unconformedTypes := map[string]*ast.Definition{} @@ -57,6 +61,7 @@ func run(pass *gqlanalysis.Pass) (interface{}, error) { ok = true } } + if !ok { needToNodeTypes = append(needToNodeTypes, v) } From 0ca785de242df3b8d031743af262d5f29e8bfcbe Mon Sep 17 00:00:00 2001 From: bannzai Date: Wed, 12 Jan 2022 14:37:05 +0900 Subject: [PATCH 05/21] :memo: fix parse flags --- cmd/nodecheck/main.go | 11 ++++- core.go | 95 +++++++++++++++++++++++-------------------- 2 files changed, 60 insertions(+), 46 deletions(-) diff --git a/cmd/nodecheck/main.go b/cmd/nodecheck/main.go index 4a96f75..12b0694 100644 --- a/cmd/nodecheck/main.go +++ b/cmd/nodecheck/main.go @@ -1,12 +1,21 @@ package main import ( + "flag" + "github.com/bannzai/nodecheck" "github.com/gqlgo/gqlanalysis/multichecker" ) +var excludes string + func main() { + flag.StringVar(&excludes, "excludes", "", "exclude GraphQL types for node check. it can specify multiple values separated by `,` and it can use regex(e.g *Connection") + flag.Parse() + + analyzer := nodecheck.Analyzer(excludes) + multichecker.Main( - nodecheck.Analyzer, + analyzer, ) } diff --git a/core.go b/core.go index abc99aa..619e818 100644 --- a/core.go +++ b/core.go @@ -1,7 +1,6 @@ package nodecheck import ( - "flag" "fmt" "regexp" "strings" @@ -11,65 +10,71 @@ import ( "github.com/gqlgo/gqlanalysis" ) -var excludes string -var Analyzer = &gqlanalysis.Analyzer{ - Name: "nodecheck", - Doc: "nodecheck finds invalid GraphQL type that type does not conform Node interface", - Flags: func() flag.FlagSet { - f := flag.NewFlagSet("node check", flag.ExitOnError) - f.StringVar(&excludes, "exclude", "", "exclude GraphQL types for node check. it can specify multiple values separated by `,` and it can use regex(e.g *Connection") - return *f - }(), - Run: run, +func Analyzer(excludes string) *gqlanalysis.Analyzer { + return &gqlanalysis.Analyzer{ + Name: "nodecheck", + Doc: "nodecheck finds invalid GraphQL type that type does not conform Node interface", + Run: run(excludes), + } } -func run(pass *gqlanalysis.Pass) (interface{}, error) { - allTypes := map[string]*ast.Definition{} - for _, def := range pass.Schema.Types { - if def.Kind == ast.Object { - allTypes[def.Name] = def - } - } +func run(excludes string) func(pass *gqlanalysis.Pass) (interface{}, error) { - allNodeImplements := map[string]*ast.Definition{} - for name, t := range allTypes { - for _, typeInterface := range pass.Schema.Implements[name] { - if typeInterface.Kind == ast.Interface && typeInterface.Name == "Node" { - allNodeImplements[name] = t + return func(pass *gqlanalysis.Pass) (interface{}, error) { + allTypes := map[string]*ast.Definition{} + for _, def := range pass.Schema.Types { + if def.Kind == ast.Object { + allTypes[def.Name] = def } } - } - unconformedTypes := map[string]*ast.Definition{} - for k, v := range allTypes { - if _, ok := allNodeImplements[k]; !ok { - unconformedTypes[v.Name] = v + allNodeImplements := map[string]*ast.Definition{} + for name, t := range allTypes { + for _, typeInterface := range pass.Schema.Implements[name] { + if typeInterface.Kind == ast.Interface && typeInterface.Name == "Node" { + allNodeImplements[name] = t + } + } } - } - needToNodeTypes := []*ast.Definition{} + unconformedTypes := map[string]*ast.Definition{} + for k, v := range allTypes { + if _, ok := allNodeImplements[k]; !ok { + unconformedTypes[v.Name] = v + } + } - for k, v := range unconformedTypes { - ok := false - for _, rules := range strings.Split(excludes, ",") { - regex := regexp.MustCompile(rules) + needToNodeTypes := []*ast.Definition{} + for k, v := range unconformedTypes { + ok := false + for _, rule := range strings.Split(excludes, ",") { + if len(rule) > 0 { + regex := regexp.MustCompile(rule) - if ok { - break + if ok { + break + } + if regex.MatchString(k) { + ok = true + } + } } - if regex.MatchString(k) { - ok = true + + if !ok { + needToNodeTypes = append(needToNodeTypes, v) } } - if !ok { - needToNodeTypes = append(needToNodeTypes, v) + if len(needToNodeTypes) > 0 { + names := make([]string, len(needToNodeTypes)) + for i, t := range needToNodeTypes { + names[i] = t.Name + } + + out := strings.Join(names, "\n") + return nil, fmt.Errorf("GraphQL types need to conform to Node type %s", out) } - } - if len(needToNodeTypes) > 0 { - return nil, fmt.Errorf("GraphQL types need to conform to Node type %s", needToNodeTypes) + return nil, nil } - - return nil, nil } From 6dc86e483f9dc4668135e4db5105605387685c52 Mon Sep 17 00:00:00 2001 From: bannzai Date: Wed, 12 Jan 2022 14:54:26 +0900 Subject: [PATCH 06/21] :shirt: fix indent --- core.go | 1 - 1 file changed, 1 deletion(-) diff --git a/core.go b/core.go index 619e818..04ff533 100644 --- a/core.go +++ b/core.go @@ -19,7 +19,6 @@ func Analyzer(excludes string) *gqlanalysis.Analyzer { } func run(excludes string) func(pass *gqlanalysis.Pass) (interface{}, error) { - return func(pass *gqlanalysis.Pass) (interface{}, error) { allTypes := map[string]*ast.Definition{} for _, def := range pass.Schema.Types { From e8728edbd485ee17efe830d88bb6da9c87f2cbe8 Mon Sep 17 00:00:00 2001 From: bannzai Date: Wed, 12 Jan 2022 15:01:45 +0900 Subject: [PATCH 07/21] :memo: fix messages --- cmd/nodecheck/main.go | 2 +- core.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/nodecheck/main.go b/cmd/nodecheck/main.go index 12b0694..65e152d 100644 --- a/cmd/nodecheck/main.go +++ b/cmd/nodecheck/main.go @@ -10,7 +10,7 @@ import ( var excludes string func main() { - flag.StringVar(&excludes, "excludes", "", "exclude GraphQL types for node check. it can specify multiple values separated by `,` and it can use regex(e.g *Connection") + flag.StringVar(&excludes, "excludes", "", "exclude GraphQL types for node check. it can specify multiple values separated by `,` and it can use regex(e.g .+Connection") flag.Parse() analyzer := nodecheck.Analyzer(excludes) diff --git a/core.go b/core.go index 04ff533..b34a492 100644 --- a/core.go +++ b/core.go @@ -70,8 +70,8 @@ func run(excludes string) func(pass *gqlanalysis.Pass) (interface{}, error) { names[i] = t.Name } - out := strings.Join(names, "\n") - return nil, fmt.Errorf("GraphQL types need to conform to Node type %s", out) + out := strings.Join(names, ",") + return nil, fmt.Errorf("GraphQL types need to conform to Node interface:\n%s", out) } return nil, nil From a2d8936ae17da32ea04d1f778959eeb96bc465fa Mon Sep 17 00:00:00 2001 From: bannzai Date: Wed, 12 Jan 2022 15:43:56 +0900 Subject: [PATCH 08/21] :memo: fix description --- core.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core.go b/core.go index b34a492..240dbeb 100644 --- a/core.go +++ b/core.go @@ -13,7 +13,7 @@ import ( func Analyzer(excludes string) *gqlanalysis.Analyzer { return &gqlanalysis.Analyzer{ Name: "nodecheck", - Doc: "nodecheck finds invalid GraphQL type that type does not conform Node interface", + Doc: "nodecheck will find any GraphQL schema that is not conform to Node interface", Run: run(excludes), } } From 29140d7df9e5305ab6bdd6dc739e10777b832e25 Mon Sep 17 00:00:00 2001 From: bannzai Date: Wed, 12 Jan 2022 15:55:06 +0900 Subject: [PATCH 09/21] :shell: go mod tidy --- go.sum | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/go.sum b/go.sum index 0eb439e..4b7ff81 100644 --- a/go.sum +++ b/go.sum @@ -8,12 +8,14 @@ github.com/agnivade/levenshtein v1.1.0 h1:n6qGwyHG61v3ABce1rPVZklEYRT8NFpCMrpZdB github.com/agnivade/levenshtein v1.1.0/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= +github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -26,6 +28,7 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gqlgo/gqlanalysis v0.2.1 h1:lYfI4ez0LXj8N25q6mWew+eDsYSo4AnXZ9z7CWm6GOU= github.com/gqlgo/gqlanalysis v0.2.1/go.mod h1:BFhVkoQezTAcK1G9e1Yety8DVKjV4Ai5reZchzq7Jl4= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/josharian/txtarfs v0.0.0-20210218200122-0702f000015a h1:8NZHLa6Gp0hW6xJ0c3F1Kse7dJw30fOcDzHuF9sLbnE= github.com/josharian/txtarfs v0.0.0-20210218200122-0702f000015a/go.mod h1:izVPOvVRsHiKkeGCT6tYBNWyDVuzj9wAaBb5R9qamfw= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -95,6 +98,7 @@ golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200827163409-021d7c6f1ec3/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 971a411db139292ca2d437e57a70fc208e876ca9 Mon Sep 17 00:00:00 2001 From: bannzai Date: Wed, 12 Jan 2022 16:05:16 +0900 Subject: [PATCH 10/21] to local var --- README.md | 70 +++++++++++++++++++++++++++++++++++++++++++ cmd/nodecheck/main.go | 3 +- 2 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..21ce5c5 --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +# nodecheck + +[![pkg.go.dev][gopkg-badge]][gopkg] + +**nodecheck** will find any GraphQL schema that is not conform to Node interface. + +```graphql +# Valid +type User implements Node { + id: ID! +} + +# Invalid. id is not exists +type Community { + name: String! +} +# Invalid. id is exists but not conform to Node +type Item { + id: ID! +} +``` + +## How to use + +A runnable linter can be created with multichecker package. +You can create own linter with your favorite Analyzers. +And nodecheck has independ flag for allow exclude types of nodecheck. + +Full example + +```go + flag.StringVar(&excludes, "excludes", "", "exclude GraphQL types for node check. it can specify multiple values separated by `,` and it can use regex(e.g .+Connection") + flag.Parse() + + analyzer := nodecheck.Analyzer(excludes) + + multichecker.Main( + analyzer, + ) +``` + +`lackid` provides a typical main function and you can install with `go install` command. + +```sh +$ go install github.com/gqlgo/lackid/cmd/lackid@latest +``` + +The `lackid` command has two flags, `schema` and `query` which will be parsed and analyzed by lackid's Analyzer. + +```sh +$ lackid -schema="server/graphql/schema/**/*.graphql" -query="client/**/*.graphql" +``` + +The default value of `schema` is "schema/*/**.graphql" and `query` is `query/*/**.graphql`. + +`schema` flag accepts URL for a endpoint of GraphQL server. +`lackid` will get schemas by an introspection query via the endpoint. + +```sh +$ lackid -schema="https://example.com" -query="client/**/*.graphql" +``` + +## Author + +[![Appify Technologies, Inc.](appify-logo.png)](http://github.com/appify-technologies) + + +[gopkg]: https://pkg.go.dev/github.com/gqlgo/lackid +[gopkg-badge]: https://pkg.go.dev/badge/github.com/gqlgo/lackid?status.svg + diff --git a/cmd/nodecheck/main.go b/cmd/nodecheck/main.go index 65e152d..7a57eac 100644 --- a/cmd/nodecheck/main.go +++ b/cmd/nodecheck/main.go @@ -7,9 +7,8 @@ import ( "github.com/gqlgo/gqlanalysis/multichecker" ) -var excludes string - func main() { + var excludes string flag.StringVar(&excludes, "excludes", "", "exclude GraphQL types for node check. it can specify multiple values separated by `,` and it can use regex(e.g .+Connection") flag.Parse() From cc7503052f6bd08526ff097053806d2bd7c21ab2 Mon Sep 17 00:00:00 2001 From: bannzai Date: Wed, 12 Jan 2022 16:09:50 +0900 Subject: [PATCH 11/21] :memo: add README --- README.md | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 21ce5c5..29e8e97 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ And nodecheck has independ flag for allow exclude types of nodecheck. Full example ```go +func main() { + var excludes string flag.StringVar(&excludes, "excludes", "", "exclude GraphQL types for node check. it can specify multiple values separated by `,` and it can use regex(e.g .+Connection") flag.Parse() @@ -37,27 +39,19 @@ Full example multichecker.Main( analyzer, ) +} ``` -`lackid` provides a typical main function and you can install with `go install` command. - -```sh -$ go install github.com/gqlgo/lackid/cmd/lackid@latest -``` - -The `lackid` command has two flags, `schema` and `query` which will be parsed and analyzed by lackid's Analyzer. +`nodecheck` provides a executable binary. So, you can get cmd tool via `go install` command. ```sh -$ lackid -schema="server/graphql/schema/**/*.graphql" -query="client/**/*.graphql" +$ go install github.com/gqlgo/nodecheck/cmd/nodecheck@latest ``` -The default value of `schema` is "schema/*/**.graphql" and `query` is `query/*/**.graphql`. - -`schema` flag accepts URL for a endpoint of GraphQL server. -`lackid` will get schemas by an introspection query via the endpoint. +The `nodecheck` command receive two flags, `schema` and `excludes`. `excludes` can specify with regex format and it can receive multiple arguments separated by ','. ```sh -$ lackid -schema="https://example.com" -query="client/**/*.graphql" +$ nodecheck -schema="server/graphql/schema/**/*.graphql" -excludes=.+Connection,.+Edge ``` ## Author @@ -65,6 +59,6 @@ $ lackid -schema="https://example.com" -query="client/**/*.graphql" [![Appify Technologies, Inc.](appify-logo.png)](http://github.com/appify-technologies) -[gopkg]: https://pkg.go.dev/github.com/gqlgo/lackid -[gopkg-badge]: https://pkg.go.dev/badge/github.com/gqlgo/lackid?status.svg +[gopkg]: https://pkg.go.dev/github.com/gqlgo/nodecheck +[gopkg-badge]: https://pkg.go.dev/badge/github.com/gqlgo/nodecheck?status.svg From 98672ab4ebdc3b4294b7a5c09bafefde975f1506 Mon Sep 17 00:00:00 2001 From: bannzai Date: Wed, 12 Jan 2022 17:17:45 +0900 Subject: [PATCH 12/21] :memo: fix indent --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 29e8e97..020122f 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ type User implements Node { type Community { name: String! } + # Invalid. id is exists but not conform to Node type Item { id: ID! From cb3c10dddfdf59960cea70a8438a9ae88bfc8e9e Mon Sep 17 00:00:00 2001 From: bannzai Date: Wed, 12 Jan 2022 17:20:28 +0900 Subject: [PATCH 13/21] :recycle: Rename to nodecheck.go --- core.go => nodecheck.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename core.go => nodecheck.go (100%) diff --git a/core.go b/nodecheck.go similarity index 100% rename from core.go rename to nodecheck.go From 69d3ecc32f7d3c708197d460580a3c9eaacd2fd4 Mon Sep 17 00:00:00 2001 From: bannzai Date: Wed, 12 Jan 2022 20:15:09 +0900 Subject: [PATCH 14/21] Use pass.Errorf --- nodecheck.go | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/nodecheck.go b/nodecheck.go index 240dbeb..28a7445 100644 --- a/nodecheck.go +++ b/nodecheck.go @@ -1,7 +1,6 @@ package nodecheck import ( - "fmt" "regexp" "strings" @@ -64,14 +63,8 @@ func run(excludes string) func(pass *gqlanalysis.Pass) (interface{}, error) { } } - if len(needToNodeTypes) > 0 { - names := make([]string, len(needToNodeTypes)) - for i, t := range needToNodeTypes { - names[i] = t.Name - } - - out := strings.Join(names, ",") - return nil, fmt.Errorf("GraphQL types need to conform to Node interface:\n%s", out) + for _, t := range needToNodeTypes { + pass.Reportf(t.Position, "%+v should conform to Node", t.Name) } return nil, nil From 5de6fe30dfc623e9f182f7ba1fb3017108bd320f Mon Sep 17 00:00:00 2001 From: bannzai Date: Wed, 12 Jan 2022 20:19:26 +0900 Subject: [PATCH 15/21] :sparkles: skip private type --- nodecheck.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nodecheck.go b/nodecheck.go index 28a7445..21ae08a 100644 --- a/nodecheck.go +++ b/nodecheck.go @@ -64,6 +64,10 @@ func run(excludes string) func(pass *gqlanalysis.Pass) (interface{}, error) { } for _, t := range needToNodeTypes { + // Skip private type. e.g) __Directive, __Enum ... + if strings.HasPrefix("__") { + break + } pass.Reportf(t.Position, "%+v should conform to Node", t.Name) } From 85b3c85e526a875d5f6ea2ef4f23773a71e1f7b1 Mon Sep 17 00:00:00 2001 From: bannzai Date: Wed, 12 Jan 2022 20:23:23 +0900 Subject: [PATCH 16/21] Pass name --- nodecheck.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nodecheck.go b/nodecheck.go index 21ae08a..5d83e16 100644 --- a/nodecheck.go +++ b/nodecheck.go @@ -65,7 +65,7 @@ func run(excludes string) func(pass *gqlanalysis.Pass) (interface{}, error) { for _, t := range needToNodeTypes { // Skip private type. e.g) __Directive, __Enum ... - if strings.HasPrefix("__") { + if strings.HasPrefix(t.Name, "__") { break } pass.Reportf(t.Position, "%+v should conform to Node", t.Name) From 473050a00de27a51c7d35b3783d6c99780cee104 Mon Sep 17 00:00:00 2001 From: bannzai Date: Wed, 12 Jan 2022 20:29:16 +0900 Subject: [PATCH 17/21] :bug: bugfix using continue --- nodecheck.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nodecheck.go b/nodecheck.go index 5d83e16..6a85aa4 100644 --- a/nodecheck.go +++ b/nodecheck.go @@ -66,7 +66,7 @@ func run(excludes string) func(pass *gqlanalysis.Pass) (interface{}, error) { for _, t := range needToNodeTypes { // Skip private type. e.g) __Directive, __Enum ... if strings.HasPrefix(t.Name, "__") { - break + continue } pass.Reportf(t.Position, "%+v should conform to Node", t.Name) } From f7f86f620c0c4a9af6297b20f4d1e92522b40df6 Mon Sep 17 00:00:00 2001 From: bannzai Date: Wed, 12 Jan 2022 20:30:21 +0900 Subject: [PATCH 18/21] :sparkles: add test case --- nodecheck_test.go | 18 ++++++++++++++++++ testdata/a/schema/model.graphql | 17 +++++++++++++++++ testdata/b/schema/model.graphql | 17 +++++++++++++++++ testdata/c/schema/model.graphql | 17 +++++++++++++++++ 4 files changed, 69 insertions(+) create mode 100644 nodecheck_test.go create mode 100644 testdata/a/schema/model.graphql create mode 100644 testdata/b/schema/model.graphql create mode 100644 testdata/c/schema/model.graphql diff --git a/nodecheck_test.go b/nodecheck_test.go new file mode 100644 index 0000000..c23157e --- /dev/null +++ b/nodecheck_test.go @@ -0,0 +1,18 @@ +package nodecheck_test + +import ( + "testing" + + "github.com/bannzai/nodecheck" + "github.com/gqlgo/gqlanalysis/analysistest" +) + +func Test(t *testing.T) { + testdata := analysistest.TestData(t) + analysistest.Run(t, testdata, nodecheck.Analyzer(""), "a") +} + +func TestWithSingleExclude(t *testing.T) { + testdata := analysistest.TestData(t) + analysistest.Run(t, testdata, nodecheck.Analyzer("Community"), "b") +} diff --git a/testdata/a/schema/model.graphql b/testdata/a/schema/model.graphql new file mode 100644 index 0000000..e354dce --- /dev/null +++ b/testdata/a/schema/model.graphql @@ -0,0 +1,17 @@ +interface Node { + id: ID! +} + +type User implements Node { + id: ID! +} + +# Invalid. id is not exists +type Community { # want "Community should conform to Node" + name: String! +} + +# Invalid. id is exists but not conform to Node +type Item { # want "Item should conform to Node" + id: ID! +} diff --git a/testdata/b/schema/model.graphql b/testdata/b/schema/model.graphql new file mode 100644 index 0000000..b6bfb15 --- /dev/null +++ b/testdata/b/schema/model.graphql @@ -0,0 +1,17 @@ +interface Node { + id: ID! +} + +type User implements Node { + id: ID! +} + +# Invalid. id is not exists +type Community { + name: String! +} + +# Invalid. id is exists but not conform to Node +type Item { # want "Item should conform to Node" + id: ID! +} diff --git a/testdata/c/schema/model.graphql b/testdata/c/schema/model.graphql new file mode 100644 index 0000000..2f12a38 --- /dev/null +++ b/testdata/c/schema/model.graphql @@ -0,0 +1,17 @@ +interface Node { + id: ID! +} + +type User implements Node { + id: ID! +} + +# Invalid. id is not exists +type Community { + name: String! +} + +# Invalid. id is exists but not conform to Node +type Item { + id: ID! +} From 4de6a6a1da68e7e95ab3ee87206cbbfa59fa8289 Mon Sep 17 00:00:00 2001 From: bannzai Date: Wed, 12 Jan 2022 20:32:06 +0900 Subject: [PATCH 19/21] Add test case --- nodecheck_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nodecheck_test.go b/nodecheck_test.go index c23157e..0af8e12 100644 --- a/nodecheck_test.go +++ b/nodecheck_test.go @@ -12,7 +12,12 @@ func Test(t *testing.T) { analysistest.Run(t, testdata, nodecheck.Analyzer(""), "a") } -func TestWithSingleExclude(t *testing.T) { +func TestWithSingleExclusion(t *testing.T) { testdata := analysistest.TestData(t) analysistest.Run(t, testdata, nodecheck.Analyzer("Community"), "b") } + +func TestWithMultipleExclusion(t *testing.T) { + testdata := analysistest.TestData(t) + analysistest.Run(t, testdata, nodecheck.Analyzer("Community,Item"), "c") +} From a159e6a7c86d96f0e9479daf81e277d12149b3b7 Mon Sep 17 00:00:00 2001 From: bannzai Date: Wed, 12 Jan 2022 20:35:52 +0900 Subject: [PATCH 20/21] Add test case --- nodecheck_test.go | 5 +++++ testdata/d/schema/model.graphql | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 testdata/d/schema/model.graphql diff --git a/nodecheck_test.go b/nodecheck_test.go index 0af8e12..8fc0d4b 100644 --- a/nodecheck_test.go +++ b/nodecheck_test.go @@ -21,3 +21,8 @@ func TestWithMultipleExclusion(t *testing.T) { testdata := analysistest.TestData(t) analysistest.Run(t, testdata, nodecheck.Analyzer("Community,Item"), "c") } + +func TestWithRegex(t *testing.T) { + testdata := analysistest.TestData(t) + analysistest.Run(t, testdata, nodecheck.Analyzer(".+Payload"), "d") +} diff --git a/testdata/d/schema/model.graphql b/testdata/d/schema/model.graphql new file mode 100644 index 0000000..a937e29 --- /dev/null +++ b/testdata/d/schema/model.graphql @@ -0,0 +1,17 @@ +interface Node { + id: ID! +} + +type User implements Node { + id: ID! +} + +# Invalid. id is not exists +type CommunityPayload { + name: String! +} + +# Invalid. id is exists but not conform to Node +type ItemPayload { + id: ID! +} From a4e1f50d46a6b8a89f4becd0d33b139829363f0e Mon Sep 17 00:00:00 2001 From: bannzai Date: Wed, 12 Jan 2022 20:37:17 +0900 Subject: [PATCH 21/21] :recycle: fix comment --- README.md | 4 ++-- testdata/a/schema/model.graphql | 6 +++--- testdata/b/schema/model.graphql | 6 +++--- testdata/c/schema/model.graphql | 6 +++--- testdata/d/schema/model.graphql | 8 ++++---- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 020122f..1744f44 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,12 @@ type User implements Node { id: ID! } -# Invalid. id is not exists +# Invalid. not conform to Node type Community { name: String! } -# Invalid. id is exists but not conform to Node +# Invalid. not conform to Node but id is exist type Item { id: ID! } diff --git a/testdata/a/schema/model.graphql b/testdata/a/schema/model.graphql index e354dce..d44c14a 100644 --- a/testdata/a/schema/model.graphql +++ b/testdata/a/schema/model.graphql @@ -6,12 +6,12 @@ type User implements Node { id: ID! } -# Invalid. id is not exists +# Invalid. not conform to Node type Community { # want "Community should conform to Node" - name: String! + name: String! } -# Invalid. id is exists but not conform to Node +# Invalid. not conform to Node but id is exist type Item { # want "Item should conform to Node" id: ID! } diff --git a/testdata/b/schema/model.graphql b/testdata/b/schema/model.graphql index b6bfb15..a545596 100644 --- a/testdata/b/schema/model.graphql +++ b/testdata/b/schema/model.graphql @@ -6,12 +6,12 @@ type User implements Node { id: ID! } -# Invalid. id is not exists +# Invalid. not conform to Node type Community { - name: String! + name: String! } -# Invalid. id is exists but not conform to Node +# Invalid. not conform to Node but id is exist type Item { # want "Item should conform to Node" id: ID! } diff --git a/testdata/c/schema/model.graphql b/testdata/c/schema/model.graphql index 2f12a38..34c1e67 100644 --- a/testdata/c/schema/model.graphql +++ b/testdata/c/schema/model.graphql @@ -6,12 +6,12 @@ type User implements Node { id: ID! } -# Invalid. id is not exists +# Invalid. not conform to Node type Community { - name: String! + name: String! } -# Invalid. id is exists but not conform to Node +# Invalid. not conform to Node but id is exist type Item { id: ID! } diff --git a/testdata/d/schema/model.graphql b/testdata/d/schema/model.graphql index a937e29..8ab3eef 100644 --- a/testdata/d/schema/model.graphql +++ b/testdata/d/schema/model.graphql @@ -6,12 +6,12 @@ type User implements Node { id: ID! } -# Invalid. id is not exists +# Invalid. not conform to Node type CommunityPayload { - name: String! + name: String! } -# Invalid. id is exists but not conform to Node +# Invalid. not conform to Node but id is exist type ItemPayload { - id: ID! + id: ID! }