From ef65986594eaeaa0594cc53c55e97268044cbdc0 Mon Sep 17 00:00:00 2001 From: Yudi Haribowo Date: Mon, 18 Nov 2024 11:55:02 +0700 Subject: [PATCH] feat(exec): Duplicate only fields requested by client --- internal/exec/exec.go | 17 ++--------------- internal/exec/selected/selected.go | 26 ++++++++++++++------------ internal/query/query.go | 11 +++++++++++ nullable_types_test.go | 23 ++++++++++++----------- types/query.go | 13 +++++++------ 5 files changed, 46 insertions(+), 44 deletions(-) diff --git a/internal/exec/exec.go b/internal/exec/exec.go index cc7dca55..a218b459 100644 --- a/internal/exec/exec.go +++ b/internal/exec/exec.go @@ -7,7 +7,6 @@ import ( "fmt" "reflect" "strconv" - "strings" "sync" "time" @@ -157,17 +156,6 @@ func (r *Request) execSelections(ctx context.Context, sels []selected.Selection, // to handle JS limitation of int64, we create duplicate of this fields as string // currently limited to these fields only as per Toko-TTS requirement var ( - targetFields = map[string]bool{ - "id": true, - "product_id": true, - "productid": true, - "product_ids": true, - "productids": true, - "shop_id": true, - "shopid": true, - "shop_ids": true, - "shopids": true, - } singularInt64Maps = map[string]bool{ "Int": true, "Int!": true, @@ -194,9 +182,7 @@ var ( func isNeedStrCounterpart(field *selected.SchemaField) (needStrCounterpart, isArray bool) { defType := field.FieldDefinition.Type.String() - fieldName := strings.ToLower(field.Alias) - isTargetField, ok := targetFields[fieldName] - if ok && isTargetField { + if field.NeedStrCounterpart { // is it single object? isTargetTypeSingular, ok := singularInt64Maps[defType] if ok && isTargetTypeSingular { @@ -212,6 +198,7 @@ func isNeedStrCounterpart(field *selected.SchemaField) (needStrCounterpart, isAr return } } + return } diff --git a/internal/exec/selected/selected.go b/internal/exec/selected/selected.go index d0f897e9..897484b8 100644 --- a/internal/exec/selected/selected.go +++ b/internal/exec/selected/selected.go @@ -47,12 +47,13 @@ type Selection interface { type SchemaField struct { resolvable.Field - Alias string - Args map[string]interface{} - PackedArgs reflect.Value - Sels []Selection - Async bool - FixedResult reflect.Value + Alias string + Args map[string]interface{} + PackedArgs reflect.Value + Sels []Selection + Async bool + FixedResult reflect.Value + NeedStrCounterpart bool } type TypeAssertion struct { @@ -152,12 +153,13 @@ func applySelectionSet(r *Request, s *resolvable.Schema, e *resolvable.Object, s fieldSels := applyField(r, s, fe.ValueExec, field.SelectionSet) flattenedSels = append(flattenedSels, &SchemaField{ - Field: *fe, - Alias: field.Alias.Name, - Args: args, - PackedArgs: packedArgs, - Sels: fieldSels, - Async: fe.HasContext || fe.ArgsPacker != nil || fe.HasError || HasAsyncSel(fieldSels), + Field: *fe, + Alias: field.Alias.Name, + Args: args, + PackedArgs: packedArgs, + Sels: fieldSels, + Async: fe.HasContext || fe.ArgsPacker != nil || fe.HasError || HasAsyncSel(fieldSels), + NeedStrCounterpart: field.NeedStrCounterpart, }) } diff --git a/internal/query/query.go b/internal/query/query.go index 7289b2e6..29e58418 100644 --- a/internal/query/query.go +++ b/internal/query/query.go @@ -98,6 +98,8 @@ func parseFragment(l *common.Lexer) *types.FragmentDefinition { func parseSelectionSet(l *common.Lexer) []types.Selection { var sels []types.Selection + originalFields := make(map[string]*types.Field, 0) + needDuplicateFields := make([]string, 0) l.ConsumeToken('{') for l.Peek() != '}' { f := parseSelection(l) @@ -105,12 +107,21 @@ func parseSelectionSet(l *common.Lexer) []types.Selection { case *types.Field: if !strings.HasSuffix(sel.Alias.Name, types.DUPLICATION_SUFFIX) { sels = append(sels, f) + originalFields[sel.Alias.Name] = sel + } else { + needDuplicateFields = append(needDuplicateFields, strings.ReplaceAll(sel.Alias.Name, types.DUPLICATION_SUFFIX, "")) } default: sels = append(sels, f) } } l.ConsumeToken('}') + for _, fieldName := range needDuplicateFields { + f, ok := originalFields[fieldName] + if ok { + f.NeedStrCounterpart = true + } + } return sels } diff --git a/nullable_types_test.go b/nullable_types_test.go index cb4a055d..3f905018 100644 --- a/nullable_types_test.go +++ b/nullable_types_test.go @@ -4,6 +4,7 @@ import ( "math" "testing" + "github.com/tokopedia/graphql-go" "github.com/tokopedia/graphql-go/decode" ) @@ -14,8 +15,8 @@ func TestNullInt_ImplementsUnmarshaler(t *testing.T) { } }() - // assert *NullInt implements decode.Unmarshaler interface - var _ decode.Unmarshaler = (*NullInt)(nil) + // assert *graphql.NullInt implements decode.Unmarshaler interface + var _ decode.Unmarshaler = (*graphql.NullInt)(nil) } func TestNullInt_UnmarshalGraphQL(t *testing.T) { @@ -27,7 +28,7 @@ func TestNullInt_UnmarshalGraphQL(t *testing.T) { b := float64(math.MinInt32 - 1) c := 1234.6 good := int32(1234) - ref := NullInt{ + ref := graphql.NullInt{ Value: &good, Set: true, } @@ -68,7 +69,7 @@ func TestNullInt_UnmarshalGraphQL(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - gt := new(NullInt) + gt := new(graphql.NullInt) if err := gt.UnmarshalGraphQL(tt.args.input); err != nil { if err.Error() != tt.wantErr { t.Errorf("UnmarshalGraphQL() error = %v, want = %s", err, tt.wantErr) @@ -85,7 +86,7 @@ func TestNullInt_UnmarshalGraphQL(t *testing.T) { tests := []struct { name string args args - wantEq NullInt + wantEq graphql.NullInt }{ { name: "int32", @@ -105,7 +106,7 @@ func TestNullInt_UnmarshalGraphQL(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - gt := new(NullInt) + gt := new(graphql.NullInt) if err := gt.UnmarshalGraphQL(tt.args.input); err != nil { t.Errorf("UnmarshalGraphQL() error = %v", err) return @@ -126,7 +127,7 @@ func TestNullFloat_ImplementsUnmarshaler(t *testing.T) { }() // assert *NullFloat implements decode.Unmarshaler interface - var _ decode.Unmarshaler = (*NullFloat)(nil) + var _ decode.Unmarshaler = (*graphql.NullFloat)(nil) } func TestNullFloat_UnmarshalGraphQL(t *testing.T) { @@ -135,7 +136,7 @@ func TestNullFloat_UnmarshalGraphQL(t *testing.T) { } good := float64(1234) - ref := NullFloat{ + ref := graphql.NullFloat{ Value: &good, Set: true, } @@ -155,7 +156,7 @@ func TestNullFloat_UnmarshalGraphQL(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - gt := new(NullFloat) + gt := new(graphql.NullFloat) if err := gt.UnmarshalGraphQL(tt.args.input); err != nil { if err.Error() != tt.wantErr { t.Errorf("UnmarshalGraphQL() error = %v, want = %s", err, tt.wantErr) @@ -172,7 +173,7 @@ func TestNullFloat_UnmarshalGraphQL(t *testing.T) { tests := []struct { name string args args - wantEq NullFloat + wantEq graphql.NullFloat }{ { name: "int", @@ -199,7 +200,7 @@ func TestNullFloat_UnmarshalGraphQL(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - gt := new(NullFloat) + gt := new(graphql.NullFloat) if err := gt.UnmarshalGraphQL(tt.args.input); err != nil { t.Errorf("UnmarshalGraphQL() error = %v", err) return diff --git a/types/query.go b/types/query.go index 627315cc..335b4847 100644 --- a/types/query.go +++ b/types/query.go @@ -39,12 +39,13 @@ type SelectionSet []Selection // Field represents a field used in a query. type Field struct { - Alias Ident - Name Ident - Arguments ArgumentList - Directives DirectiveList - SelectionSet SelectionSet - SelectionSetLoc errors.Location + Alias Ident + Name Ident + Arguments ArgumentList + Directives DirectiveList + SelectionSet SelectionSet + SelectionSetLoc errors.Location + NeedStrCounterpart bool } func (Field) isSelection() {}