Added 100% coverage to whats written so far; travis build script; debugged potential issues with testing
This commit is contained in:
parent
9d99bce47e
commit
922f6a7b41
|
@ -0,0 +1,10 @@
|
|||
language: go
|
||||
go:
|
||||
- "1.x"
|
||||
- "1.8"
|
||||
- "1.10.x"
|
||||
- master
|
||||
install:
|
||||
- "dep ensure"
|
||||
build:
|
||||
- "go test -v ./..."
|
|
@ -13,9 +13,41 @@
|
|||
revision = "4d30d0ff60440c2d0480a15747c96ee71c3c53d4"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/gopherjs/gopherjs"
|
||||
packages = ["js"]
|
||||
revision = "0892b62f0d9fb5857760c3cfca837207185117ee"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/jtolds/gls"
|
||||
packages = ["."]
|
||||
revision = "77f18212c9c7edc9bd6a33d383a7b545ce62f064"
|
||||
version = "v4.2.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/smartystreets/assertions"
|
||||
packages = [
|
||||
".",
|
||||
"internal/go-render/render",
|
||||
"internal/oglematchers"
|
||||
]
|
||||
revision = "7678a5452ebea5b7090a6b163f844c133f523da2"
|
||||
version = "1.8.3"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/smartystreets/goconvey"
|
||||
packages = [
|
||||
"convey",
|
||||
"convey/gotest",
|
||||
"convey/reporting"
|
||||
]
|
||||
revision = "9e8dc3f972df6c8fcc0375ef492c24d0bb204857"
|
||||
version = "1.6.3"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "35d3fdde995b8cf314934259f07ffb089e30092e11fb8e404ea398ce1c76a431"
|
||||
inputs-digest = "751d4fc4b7be23b18796aed082f5e83d9ba309c184255d4c63d8225cc0048152"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
69
router.go
69
router.go
|
@ -14,29 +14,34 @@ const (
|
|||
post = http.MethodPost
|
||||
get = http.MethodGet
|
||||
put = http.MethodPut
|
||||
patch = http.MethodPatch
|
||||
delete = http.MethodDelete
|
||||
)
|
||||
|
||||
// HandlerRequest ...
|
||||
type HandlerRequest struct {
|
||||
// APIGRequest is used as the input of handler functions.
|
||||
// The Claims, Path, and QryStr will be populated by the the APIGatewayProxyRequest.
|
||||
// The Request itself is also passed through if you need further access.
|
||||
type APIGRequest struct {
|
||||
Claims map[string]interface{}
|
||||
Path map[string]string
|
||||
QryStr map[string]string
|
||||
Request *events.APIGatewayProxyRequest
|
||||
}
|
||||
|
||||
// HandlerResponse ...
|
||||
type HandlerResponse struct {
|
||||
// APIGResponse is used as the output of handler functions.
|
||||
// Populate Status and Body with your http response or populate Err with your error.
|
||||
type APIGResponse struct {
|
||||
Status int
|
||||
Body []byte
|
||||
Err error
|
||||
}
|
||||
|
||||
// Handler ...
|
||||
type Handler func(req *HandlerRequest, res *HandlerResponse)
|
||||
// APIGHandler is the interface a handler function must implement to be used
|
||||
// with Get, Post, Put, Patch, and Delete.
|
||||
type APIGHandler func(req *APIGRequest, res *APIGResponse)
|
||||
|
||||
// Router ...
|
||||
type Router struct {
|
||||
// APIGRouter is the object that handlers build upon and is used in the end to respond.
|
||||
type APIGRouter struct {
|
||||
request *events.APIGatewayProxyRequest
|
||||
endpoints map[string]*radix.Tree
|
||||
params map[string]string
|
||||
|
@ -45,14 +50,15 @@ type Router struct {
|
|||
|
||||
// NOTE: Begin router methods.
|
||||
|
||||
// New ...
|
||||
func New(r *events.APIGatewayProxyRequest, svcprefix string) *Router {
|
||||
return &Router{
|
||||
// NewAPIGRouter creates a new router using the request and a prefix to strip from your incoming requests.
|
||||
func NewAPIGRouter(r *events.APIGatewayProxyRequest, svcprefix string) *APIGRouter {
|
||||
return &APIGRouter{
|
||||
request: r,
|
||||
endpoints: map[string]*radix.Tree{
|
||||
post: radix.New(),
|
||||
get: radix.New(),
|
||||
put: radix.New(),
|
||||
patch: radix.New(),
|
||||
delete: radix.New(),
|
||||
},
|
||||
params: map[string]string{},
|
||||
|
@ -60,28 +66,33 @@ func New(r *events.APIGatewayProxyRequest, svcprefix string) *Router {
|
|||
}
|
||||
}
|
||||
|
||||
// Get ...
|
||||
func (r *Router) Get(route string, handler Handler) {
|
||||
// Get creates a new get endpoint.
|
||||
func (r *APIGRouter) Get(route string, handler APIGHandler) {
|
||||
r.addEndpoint(get, route, handler)
|
||||
}
|
||||
|
||||
// Post ...
|
||||
func (r *Router) Post(route string, handler Handler) {
|
||||
// Post creates a new post endpoint.
|
||||
func (r *APIGRouter) Post(route string, handler APIGHandler) {
|
||||
r.addEndpoint(post, route, handler)
|
||||
}
|
||||
|
||||
// Put ...
|
||||
func (r *Router) Put(route string, handler Handler) {
|
||||
// Put creates a new put endpoint.
|
||||
func (r *APIGRouter) Put(route string, handler APIGHandler) {
|
||||
r.addEndpoint(put, route, handler)
|
||||
}
|
||||
|
||||
// Delete ...
|
||||
func (r *Router) Delete(route string, handler Handler) {
|
||||
// Patch creates a new patch endpoint
|
||||
func (r *APIGRouter) Patch(route string, handler APIGHandler) {
|
||||
r.addEndpoint(patch, route, handler)
|
||||
}
|
||||
|
||||
// Delete creates a new delete endpoint.
|
||||
func (r *APIGRouter) Delete(route string, handler APIGHandler) {
|
||||
r.addEndpoint(delete, route, handler)
|
||||
}
|
||||
|
||||
// Respond ...
|
||||
func (r *Router) Respond() events.APIGatewayProxyResponse {
|
||||
// Respond returns an APIGatewayProxyResponse to respond to the lambda request.
|
||||
func (r *APIGRouter) Respond() events.APIGatewayProxyResponse {
|
||||
var (
|
||||
handlerInterface interface{}
|
||||
ok bool
|
||||
|
@ -109,15 +120,17 @@ func (r *Router) Respond() events.APIGatewayProxyResponse {
|
|||
return response
|
||||
}
|
||||
|
||||
handler := handlerInterface.(Handler)
|
||||
handler := handlerInterface.(APIGHandler)
|
||||
|
||||
req := &HandlerRequest{
|
||||
Claims: r.request.RequestContext.Authorizer["claims"].(map[string]interface{}),
|
||||
req := &APIGRequest{
|
||||
Path: r.request.PathParameters,
|
||||
QryStr: r.request.QueryStringParameters,
|
||||
Request: r.request,
|
||||
}
|
||||
res := &HandlerResponse{}
|
||||
if r.request.RequestContext.Authorizer["claims"] != nil {
|
||||
req.Claims = r.request.RequestContext.Authorizer["claims"].(map[string]interface{})
|
||||
}
|
||||
res := &APIGResponse{}
|
||||
|
||||
handler(req, res)
|
||||
status, respbody, err := res.deconstruct()
|
||||
|
@ -147,11 +160,11 @@ func stripSlashesAndSplit(s string) []string {
|
|||
return strings.Split(s, "/")
|
||||
}
|
||||
|
||||
func (res *HandlerResponse) deconstruct() (int, []byte, error) {
|
||||
func (res *APIGResponse) deconstruct() (int, []byte, error) {
|
||||
return res.Status, res.Body, res.Err
|
||||
}
|
||||
|
||||
func (r *Router) addEndpoint(method string, route string, handler Handler) {
|
||||
func (r *APIGRouter) addEndpoint(method string, route string, handler APIGHandler) {
|
||||
if _, overwrite := r.endpoints[method].Insert(route, handler); overwrite {
|
||||
panic("endpoint already existent")
|
||||
}
|
||||
|
@ -163,5 +176,5 @@ func (r *Router) addEndpoint(method string, route string, handler Handler) {
|
|||
}
|
||||
}
|
||||
|
||||
log.Printf("router: %+v", *r)
|
||||
log.Printf("endpoint: %+v %+v", method, route)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
package lambdarouter
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-lambda-go/events"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestRouterSpec(t *testing.T) {
|
||||
|
||||
Convey("Given an instantiated router", t, func() {
|
||||
request := events.APIGatewayProxyRequest{}
|
||||
rtr := NewAPIGRouter(&request, "shipping")
|
||||
|
||||
Convey("When the handler func does NOT return an error", func() {
|
||||
hdlrfunc := func(req *APIGRequest, res *APIGResponse) {
|
||||
log.Printf("claims: %+v", req.Claims)
|
||||
res.Status = http.StatusOK
|
||||
res.Body = []byte("hello")
|
||||
res.Err = nil
|
||||
|
||||
}
|
||||
|
||||
Convey("And a Get handler expecting the pattern /orders/filter/by_user/{id} is defined", func() {
|
||||
rtr.Get("/orders/filter/by_user/{id}", hdlrfunc)
|
||||
rtr.Post("/orders", func(req *APIGRequest, res *APIGResponse) {})
|
||||
rtr.Put("/orders", func(req *APIGRequest, res *APIGResponse) {})
|
||||
rtr.Patch("/orders", func(req *APIGRequest, res *APIGResponse) {})
|
||||
rtr.Delete("/orders/{id}", func(req *APIGRequest, res *APIGResponse) {})
|
||||
|
||||
Convey("And the request matches the pattern and the path params are filled", func() {
|
||||
request.HTTPMethod = http.MethodGet
|
||||
request.Path = "/shipping/orders/filter/by_user/4d50ff90-66e3-4047-bf37-0ca25837e41d"
|
||||
request.PathParameters = map[string]string{
|
||||
"id": "4d50ff90-66e3-4047-bf37-0ca25837e41d",
|
||||
}
|
||||
request.RequestContext.Authorizer = map[string]interface{}{
|
||||
"claims": map[string]interface{}{
|
||||
"cognito:username": "mitchell",
|
||||
},
|
||||
}
|
||||
|
||||
Convey("The router will return the expected status and body", func() {
|
||||
response := rtr.Respond()
|
||||
|
||||
So(response.StatusCode, ShouldEqual, http.StatusOK)
|
||||
So(response.Body, ShouldEqual, "hello")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("And the request does NOT match the pattern", func() {
|
||||
request.HTTPMethod = http.MethodGet
|
||||
request.Path = "/orders/filter"
|
||||
|
||||
Convey("The router will return and error body and a not found status", func() {
|
||||
response := rtr.Respond()
|
||||
|
||||
So(response.StatusCode, ShouldEqual, http.StatusNotFound)
|
||||
So(response.Body, ShouldEqual, "{\"error\":\"no route matching path found\"}")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("And a Get handler expecting the pattern /orders/filter/by_user/{id} is defined AGAIN", func() {
|
||||
So(func() {
|
||||
rtr.Get("/orders/filter/by_user/{id}", hdlrfunc)
|
||||
}, ShouldPanicWith, "endpoint already existent")
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Convey("When the handler func does return a record not found", func() {
|
||||
hdlrfunc := func(req *APIGRequest, res *APIGResponse) {
|
||||
res.Status = http.StatusBadRequest
|
||||
res.Body = []byte("hello")
|
||||
res.Err = errors.New("record not found")
|
||||
|
||||
}
|
||||
|
||||
Convey("And a Get handler expecting the pattern /orders/filter/by_user/{id} is defined", func() {
|
||||
rtr.Get("/orders/filter/by_user/{id}", hdlrfunc)
|
||||
|
||||
Convey("And the request matches the pattern and the path params are filled", func() {
|
||||
request.HTTPMethod = http.MethodGet
|
||||
request.Path = "/shipping/orders/filter/by_user/4d50ff90-66e3-4047-bf37-0ca25837e41d"
|
||||
request.PathParameters = map[string]string{
|
||||
"id": "4d50ff90-66e3-4047-bf37-0ca25837e41d",
|
||||
}
|
||||
|
||||
Convey("The router will return the expected status and body", func() {
|
||||
response := rtr.Respond()
|
||||
|
||||
So(response.StatusCode, ShouldEqual, http.StatusNoContent)
|
||||
So(response.Body, ShouldEqual, "{\"error\":\"record not found\"}")
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When the handler func does return a status < 400", func() {
|
||||
hdlrfunc := func(req *APIGRequest, res *APIGResponse) {
|
||||
res.Status = http.StatusOK
|
||||
res.Body = []byte("hello")
|
||||
res.Err = errors.New("bad request")
|
||||
|
||||
}
|
||||
|
||||
Convey("And a Get handler expecting the pattern /orders/filter/by_user/{id} is defined", func() {
|
||||
rtr.Get("/orders/filter/by_user/{id}", hdlrfunc)
|
||||
|
||||
Convey("And the request matches the pattern and the path params are filled", func() {
|
||||
request.HTTPMethod = http.MethodGet
|
||||
request.Path = "/shipping/orders/filter/by_user/4d50ff90-66e3-4047-bf37-0ca25837e41d"
|
||||
request.PathParameters = map[string]string{
|
||||
"id": "4d50ff90-66e3-4047-bf37-0ca25837e41d",
|
||||
}
|
||||
|
||||
Convey("The router will return the expected status and body", func() {
|
||||
response := rtr.Respond()
|
||||
|
||||
So(response.StatusCode, ShouldEqual, http.StatusBadRequest)
|
||||
So(response.Body, ShouldEqual, "{\"error\":\"bad request\"}")
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue