Skip to content

Commit 71d61e8

Browse files
committed
chore(tool): go mod tidy
1 parent 46243d3 commit 71d61e8

File tree

4 files changed

+344
-4
lines changed

4 files changed

+344
-4
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package examples
2+
3+
// This example demonstrates how to use Go generics with gqlschemagen
4+
// to create reusable Relay-style connection types.
5+
6+
// Edge represents a Relay connection edge with a generic node type
7+
type Edge[T any] struct {
8+
Node T `json:"node"`
9+
Cursor string `json:"cursor"`
10+
}
11+
12+
// Connection represents a Relay connection with generic edge type
13+
type Connection[T any] struct {
14+
Edges []*Edge[T] `json:"edges"`
15+
PageInfo *PageInfo `json:"pageInfo"`
16+
}
17+
18+
/**
19+
* @gqlType
20+
* Information about pagination in a connection
21+
*/
22+
type PageInfo struct {
23+
HasNextPage bool `json:"hasNextPage"`
24+
HasPreviousPage bool `json:"hasPreviousPage"`
25+
StartCursor string `json:"startCursor"`
26+
EndCursor string `json:"endCursor"`
27+
}
28+
29+
/**
30+
* @gqlType
31+
* A user in the system
32+
*/
33+
type User struct {
34+
ID string `json:"id"`
35+
Name string `json:"name"`
36+
Email string `json:"email"`
37+
}
38+
39+
/**
40+
* @gqlType
41+
* A blog post
42+
*/
43+
type Post struct {
44+
ID string `json:"id"`
45+
Title string `json:"title"`
46+
Content string `json:"content"`
47+
}
48+
49+
/**
50+
* @gqlType
51+
* Paginated list of users with total count
52+
*/
53+
type UserConnection struct {
54+
Connection[*User]
55+
TotalCount int `gql:"type:Int!"`
56+
}
57+
58+
/**
59+
* @gqlType
60+
* Paginated list of posts with metadata
61+
*/
62+
type PostConnection struct {
63+
Connection[*Post]
64+
TotalCount int `gql:"type:Int!"`
65+
HasUnpublished bool `json:"hasUnpublished"`
66+
}
67+
68+
// Generated GraphQL schema will include:
69+
//
70+
// type UserConnection {
71+
// edges: [Edge!]!
72+
// pageInfo: PageInfo!
73+
// totalCount: Int!
74+
// }
75+
//
76+
// type PostConnection {
77+
// edges: [Edge!]!
78+
// pageInfo: PageInfo!
79+
// totalCount: Int!
80+
// hasUnpublished: Boolean!
81+
// }
82+
//
83+
// type PageInfo {
84+
// hasNextPage: Boolean!
85+
// hasPreviousPage: Boolean!
86+
// startCursor: String!
87+
// endCursor: String!
88+
// }

