Skip to content

Commit

Permalink
Merge pull request #30 from jensneuse/add-context-based-middleware
Browse files Browse the repository at this point in the history
Add context based middleware
  • Loading branch information
jensneuse authored Mar 18, 2019
2 parents 36ac846 + edc6268 commit 3b6a6aa
Show file tree
Hide file tree
Showing 93 changed files with 7,332 additions and 4,891 deletions.
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ Currently implemented:
- validation
- schema formatting

## Docs

https://jens-neuse.gitbook.io/graphql-go-tools

## WIP

- (remote) schema introspection
Expand Down Expand Up @@ -72,15 +76,12 @@ By fixing this issue validation time for the introspection query dropped from ~

## Contributors

This repository was initially developed and maintained by one single person:
[Jens Neuse][jens-neuse-github].

These users are actively maintaining and/or developing as of today:

- [Jens Neuse][jens-neuse-github] (Project Lead)
- [Jens Neuse][jens-neuse-github] (Project Lead & Active Maintainer)
- [Mantas Vidutis][mantas-vidutis-github](Contributions to the http proxy & the Context Middleware)
- [Jonas Bergner][jonas-bergner-github] (Contributions to the initial version of the parser, contributions to the tests)

[jens-neuse-github]: https://github.com/jensneuse
[mantas-vidutis-github]: https://github.com/mvid
[jonas-bergner-github]: https://github.com/java-jonas

## Contributions
Expand Down
5 changes: 5 additions & 0 deletions hack/hack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package hack

// package hack contains not yet finished parts of the library
// the contents should still be part of the main codebase to keep the discussion going
// but we'll keep it separated from the main codebase
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package example
package middleware

