Skip to content

Commit 93de4d4

Browse files
committed
Dynamic gql queries
1 parent e4ce0d7 commit 93de4d4

File tree

1 file changed

+191
-0
lines changed

1 file changed

+191
-0
lines changed

builder/query_builder.go

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
"reflect"
8+
"strings"
9+
10+
"github.com/cli/cli/internal/ghinstance"
11+
"github.com/cli/cli/pkg/cmd/factory"
12+
"github.com/shurcooL/githubv4"
13+
"github.com/shurcooL/graphql"
14+
)
15+
16+
type QueryBuilder interface {
17+
AddField(string)
18+
AddNode(string) *Node
19+
AddVariable(string, string)
20+
Generate() reflect.Value
21+
}
22+
23+
type Node struct {
24+
Kind string
25+
Fields []string
26+
Nodes []*Node
27+
Arrays []*Node
28+
Variables map[string]string
29+
}
30+
31+
type Comments struct {
32+
Nodes []Comment
33+
TotalCount int
34+
}
35+
36+
type Comment struct {
37+
Body string
38+
IsMinimized bool
39+
}
40+
41+
type PullRequest struct {
42+
Comments Comments
43+
}
44+
45+
type Repository struct {
46+
Name string
47+
}
48+
49+
func QueryBuilderForKind(kind string) QueryBuilder {
50+
return &Node{Kind: kind, Fields: []string{}, Nodes: []*Node{}, Variables: map[string]string{}}
51+
}
52+
53+
func (n *Node) AddField(field string) {
54+
n.Fields = append(n.Fields, field)
55+
}
56+
57+
func (n *Node) AddVariable(key string, value string) {
58+
n.Variables[key] = value
59+
}
60+
61+
func (n *Node) AddNode(kind string) *Node {
62+
nn := Node{Kind: kind, Fields: []string{}, Nodes: []*Node{}, Variables: map[string]string{}}
63+
n.Nodes = append(n.Nodes, &nn)
64+
return &nn
65+
}
66+
67+
func (n *Node) AddArray(kind string) *Node {
68+
nn := Node{Kind: kind, Fields: []string{}, Nodes: []*Node{}, Variables: map[string]string{}}
69+
n.Arrays = append(n.Arrays, &nn)
70+
return &nn
71+
}
72+
73+
func (n *Node) Generate() reflect.Value {
74+
x := reflect.StructOf(n.generate())
75+
b := reflect.StructField{
76+
Name: strings.Title(n.Kind),
77+
Type: x,
78+
Tag: variablesToTag(n),
79+
}
80+
y := reflect.StructOf([]reflect.StructField{b})
81+
return reflect.New(y).Elem()
82+
}
83+
84+
func (n *Node) generate() []reflect.StructField {
85+
fields := []reflect.StructField{}
86+
t := typeFromKind(n.Kind)
87+
88+
for _, f := range n.Fields {
89+
s, _ := t.FieldByName(strings.Title(f))
90+
n := reflect.StructField{
91+
Name: strings.Title(f),
92+
Type: s.Type,
93+
}
94+
fields = append(fields, n)
95+
}
96+
97+
for _, a := range n.Arrays {
98+
x := reflect.StructOf(a.generate())
99+
b := reflect.StructField{
100+
Name: strings.Title(a.Kind),
101+
Type: reflect.SliceOf(x),
102+
Tag: variablesToTag(a),
103+
}
104+
fields = append(fields, b)
105+
}
106+
107+
for _, nn := range n.Nodes {
108+
x := reflect.StructOf(nn.generate())
109+
b := reflect.StructField{
110+
Name: strings.Title(nn.Kind),
111+
Type: x,
112+
Tag: variablesToTag(nn),
113+
}
114+
fields = append(fields, b)
115+
}
116+
117+
return fields
118+
}
119+
120+
func variablesToTag(n *Node) reflect.StructTag {
121+
m := n.Variables
122+
var b strings.Builder
123+
if len(m) > 0 {
124+
fmt.Fprintf(&b, `graphql:"%s(`, n.Kind)
125+
}
126+
s := []string{}
127+
for k, v := range m {
128+
s = append(s, fmt.Sprintf("%s: %s", k, v))
129+
}
130+
fmt.Fprintf(&b, strings.Join(s, ", "))
131+
if len(m) > 0 {
132+
fmt.Fprintf(&b, `)"`)
133+
}
134+
return reflect.StructTag(b.String())
135+
}
136+
137+
func typeFromKind(kind string) reflect.Type {
138+
m := map[string]interface{}{
139+
"repository": Repository{},
140+
"pullRequest": PullRequest{},
141+
"comments": Comments{},
142+
"nodes": Comment{},
143+
}
144+
return reflect.TypeOf(m[kind])
145+
}
146+
147+
func main() {
148+
qb := QueryBuilderForKind("repository")
149+
qb.AddVariable("owner", "$owner")
150+
qb.AddVariable("name", "$repo")
151+
prqb := qb.AddNode("pullRequest")
152+
prqb.AddVariable("number", "$number")
153+
cqb := prqb.AddNode("comments")
154+
cqb.AddVariable("first", "100")
155+
cqb.AddField("totalCount")
156+
nqb := cqb.AddArray("nodes")
157+
nqb.AddField("body")
158+
// nqb.AddField("isMinimized")
159+
160+
if os.Getenv("NAME") != "" {
161+
qb.AddField("name")
162+
}
163+
164+
x := qb.Generate()
165+
166+
// type response struct {
167+
// Repository struct {
168+
// PullRequest struct {
169+
// Comments struct {
170+
// Nodes []struct {
171+
// Body string
172+
// }
173+
// TotalCount int
174+
// } "graphql:\"comments(first: 100)\""
175+
// } "graphql:\"pullRequest(number: $number)\""
176+
// } "graphql:\"repository(owner: $owner, name: $repo)\""
177+
// }
178+
179+
variables := map[string]interface{}{
180+
"owner": githubv4.String("cli"),
181+
"repo": githubv4.String("cli"),
182+
"number": githubv4.Int(2776),
183+
}
184+
185+
f := factory.New("1.6")
186+
cfg, _ := f.Config()
187+
h := factory.NewHTTPClient(f.IOStreams, cfg, "1.6", false)
188+
c := graphql.NewClient(ghinstance.GraphQLEndpoint("github.com"), h)
189+
c.Query(context.Background(), x.Addr().Interface(), variables)
190+
fmt.Println(x)
191+
}

0 commit comments

Comments
 (0)