generator/generics_test.go

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
package generator
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"strings"
7+
"testing"
8+
)
9+
10+
func TestGenericsSupport(t *testing.T) {
11+
tests := []struct {
12+
name string
13+
code string
14+
expectedSchema string
15+
shouldContain []string
16+
}{
17+
{
18+
name: "simple generic embedded struct",
19+
code: `package test
20+
21+
// Edge represents a Relay connection edge
22+
type Edge[T any] struct {
23+
Node T ` + "`json:\"node\"`" + `
24+
Cursor string ` + "`json:\"cursor\"`" + `
25+
}
26+
27+
// Connection represents a Relay connection
28+
type Connection[T any] struct {
29+
Edges []*Edge[T] ` + "`json:\"edges\"`" + `
30+
PageInfo *PageInfo ` + "`json:\"pageInfo\"`" + `
31+
}
32+
33+
type PageInfo struct {
34+
HasNextPage bool ` + "`json:\"hasNextPage\"`" + `
35+
}
36+
37+
type Comment struct {
38+
ID string ` + "`json:\"id\"`" + `
39+
Text string ` + "`json:\"text\"`" + `
40+
}
41+
42+
/**
43+
* @gqlType
44+
*/
45+
type CommentConnection struct {
46+
Connection[*Comment]
47+
Count int64 ` + "`gql:\"type:Int\"`" + `
48+
}
49+
`,
50+
shouldContain: []string{
51+
"type CommentConnection",
52+
"edges:",
53+
"pageInfo:",
54+
"count: Int",
55+
},
56+
},
57+
{
58+
name: "generic type alias",
59+
code: `package test
60+
61+
type Edge[T any] struct {
62+
Node T ` + "`json:\"node\"`" + `
63+
Cursor string ` + "`json:\"cursor\"`" + `
64+
}
65+
66+
/**
67+
* @gqlType
68+
*/
69+
type User struct {
70+
Name string ` + "`json:\"name\"`" + `
71+
}
72+
73+
/**
74+
* @gqlType
75+
*/
76+
type UserEdge struct {
77+
Edge[*User]
78+
}
79+
`,
80+
shouldContain: []string{
81+
"type UserEdge",
82+
"node:",
83+
"cursor:",
84+
},
85+
},
86+
{
87+
name: "nested generic with count field",
88+
code: `package test
89+
90+
type Edge[T any] struct {
91+
Node T ` + "`json:\"node\"`" + `
92+
Cursor string ` + "`json:\"cursor\"`" + `
93+
}
94+
95+
type Connection[T any] struct {
96+
Edges []*Edge[T] ` + "`json:\"edges\"`" + `
97+
PageInfo *PageInfo ` + "`json:\"pageInfo\"`" + `
98+
}
99+
100+
/**
101+
* @gqlType
102+
*/
103+
type PageInfo struct {
104+
HasNextPage bool ` + "`json:\"hasNextPage\"`" + `
105+
HasPreviousPage bool ` + "`json:\"hasPreviousPage\"`" + `
106+
StartCursor string ` + "`json:\"startCursor\"`" + `
107+
EndCursor string ` + "`json:\"endCursor\"`" + `
108+
}
109+
110+
/**
111+
* @gqlType
112+
*/
113+
type Post struct {
114+
ID string ` + "`json:\"id\"`" + `
115+
Title string ` + "`json:\"title\"`" + `
116+
}
117+
118+
/**
119+
* @gqlType
120+
*/
121+
type PostConnection struct {
122+
Connection[*Post]
123+
TotalCount int ` + "`gql:\"type:Int!\"`" + `
124+
}
125+
`,
126+
shouldContain: []string{
127+
"type PostConnection",
128+
"edges:",
129+
"pageInfo:",
130+
"totalCount: Int!",
131+
"hasNextPage:",
132+
"hasPreviousPage:",
133+
},
134+
},
135+
}
136+
137+
for _, tt := range tests {
138+
t.Run(tt.name, func(t *testing.T) {
139+
// Create temporary test files
140+
tmpDir := t.TempDir()
141+
testFile := filepath.Join(tmpDir, "test.go")
142+
143+
// Write test file
144+
if err := os.WriteFile(testFile, []byte(tt.code), 0644); err != nil {
145+
t.Fatalf("failed to write test file: %v", err)
146+
}
147+
148+
// Parse the package
149+
parser := NewParser()
150+
if err := parser.Walk(tmpDir); err != nil {
151+
t.Fatalf("Walk() error = %v", err)
152+
}
153+
154+
// Create config
155+
cfg := NewConfig()
156+
cfg.Output = filepath.Join(tmpDir, "schema.graphql")
157+
cfg.GenStrategy = GenStrategySingle
158+
159+
// Create generator and generate schema
160+
gen := NewGenerator(parser, cfg)
161+
if err := gen.Run(); err != nil {
162+
t.Fatalf("Run() error = %v", err)
163+
}
164+
165+
// Read generated schema file
166+
generated, err := os.ReadFile(cfg.Output)
167+
if err != nil {
168+
t.Fatalf("Failed to read generated file: %v", err)
169+
}
170+
171+
schema := string(generated)
172+
173+
// Check expected content
174+
for _, expected := range tt.shouldContain {
175+
if !strings.Contains(schema, expected) {
176+
t.Errorf("Generated schema missing expected content: %q\n\nGenerated schema:\n%s", expected, schema)
177+
}
178+
}
179+
})
180+
}
181+
}
182+
183+
func TestGenericEmbeddedFieldExpansion(t *testing.T) {
184+
code := `package test
185+
186+
type BaseStruct[T any] struct {
187+
Data T ` + "`json:\"data\"`" + `
188+
Count int ` + "`json:\"count\"`" + `
189+
Error string ` + "`json:\"error\" gql:\"omit\"`" + `
190+
}
191+
192+
type User struct {
193+
ID string ` + "`json:\"id\"`" + `
194+
Name string ` + "`json:\"name\"`" + `
195+
}
196+
197+
/**
198+
* @gqlType
199+
*/
200+
type UserResponse struct {
201+
BaseStruct[*User]
202+
Success bool ` + "`json:\"success\"`" + `
203+
}
204+
`
205+
206+
tmpDir := t.TempDir()
207+
testFile := filepath.Join(tmpDir, "test.go")
208+
209+
if err := os.WriteFile(testFile, []byte(code), 0644); err != nil {
210+
t.Fatalf("failed to write test file: %v", err)
211+
}
212+
213+
parser := NewParser()
214+
if err := parser.Walk(tmpDir); err != nil {
215+
t.Fatalf("Walk() error = %v", err)
216+
}
217+
218+
cfg := NewConfig()
219+
cfg.Output = filepath.Join(tmpDir, "schema.graphql")
220+
cfg.GenStrategy = GenStrategySingle
221+
222+
gen := NewGenerator(parser, cfg)
223+
if err := gen.Run(); err != nil {
224+
t.Fatalf("Run() error = %v", err)
225+
}
226+
227+
// Read generated schema file
228+
generated, err := os.ReadFile(cfg.Output)
229+
if err != nil {
230+
t.Fatalf("Failed to read generated file: %v", err)
231+
}
232+
233+
schema := string(generated)
234+
235+
// Should contain fields from embedded generic struct
236+
expectedFields := []string{
237+
"data:",
238+
"count:",
239+
"success:",
240+
}
241+
242+
for _, field := range expectedFields {
243+
if !strings.Contains(schema, field) {
244+
t.Errorf("Schema missing expected field: %q\n\nGenerated schema:\n%s", field, schema)
245+
}
246+
}
247+
248+
// Should NOT contain omitted field
249+
if strings.Contains(schema, "error:") {
250+
t.Errorf("Schema should not contain omitted field 'error'\n\nGenerated schema:\n%s", schema)
251+
}
252+
}

go.mod

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ module github.com/pablor21/gqlschemagen
22

33
go 1.24
44

5-
require gopkg.in/yaml.v3 v3.0.1
6-
75
require (
8-
github.com/fsnotify/fsnotify v1.9.0 // indirect
9-
golang.org/x/sys v0.13.0 // indirect
6+
github.com/fsnotify/fsnotify v1.9.0
7+
gopkg.in/yaml.v3 v3.0.1
108
)
9+
10+
require golang.org/x/sys v0.13.0 // indirect

gqlschemagen

4.25 MB
Binary file not shown.

0 commit comments

Comments
 (0)