forked from sbstjn/appsync-resolvers
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from jonsaw/middleware-intro
Merge Middleware into Master
- Loading branch information
Showing
9 changed files
with
234 additions
and
32 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package resolvers | ||
|
||
import "fmt" | ||
|
||
type dispatch struct { | ||
repository *Repository | ||
} | ||
|
||
func (d dispatch) Serve(in Invocation) (interface{}, error) { | ||
handler, found := d.repository.resolvers[in.Resolve] | ||
|
||
if found { | ||
return handler.call(in.payload()) | ||
} | ||
|
||
return nil, fmt.Errorf("No resolver found: %s", in.Resolve) | ||
} |
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,15 @@ | ||
package resolvers | ||
|
||
// Handler responds to requests in resolvers | ||
type Handler interface { | ||
Serve(Invocation) (interface{}, error) | ||
} | ||
|
||
// The HandlerFunc type is an adapter to allow the use of | ||
// ordinary functions | ||
type HandlerFunc func(Invocation) (interface{}, error) | ||
|
||
// Serve calls from resolver | ||
func (f HandlerFunc) Serve(in Invocation) (interface{}, error) { | ||
return f(in) | ||
} |
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
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 |
---|---|---|
@@ -1,6 +1,8 @@ | ||
package resolvers | ||
|
||
// New returns a new Repository with a list of resolver | ||
func New() Repository { | ||
return Repository{} | ||
func New() *Repository { | ||
r := &Repository{} | ||
r.buildChain() | ||
return r | ||
} |
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,14 @@ | ||
package resolvers | ||
|
||
// Use appends middleware to repository | ||
func (r *Repository) Use(middleware func(Handler) Handler) { | ||
r.middleware = append(r.middleware, middleware) | ||
r.buildChain() | ||
} | ||
|
||
func (r *Repository) buildChain() { | ||
r.handler = dispatch{repository: r} | ||
for i := len(r.middleware) - 1; i >= 0; i-- { | ||
r.handler = r.middleware[i](r.handler) | ||
} | ||
} |
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,151 @@ | ||
package resolvers | ||
|
||
import ( | ||
"encoding/json" | ||
|
||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
type graphQLError struct { | ||
Type string `json:"error_type"` | ||
Message string `json:"error_message"` | ||
Data interface{} `json:"error_data"` | ||
} | ||
|
||
func (e *graphQLError) Error() string { | ||
return e.Message | ||
} | ||
|
||
func newGraphQLError(t string, m string, d interface{}) *graphQLError { | ||
return &graphQLError{ | ||
Type: t, | ||
Message: m, | ||
Data: d, | ||
} | ||
} | ||
|
||
func sequence(ch chan string, seq ...string) bool { | ||
for _, str := range seq { | ||
if msg := <-ch; msg != str { | ||
return false | ||
} | ||
} | ||
return true | ||
} | ||
|
||
var _ = Describe("Middleware", func() { | ||
type arguments struct { | ||
Bar string `json:"bar"` | ||
} | ||
type response struct { | ||
Foo string | ||
} | ||
|
||
Context("With no hijacking", func() { | ||
ch := make(chan string, 10) | ||
r := New() | ||
r.Add("example.resolver", func(arg arguments) (response, error) { | ||
ch <- "handler" | ||
return response{"bar"}, nil | ||
}) | ||
r.Use(func(h Handler) Handler { | ||
m := func(in Invocation) (interface{}, error) { | ||
ch <- "before 1" | ||
out, err := h.Serve(in) | ||
ch <- "after 1" | ||
return out, err | ||
} | ||
return HandlerFunc(m) | ||
}) | ||
r.Use(func(h Handler) Handler { | ||
m := func(in Invocation) (interface{}, error) { | ||
ch <- "before 2" | ||
out, err := h.Serve(in) | ||
ch <- "after 2" | ||
return out, err | ||
} | ||
return HandlerFunc(m) | ||
}) | ||
res, err := r.Handle(Invocation{ | ||
Resolve: "example.resolver", | ||
Context: ContextData{ | ||
Arguments: json.RawMessage(`{"bar":"foo"}`), | ||
}, | ||
}) | ||
|
||
It("Should not error", func() { | ||
Expect(err).ToNot(HaveOccurred()) | ||
}) | ||
|
||
It("Should have data", func() { | ||
Expect(res.(response).Foo).To(Equal("bar")) | ||
}) | ||
|
||
It("Should be in sequence", func() { | ||
Expect( | ||
sequence(ch, | ||
"before 1", | ||
"before 2", | ||
"handler", | ||
"after 2", | ||
"after 1", | ||
)).To(BeTrue()) | ||
}) | ||
}) | ||
|
||
Context("With custom error middleware", func() { | ||
ch := make(chan string, 10) | ||
r := New() | ||
r.Add("example.resolver", func(arg arguments) (*response, error) { | ||
ch <- "handler" | ||
return nil, newGraphQLError("BAD_REQUEST", "Invalid type", response{"bar"}) | ||
}) | ||
r.Use(func(h Handler) Handler { | ||
m := func(in Invocation) (interface{}, error) { | ||
ch <- "before 1" | ||
out, err := h.Serve(in) | ||
ch <- "after 1" | ||
return out, err | ||
} | ||
return HandlerFunc(m) | ||
}) | ||
r.Use(func(h Handler) Handler { | ||
m := func(in Invocation) (interface{}, error) { | ||
out, err := h.Serve(in) | ||
if err != nil { | ||
if errData, ok := err.(*graphQLError); ok { | ||
return errData, nil | ||
} | ||
} | ||
return out, err | ||
} | ||
return HandlerFunc(m) | ||
}) | ||
res, err := r.Handle(Invocation{ | ||
Resolve: "example.resolver", | ||
Context: ContextData{ | ||
Arguments: json.RawMessage(`{"bar":"foo"}`), | ||
}, | ||
}) | ||
|
||
It("Should not error", func() { | ||
Expect(err).ToNot(HaveOccurred()) | ||
}) | ||
|
||
It("Should have error data", func() { | ||
Expect(res.(*graphQLError).Message).To(Equal("Invalid type")) | ||
Expect(res.(*graphQLError).Type).To(Equal("BAD_REQUEST")) | ||
Expect(res.(*graphQLError).Data.(response).Foo).To(Equal("bar")) | ||
}) | ||
|
||
It("Should be in sequence", func() { | ||
Expect( | ||
sequence(ch, | ||
"before 1", | ||
"handler", | ||
"after 1", | ||
)).To(BeTrue()) | ||
}) | ||
}) | ||
}) |
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 |
---|---|---|
@@ -1,31 +1,32 @@ | ||
package resolvers | ||
|
||
import ( | ||
"fmt" | ||
"reflect" | ||
) | ||
|
||
// Repository stores all resolvers | ||
type Repository map[string]resolver | ||
type Repository struct { | ||
handler Handler | ||
middleware []func(Handler) Handler | ||
resolvers map[string]resolver | ||
} | ||
|
||
// Add stores a new resolver | ||
func (r Repository) Add(resolve string, handler interface{}) error { | ||
func (r *Repository) Add(resolve string, handler interface{}) error { | ||
if r.resolvers == nil { | ||
r.resolvers = map[string]resolver{} | ||
} | ||
|
||
err := validators.run(reflect.TypeOf(handler)) | ||
|
||
if err == nil { | ||
r[resolve] = resolver{handler} | ||
r.resolvers[resolve] = resolver{handler} | ||
} | ||
|
||
return err | ||
} | ||
|
||
// Handle responds to the AppSync request | ||
func (r Repository) Handle(in invocation) (interface{}, error) { | ||
handler, found := r[in.Resolve] | ||
|
||
if found { | ||
return handler.call(in.payload()) | ||
} | ||
|
||
return nil, fmt.Errorf("No resolver found: %s", in.Resolve) | ||
func (r *Repository) Handle(in Invocation) (interface{}, error) { | ||
return r.handler.Serve(in) | ||
} |
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