Skip to content

Commit 9edd548

Browse files
fix: Fix double-quote bug + Add more definition handlers (#3)
1 parent c1c30bb commit 9edd548

File tree

5 files changed

+203
-49
lines changed

5 files changed

+203
-49
lines changed

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,12 @@ go run cmd/graphql-schema-picker/main.go \
4242
pick \
4343
--sdl-file examples/hasura.sdl.graphqls \
4444
--definitions Aircrafts
45-
```
45+
```
46+
47+
## Similar Tools
48+
49+
- https://github.com/n1ru4l/graphql-public-schema-filter
50+
- https://github.com/kesne/graphql-schema-subset
51+
- https://github.com/xometry/graphql-code-generator-subset-plugin
52+
- https://the-guild.dev/graphql/tools/docs/api/classes/wrap_src.pruneschema
53+
- https://pothos-graphql.dev/docs/plugins/sub-graph

internal/cli/graph.go

Lines changed: 66 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,101 @@
11
package cli
22

33
import (
4-
"errors"
54
"github.com/charmbracelet/log"
65
"github.com/dominikbraun/graph"
76
"github.com/graphql-go/graphql/language/ast"
87
"github.com/graphql-go/graphql/language/kinds"
8+
"strings"
99
)
1010

1111
type Vertex struct {
1212
Name string
1313
Node ast.Node
1414
}
1515

16+
type Describer interface {
17+
GetDescription() *ast.StringValue
18+
}
19+
20+
func sanitizeComment(d Describer) string {
21+
desc := d.GetDescription()
22+
if desc == nil {
23+
return ""
24+
}
25+
26+
return strings.ReplaceAll(desc.Value, `"`, `'`)
27+
}
28+
1629
func NewVertex(node ast.Node) Vertex {
1730
var name string
1831
switch node.GetKind() {
1932
case kinds.ScalarDefinition:
2033
obj := node.(*ast.ScalarDefinition)
2134
name = obj.GetName().Value
35+
36+
// Sanitize description (e.g., remove double-quotes)
37+
if obj.Description != nil {
38+
obj.Description = &ast.StringValue{
39+
Kind: kinds.StringValue,
40+
Value: sanitizeComment(obj),
41+
}
42+
}
2243
case kinds.InterfaceDefinition:
2344
obj := node.(*ast.InterfaceDefinition)
2445
name = obj.GetName().Value
46+
47+
// Sanitize description (e.g., remove double-quotes)
48+
if obj.Description != nil {
49+
obj.Description = &ast.StringValue{
50+
Kind: kinds.StringValue,
51+
Value: sanitizeComment(obj),
52+
}
53+
}
2554
case kinds.UnionDefinition:
2655
obj := node.(*ast.UnionDefinition)
2756
name = obj.GetName().Value
57+
58+
// Sanitize description (e.g., remove double-quotes)
59+
if obj.Description != nil {
60+
obj.Description = &ast.StringValue{
61+
Kind: kinds.StringValue,
62+
Value: sanitizeComment(obj),
63+
}
64+
}
2865
case kinds.EnumDefinition:
2966
obj := node.(*ast.EnumDefinition)
3067
name = obj.GetName().Value
68+
69+
// Sanitize description (e.g., remove double-quotes)
70+
if obj.Description != nil {
71+
obj.Description = &ast.StringValue{
72+
Kind: kinds.StringValue,
73+
Value: sanitizeComment(obj),
74+
}
75+
}
3176
case kinds.InputObjectDefinition:
3277
obj := node.(*ast.InputObjectDefinition)
3378
name = obj.GetName().Value
79+
80+
// Sanitize description (e.g., remove double-quotes)
81+
if obj.Description != nil {
82+
obj.Description = &ast.StringValue{
83+
Kind: kinds.StringValue,
84+
Value: sanitizeComment(obj),
85+
}
86+
}
87+
3488
case kinds.ObjectDefinition:
3589
obj := node.(*ast.ObjectDefinition)
3690
name = obj.GetName().Value
91+
92+
// Sanitize description (e.g., remove double-quotes)
93+
if obj.Description != nil {
94+
obj.Description = &ast.StringValue{
95+
Kind: kinds.StringValue,
96+
Value: sanitizeComment(obj),
97+
}
98+
}
3799
default:
38100
panic("NewVertex: unsupported node kind: " + node.GetKind())
39101
}
@@ -60,7 +122,7 @@ func buildPrunedGraph(doc *ast.Document) graph.Graph[string, Vertex] {
60122
loadTopLevelDefinitions(g, doc)
61123

62124
// Build edges between vertices.
63-
buildEdges(g, doc)
125+
buildEdges(g)
64126

65127
// Prunes any vertices that don't appear in any edges
66128
return prune(g)
@@ -82,57 +144,15 @@ func loadTopLevelDefinitions(g graph.Graph[string, Vertex], doc *ast.Document) {
82144
case kinds.InputObjectDefinition:
83145
v := NewVertex(d)
84146
_ = g.AddVertex(v)
85-
log.Debugf("Adding vertex for definition %d (%s) -- %s", i, d.GetKind(), v.Name)
147+
log.Debugf("Adding vertex for definition %d (%s) -- %s",
148+
i, d.GetKind(), v.Name)
86149

87150
default:
88151
log.Warnf("Ignoring definition %d (%s)", i, d.GetKind())
89152
}
90153
}
91154
}
92155

93-
func buildEdges(g graph.Graph[string, Vertex], doc *ast.Document) {
94-
for _, desired := range desiredDefinitions {
95-
v, err := g.Vertex(desired)
96-
if err != nil {
97-
if errors.Is(err, graph.ErrVertexNotFound) {
98-
log.Errorf("unable to find definition for: %s", desired)
99-
} else {
100-
log.Fatal("unable to read vertex", "err", err)
101-
}
102-
}
103-
104-
switch v.Node.GetKind() {
105-
// TODO support input objects, input values, interfaces, and unions... otherwise we're missing things like AircraftsBoolExp
106-
case kinds.ObjectDefinition:
107-
obj := v.Node.(*ast.ObjectDefinition)
108-
fields := obj.Fields
109-
// TODO iterate through node's fields
110-
for _, f := range fields {
111-
112-
// Is the field a primitive scalar (e.g., Int, String)?
113-
// If so, we can skip it, as it's natively a part of any
114-
// GraphQL schema.
115-
rootType := getRootTypeNameHelper(f.Type, 0)
116-
if isBasicType(rootType) {
117-
continue
118-
}
119-
120-
log.Debug("Found field in object",
121-
"object", obj.Name.Value,
122-
"name", f.Name.Value,
123-
"type", rootType,
124-
)
125-
126-
_ = g.AddEdge(obj.Name.Value, rootType)
127-
128-
// TODO Fields also consist of their arguments, which themselves
129-
// may be non-primitive dependencies.
130-
//litter.Dump(f.Arguments)
131-
}
132-
}
133-
}
134-
}
135-
136156
func prune(in graph.Graph[string, Vertex]) graph.Graph[string, Vertex] {
137157
m, err := in.AdjacencyMap()
138158
if err != nil {

internal/cli/graph_edges.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package cli
2+
3+
import (
4+
"errors"
5+
"github.com/charmbracelet/log"
6+
"github.com/dominikbraun/graph"
7+
"github.com/graphql-go/graphql/language/ast"
8+
"github.com/graphql-go/graphql/language/kinds"
9+
)
10+
11+
func buildEdges(g graph.Graph[string, Vertex]) {
12+
for _, desired := range desiredDefinitions {
13+
v, err := g.Vertex(desired)
14+
if err != nil {
15+
if errors.Is(err, graph.ErrVertexNotFound) {
16+
log.Errorf("unable to find definition for: %s", desired)
17+
} else {
18+
log.Fatal("unable to read vertex", "err", err)
19+
}
20+
}
21+
22+
switch v.Node.GetKind() {
23+
case kinds.ObjectDefinition:
24+
buildEdgesForObject(v, g)
25+
case kinds.InterfaceDefinition:
26+
buildEdgesForInterface(v, g)
27+
case kinds.UnionDefinition:
28+
buildEdgesForUnion(v, g)
29+
case kinds.InputObjectDefinition:
30+
buildEdgesForInputObject(v, g)
31+
default:
32+
log.Warnf("Ignoring dependencies for %s node", v.Node.GetKind())
33+
}
34+
}
35+
}
36+
37+
func buildEdgesFromFieldDefs(
38+
g graph.Graph[string, Vertex],
39+
name string,
40+
fields []*ast.FieldDefinition,
41+
) {
42+
for _, f := range fields {
43+
// Is the field a primitive scalar (e.g., Int, String)?
44+
// If so, we can skip it, as it's natively a part of any
45+
// GraphQL schema.
46+
rootType := getRootTypeNameHelper(f.Type, 0)
47+
if isBasicType(rootType) {
48+
continue
49+
}
50+
51+
log.Debug("Found field in object",
52+
"object", name,
53+
"name", f.Name.Value,
54+
"type", rootType,
55+
)
56+
57+
_ = g.AddEdge(name, rootType)
58+
59+
// Iterate through f.Argument --
60+
// since Fields are also dependencies themselves!
61+
args := f.Arguments
62+
for _, arg := range args {
63+
root := getRootTypeNameHelper(arg.Type, 0)
64+
if isBasicType(root) {
65+
continue
66+
}
67+
_ = g.AddEdge(name, root)
68+
}
69+
}
70+
}
71+
72+
func buildEdgesForObject(v Vertex, g graph.Graph[string, Vertex]) {
73+
obj := v.Node.(*ast.ObjectDefinition)
74+
fields := obj.Fields
75+
buildEdgesFromFieldDefs(g, obj.Name.Value, fields)
76+
}
77+
78+
func buildEdgesForInterface(v Vertex, g graph.Graph[string, Vertex]) {
79+
obj := v.Node.(*ast.InterfaceDefinition)
80+
fields := obj.Fields
81+
buildEdgesFromFieldDefs(g, obj.Name.Value, fields)
82+
}
83+
84+
func buildEdgesForUnion(v Vertex, g graph.Graph[string, Vertex]) {
85+
// TODO add support
86+
}
87+
88+
func buildEdgesForInputObject(v Vertex, g graph.Graph[string, Vertex]) {
89+
obj := v.Node.(*ast.InputObjectDefinition)
90+
fields := obj.Fields
91+
name := obj.Name.Value
92+
93+
for _, f := range fields {
94+
// Is the field a primitive scalar (e.g., Int, String)?
95+
// If so, we can skip it, as it's natively a part of any
96+
// GraphQL schema.
97+
rootType := getRootTypeNameHelper(f.Type, 0)
98+
if isBasicType(rootType) {
99+
continue
100+
}
101+
102+
log.Debug("Found field in object",
103+
"object", name,
104+
"name", f.Name.Value,
105+
"type", rootType,
106+
)
107+
108+
_ = g.AddEdge(name, rootType)
109+
}
110+
}

internal/cli/print.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
)
1010

1111
func printSDL(doc *ast.Document) {
12-
sdl := printer.Print(doc)
12+
log.Infof("Printing new SDL to file with %d definitions", len(doc.Definitions))
1313

1414
// Create a new file where we'll write the new SDL
1515
// TODO make configurable
@@ -22,8 +22,18 @@ func printSDL(doc *ast.Document) {
2222

2323
w := bufio.NewWriter(f)
2424

25-
_, err = w.WriteString(sdl.(string))
25+
sdl := printer.Print(doc)
26+
27+
sdlString, ok := sdl.(string)
28+
if !ok {
29+
log.Fatal("expected SDL to be a string")
30+
}
31+
32+
log.Debug(sdlString)
33+
34+
_, err = w.WriteString(sdlString)
2635
if err != nil {
2736
log.Fatal("unable to produce new SDL file", "err", err)
2837
}
38+
defer w.Flush()
2939
}

test/schema.graphql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1+
"""
2+
Representation of a "Foo"
3+
"""
14
type Foo {
25
name: String
36
bar: Bar
47
}
58

9+
"""
10+
Representation of a "Bar
11+
"""
612
type Bar {
713
name: String
814
quantity: Int!

0 commit comments

Comments
 (0)