forked from wundergraph/graphql-go-tools
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
introduce CustomExecutionEngineV2 (#346)
Changes in this PR are based on @buraksezer 's awesome research in this commit: 23f7686 This PR introduces interfaces for a `CustomExecutionEngineV2` including execution stages. By introducing those changes it will be possible to provide its own `ExecutionEngineV2` or decorate the already implemented `ExecutionEngineV2`. Changes: - added a benchmark test for `ExecutionEngineV2.Execute` to track performance impacts with this change (none so far) - introduced the following interfaces: - `CustomExecutionEngineV2NormalizerStage` (optional) - `CustomExecutionEngineV2ValidatorStage` (optional) - `CustomExecutionEngineV2ResolverStage` (required) - `ExecutionEngineV2Executor` - added a default implementation for `ExecutionEngineV2Executor` called `CustomExecutionEngineV2Executor` - `ExecutionEngineV2` now implements all interfaces mentioned before and decorates the `CustomExecutionEngineV2Executor` (so it is basically a `CustomExecutionEngineV2`) - moved the `internalExecutionContext` to `CustomExecutionEngineV2` as it is an implementation detail - `ExecutionOptionsV2` are now decoupled from `internalExecutionContext` so that implementing them outside of the `graphql` package is now possible
- Loading branch information
Showing
4 changed files
with
473 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
package graphql | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"sync" | ||
|
||
"github.com/TykTechnologies/graphql-go-tools/pkg/engine/plan" | ||
"github.com/TykTechnologies/graphql-go-tools/pkg/engine/resolve" | ||
"github.com/TykTechnologies/graphql-go-tools/pkg/operationreport" | ||
"github.com/TykTechnologies/graphql-go-tools/pkg/postprocess" | ||
) | ||
|
||
var ( | ||
ErrRequiredStagesMissing = errors.New("required stages for custom execution engine v2 are missing") | ||
) | ||
|
||
type CustomExecutionEngineV2NormalizerStage interface { | ||
Normalize(operation *Request) error | ||
} | ||
|
||
type CustomExecutionEngineV2ValidatorStage interface { | ||
ValidateForSchema(operation *Request) error | ||
} | ||
|
||
type CustomExecutionEngineV2ResolverStage interface { | ||
Setup(ctx context.Context, postProcessor *postprocess.Processor, resolveContext *resolve.Context, operation *Request, options ...ExecutionOptionsV2) | ||
Plan(postProcessor *postprocess.Processor, operation *Request, report *operationreport.Report) (plan.Plan, error) | ||
Resolve(resolveContext *resolve.Context, planResult plan.Plan, writer resolve.FlushWriter) error | ||
Teardown() | ||
} | ||
|
||
type CustomExecutionEngineV2 interface { | ||
CustomExecutionEngineV2NormalizerStage | ||
CustomExecutionEngineV2ValidatorStage | ||
CustomExecutionEngineV2ResolverStage | ||
} | ||
|
||
type ExecutionEngineV2Executor interface { | ||
Execute(ctx context.Context, operation *Request, writer resolve.FlushWriter, options ...ExecutionOptionsV2) error | ||
} | ||
|
||
type CustomExecutionEngineV2Stages struct { | ||
RequiredStages CustomExecutionEngineV2RequiredStages | ||
OptionalStages *CustomExecutionEngineV2OptionalStages | ||
} | ||
|
||
func (c *CustomExecutionEngineV2Stages) AllRequiredStagesProvided() bool { | ||
return c.RequiredStages.ResolverStage != nil | ||
} | ||
|
||
type CustomExecutionEngineV2RequiredStages struct { | ||
ResolverStage CustomExecutionEngineV2ResolverStage | ||
} | ||
|
||
type CustomExecutionEngineV2OptionalStages struct { | ||
NormalizerStage CustomExecutionEngineV2NormalizerStage | ||
ValidatorStage CustomExecutionEngineV2ValidatorStage | ||
} | ||
|
||
type CustomExecutionEngineV2Executor struct { | ||
ExecutionStages CustomExecutionEngineV2Stages | ||
internalExecutionContextPool sync.Pool | ||
} | ||
|
||
func NewCustomExecutionEngineV2Executor(executionEngineV2 CustomExecutionEngineV2) (*CustomExecutionEngineV2Executor, error) { | ||
executionStages := CustomExecutionEngineV2Stages{ | ||
RequiredStages: CustomExecutionEngineV2RequiredStages{ | ||
ResolverStage: executionEngineV2, | ||
}, | ||
OptionalStages: &CustomExecutionEngineV2OptionalStages{ | ||
NormalizerStage: executionEngineV2, | ||
ValidatorStage: executionEngineV2, | ||
}, | ||
} | ||
|
||
return NewCustomExecutionEngineV2ExecutorByStages(executionStages) | ||
} | ||
|
||
func NewCustomExecutionEngineV2ExecutorByStages(executionStages CustomExecutionEngineV2Stages) (*CustomExecutionEngineV2Executor, error) { | ||
return &CustomExecutionEngineV2Executor{ | ||
ExecutionStages: executionStages, | ||
internalExecutionContextPool: sync.Pool{ | ||
New: func() interface{} { | ||
return newInternalExecutionContext() | ||
}, | ||
}, | ||
}, nil | ||
} | ||
|
||
func (c *CustomExecutionEngineV2Executor) getExecutionCtx() *internalExecutionContext { | ||
return c.internalExecutionContextPool.Get().(*internalExecutionContext) | ||
} | ||
|
||
func (c *CustomExecutionEngineV2Executor) putExecutionCtx(ctx *internalExecutionContext) { | ||
ctx.reset() | ||
c.internalExecutionContextPool.Put(ctx) | ||
} | ||
|
||
func (c *CustomExecutionEngineV2Executor) Execute(ctx context.Context, operation *Request, writer resolve.FlushWriter, options ...ExecutionOptionsV2) error { | ||
if !c.ExecutionStages.AllRequiredStagesProvided() { | ||
return ErrRequiredStagesMissing | ||
} | ||
|
||
var err error | ||
if c.ExecutionStages.OptionalStages != nil && c.ExecutionStages.OptionalStages.NormalizerStage != nil { | ||
err = c.ExecutionStages.OptionalStages.NormalizerStage.Normalize(operation) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
if c.ExecutionStages.OptionalStages != nil && c.ExecutionStages.OptionalStages.ValidatorStage != nil { | ||
err = c.ExecutionStages.OptionalStages.ValidatorStage.ValidateForSchema(operation) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
execContext := c.getExecutionCtx() | ||
defer c.putExecutionCtx(execContext) | ||
execContext.prepare(ctx, operation.Variables, operation.request) | ||
c.ExecutionStages.RequiredStages.ResolverStage.Setup(ctx, execContext.postProcessor, execContext.resolveContext, operation, options...) | ||
|
||
var report operationreport.Report | ||
planResult, err := c.ExecutionStages.RequiredStages.ResolverStage.Plan(execContext.postProcessor, operation, &report) | ||
if err != nil { | ||
return err | ||
} else if report.HasErrors() { | ||
return report | ||
} | ||
|
||
err = c.ExecutionStages.RequiredStages.ResolverStage.Resolve(execContext.resolveContext, planResult, writer) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
c.ExecutionStages.RequiredStages.ResolverStage.Teardown() | ||
return nil | ||
} | ||
|
||
// Interface Guards | ||
var ( | ||
_ ExecutionEngineV2Executor = (*CustomExecutionEngineV2Executor)(nil) | ||
) |
Oops, something went wrong.