Skip to content

Commit

Permalink
feat: Add transformation option to the field configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
bakjos committed Aug 1, 2023
1 parent f71b0c4 commit c02090c
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 36 deletions.
129 changes: 115 additions & 14 deletions pkg/engine/plan/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import (
"regexp"
"strings"

"github.com/jensneuse/pipeline/pkg/pipe"
"github.com/jensneuse/pipeline/pkg/step"

"github.com/wundergraph/graphql-go-tools/pkg/ast"
"github.com/wundergraph/graphql-go-tools/pkg/astimport"
"github.com/wundergraph/graphql-go-tools/pkg/astvisitor"
Expand Down Expand Up @@ -122,6 +125,8 @@ type FieldConfiguration struct {
// e.g. {"response":"{\"foo\":\"bar\"}"} will be returned as {"foo":"bar"} when path is "response"
// This way, it is possible to resolve a JSON string as part of the response without extra String encoding of the JSON
UnescapeResponseJson bool
// A Template representing the JSON Transformation
Transformation string
}

type ArgumentsConfigurations []ArgumentConfiguration
Expand Down Expand Up @@ -568,12 +573,13 @@ func (v *Visitor) EnterField(ref int) {
}

path := v.resolveFieldPath(ref)
transformation := v.resolveTransformation(ref)
fieldDefinitionType := v.Definition.FieldDefinitionType(fieldDefinition)
bufferID, hasBuffer := v.fieldBuffers[ref]

v.currentField = &resolve.Field{
Name: fieldAliasOrName,
Value: v.resolveFieldValue(ref, fieldDefinitionType, true, path),
Value: v.resolveFieldValue(ref, fieldDefinitionType, true, path, transformation),
HasBuffer: hasBuffer,
BufferID: bufferID,
OnTypeNames: v.resolveOnTypeNames(),
Expand Down Expand Up @@ -708,7 +714,7 @@ func (v *Visitor) skipField(ref int) bool {
return false
}

func (v *Visitor) resolveFieldValue(fieldRef, typeRef int, nullable bool, path []string) resolve.Node {
func (v *Visitor) resolveFieldValue(fieldRef, typeRef int, nullable bool, path []string, transformation *pipe.Pipeline) resolve.Node {
ofType := v.Definition.Types[typeRef].OfType

fieldName := v.Operation.FieldNameString(fieldRef)
Expand All @@ -721,14 +727,21 @@ func (v *Visitor) resolveFieldValue(fieldRef, typeRef int, nullable bool, path [

switch v.Definition.Types[typeRef].TypeKind {
case ast.TypeKindNonNull:
return v.resolveFieldValue(fieldRef, ofType, false, path)
return v.resolveFieldValue(fieldRef, ofType, false, path, transformation)
case ast.TypeKindList:
listItem := v.resolveFieldValue(fieldRef, ofType, true, nil)
return &resolve.Array{
listItem := v.resolveFieldValue(fieldRef, ofType, true, nil, transformation)
array := &resolve.Array{
Nullable: nullable,
Path: path,
Item: listItem,
}
if transformation != nil {
return &resolve.Transformation{
InnerValue: array,
Pipeline: transformation,
}
}
return array
case ast.TypeKindNamed:
typeName := v.Definition.ResolveTypeNameString(typeRef)
customResolve, ok := v.Config.CustomResolveMap[typeName]
Expand All @@ -747,50 +760,99 @@ func (v *Visitor) resolveFieldValue(fieldRef, typeRef int, nullable bool, path [
fieldExport := v.resolveFieldExport(fieldRef)
switch typeName {
case "String":
return &resolve.String{
value := &resolve.String{
Path: path,
Nullable: nullable,
Export: fieldExport,
UnescapeResponseJson: unescapeResponseJson,
}
if transformation != nil {
return &resolve.Transformation{
InnerValue: value,
Pipeline: transformation,
}
}
return value
case "Boolean":
return &resolve.Boolean{
value := &resolve.Boolean{
Path: path,
Nullable: nullable,
Export: fieldExport,
}
if transformation != nil {
return &resolve.Transformation{
InnerValue: value,
Pipeline: transformation,
}
}
return value
case "Int":
return &resolve.Integer{
value := &resolve.Integer{
Path: path,
Nullable: nullable,
Export: fieldExport,
}
if transformation != nil {
return &resolve.Transformation{
InnerValue: value,
Pipeline: transformation,
}
}
return value
case "Float":
return &resolve.Float{
value := &resolve.Float{
Path: path,
Nullable: nullable,
Export: fieldExport,
}
if transformation != nil {
return &resolve.Transformation{
InnerValue: value,
Pipeline: transformation,
}
}
return value
case "BigInt":
return &resolve.BigInt{
value := &resolve.BigInt{
Path: path,
Nullable: nullable,
Export: fieldExport,
}
if transformation != nil {
return &resolve.Transformation{
InnerValue: value,
Pipeline: transformation,
}
}
return value
default:
return &resolve.String{
value := &resolve.String{
Path: path,
Nullable: nullable,
Export: fieldExport,
UnescapeResponseJson: unescapeResponseJson,
}
if transformation != nil {
return &resolve.Transformation{
InnerValue: value,
Pipeline: transformation,
}
}
return value
}
case ast.NodeKindEnumTypeDefinition:
return &resolve.String{
value := &resolve.String{
Path: path,
Nullable: nullable,
UnescapeResponseJson: unescapeResponseJson,
}
if transformation != nil {
return &resolve.Transformation{
InnerValue: value,
Pipeline: transformation,
}
}
return value
case ast.NodeKindObjectTypeDefinition, ast.NodeKindInterfaceTypeDefinition, ast.NodeKindUnionTypeDefinition:
object := &resolve.Object{
Nullable: nullable,
Expand All @@ -805,12 +867,32 @@ func (v *Visitor) resolveFieldValue(fieldRef, typeRef int, nullable bool, path [
fields: &object.Fields,
})
})
if transformation != nil {
return &resolve.Transformation{
InnerValue: object,
Pipeline: transformation,
}
}
return object
default:
return &resolve.Null{}
value := &resolve.Null{}
if transformation != nil {
return &resolve.Transformation{
InnerValue: value,
Pipeline: transformation,
}
}
return value
}
default:
return &resolve.Null{}
value := &resolve.Null{}
if transformation != nil {
return &resolve.Transformation{
InnerValue: value,
Pipeline: transformation,
}
}
return value
}
}

Expand Down Expand Up @@ -975,6 +1057,25 @@ func (v *Visitor) resolveFieldPath(ref int) []string {
return []string{fieldName}
}

func (v *Visitor) resolveTransformation(ref int) *pipe.Pipeline {
typeName := v.Walker.EnclosingTypeDefinition.NameString(v.Definition)
fieldName := v.Operation.FieldNameUnsafeString(ref)

for i := range v.Config.Fields {
if v.Config.Fields[i].TypeName == typeName && v.Config.Fields[i].FieldName == fieldName {
if len(v.Config.Fields[i].Transformation) > 0 {
step, err := step.NewJSON(v.Config.Fields[i].Transformation)
if err == nil {
return &pipe.Pipeline{Steps: []pipe.Step{step}}
}

}
return nil
}
}
return nil
}

func (v *Visitor) EnterDocument(operation, definition *ast.Document) {
v.Operation, v.Definition = operation, definition
v.fieldConfigs = map[int]*FieldConfiguration{}
Expand Down
Loading

0 comments on commit c02090c

Please sign in to comment.