From 6067126fac51752e52bf557d19b095000faa619f Mon Sep 17 00:00:00 2001 From: spetrunin Date: Sun, 19 Jan 2025 00:29:16 +0200 Subject: [PATCH 1/6] feat: implement variables mapper --- v2/pkg/astnormalization/variables_mapper.go | 31 ++ .../astnormalization/variables_mapper_test.go | 432 ++++++++++++++++++ v2/pkg/astnormalization/variables_mapping.go | 122 +++++ 3 files changed, 585 insertions(+) create mode 100644 v2/pkg/astnormalization/variables_mapper.go create mode 100644 v2/pkg/astnormalization/variables_mapper_test.go create mode 100644 v2/pkg/astnormalization/variables_mapping.go diff --git a/v2/pkg/astnormalization/variables_mapper.go b/v2/pkg/astnormalization/variables_mapper.go new file mode 100644 index 000000000..383b4ac8c --- /dev/null +++ b/v2/pkg/astnormalization/variables_mapper.go @@ -0,0 +1,31 @@ +package astnormalization + +import ( + "github.com/wundergraph/graphql-go-tools/v2/pkg/ast" + "github.com/wundergraph/graphql-go-tools/v2/pkg/astvisitor" + "github.com/wundergraph/graphql-go-tools/v2/pkg/operationreport" +) + +type VariablesMapper struct { + walker *astvisitor.Walker + variablesMappingVisitor *variablesMappingVisitor +} + +func NewVariablesMapper() *VariablesMapper { + walker := astvisitor.NewWalker(8) + mapper := remapVariables(&walker) + + return &VariablesMapper{ + walker: &walker, + variablesMappingVisitor: mapper, + } +} + +func (v *VariablesMapper) NormalizeOperation(operation, definition *ast.Document, report *operationreport.Report) map[string]string { + v.walker.Walk(operation, definition, report) + if report.HasErrors() { + return nil + } + + return v.variablesMappingVisitor.mapping +} diff --git a/v2/pkg/astnormalization/variables_mapper_test.go b/v2/pkg/astnormalization/variables_mapper_test.go new file mode 100644 index 000000000..0a1274a70 --- /dev/null +++ b/v2/pkg/astnormalization/variables_mapper_test.go @@ -0,0 +1,432 @@ +package astnormalization + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/wundergraph/graphql-go-tools/v2/pkg/internal/unsafeparser" + "github.com/wundergraph/graphql-go-tools/v2/pkg/internal/unsafeprinter" + "github.com/wundergraph/graphql-go-tools/v2/pkg/operationreport" +) + +func TestVariablesMapper(t *testing.T) { + definition := unsafeparser.ParseGraphqlDocumentStringWithBaseSchema(` + type Object { + id: ID! + name: String! + echo(value: String!): String! + echoTwo(value: String!): String! + copy(input: InputObject!): Object! + } + + input InputObject { + id: ID! + name: String! + } + + type Query { + object(id: ID!): Object + } + + type Mutation { + updateObject(name: String!): Object! + } + + type Subscription { + subscribe(id: ID!): Object! + }`) + + variablesMapper := NewVariablesMapper() + + normalizer := NewWithOpts( + WithRemoveNotMatchingOperationDefinitions(), + WithInlineFragmentSpreads(), + WithRemoveFragmentDefinitions(), + WithRemoveUnusedVariables(), + ) + variablesNormalizer := NewVariablesNormalizer() + + testCases := []struct { + name string + input string + output string + variablesMapping map[string]string + }{ + { + name: "1.1 Simple external variable (query)", + input: ` + query MyQuery($varOne: ID!) { + object(id: $varOne) { + name + } + }`, + output: ` + query MyQuery($a: ID!) { + object(id: $a) { + name + } + }`, + variablesMapping: map[string]string{ + "a": "varOne", + }, + }, + { + name: "1.2 Simple external variable (mutation)", + input: ` + mutation MyMutation($varOne: String!) { + updateObject(name: $varOne) { + name + } + }`, + output: ` + mutation MyMutation($a: String!) { + updateObject(name: $a) { + name + } + }`, + variablesMapping: map[string]string{ + "a": "varOne", + }, + }, + { + name: "1.3 Simple external variable (subscription)", + input: ` + subscription MySubscription($varOne: ID!) { + subscribe(id: $varOne) { + name + } + }`, + output: ` + subscription MySubscription($a: ID!) { + subscribe(id: $a) { + name + } + }`, + variablesMapping: map[string]string{ + "a": "varOne", + }, + }, + { + name: "2 Simple inline variable", + input: ` + query MyQuery { + object(id: "abc123") { + name + } + }`, + output: ` + query MyQuery($a: ID!) { + object(id: $a) { + name + } + }`, + variablesMapping: map[string]string{ + "a": "a", + }, + }, + { + name: "3.1 Colliding external variable", + input: ` + query MyQuery($a: ID!) { + object(id: $a) { + name + } + }`, + output: ` + query MyQuery($a: ID!) { + object(id: $a) { + name + } + }`, + variablesMapping: map[string]string{ + "a": "a", + }, + }, + { + name: "3.2 Colliding external variable used in 2 places", + input: ` + query MyQuery($a: String!) { + echo(id: $a) + echoTwo(id: $a) + }`, + output: ` + query MyQuery($a: String!) { + echo(id: $a) + echoTwo(id: $a) + }`, + variablesMapping: map[string]string{ + "a": "a", + }, + }, + { + name: "3.3 Colliding external variable along with inline values", + input: ` + query MyQuery($a: String!) { + object(id: 1) { + echo(value: "Hello World") + echoTwo(value: $a) + } + }`, + output: ` + query MyQuery($a: ID!, $b: String!, $c: String!) { + object(id: $a) { + echo(value: $b) + echoTwo(value: $c) + } + }`, + + variablesMapping: map[string]string{ + "a": "b", + "b": "c", + "c": "a", + }, + }, + { + name: "3.4 Colliding external variables", + input: ` + query MyQuery($b: String!, $e: String! $c: ID!) { + object(id: $c) { + echo(value: $e) + echoTwo(value: $b) + } + }`, + output: ` + query MyQuery($a: ID!, $b: String!, $c: String!) { + object(id: $a) { + echo(value: $b) + echoTwo(value: $c) + } + }`, + + variablesMapping: map[string]string{ + "a": "c", + "b": "e", + "c": "b", + }, + }, + { + name: "3.5 all inline values", + input: ` + query MyQuery { + object(id: 1) { + echo(value: "Hello") + echoTwo(value: "World") + } + }`, + output: ` + query MyQuery($a: ID!, $b: String!, $c: String!){ + object(id: $a) { + echo(value: $b) + echoTwo(value: $c) + } + }`, + + variablesMapping: map[string]string{ + "a": "a", + "b": "b", + "c": "c", + }, + }, + { + name: "4 Inline variable and external variable", + input: ` + query MyQuery($varOne: ID!) { + object(id: $varOne) { + name + echo(value: "Hello World!") + } + }`, + output: ` + query MyQuery($a: ID! $b: String!) { + object(id: $a) { + name + echo(value: $b) + } + }`, + + variablesMapping: map[string]string{ + "a": "varOne", + "b": "a", + }, + }, + { + name: "5.1 Multiple external variables", + input: ` + query MyQuery($varOne: ID! $varTwo: String!) { + object(id: $varOne) { + name + echo(value: $varTwo) + } + }`, + output: ` + query MyQuery($a: ID! $b: String!) { + object(id: $a) { + name + echo(value: $b) + } + }`, + variablesMapping: map[string]string{ + "a": "varOne", + "b": "varTwo", + }, + }, + { + name: "6 Multiple colliding external variables", + input: ` + query MyQuery($a: String! $b: ID!) { + object(id: $b) { + echo(value: $a) + name + } + }`, + output: ` + query MyQuery($a: ID! $b: String!) { + object(id: $a) { + echo(value: $b) + name + } + }`, + variablesMapping: map[string]string{ + "a": "b", + "b": "a", + }, + }, + { + name: "7 multiple inline variables", + input: ` + query MyQuery { + object(id: "abc123") { + echo(value: "Hello World!") + name + } + }`, + output: ` + query MyQuery($a: ID! $b: String!) { + object(id: $a) { + echo(value: $b) + name + } + }`, + variablesMapping: map[string]string{ + "a": "a", + "b": "b", + }, + }, + { + name: "8 Inline variable with multiple colliding external variables", + input: ` + query MyQuery($a: ID! $b: String! ) { + object(id: $a) { + name + copy(input: { id: "abc123", name: "MyObject"}), + echo(value: $b) + } + }`, + output: ` + query MyQuery($a: ID! $b: InputObject! $c: String!) { + object(id: $a) { + name + copy(input: $b), + echo(value: $c) + } + }`, + variablesMapping: map[string]string{ + "a": "a", + "b": "c", + "c": "b", + }, + }, + { + name: "9 Inline variable with multiple colliding external variables", + input: ` + query MyQuery($a: ID! $b: String! $c: String! ) { + object(id: $a) { + name + copy(input: { id: "abc123", name: $b}) + echo(value: $c) + } + }`, + output: ` + query MyQuery($a: ID! $b: InputObject! $c: String! ) { + object(id: $a) { + name + copy(input: $b) + echo(value: $c) + } + }`, + variablesMapping: map[string]string{ + "a": "a", + "b": "d", + "c": "c", + }, + }, + { + name: "11 Reused external variable", + input: ` + query MyQuery($varOne: String!) { + object(id: 1) { + echo(value: $varOne) + echoTwo(value: $varOne) + } + }`, + output: ` + query MyQuery($a: ID!, $b: String!) { + object(id: $a) { + echo(value: $b) + echoTwo(value: $b) + } + }`, + variablesMapping: map[string]string{ + "a": "a", + "b": "varOne", + }, + }, + { + name: "12 Reused inline value", + input: ` + query MyQuery { + object(id: 1) { + echo(value: 12) + echoTwo(value: 12) + } + }`, + output: ` + query MyQuery ($a: ID!, $b: String!) { + object(id: $a) { + echo(value: $b) + echoTwo(value: $b) + } + }`, + variablesMapping: map[string]string{ + "a": "a", + "b": "b", + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + operation := unsafeparser.ParseGraphqlDocumentString(tc.input) + report := &operationreport.Report{} + + normalizer.NormalizeNamedOperation(&operation, &definition, operation.OperationDefinitionNameBytes(0), report) + require.False(t, report.HasErrors()) + fmt.Println("normalized", unsafeprinter.PrettyPrint(&operation)) + + variablesNormalizer.NormalizeOperation(&operation, &definition, report) + require.False(t, report.HasErrors()) + fmt.Println("variables normalized", unsafeprinter.PrettyPrint(&operation)) + + mapping := variablesMapper.NormalizeOperation(&operation, &definition, report) + require.False(t, report.HasErrors()) + + expectedOut := unsafeprinter.Prettify(tc.output) + printedOperation := unsafeprinter.PrettyPrint(&operation) + assert.Equal(t, expectedOut, printedOperation) + assert.Equal(t, tc.variablesMapping, mapping) + }) + } +} diff --git a/v2/pkg/astnormalization/variables_mapping.go b/v2/pkg/astnormalization/variables_mapping.go new file mode 100644 index 000000000..70fa13add --- /dev/null +++ b/v2/pkg/astnormalization/variables_mapping.go @@ -0,0 +1,122 @@ +package astnormalization + +import ( + "cmp" + "math" + "slices" + + "github.com/wundergraph/graphql-go-tools/v2/pkg/ast" + "github.com/wundergraph/graphql-go-tools/v2/pkg/astvisitor" +) + +func remapVariables(walker *astvisitor.Walker) *variablesMappingVisitor { + visitor := &variablesMappingVisitor{ + Walker: walker, + } + walker.RegisterDocumentVisitor(visitor) + walker.RegisterEnterOperationVisitor(visitor) + walker.RegisterEnterArgumentVisitor(visitor) + return visitor +} + +type variablesMappingVisitor struct { + *astvisitor.Walker + operation, definition *ast.Document + mapping map[string]string + variables []*variableItem + operationRef int +} + +type variableItem struct { + variableName string + valueRefs []int + variableDefinitionRef int +} + +func (v *variablesMappingVisitor) LeaveDocument(operation, definition *ast.Document) { + for _, variableItem := range v.variables { + mappingName := v.generateUnusedVariableMappingName() + v.mapping[string(mappingName)] = variableItem.variableName + + newVariableName := v.operation.Input.AppendInputBytes(mappingName) + + // set new variable name for all variable values + for _, variableValueRef := range variableItem.valueRefs { + v.operation.VariableValues[variableValueRef].Name = newVariableName + } + + // set new variable name for variable definition + v.operation.VariableValues[v.operation.VariableDefinitions[variableItem.variableDefinitionRef].VariableValue.Ref].Name = newVariableName + } + + slices.SortFunc(v.operation.OperationDefinitions[v.operationRef].VariableDefinitions.Refs, func(i, j int) int { + return cmp.Compare( + v.operation.VariableValueNameString(v.operation.VariableDefinitions[i].VariableValue.Ref), + v.operation.VariableValueNameString(v.operation.VariableDefinitions[j].VariableValue.Ref), + ) + }) +} + +func (v *variablesMappingVisitor) EnterArgument(ref int) { + if v.operation.Arguments[ref].Value.Kind != ast.ValueKindVariable { + return + } + if len(v.Ancestors) == 0 || v.Ancestors[0].Kind != ast.NodeKindOperationDefinition { + return + } + + varValueRef := v.operation.Arguments[ref].Value.Ref + varNameBytes := v.operation.VariableValueNameBytes(varValueRef) + // explicitly convert to string to convert unsafe + varName := string(varNameBytes) + + variableDefinitionRef, exists := v.operation.VariableDefinitionByNameAndOperation(v.operationRef, varNameBytes) + if !exists { + return + } + + idx := slices.IndexFunc(v.variables, func(i *variableItem) bool { + return i.variableName == varName + }) + if idx == -1 { + v.variables = append(v.variables, &variableItem{ + variableName: varName, + valueRefs: []int{varValueRef}, + variableDefinitionRef: variableDefinitionRef, + }) + return + } + + v.variables[idx].valueRefs = append(v.variables[idx].valueRefs, varValueRef) +} + +func (v *variablesMappingVisitor) EnterDocument(operation, definition *ast.Document) { + v.operation, v.definition = operation, definition + v.mapping = make(map[string]string, len(operation.VariableDefinitions)) + v.variables = make([]*variableItem, 0, len(operation.VariableDefinitions)) +} + +func (v *variablesMappingVisitor) EnterOperationDefinition(ref int) { + v.operationRef = ref +} + +const alphabet = `abcdefghijklmnopqrstuvwxyz` + +func (v *variablesMappingVisitor) generateUnusedVariableMappingName() []byte { + var i, k int64 + + for i = 1; i < math.MaxInt16; i++ { + out := make([]byte, i) + for j := range alphabet { + for k = 0; k < i; k++ { + out[k] = alphabet[j] + } + _, exists := v.mapping[string(out)] + if !exists { + return out + } + } + } + + return nil +} From 9b75a8ae2f47c6cf002daf1e82699859cc8e1898 Mon Sep 17 00:00:00 2001 From: David Stutt Date: Sun, 19 Jan 2025 15:55:20 +0000 Subject: [PATCH 2/6] feat: generate smaller variable names --- v2/pkg/ast/ast_operation_definition.go | 59 +++++++++++++ v2/pkg/ast/ast_operation_definition_test.go | 95 +++++++++++++++++++++ 2 files changed, 154 insertions(+) diff --git a/v2/pkg/ast/ast_operation_definition.go b/v2/pkg/ast/ast_operation_definition.go index c6354e500..34eadc5c8 100644 --- a/v2/pkg/ast/ast_operation_definition.go +++ b/v2/pkg/ast/ast_operation_definition.go @@ -141,3 +141,62 @@ func (d *Document) GenerateUnusedVariableDefinitionName(operationDefinition int) return nil } + +func (d *Document) GenerateUnusedVariableDefinitionNameV2(operationDefinition int) []byte { + l := NewDefaultLetterIndices() + for maxIndex := 0; maxIndex < math.MaxInt64; maxIndex++ { + if _, exists := d.VariableDefinitionByNameAndOperation(operationDefinition, l.Bytes()); !exists { + return l.Bytes() + } + l.Increment() + } + return nil +} + +type LetterIndices struct { + indices []int + bytes []byte +} + +func (l LetterIndices) maxIndex() int { + return len(l.indices) - 1 +} + +func (l *LetterIndices) Increment() { + for i := l.maxIndex(); i > -1; i-- { + if l.indices[i] > 24 { + l.reset(i) + } else { + l.incrementAt(i) + return + } + } + l.indices = append(l.indices, 0) + l.bytes = append(l.bytes, alphabet[0]) +} + +func (l *LetterIndices) incrementAt(index int) { + l.indices[index]++ + l.bytes[index] = alphabet[l.indices[index]] +} + +func (l *LetterIndices) reset(i int) { + l.indices[i] = 0 + l.bytes[i] = alphabet[0] +} + +func (l LetterIndices) Render() string { + return string(l.bytes) +} + +func (l LetterIndices) Bytes() []byte { + return l.bytes +} + +func NewLetterIndices(indices []int, renderable []byte) LetterIndices { + return LetterIndices{indices: indices, bytes: renderable} +} + +func NewDefaultLetterIndices() LetterIndices { + return LetterIndices{indices: []int{0}, bytes: []byte{alphabet[0]}} +} diff --git a/v2/pkg/ast/ast_operation_definition_test.go b/v2/pkg/ast/ast_operation_definition_test.go index 2deaec822..27b179c3f 100644 --- a/v2/pkg/ast/ast_operation_definition_test.go +++ b/v2/pkg/ast/ast_operation_definition_test.go @@ -1,6 +1,9 @@ package ast_test import ( + "fmt" + "github.com/wundergraph/graphql-go-tools/v2/pkg/ast" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -46,4 +49,96 @@ func TestDocument_OperationNameExists(t *testing.T) { "MyOperation", true, )) + + t.Run("LetterIndices.Increment works correctly", func(t *testing.T) { + input := ast.NewLetterIndices([]int{0, 25}, []byte{'a', 'z'}) + input.Increment() + assert.Equal(t, ast.NewLetterIndices([]int{1, 0}, []byte{'b', 'a'}), input) + input.Increment() + assert.Equal(t, ast.NewLetterIndices([]int{1, 1}, []byte{'b', 'b'}), input) + input = ast.NewLetterIndices([]int{1, 25}, []byte{'b', 'z'}) + input.Increment() + assert.Equal(t, ast.NewLetterIndices([]int{2, 0}, []byte{'c', 'a'}), input) + input = ast.NewLetterIndices([]int{25, 25}, []byte{'z', 'z'}) + input.Increment() + assert.Equal(t, ast.NewLetterIndices([]int{0, 0, 0}, []byte{'a', 'a', 'a'}), input) + }) + + t.Run("schema string is generated correctly #1", func(t *testing.T) { + assert.Equal(t, + `query ($a: Int! $b: Int! $c: Int! $d: Int! $e: Int! $f: Int! $g: Int! $h: Int! $i: Int! $j: Int! $k: Int! $l: Int! $m: Int! $n: Int! $o: Int! $p: Int! $q: Int! $r: Int! $s: Int! $t: Int! $u: Int! $v: Int! $w: Int! $x: Int! $y: Int! $z: Int! $aa: Int! $ab: Int!) { + fielda(arg: $a) + fieldb(arg: $b) + fieldc(arg: $c) + fieldd(arg: $d) + fielde(arg: $e) + fieldf(arg: $f) + fieldg(arg: $g) + fieldh(arg: $h) + fieldi(arg: $i) + fieldj(arg: $j) + fieldk(arg: $k) + fieldl(arg: $l) + fieldm(arg: $m) + fieldn(arg: $n) + fieldo(arg: $o) + fieldp(arg: $p) + fieldq(arg: $q) + fieldr(arg: $r) + fields(arg: $s) + fieldt(arg: $t) + fieldu(arg: $u) + fieldv(arg: $v) + fieldw(arg: $w) + fieldx(arg: $x) + fieldy(arg: $y) + fieldz(arg: $z) + fieldaa(arg: $aa) + fieldab(arg: $ab) +}`, + schemaString(28)) + }) + + t.Run("test that schema string is generated correctly #2", func(t *testing.T) { + assert.True(t, strings.HasSuffix(schemaString(704), "fieldzy(arg: $zy)\n\tfieldzz(arg: $zz)\n\tfieldaaa(arg: $aaa)\n\tfieldaab(arg: $aab)\n}")) + }) + + t.Run("test that schema string is generated correctly #3", func(t *testing.T) { + x := schemaString(18280) + assert.True(t, strings.HasSuffix(x, "fieldzzy(arg: $zzy)\n\tfieldzzz(arg: $zzz)\n\tfieldaaaa(arg: $aaaa)\n\tfieldaaab(arg: $aaab)\n}")) + }) + + t.Run("next variable #1", func(t *testing.T) { + op := unsafeparser.ParseGraphqlDocumentString(schemaString(1)) + assert.Equal(t, "b", string(op.GenerateUnusedVariableDefinitionNameV2(0))) + }) + + t.Run("next variable #2", func(t *testing.T) { + op := unsafeparser.ParseGraphqlDocumentString(schemaString(26)) + assert.Equal(t, "aa", string(op.GenerateUnusedVariableDefinitionNameV2(0))) + }) + + t.Run("next variable #3", func(t *testing.T) { + op := unsafeparser.ParseGraphqlDocumentString(schemaString(702)) + assert.Equal(t, "aaa", string(op.GenerateUnusedVariableDefinitionNameV2(0))) + }) + + t.Run("next variable #4", func(t *testing.T) { + op := unsafeparser.ParseGraphqlDocumentString(schemaString(18278)) + assert.Equal(t, "aaaa", string(op.GenerateUnusedVariableDefinitionNameV2(0))) + }) +} + +func schemaString(varNumber int) string { + vars := make([]string, varNumber) + out := make([]string, varNumber) + l := ast.NewDefaultLetterIndices() + for i := 0; i < varNumber; i++ { + varName := l.Render() + out[i] = fmt.Sprintf(" field%s(arg: $%s)", varName, varName) + vars[i] = fmt.Sprintf("$%s: Int!", varName) + l.Increment() + } + prefix := "query (" + fmt.Sprintf(strings.Join(vars, " ")) + ") {\n" + return prefix + strings.Join(out, "\n") + "\n}" } From 6c58b12a54a5979effc513db563702bc73664188 Mon Sep 17 00:00:00 2001 From: David Stutt Date: Sun, 19 Jan 2025 16:01:20 +0000 Subject: [PATCH 3/6] chore: update test --- v2/pkg/ast/ast_operation_definition_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/v2/pkg/ast/ast_operation_definition_test.go b/v2/pkg/ast/ast_operation_definition_test.go index 27b179c3f..ef00a7808 100644 --- a/v2/pkg/ast/ast_operation_definition_test.go +++ b/v2/pkg/ast/ast_operation_definition_test.go @@ -104,8 +104,7 @@ func TestDocument_OperationNameExists(t *testing.T) { }) t.Run("test that schema string is generated correctly #3", func(t *testing.T) { - x := schemaString(18280) - assert.True(t, strings.HasSuffix(x, "fieldzzy(arg: $zzy)\n\tfieldzzz(arg: $zzz)\n\tfieldaaaa(arg: $aaaa)\n\tfieldaaab(arg: $aaab)\n}")) + assert.True(t, strings.HasSuffix(schemaString(18280), "fieldzzy(arg: $zzy)\n\tfieldzzz(arg: $zzz)\n\tfieldaaaa(arg: $aaaa)\n\tfieldaaab(arg: $aaab)\n}")) }) t.Run("next variable #1", func(t *testing.T) { From e7c82cfa9645764dc12cd7a18423b9325c838aad Mon Sep 17 00:00:00 2001 From: David Stutt Date: Sun, 19 Jan 2025 16:06:48 +0000 Subject: [PATCH 4/6] chore: renaming --- v2/pkg/ast/ast_operation_definition.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/v2/pkg/ast/ast_operation_definition.go b/v2/pkg/ast/ast_operation_definition.go index 34eadc5c8..66f8607be 100644 --- a/v2/pkg/ast/ast_operation_definition.go +++ b/v2/pkg/ast/ast_operation_definition.go @@ -165,7 +165,7 @@ func (l LetterIndices) maxIndex() int { func (l *LetterIndices) Increment() { for i := l.maxIndex(); i > -1; i-- { if l.indices[i] > 24 { - l.reset(i) + l.resetAt(i) } else { l.incrementAt(i) return @@ -180,9 +180,9 @@ func (l *LetterIndices) incrementAt(index int) { l.bytes[index] = alphabet[l.indices[index]] } -func (l *LetterIndices) reset(i int) { - l.indices[i] = 0 - l.bytes[i] = alphabet[0] +func (l *LetterIndices) resetAt(index int) { + l.indices[index] = 0 + l.bytes[index] = alphabet[0] } func (l LetterIndices) Render() string { @@ -193,8 +193,8 @@ func (l LetterIndices) Bytes() []byte { return l.bytes } -func NewLetterIndices(indices []int, renderable []byte) LetterIndices { - return LetterIndices{indices: indices, bytes: renderable} +func NewLetterIndices(indices []int, bytes []byte) LetterIndices { + return LetterIndices{indices: indices, bytes: bytes} } func NewDefaultLetterIndices() LetterIndices { From 42488e802ea724008796e84b1e7f63bd416dde35 Mon Sep 17 00:00:00 2001 From: David Stutt Date: Sun, 19 Jan 2025 16:14:11 +0000 Subject: [PATCH 5/6] chore: remove unnecessary Sprintf --- v2/pkg/ast/ast_operation_definition_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/pkg/ast/ast_operation_definition_test.go b/v2/pkg/ast/ast_operation_definition_test.go index ef00a7808..cc45d7710 100644 --- a/v2/pkg/ast/ast_operation_definition_test.go +++ b/v2/pkg/ast/ast_operation_definition_test.go @@ -138,6 +138,6 @@ func schemaString(varNumber int) string { vars[i] = fmt.Sprintf("$%s: Int!", varName) l.Increment() } - prefix := "query (" + fmt.Sprintf(strings.Join(vars, " ")) + ") {\n" + prefix := "query (" + strings.Join(vars, " ") + ") {\n" return prefix + strings.Join(out, "\n") + "\n}" } From e21333caf40189165db26349e8581d5b760398fe Mon Sep 17 00:00:00 2001 From: David Stutt Date: Mon, 20 Jan 2025 13:23:54 +0000 Subject: [PATCH 6/6] chore: small improvements --- v2/pkg/ast/ast_operation_definition.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/v2/pkg/ast/ast_operation_definition.go b/v2/pkg/ast/ast_operation_definition.go index 66f8607be..606eb248c 100644 --- a/v2/pkg/ast/ast_operation_definition.go +++ b/v2/pkg/ast/ast_operation_definition.go @@ -144,13 +144,13 @@ func (d *Document) GenerateUnusedVariableDefinitionName(operationDefinition int) func (d *Document) GenerateUnusedVariableDefinitionNameV2(operationDefinition int) []byte { l := NewDefaultLetterIndices() - for maxIndex := 0; maxIndex < math.MaxInt64; maxIndex++ { - if _, exists := d.VariableDefinitionByNameAndOperation(operationDefinition, l.Bytes()); !exists { - return l.Bytes() + for { + bytes := l.Bytes() + if _, exists := d.VariableDefinitionByNameAndOperation(operationDefinition, bytes); !exists { + return bytes } l.Increment() } - return nil } type LetterIndices struct { @@ -158,7 +158,7 @@ type LetterIndices struct { bytes []byte } -func (l LetterIndices) maxIndex() int { +func (l *LetterIndices) maxIndex() int { return len(l.indices) - 1 } @@ -185,18 +185,18 @@ func (l *LetterIndices) resetAt(index int) { l.bytes[index] = alphabet[0] } -func (l LetterIndices) Render() string { +func (l *LetterIndices) Render() string { return string(l.bytes) } -func (l LetterIndices) Bytes() []byte { +func (l *LetterIndices) Bytes() []byte { return l.bytes } -func NewLetterIndices(indices []int, bytes []byte) LetterIndices { - return LetterIndices{indices: indices, bytes: bytes} +func NewLetterIndices(indices []int, bytes []byte) *LetterIndices { + return &LetterIndices{indices: indices, bytes: bytes} } -func NewDefaultLetterIndices() LetterIndices { - return LetterIndices{indices: []int{0}, bytes: []byte{alphabet[0]}} +func NewDefaultLetterIndices() *LetterIndices { + return &LetterIndices{indices: []int{0}, bytes: []byte{alphabet[0]}} }