Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,7 @@ type ResolveInfo struct {
RootValue interface{}
Operation ast.Definition
VariableValues map[string]interface{}
ArgumentValues map[string]interface{}
}

type Fields map[string]*Field
Expand Down
27 changes: 19 additions & 8 deletions executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,14 @@ type buildExecutionCtxParams struct {
}

type executionContext struct {
Schema Schema
Fragments map[string]ast.Definition
Root interface{}
Operation ast.Definition
VariableValues map[string]interface{}
Errors []gqlerrors.FormattedError
Context context.Context
Schema Schema
Fragments map[string]ast.Definition
Root interface{}
Operation ast.Definition
VariableValues map[string]interface{}
Errors []gqlerrors.FormattedError
Context context.Context
PluginExecRegistry *PluginExecutionRegistry
}

func buildExecutionContext(p buildExecutionCtxParams) (*executionContext, error) {
Expand Down Expand Up @@ -155,6 +156,7 @@ func buildExecutionContext(p buildExecutionCtxParams) (*executionContext, error)
eCtx.Operation = operation
eCtx.VariableValues = variableValues
eCtx.Context = p.Context
eCtx.PluginExecRegistry = NewPluginExecRegistry()
return eCtx, nil
}

Expand Down Expand Up @@ -279,8 +281,13 @@ func executeFields(p executeFieldsParams) *Result {

dethunkMapWithBreadthFirstTraversal(finalResults)

finalModified, errs := p.ExecutionContext.PluginExecRegistry.Execute(p.ExecutionContext.Context, finalResults)
if len(errs) > 0 {
p.ExecutionContext.Errors = append(p.ExecutionContext.Errors, errs...)
}

return &Result{
Data: finalResults,
Data: finalModified,
Errors: p.ExecutionContext.Errors,
}
}
Expand Down Expand Up @@ -637,6 +644,7 @@ func resolveField(eCtx *executionContext, parentType *Object, source interface{}
RootValue: eCtx.Root,
Operation: eCtx.Operation,
VariableValues: eCtx.VariableValues,
ArgumentValues: args,
}

var resolveFnError error
Expand All @@ -663,6 +671,9 @@ func resolveField(eCtx *executionContext, parentType *Object, source interface{}
}

completed := completeValueCatchingError(eCtx, returnType, fieldASTs, info, path, result)

handlePluginsResolveFieldFinished(eCtx, info)

return completed, resultState
}

Expand Down
81 changes: 81 additions & 0 deletions plugins.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package graphql

import (
"context"
"fmt"
"strings"

"github.com/graphql-go/graphql/gqlerrors"
)

// Plugin is an interface for custom post-processing based on the execution context
// all the pre-processing happens in Resolve*()
// plugins differs from extensions by the time of execution and by how they modify the result
// in resolve func plugin analyzes execution context for each field in order to collect information of
// what to process during execution
// execution happens once query resolve is fully finished
type Plugin interface {
// Name returns name of the plugin
Name() string
// IsCompatible tests whether current field is compatible with the plugin
IsCompatible(ctx context.Context, i ResolveInfo) bool
// Execute runs plugin processing on the data accessed by provided json pointer
Execute(ctx context.Context, pointer string, data interface{}, i ResolveInfo) (interface{}, error)
}

func handlePluginsResolveFieldFinished(eCtx *executionContext, info ResolveInfo) {
for _, p := range info.Schema.plugins {
if p.IsCompatible(eCtx.Context, info) {
eCtx.PluginExecRegistry.Register(PluginExecutable{
info: info,
plugin: p,
})
}
}
}

type PluginExecutionRegistry struct {
plugins []PluginExecutable
}

func NewPluginExecRegistry() *PluginExecutionRegistry {
return &PluginExecutionRegistry{
plugins: make([]PluginExecutable, 0),
}
}

type PluginExecutable struct {
info ResolveInfo
plugin Plugin
}

func (pr *PluginExecutionRegistry) Register(pe PluginExecutable) {
pr.plugins = append(pr.plugins, pe)
}

func (pr *PluginExecutionRegistry) Execute(ctx context.Context, data interface{}) (interface{}, []gqlerrors.FormattedError) {
var plgErrs []gqlerrors.FormattedError
var err error
for _, p := range pr.plugins {
elPath := constructPointer(p.info.Path.AsArray())
data, err = p.plugin.Execute(ctx, elPath, data, p.info)
if err != nil {
plgErrs = append(plgErrs, gqlerrors.FormatError(
fmt.Errorf("%s.PluginExecution: %v", p.plugin.Name(), err)))
}
}

return data, plgErrs
}

func constructPointer(path []interface{}) string {
var buf = strings.Builder{}
buf.WriteString("/")
for i := 0; i < len(path); i++ {
buf.WriteString(fmt.Sprintf("%v", path[i]))
if i != len(path)-1 {
buf.WriteString("/")
}
}
return buf.String()
}
12 changes: 12 additions & 0 deletions schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ type SchemaConfig struct {
Types []Type
Directives []*Directive
Extensions []Extension
Plugins []Plugin
}

type TypeMap map[string]Type
Expand Down Expand Up @@ -41,6 +42,7 @@ type Schema struct {
implementations map[string][]*Object
possibleTypeMap map[string]map[string]bool
extensions []Extension
plugins []Plugin
}

func NewSchema(config SchemaConfig) (Schema, error) {
Expand Down Expand Up @@ -142,6 +144,11 @@ func NewSchema(config SchemaConfig) (Schema, error) {
schema.extensions = config.Extensions
}

// Add plugins from config
if len(config.Plugins) != 0 {
schema.plugins = config.Plugins
}

return schema, nil
}

Expand Down Expand Up @@ -267,6 +274,11 @@ func (gq *Schema) AddExtensions(e ...Extension) {
gq.extensions = append(gq.extensions, e...)
}

// AddPlugins can be used to add additional plugins to the schema
func (gq *Schema) AddPlugins(p ...Plugin) {
gq.plugins = append(gq.plugins, p...)
}

// map-reduce
func typeMapReducer(schema *Schema, typeMap TypeMap, objectType Type) (TypeMap, error) {
var err error
Expand Down