import (
"bytes"
"context"
"fmt"
"github.com/jensneuse/graphql-go-tools/pkg/document"
"github.com/jensneuse/graphql-go-tools/pkg/lexing/runes"
Expand All @@ -16,7 +17,10 @@ import (
type AssetUrlMiddleware struct {
}

func (a *AssetUrlMiddleware) OnRequest(l *lookup.Lookup, w *lookup.Walker, parser *parser.Parser, mod *parser.ManualAstMod) {
func (a *AssetUrlMiddleware) OnRequest(context context.Context, l *lookup.Lookup, w *lookup.Walker, parser *parser.Parser, mod *parser.ManualAstMod) error {

w.SetLookup(l)
w.WalkExecutable()

// get the required names (int)
// if they don't exist in the type system definition we'd receive '-1' which indicates the literal doesn't exist
Expand All @@ -26,7 +30,7 @@ func (a *AssetUrlMiddleware) OnRequest(l *lookup.Lookup, w *lookup.Walker, parse

// handle gracefully/error logging
if assetName == -1 || urlName == -1 || handleName == -1 {
return
return nil
}

field := document.Field{
Expand Down Expand Up @@ -65,10 +69,10 @@ func (a *AssetUrlMiddleware) OnRequest(l *lookup.Lookup, w *lookup.Walker, parse
mod.AppendFieldToSelectionSet(handleFieldRef, setRef) // append the handler field
}

return
return nil
}

func (a *AssetUrlMiddleware) OnResponse(response *[]byte, l *lookup.Lookup, w *lookup.Walker, parser *parser.Parser, mod *parser.ManualAstMod) (err error) {
func (a *AssetUrlMiddleware) OnResponse(context context.Context, response *[]byte, l *lookup.Lookup, w *lookup.Walker, parser *parser.Parser, mod *parser.ManualAstMod) (err error) {

w.SetLookup(l)
w.WalkExecutable()
Expand Down
76 changes: 76 additions & 0 deletions hack/middleware/asset_middleware_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package middleware

import (
"context"
"fmt"
"github.com/jensneuse/graphql-go-tools/pkg/middleware"
"github.com/jensneuse/graphql-go-tools/pkg/testhelper"
"testing"
)

func TestAssetMiddleware(t *testing.T) {

run := func(schema, queryBefore, queryAfter string) {

got := middleware.InvokeMiddleware(&AssetUrlMiddleware{}, context.Background(), schema, queryBefore)
want := testhelper.UglifyRequestString(queryAfter)

if want != got {
panic(fmt.Errorf("want:\n%s\ngot:\n%s\n", want, got))
}
}

t.Run("asset handle transform", func(t *testing.T) {
run(assetSchema,
`query testQueryWithoutHandle {
assets(first: 1) {
id
fileName
url(transformation: {image: {resize: {width: 100, height: 100}}})
}
}`,
` query testQueryWithoutHandle {
assets(first: 1) {
id
fileName
handle
}
}`)
})
t.Run("asset handle transform on empty selectionSet", func(t *testing.T) {
run(assetSchema,
`query testQueryWithoutHandle {
assets(first: 1) {
}
}`,
` query testQueryWithoutHandle {
assets(first: 1) {
handle
}
}`)
})

}

var assetSchema = `
schema {
query: Query
}
type Query {
assets(first: Int): [Asset]
}
type Asset implements Node @RequestMiddleware {
status: Status!
updatedAt: DateTime!
createdAt: DateTime!
id: ID!
handle: String!
fileName: String!
height: Float
width: Float
size: Float
mimeType: String
url: String!
}`
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package stage2

import (
"bytes"
"context"
"fmt"
middleware2 "github.com/jensneuse/graphql-go-tools/hack/middleware"
"github.com/jensneuse/graphql-go-tools/pkg/lookup"
"github.com/jensneuse/graphql-go-tools/pkg/parser"
"github.com/jensneuse/graphql-go-tools/pkg/printer"
"github.com/jensneuse/graphql-go-tools/pkg/proxy/middleware/example"
"github.com/jensneuse/graphql-go-tools/pkg/validator"
)

Expand Down Expand Up @@ -78,16 +79,16 @@ func (p *Proxy) Request(path string, request []byte) (response []byte, err error
return
}

middleware := example.AssetUrlMiddleware{}
middleware.OnRequest(p.look, p.walk, p.parse, p.mod)
middleware := middleware2.AssetUrlMiddleware{}
middleware.OnRequest(context.Background(), p.look, p.walk, p.parse, p.mod)

p.astPrint.SetInput(p.parse, p.look, p.walk)
p.buff.Reset()
p.astPrint.PrintExecutableSchema(p.buff)

response = prisma.Query(p.buff.Bytes())

err = middleware.OnResponse(&response, p.look, p.walk, p.parse, p.mod)
err = middleware.OnResponse(context.Background(), &response, p.look, p.walk, p.parse, p.mod)
if err != nil {
return
}
Expand Down
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion pkg/document/arguments.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func (a Argument) NodeValueType() ValueType {
}

func (a Argument) NodeValueReference() int {
panic("implement me")
return a.Value
}

func (a Argument) NodeUnionMemberTypes() []int {
Expand Down
13 changes: 1 addition & 12 deletions pkg/document/directivedefinition.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type DirectiveDefinition struct {
Description ByteSliceReference
Name int
ArgumentsDefinition int
DirectiveLocations DirectiveLocations
DirectiveLocations []int
Position position.Position
}

Expand Down Expand Up @@ -140,16 +140,5 @@ func (d DirectiveDefinition) NodeUnionMemberTypes() []int {
panic("implement me")
}

// ContainsLocation returns if the $location is contained
func (d DirectiveDefinition) ContainsLocation(location DirectiveLocation) bool {
for _, dirLoc := range d.DirectiveLocations {
if dirLoc == location {
return true
}
}

return false
}

// DirectiveDefinitions is the plural of DirectiveDefinition
type DirectiveDefinitions []DirectiveDefinition
10 changes: 6 additions & 4 deletions pkg/lexer/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

// Lexer emits tokens from a input reader
type Lexer struct {
_storage [65535]byte
input []byte
inputPosition int
typeSystemEndPosition int
Expand All @@ -34,7 +35,9 @@ func (l *Lexer) SetTypeSystemInput(input []byte) error {
return fmt.Errorf("SetTypeSystemInput: input size must not be > %d, got: %d", uint16Max, len(input))
}

l.input = input
l.input = l._storage[:0]
l.input = append(l.input, input...)

l.inputPosition = 0
l.textPosition.LineStart = 1
l.textPosition.CharStart = 1
Expand All @@ -44,7 +47,7 @@ func (l *Lexer) SetTypeSystemInput(input []byte) error {
}

func (l *Lexer) ResetTypeSystemInput() {
l.input = l.input[:0]
l.input = l._storage[:0]
l.inputPosition = 0
l.textPosition.LineStart = 1
l.textPosition.CharStart = 1
Expand All @@ -65,8 +68,7 @@ func (l *Lexer) AppendBytes(input []byte) (err error) {

func (l *Lexer) SetExecutableInput(input []byte) error {

l.input = l.input[:l.typeSystemEndPosition]
l.input = append(l.input, input...)
l.input = append(l.input[:l.typeSystemEndPosition], input...)

if len(input) > uint16Max {
return fmt.Errorf("SetTypeSystemInput: input size must not be > %d, got: %d", uint16Max, len(input))
Expand Down
73 changes: 73 additions & 0 deletions pkg/lookup/iterable.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,20 @@ func (w *Walker) FragmentDefinitionIterable() FragmentDefinitionIterable {
}
}

type DirectiveDefinitionIterable struct {
Iterable
}

func (d *DirectiveDefinitionIterable) Value() (definition document.DirectiveDefinition, ref int) {
ref = d.ref()
definition = d.w.l.DirectiveDefinition(ref)
return
}

func (w *Walker) DirectiveDefinitionIterable() DirectiveDefinitionIterable {
return DirectiveDefinitionIterable{Iterable: w.newIterable(w.c.directiveDefinitions)}
}

type OperationDefinitionIterable struct {
Iterable
}
Expand Down Expand Up @@ -158,3 +172,62 @@ func (w *Walker) TypeSystemDefinitionOrderedRootNodes() TypeSystemDefinitionOrde
Iterable: w.newIterable(refs),
}
}

type FieldsContainingDirectiveIterator struct {
fieldRefs []int
directives []int
objectTypeDefinitionRefs []int
current int
}

func (f *FieldsContainingDirectiveIterator) Next() bool {
f.current++
return len(f.fieldRefs)-1 >= f.current
}

func (f *FieldsContainingDirectiveIterator) Value() (fieldDefinitionRef, objectTypeDefinitionRef, directiveRef int) {
fieldDefinitionRef = f.fieldRefs[f.current]
objectTypeDefinitionRef = f.objectTypeDefinitionRefs[f.current]
directiveRef = f.directives[f.current]
return
}

func (w *Walker) FieldsContainingDirectiveIterator(directiveNameRef int) FieldsContainingDirectiveIterator {

fields := w.c.fieldsContainingDirectiveFields[:0]
objects := w.c.fieldsContainingDirectiveObjects[:0]
directiveRefs := w.c.fieldsContainingDirectiveDirectives[:0]

sets := w.DirectiveSetIterable()
for sets.Next() {
set, parent := sets.Value()
directives := w.l.DirectiveIterable(set)
for directives.Next() {
directive, directiveRef := directives.Value()
if directive.Name != directiveNameRef {
continue
}

field := w.Node(parent)
if field.Kind != FIELD_DEFINITION {
continue
}

fieldType := w.Node(field.Parent)
if fieldType.Kind != OBJECT_TYPE_DEFINITION {
continue
}

fields = append(fields, field.Ref)
objects = append(objects, fieldType.Ref)
directiveRefs = append(directiveRefs, directiveRef)
}
}

return FieldsContainingDirectiveIterator{
current: -1,
objectTypeDefinitionRefs: objects,
fieldRefs: fields,
directives: directiveRefs,
}
}
Loading

0 comments on commit 3b6a6aa

Please sign in to comment.