Skip to content

Commit 184a510

Browse files
authored
Merge pull request #276 from Yamashou/fragment-spred
Bug Fragment spred
2 parents 1a7ee26 + 2e3a5e0 commit 184a510

File tree

5 files changed

+587
-72
lines changed

5 files changed

+587
-72
lines changed

clientgenv2/source_generator.go

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,39 @@ func NewLayerTypeName(base, thisField string) string {
227227
return fmt.Sprintf("%s_%s", cases.Title(language.Und, cases.NoLower).String(base), thisField)
228228
}
229229

230+
func (r *SourceGenerator) expandFragmentFields(responseFields ResponseFieldList) ResponseFieldList {
231+
result := make(ResponseFieldList, 0, len(responseFields))
232+
for _, field := range responseFields {
233+
if field.IsFragmentSpread {
234+
for _, fragmentField := range field.ResponseFields {
235+
result = append(result, fragmentField)
236+
}
237+
} else {
238+
result = append(result, field)
239+
}
240+
}
241+
return result
242+
}
243+
244+
func (r *SourceGenerator) hasFragmentSpread(fields ResponseFieldList) bool {
245+
for _, field := range fields {
246+
if field.IsFragmentSpread {
247+
return true
248+
}
249+
}
250+
return false
251+
}
252+
253+
func (r *SourceGenerator) collectFragmentFields(fields ResponseFieldList) ResponseFieldList {
254+
var fragmentFields ResponseFieldList
255+
for _, field := range fields {
256+
if field.IsFragmentSpread {
257+
fragmentFields = append(fragmentFields, field.ResponseFields...)
258+
}
259+
}
260+
return fragmentFields
261+
}
262+
230263
func (r *SourceGenerator) NewResponseField(selection ast.Selection, typeName string) *ResponseField {
231264
var isOptional bool
232265
switch selection := selection.(type) {
@@ -312,24 +345,51 @@ func (r *SourceGenerator) NewResponseField(selection ast.Selection, typeName str
312345

313346
case *ast.InlineFragment:
314347
// InlineFragmentは子要素をそのままstructとしてもつので、ここで、構造体の型を作成します
348+
// InlineFragment has child elements, so create a struct type here
315349
name := NewLayerTypeName(typeName, templates.ToGo(selection.TypeCondition))
316350
fieldsResponseFields := r.NewResponseFields(selection.SelectionSet, name)
317351

318-
if fieldsResponseFields.IsFragmentSpread() {
352+
hasFragmentSpread := r.hasFragmentSpread(fieldsResponseFields)
353+
fragmentFields := r.collectFragmentFields(fieldsResponseFields)
354+
355+
// フラグメントスプレッドがある場合
356+
// if there is a fragment spread
357+
if hasFragmentSpread {
358+
// フラグメントからの全フィールドを集めます
359+
// collect all fields from fragment
360+
allFields := make(ResponseFieldList, 0)
361+
for _, field := range fieldsResponseFields {
362+
if !field.IsFragmentSpread {
363+
allFields = append(allFields, field)
364+
}
365+
}
366+
// フラグメントのフィールドを追加
367+
// append fragment fields
368+
allFields = append(allFields, fragmentFields...)
369+
370+
// 構造体を生成
371+
// generate struct
372+
structType := allFields.StructType()
373+
r.StructSources = append(r.StructSources, &StructSource{
374+
Name: name,
375+
Type: structType,
376+
})
319377
typ := types.NewNamed(
320-
types.NewTypeName(0, r.client.Pkg(), templates.ToGo(fieldsResponseFields[0].Name), nil),
321-
fieldsResponseFields.StructType(),
378+
types.NewTypeName(0, r.client.Pkg(), name, nil),
379+
structType,
322380
nil,
323381
)
324382

325383
return &ResponseField{
326-
Name: selection.TypeCondition,
327-
Type: typ,
328-
Tags: []string{fmt.Sprintf(`graphql:"... on %s"`, selection.TypeCondition)},
329-
ResponseFields: fieldsResponseFields,
384+
Name: selection.TypeCondition,
385+
Type: typ,
386+
IsInlineFragment: true,
387+
Tags: []string{fmt.Sprintf(`graphql:"... on %s"`, selection.TypeCondition)},
388+
ResponseFields: allFields.SortByName(),
330389
}
331390
}
332-
391+
// フラグメントスプレッドがない場合
392+
// if there is no fragment spread
333393
structType := fieldsResponseFields.StructType()
334394
r.StructSources = append(r.StructSources, &StructSource{
335395
Name: name,
@@ -346,7 +406,7 @@ func (r *SourceGenerator) NewResponseField(selection ast.Selection, typeName str
346406
Type: typ,
347407
IsInlineFragment: true,
348408
Tags: []string{fmt.Sprintf(`graphql:"... on %s"`, selection.TypeCondition)},
349-
ResponseFields: fieldsResponseFields,
409+
ResponseFields: fieldsResponseFields.SortByName(),
350410
}
351411
}
352412

clientgenv2/source_generator_test.go

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package clientgenv2
22

33
import (
4+
"github.com/99designs/gqlgen/codegen/config"
5+
gqlgencConfig "github.com/Yamashou/gqlgenc/config"
46
"go/types"
57
"testing"
68
)
@@ -235,3 +237,143 @@ func TestMergeFieldsRecursively(t *testing.T) {
235237
})
236238
}
237239
}
240+
241+
func TestFragmentSpreadExpansionInInlineFragment(t *testing.T) {
242+
// このテストでは、フラグメントスプレッドがあるインラインフラグメントで
243+
// `hasFragmentSpread` と `collectFragmentFields` 関数が正しく動作することを確認します
244+
//
245+
// This test verifies that `hasFragmentSpread` and `collectFragmentFields` functions
246+
// work correctly when handling fragment spreads in inline fragments.
247+
248+
// テストデータを作成
249+
// Create test data
250+
// フラグメントスプレッドを含むフィールドのセット
251+
// A set of fields containing fragment spreads
252+
responseFields := ResponseFieldList{
253+
{
254+
Name: "languages",
255+
Type: types.NewPointer(types.NewNamed(
256+
types.NewTypeName(0, nil, "LanguagesConnection", nil),
257+
types.NewStruct(nil, nil),
258+
nil,
259+
)),
260+
Tags: []string{`json:"languages,omitempty" graphql:"languages"`},
261+
},
262+
{
263+
Name: "RepositoryFragment",
264+
Type: types.NewPointer(types.NewNamed(types.NewTypeName(0, nil, "RepositoryFragment", nil), types.NewStruct(nil, nil), nil)),
265+
IsFragmentSpread: true,
266+
ResponseFields: ResponseFieldList{
267+
{
268+
Name: "id",
269+
Type: types.Typ[types.String],
270+
Tags: []string{`json:"id" graphql:"id"`},
271+
},
272+
{
273+
Name: "name",
274+
Type: types.Typ[types.String],
275+
Tags: []string{`json:"name" graphql:"name"`},
276+
},
277+
},
278+
},
279+
}
280+
281+
mockCfg := &config.Config{}
282+
mockBinder := &config.Binder{}
283+
mockPackageConfig := config.PackageConfig{}
284+
mockGenCfg := &gqlgencConfig.GenerateConfig{}
285+
286+
sg := &SourceGenerator{
287+
cfg: mockCfg,
288+
binder: mockBinder,
289+
client: mockPackageConfig,
290+
genCfg: mockGenCfg,
291+
StructSources: make([]*StructSource, 0),
292+
}
293+
294+
// フラグメントスプレッドの存在を確認
295+
// Verify the existence of fragment spreads
296+
hasFragmentSpread := sg.hasFragmentSpread(responseFields)
297+
if !hasFragmentSpread {
298+
t.Errorf("Expected hasFragmentSpread to return true when a fragment spread is present")
299+
}
300+
301+
// フラグメントフィールドの収集をテスト
302+
// Test the collection of fragment fields
303+
fragmentFields := sg.collectFragmentFields(responseFields)
304+
if len(fragmentFields) != 2 {
305+
t.Errorf("Expected 2 fields from fragment spread, got %d", len(fragmentFields))
306+
}
307+
308+
// フィールド名を検証
309+
// Validate field names
310+
foundID := false
311+
foundName := false
312+
313+
for _, field := range fragmentFields {
314+
switch field.Name {
315+
case "id":
316+
foundID = true
317+
case "name":
318+
foundName = true
319+
}
320+
}
321+
322+
if !foundID {
323+
t.Errorf("Expected to find 'id' field from fragment")
324+
}
325+
if !foundName {
326+
t.Errorf("Expected to find 'name' field from fragment")
327+
}
328+
329+
// 実際のフラグメントスプレッド展開処理をテスト
330+
// Test the actual fragment spread expansion process
331+
// フラグメントスプレッドを含むフィールドから、すべてのフィールドを集める
332+
// Collect all fields from fields containing fragment spreads
333+
allFields := make(ResponseFieldList, 0)
334+
for _, field := range responseFields {
335+
if !field.IsFragmentSpread {
336+
allFields = append(allFields, field)
337+
}
338+
}
339+
// フラグメントのフィールドを追加
340+
// Add fields from fragments
341+
allFields = append(allFields, fragmentFields...)
342+
343+
// フィールドの数を検証
344+
// Validate the number of fields
345+
if len(allFields) != 3 { // languages + id + name
346+
t.Errorf("Expected 3 fields after expansion, got %d", len(allFields))
347+
}
348+
349+
// フィールド名を検証
350+
// Validate field names
351+
foundLanguages := false
352+
foundID = false
353+
foundName = false
354+
355+
for _, field := range allFields {
356+
switch field.Name {
357+
case "languages":
358+
foundLanguages = true
359+
case "id":
360+
foundID = true
361+
case "name":
362+
foundName = true
363+
case "RepositoryFragment":
364+
// このフィールドは展開されるため、ここには存在しないはず
365+
// This field should not exist after expansion
366+
t.Errorf("RepositoryFragment field should not exist after expansion")
367+
}
368+
}
369+
370+
if !foundLanguages {
371+
t.Errorf("Expected to find 'languages' field")
372+
}
373+
if !foundID {
374+
t.Errorf("Expected to find 'id' field from fragment")
375+
}
376+
if !foundName {
377+
t.Errorf("Expected to find 'name' field from fragment")
378+
}
379+
}

0 commit comments

Comments
 (0)