Skip to content

Commit

Permalink
feat: rewrite variable renderer to use astjson (#946)
Browse files Browse the repository at this point in the history
  • Loading branch information
jensneuse authored Nov 5, 2024
1 parent 31ff391 commit 0d2d642
Show file tree
Hide file tree
Showing 23 changed files with 5,482 additions and 9,741 deletions.
5 changes: 4 additions & 1 deletion execution/engine/execution_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

lru "github.com/hashicorp/golang-lru"
"github.com/jensneuse/abstractlogger"
"github.com/wundergraph/astjson"

"github.com/wundergraph/graphql-go-tools/execution/graphql"
"github.com/wundergraph/graphql-go-tools/v2/pkg/ast"
Expand Down Expand Up @@ -51,7 +52,9 @@ func (e *internalExecutionContext) setContext(ctx context.Context) {
}

func (e *internalExecutionContext) setVariables(variables []byte) {
e.resolveContext.Variables = variables
if len(variables) != 0 {
e.resolveContext.Variables = astjson.MustParseBytes(variables)
}
}

func (e *internalExecutionContext) reset() {
Expand Down
70 changes: 0 additions & 70 deletions execution/engine/execution_engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -750,76 +750,6 @@ func TestExecutionEngine_Execute(t *testing.T) {
expectedResponse: `{"data":{"heroes":["Human","Droid"]}}`,
}))

t.Run("execute operation with null and omitted input variables", runWithoutError(ExecutionEngineTestCase{
schema: func(t *testing.T) *graphql.Schema {
t.Helper()
schema := `
type Query {
heroes(names: [String!], height: String): [String!]
}`
parseSchema, err := graphql.NewSchemaFromString(schema)
require.NoError(t, err)
return parseSchema
}(t),
operation: func(t *testing.T) graphql.Request {
return graphql.Request{
OperationName: "MyHeroes",
Variables: []byte(`{"height": null}`),
Query: `query MyHeroes($heroNames: [String!], $height: String){
heroes(names: $heroNames, height: $height)
}`,
}
},
dataSources: []plan.DataSource{
mustGraphqlDataSourceConfiguration(t,
"id",
mustFactory(t,
testNetHttpClient(t, roundTripperTestCase{
expectedHost: "example.com",
expectedPath: "/",
expectedBody: `{"query":"query($heroNames: [String!], $height: String){heroes(names: $heroNames, height: $height)}","variables":{"height":null}}`,
sendResponseBody: `{"data":{"heroes":[]}}`,
sendStatusCode: 200,
}),
),
&plan.DataSourceMetadata{
RootNodes: []plan.TypeField{
{TypeName: "Query", FieldNames: []string{"heroes"}},
},
},
mustConfiguration(t, graphql_datasource.ConfigurationInput{
Fetch: &graphql_datasource.FetchConfiguration{
URL: "https://example.com/",
Method: "POST",
},
SchemaConfiguration: mustSchemaConfig(
t,
nil,
`type Query { heroes(names: [String!], height: String): [String!] }`,
),
}),
),
},
fields: []plan.FieldConfiguration{
{
TypeName: "Query",
FieldName: "heroes",
Path: []string{"heroes"},
Arguments: []plan.ArgumentConfiguration{
{
Name: "names",
SourceType: plan.FieldArgumentSource,
},
{
Name: "height",
SourceType: plan.FieldArgumentSource,
},
},
},
},
expectedResponse: `{"data":{"heroes":[]}}`,
}))

