Skip to content

Commit

Permalink
sync wg changes (#383)
Browse files Browse the repository at this point in the history
This PR syncs following wg changes:
- wundergraph#547
- wundergraph#554
- wundergraph#555
- wundergraph#556
- wundergraph#544
- wundergraph#564
- wundergraph#567
- wundergraph#568
- wundergraph#575

---------

Co-authored-by: David Steinberger <[email protected]>
Co-authored-by: phob0s <[email protected]>
Co-authored-by: phob0s <[email protected]>
Co-authored-by: Sergiy <[email protected]>
Co-authored-by: MikeWang <[email protected]>
Co-authored-by: Jens Neuse <[email protected]>
Co-authored-by: Alberto García Hierro <[email protected]>
Co-authored-by: Aenimus <[email protected]>
Co-authored-by: David Stutt <[email protected]>
  • Loading branch information
10 people authored Aug 22, 2023
1 parent e336e7b commit 8693715
Show file tree
Hide file tree
Showing 37 changed files with 1,774 additions and 201 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ require (
github.com/r3labs/sse/v2 v2.8.1
github.com/santhosh-tekuri/jsonschema/v5 v5.3.0
github.com/sebdah/goldie v0.0.0-20180424091453-8784dd1ab561
github.com/sebdah/goldie/v2 v2.5.3
github.com/spf13/cobra v0.0.5
github.com/spf13/viper v1.3.2
github.com/stretchr/testify v1.8.1
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,10 @@ github.com/santhosh-tekuri/jsonschema/v5 v5.3.0 h1:uIkTLo0AGRc8l7h5l9r+GcYi9qfVP
github.com/santhosh-tekuri/jsonschema/v5 v5.3.0/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0=
github.com/sebdah/goldie v0.0.0-20180424091453-8784dd1ab561 h1:IY+sDBJR/wRtsxq+626xJnt4Tw7/ROA9cDIR8MMhWyg=
github.com/sebdah/goldie v0.0.0-20180424091453-8784dd1ab561/go.mod h1:lvjGftC8oe7XPtyrOidaMi0rp5B9+XY/ZRUynGnuaxQ=
github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624Y=
github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
Expand Down
19 changes: 10 additions & 9 deletions internal/pkg/quotes/quotes.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
package quotes

const (
quoteStr = "\""
quoteByte = '"'
quoteStr = string(quoteByte)
)

var (
quoteBytes = []byte(quoteStr)
)

func WrapBytes(bytes []byte) []byte {
cp := make([]byte, len(bytes))
copy(cp, bytes)
return append(quoteBytes, append(cp, quoteBytes...)...)
// WrapBytes returns a new slice wrapping the given s
// in quotes (") by making a copy.
func WrapBytes(s []byte) []byte {
cp := make([]byte, len(s)+2)
cp[0] = quoteByte
copy(cp[1:], s)
cp[len(s)+1] = quoteByte
return cp
}

func WrapString(str string) string {
Expand Down
24 changes: 24 additions & 0 deletions internal/pkg/quotes/quotes_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package quotes

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestWrapBytes(t *testing.T) {
testCases := []struct {
s []byte
want []byte
}{
{nil, []byte(`""`)},
{[]byte("foo"), []byte(`"foo"`)},
}
for _, tc := range testCases {
tc := tc
t.Run(string(tc.s), func(t *testing.T) {
r := WrapBytes(tc.s)
assert.Equal(t, tc.want, r)
})
}
}
71 changes: 38 additions & 33 deletions pkg/ast/ast_value.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import (
"fmt"
"io"

"github.com/tidwall/sjson"

"github.com/TykTechnologies/graphql-go-tools/internal/pkg/quotes"
"github.com/TykTechnologies/graphql-go-tools/internal/pkg/unsafebytes"
"github.com/TykTechnologies/graphql-go-tools/pkg/lexer/literal"
Expand Down Expand Up @@ -121,62 +119,69 @@ func (d *Document) ValueContainsVariable(value Value) bool {
}
}

func (d *Document) ValueToJSON(value Value) ([]byte, error) {
func (d *Document) writeJSONValue(buf *bytes.Buffer, value Value) error {
switch value.Kind {
case ValueKindNull:
return literal.NULL, nil
buf.Write(literal.NULL)
case ValueKindEnum:
return quotes.WrapBytes(d.EnumValueNameBytes(value.Ref)), nil
buf.Write(quotes.WrapBytes(d.EnumValueNameBytes(value.Ref)))
case ValueKindInteger:
intValueBytes := d.IntValueRaw(value.Ref)
if d.IntValueIsNegative(value.Ref) {
return append(literal.SUB, intValueBytes...), nil
buf.WriteByte(literal.SUB_BYTE)
}
return intValueBytes, nil
buf.Write(intValueBytes)
case ValueKindFloat:
floatValueBytes := d.FloatValueRaw(value.Ref)
if d.FloatValueIsNegative(value.Ref) {
return append(literal.SUB, floatValueBytes...), nil
buf.WriteByte(literal.SUB_BYTE)
}
return floatValueBytes, nil
buf.Write(floatValueBytes)
case ValueKindBoolean:
if value.Ref == 0 {
return literal.FALSE, nil
buf.Write(literal.FALSE)
} else {
buf.Write(literal.TRUE)
}
return literal.TRUE, nil
case ValueKindString:
return quotes.WrapBytes(d.StringValueContentBytes(value.Ref)), nil
buf.Write(quotes.WrapBytes(d.StringValueContentBytes(value.Ref)))
case ValueKindList:
out := []byte("[]")
for _, i := range d.ListValues[value.Ref].Refs {
item, err := d.ValueToJSON(d.Values[i])
if err != nil {
return nil, err
buf.WriteByte(literal.LBRACK_BYTE)
for ii, ref := range d.ListValues[value.Ref].Refs {
if ii > 0 {
buf.WriteByte(literal.COMMA_BYTE)
}
out, err = sjson.SetRawBytes(out, "-1", item)
if err != nil {
return nil, err
if err := d.writeJSONValue(buf, d.Values[ref]); err != nil {
return err
}
}
return out, nil
buf.WriteByte(literal.RBRACK_BYTE)
case ValueKindObject:
out := []byte("{}")
for i := len(d.ObjectValues[value.Ref].Refs) - 1; i >= 0; i-- {
ref := d.ObjectValues[value.Ref].Refs[i]
fieldNameString := d.ObjectFieldNameString(ref)
fieldValueBytes, err := d.ValueToJSON(d.ObjectFieldValue(ref))
if err != nil {
return nil, err
buf.WriteByte(literal.LBRACE_BYTE)
for ii, ref := range d.ObjectValues[value.Ref].Refs {
if ii > 0 {
buf.WriteByte(literal.COMMA_BYTE)
}
out, err = sjson.SetRawBytes(out, fieldNameString, fieldValueBytes)
if err != nil {
return nil, err
fieldNameBytes := d.ObjectFieldNameBytes(ref)
buf.Write(quotes.WrapBytes(fieldNameBytes))
buf.WriteByte(literal.COLON_BYTE)
if err := d.writeJSONValue(buf, d.ObjectFieldValue(ref)); err != nil {
return err
}
}
return out, nil
buf.WriteByte(literal.RBRACE_BYTE)
default:
return nil, fmt.Errorf("ValueToJSON: not implemented for kind: %s", value.Kind.String())
return fmt.Errorf("ValueToJSON: not implemented for kind: %s", value.Kind.String())
}
return nil
}

func (d *Document) ValueToJSON(value Value) ([]byte, error) {
var buf bytes.Buffer
if err := d.writeJSONValue(&buf, value); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

// nolint
Expand Down
104 changes: 65 additions & 39 deletions pkg/astnormalization/inject_input_default_values.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package astnormalization
import (
"errors"
"fmt"

"github.com/buger/jsonparser"

"github.com/TykTechnologies/graphql-go-tools/pkg/ast"
"github.com/TykTechnologies/graphql-go-tools/pkg/astvisitor"
)
Expand Down Expand Up @@ -48,25 +50,32 @@ func (v *inputFieldDefaultInjectionVisitor) EnterVariableDefinition(ref int) {
if v.isScalarTypeOrExtension(typeRef, v.operation) {
return
}
newVal, err := v.processObjectOrListInput(typeRef, variableVal, v.operation)
newVal, replaced, err := v.processObjectOrListInput(typeRef, variableVal, v.operation)
if err != nil {
v.StopWithInternalErr(err)
return
}
newVariables, err := jsonparser.Set(v.operation.Input.Variables, newVal, v.variableName)
if err != nil {
v.StopWithInternalErr(err)
return
if replaced {
newVariables, err := jsonparser.Set(v.operation.Input.Variables, newVal, v.variableName)
if err != nil {
v.StopWithInternalErr(err)
return
}
v.operation.Input.Variables = newVariables
}
v.operation.Input.Variables = newVariables
}

func (v *inputFieldDefaultInjectionVisitor) recursiveInjectInputFields(inputObjectRef int, varValue []byte) ([]byte, error) {
finalVal := varValue
// recursiveInjectInputFields injects default values in input types starting from
// inputObjectRef and walking to its descendants. If no replacements are done it
// returns (varValue, false). If injecting a default value caused varValue to change
// it returns (newValue, true).
func (v *inputFieldDefaultInjectionVisitor) recursiveInjectInputFields(inputObjectRef int, varValue []byte) ([]byte, bool, error) {
objectDef := v.definition.InputObjectTypeDefinitions[inputObjectRef]
if !objectDef.HasInputFieldsDefinition {
return varValue, nil
return varValue, false, nil
}
finalVal := varValue
hasDoneAnyReplacements := false
for _, ref := range objectDef.InputFieldsDefinition.Refs {
valDef := v.definition.InputValueDefinitions[ref]
fieldName := v.definition.InputValueDefinitionNameString(ref)
Expand All @@ -76,7 +85,7 @@ func (v *inputFieldDefaultInjectionVisitor) recursiveInjectInputFields(inputObje
varVal, _, _, err := jsonparser.Get(varValue, fieldName)
if err != nil && err != jsonparser.KeyPathNotFoundError {
v.StopWithInternalErr(err)
return nil, err
return nil, false, err
}
existsInVal := err != jsonparser.KeyPathNotFoundError

Expand All @@ -87,19 +96,22 @@ func (v *inputFieldDefaultInjectionVisitor) recursiveInjectInputFields(inputObje
} else if hasDefault {
defVal, err := v.definition.ValueToJSON(valDef.DefaultValue.Value)
if err != nil {
return nil, err
return nil, false, err
}
valToUse = defVal
} else {
continue
}
fieldValue, err := v.processObjectOrListInput(valDef.Type, valToUse, v.definition)
fieldValue, replaced, err := v.processObjectOrListInput(valDef.Type, valToUse, v.definition)
if err != nil {
return nil, err
return nil, false, err
}
finalVal, err = jsonparser.Set(finalVal, fieldValue, fieldName)
if err != nil {
return nil, err
if (!existsInVal && hasDefault) || replaced {
finalVal, err = jsonparser.Set(finalVal, fieldValue, fieldName)
if err != nil {
return nil, false, err
}
hasDoneAnyReplacements = true
}
continue
}
Expand All @@ -112,15 +124,16 @@ func (v *inputFieldDefaultInjectionVisitor) recursiveInjectInputFields(inputObje
}
defVal, err := v.definition.ValueToJSON(valDef.DefaultValue.Value)
if err != nil {
return nil, err
return nil, false, err
}

finalVal, err = jsonparser.Set(finalVal, defVal, fieldName)
if err != nil {
return nil, err
return nil, false, err
}
hasDoneAnyReplacements = true
}
return finalVal, nil
return finalVal, hasDoneAnyReplacements, nil
}

func (v *inputFieldDefaultInjectionVisitor) isScalarTypeOrExtension(typeRef int, typeDoc *ast.Document) bool {
Expand All @@ -139,63 +152,76 @@ func (v *inputFieldDefaultInjectionVisitor) isScalarTypeOrExtension(typeRef int,
return false
}

func (v *inputFieldDefaultInjectionVisitor) processObjectOrListInput(fieldType int, defaultValue []byte, typeDoc *ast.Document) ([]byte, error) {
finalVal := defaultValue
// processObjectOrListInput walks over an input object or list, assigning default values
// from the schema if necessary. If there are no changes to be made it (defaultValue, false),
// and if any value is replaced by its default in the schema it returns (newValue, true).
func (v *inputFieldDefaultInjectionVisitor) processObjectOrListInput(fieldType int, defaultValue []byte, typeDoc *ast.Document) ([]byte, bool, error) {
fieldIsList := typeDoc.TypeIsList(fieldType)
varVal, valType, _, err := jsonparser.Get(defaultValue)
if err != nil {
return nil, err
return nil, false, err

}
node, found := v.definition.Index.FirstNodeByNameBytes(typeDoc.ResolveTypeNameBytes(fieldType))
if !found {
return finalVal, nil
return defaultValue, false, nil
}
if node.Kind == ast.NodeKindScalarTypeDefinition {
return finalVal, nil
return defaultValue, false, nil
}
finalVal := defaultValue
replaced := false
valIsList := valType == jsonparser.Array
if fieldIsList && valIsList {
_, err := jsonparser.ArrayEach(varVal, v.jsonWalker(typeDoc.ResolveListOrNameType(fieldType), defaultValue, &node, typeDoc, &finalVal))
_, err := jsonparser.ArrayEach(varVal, v.jsonWalker(typeDoc.ResolveListOrNameType(fieldType), defaultValue, &node, typeDoc, &finalVal, &replaced))
if err != nil {
return nil, err
return nil, false, err

}
} else if !fieldIsList && !valIsList {
finalVal, err = v.recursiveInjectInputFields(node.Ref, defaultValue)
finalVal, replaced, err = v.recursiveInjectInputFields(node.Ref, defaultValue)
if err != nil {
return nil, err
return nil, false, err
}
} else {
return nil, errors.New("mismatched input value")
return nil, false, errors.New("mismatched input value")
}
return finalVal, nil
return finalVal, replaced, nil
}

func (v *inputFieldDefaultInjectionVisitor) jsonWalker(fieldType int, defaultValue []byte, node *ast.Node, typeDoc *ast.Document, finalVal *[]byte) func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
// jsonWalker returns a function for visiting an array using jsonparser.ArrayEach that recursively applies
// default values from the schema if necessary, using v.processObjectOrListInput() and v.recursiveInjectInputFields().
// If any changes are made, it returns (newValue, true), otherwise it returns (defaultValue, false).
func (v *inputFieldDefaultInjectionVisitor) jsonWalker(fieldType int, defaultValue []byte, node *ast.Node, typeDoc *ast.Document, finalVal *[]byte, finalValueReplaced *bool) func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
i := 0
listOfList := typeDoc.TypeIsList(typeDoc.Types[fieldType].OfType)
return func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
if err != nil {
return
}
if listOfList && dataType == jsonparser.Array {
newVal, err := v.processObjectOrListInput(typeDoc.Types[fieldType].OfType, value, typeDoc)
newVal, replaced, err := v.processObjectOrListInput(typeDoc.Types[fieldType].OfType, value, typeDoc)
if err != nil {
return
}
*finalVal, err = jsonparser.Set(defaultValue, newVal, fmt.Sprintf("[%d]", i))
if err != nil {
return
if replaced {
*finalVal, err = jsonparser.Set(defaultValue, newVal, fmt.Sprintf("[%d]", i))
if err != nil {
return
}
*finalValueReplaced = true
}
} else if !listOfList && dataType == jsonparser.Object {
newVal, err := v.recursiveInjectInputFields(node.Ref, value)
newVal, replaced, err := v.recursiveInjectInputFields(node.Ref, value)
if err != nil {
return
}
*finalVal, err = jsonparser.Set(defaultValue, newVal, fmt.Sprintf("[%d]", i))
if err != nil {
return
if replaced {
*finalVal, err = jsonparser.Set(defaultValue, newVal, fmt.Sprintf("[%d]", i))
if err != nil {
return
}
*finalValueReplaced = true
}
} else {
return
Expand Down
Loading

0 comments on commit 8693715

Please sign in to comment.