t.Run("execute operation with null variable on required type", runWithAndCompareError(ExecutionEngineTestCase{
schema: func(t *testing.T) *graphql.Schema {
t.Helper()
Expand Down
2 changes: 1 addition & 1 deletion v2/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ require (
github.com/tidwall/gjson v1.17.0
github.com/tidwall/sjson v1.2.5
github.com/vektah/gqlparser/v2 v2.5.11
github.com/wundergraph/astjson v0.0.0-20240910140849-bb15f94bd362
github.com/wundergraph/astjson v0.0.0-20241029194815-849566801950
go.uber.org/atomic v1.11.0
go.uber.org/zap v1.26.0
golang.org/x/sync v0.7.0
Expand Down
2 changes: 2 additions & 0 deletions v2/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ github.com/vektah/gqlparser/v2 v2.5.11 h1:JJxLtXIoN7+3x6MBdtIP59TP1RANnY7pXOaDnA
github.com/vektah/gqlparser/v2 v2.5.11/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc=
github.com/wundergraph/astjson v0.0.0-20240910140849-bb15f94bd362 h1:MxNSJqQFJyhKwU4xPj6diIRLm+oY1wNbAZW0jJpikBE=
github.com/wundergraph/astjson v0.0.0-20240910140849-bb15f94bd362/go.mod h1:eOTL6acwctsN4F3b7YE+eE2t8zcJ/doLm9sZzsxxxrE=
github.com/wundergraph/astjson v0.0.0-20241029194815-849566801950 h1:NZOeWkezsy5F5OKFhvsrdNq1XzUke/WHhI/9elBjivI=
github.com/wundergraph/astjson v0.0.0-20241029194815-849566801950/go.mod h1:eOTL6acwctsN4F3b7YE+eE2t8zcJ/doLm9sZzsxxxrE=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
Expand Down
41 changes: 2 additions & 39 deletions v2/pkg/engine/datasource/graphql_datasource/graphql_datasource.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ import (
"github.com/wundergraph/graphql-go-tools/v2/pkg/operationreport"
)

const removeNullVariablesDirectiveName = "removeNullVariables"

var (
DefaultPostProcessingConfiguration = resolve.PostProcessingConfiguration{
SelectResponseDataPath: []string{"data"},
Expand Down Expand Up @@ -74,7 +72,6 @@ type Planner[T Configuration] struct {
addDirectivesToVariableDefinitions map[int][]int
insideCustomScalarField bool
customScalarFieldRef int
unnulVariables bool
parentTypeNodes []ast.Node

// federation
Expand Down Expand Up @@ -296,10 +293,6 @@ func (p *Planner[T]) ConfigureFetch() resolve.FetchConfiguration {
input = httpclient.SetInputBodyWithPath(input, p.upstreamVariables, "variables")
input = httpclient.SetInputBodyWithPath(input, p.printOperation(), "query")

if p.unnulVariables {
input = httpclient.SetInputFlag(input, httpclient.UNNULL_VARIABLES)
}

header, err := json.Marshal(p.config.fetch.Header)
if err != nil {
p.stopWithError(errors.WithStack(fmt.Errorf("ConfigureFetch: failed to marshal header: %w", err)))
Expand Down Expand Up @@ -406,12 +399,6 @@ func (p *Planner[T]) ConfigureSubscription() plan.SubscriptionConfiguration {
}

func (p *Planner[T]) EnterOperationDefinition(ref int) {
if p.visitor.Operation.OperationDefinitions[ref].HasDirectives &&
p.visitor.Operation.OperationDefinitions[ref].Directives.HasDirectiveByName(p.visitor.Operation, removeNullVariablesDirectiveName) {
p.unnulVariables = true
p.visitor.Operation.OperationDefinitions[ref].Directives.RemoveDirectiveByName(p.visitor.Operation, removeNullVariablesDirectiveName)
}

operationType := p.visitor.Operation.OperationDefinitions[ref].OperationType
if p.dataSourcePlannerConfig.IsNested {
operationType = ast.OperationTypeQuery
Expand Down Expand Up @@ -1643,30 +1630,6 @@ type Source struct {
httpClient *http.Client
}

func (s *Source) compactAndUnNullVariables(input []byte) []byte {
undefinedVariables := httpclient.UndefinedVariables(input)
variables, _, _, err := jsonparser.Get(input, "body", "variables")
if err != nil {
return input
}
if bytes.Equal(variables, []byte("null")) || bytes.Equal(variables, []byte("{}")) {
return input
}
if bytes.ContainsAny(variables, " \t\n\r") {
buf := bytes.NewBuffer(make([]byte, 0, len(variables)))
if err := json.Compact(buf, variables); err != nil {
panic(fmt.Errorf("compacting variables: %w", err))
}
variables = buf.Bytes()
}

removeNullVariables := httpclient.IsInputFlagSet(input, httpclient.UNNULL_VARIABLES)
variables = s.cleanupVariables(variables, removeNullVariables, undefinedVariables)

input, _ = jsonparser.Set(input, variables, "body", "variables")
return input
}

// cleanupVariables removes null variables and empty objects from the input if removeNullVariables is true
// otherwise returns the input as is
func (s *Source) cleanupVariables(variables []byte, removeNullVariables bool, undefinedVariables []string) []byte {
Expand Down Expand Up @@ -1726,12 +1689,12 @@ func (s *Source) replaceEmptyObject(variables []byte) ([]byte, bool) {
}

func (s *Source) LoadWithFiles(ctx context.Context, input []byte, files []httpclient.File, out *bytes.Buffer) (err error) {
input = s.compactAndUnNullVariables(input)
//input = s.compactAndUnNullVariables(input)
return httpclient.DoMultipartForm(s.httpClient, ctx, input, files, out)
}

func (s *Source) Load(ctx context.Context, input []byte, out *bytes.Buffer) (err error) {
input = s.compactAndUnNullVariables(input)
//input = s.compactAndUnNullVariables(input)
return httpclient.Do(s.httpClient, ctx, input, out)
}

Expand Down
Loading

0 comments on commit 0d2d642

Please sign in to comment.