Merge pull request #164 from adamryman/wrap-all-execpt

WrapAll(middlewares, exclude ...string)
This commit is contained in:
Adam Ryman 2017-03-28 15:30:00 -07:00 коммит произвёл GitHub
Родитель b48ccfdf17 a07dda320c
Коммит 09b206d71c
13 изменённых файлов: 282 добавлений и 18 удалений

Просмотреть файл

@ -5,13 +5,15 @@ END_COLOR_LINE=$(UNDER)$(NO_COLOR)
TRANSPORT_TEST_MSG=\n$(OK_COLOR)Starting transport end to end test:$(END_COLOR_LINE)
MIDDLEWARES_TEST_MSG=\n$(OK_COLOR)Starting middlewares end to end test:$(END_COLOR_LINE)
CLI_TEST_MSG=\n$(OK_COLOR)Start server and cliclient generate, build, and run test:$(END_COLOR_LINE)
export PATH := $(CURDIR):$(PATH)
all: test
test: clean test-transport test-cli
test: clean test-transport test-middlewares test-cli
truss:
go build -o truss github.com/TuneLab/go-truss/cmd/truss
@ -25,6 +27,12 @@ test-transport: protoc-gen-truss-protocast truss
$(MAKE) -C transport
rm -f ./truss ./protoc-gen-truss-protocast
test-middlewares: protoc-gen-truss-protocast truss
@which truss
@printf '$(MIDDLEWARES_TEST_MSG)'
$(MAKE) -C middlewares
rm -f ./truss ./protoc-gen-truss-protocast
test-cli: protoc-gen-truss-protocast truss
@which truss
@printf '$(CLI_TEST_MSG)'
@ -35,4 +43,5 @@ clean:
rm -f ./truss ./protoc-gen-truss-protocast
go test ./cli -clean
$(MAKE) -C transport clean
$(MAKE) -C middlewares clean

Просмотреть файл

@ -31,6 +31,13 @@ the test. Then each `transport/{TRANSPORT}_test.go` imports the generated code,
runs requests against the server, checking for errors and that the input values
add to the expected output value(s).
# ./middlewares
`middlewares` is very similar to transport, having prewritten handlers and
middlewares, it generates a truss service and copies these into that service.
Then it runs tests against the endpoints (avoiding the transport logic) to see
if middlewares were correctly applied and/or excluded from the endpoints.
# ./cli
The truss CLI integration runner does the following tasks:

Просмотреть файл

@ -0,0 +1,22 @@
NO_COLOR=\e[0m
OK_COLOR=\e[38;5;118m
UNDER=\n________________________________________________________________________________\n
END_COLOR_LINE=$(UNDER)$(NO_COLOR)
TRUSS_MSG=\n$(OK_COLOR)Running Truss...$(END_COLOR_LINE)
TEST_RUNNING_MSG=\n$(OK_COLOR)Running middlewares tests:$(END_COLOR_LINE)
all: test
test:
@echo -e '$(TRUSS_MSG)'
truss middlewares-test.proto
cp -r middlewares middlewarestest-service
cp -r handlers middlewarestest-service
@echo -e '$(TEST_RUNNING_MSG)'
go test -v
$(MAKE) clean
clean:
rm -rf middlewarestest-service

Просмотреть файл

@ -0,0 +1,46 @@
package handler
import (
"golang.org/x/net/context"
pb "github.com/TuneLab/go-truss/cmd/_integration-tests/middlewares/middlewarestest-service"
)
// NewService returns a naïve, stateless implementation of Service.
func NewService() pb.MiddlewaresTestServer {
return middlewarestestService{}
}
type middlewarestestService struct{}
// AlwaysWrapped implements Service.
func (s middlewarestestService) AlwaysWrapped(ctx context.Context, in *pb.Empty) (*pb.WrapAllTest, error) {
var resp pb.WrapAllTest
always := ctx.Value("Always")
if a, ok := always.(bool); ok {
resp.Always = a
}
notSometimes := ctx.Value("NotSometimes")
if ns, ok := notSometimes.(bool); ok {
resp.NotSometimes = ns
}
return &resp, nil
}
// SometimesWrapped implements Service.
func (s middlewarestestService) SometimesWrapped(ctx context.Context, in *pb.Empty) (*pb.WrapAllTest, error) {
var resp pb.WrapAllTest
always := ctx.Value("Always")
if a, ok := always.(bool); ok {
resp.Always = a
}
notSometimes := ctx.Value("NotSometimes")
if ns, ok := notSometimes.(bool); ok {
resp.NotSometimes = ns
}
return &resp, nil
}

Просмотреть файл

@ -0,0 +1,34 @@
syntax = "proto3";
package middlewares;
import "github.com/TuneLab/go-truss/deftree/googlethirdparty/annotations.proto";
service MiddlewaresTest {
// Test endpoints.WrapAll(middleware, exclude ...)
// AlwaysWrapped will never be excluded in endpoints.WrapAll
rpc AlwaysWrapped (Empty) returns (WrapAllTest) {
option (google.api.http) = {
get: "/alwayswrapped"
};
}
// SometimesWrapped will be excluded from the middleware that set the
// NotSometimes value
rpc SometimesWrapped (Empty) returns (WrapAllTest) {
option (google.api.http) = {
get: "/sometimeswrapped"
};
}
}
message Empty {}
// WrapAllTest will be populated by middlewares which were wrapped
// around endpoints using WrapAll
message WrapAllTest {
// Always will be set to true
bool Always = 1;
// NotSometimes will be set to true for endpoint AlwaysWrapped and set to
// false for SometimesWrapped
bool NotSometimes = 2;
}

Просмотреть файл

@ -0,0 +1,35 @@
package middlewares
import (
"github.com/go-kit/kit/endpoint"
"golang.org/x/net/context"
svc "github.com/TuneLab/go-truss/cmd/_integration-tests/middlewares/middlewarestest-service/generated"
)
// WrapEndpoints accepts the service's entire collection of endpoints, so that a
// set of middlewares can be wrapped around every middleware (e.g., access
// logging and instrumentation), and others wrapped selectively around some
// endpoints and not others (e.g., endpoints requiring authenticated access).
// Note that the final middleware applied will be the outermost middleware
// (i.e. applied first)
func WrapEndpoints(in svc.Endpoints) svc.Endpoints {
// Pass in the middlewares you want applied to every endpoint.
// optionally pass in endpoints by name that you want to be excluded
// e.g.
// in.WrapAll(authMiddleware, "Status", "Ping")
in.WrapAll(addBoolToContext("NotSometimes"), "SometimesWrapped")
in.WrapAll(addBoolToContext("Always"))
return in
}
func addBoolToContext(key string) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
ctx = context.WithValue(ctx, key, true)
return next(ctx, request)
}
}
}

Просмотреть файл

@ -0,0 +1,9 @@
package middlewares
import (
pb "github.com/TuneLab/go-truss/cmd/_integration-tests/middlewares/middlewarestest-service"
)
func WrapService(in pb.MiddlewaresTestServer) pb.MiddlewaresTestServer {
return in
}

Просмотреть файл

@ -0,0 +1,43 @@
package test
import (
"testing"
"golang.org/x/net/context"
pb "github.com/TuneLab/go-truss/cmd/_integration-tests/middlewares/middlewarestest-service"
)
func TestAlwaysWrapped(t *testing.T) {
ctx := context.Background()
resp, err := middlewareEndpoints.AlwaysWrapped(ctx, &pb.Empty{})
if err != nil {
t.Fatal(err)
}
if !resp.Always {
t.Error("Always middleware did not wrap AlwaysWrap endpoint")
}
if !resp.NotSometimes {
t.Error("NotSometimes middleware did not wrap AlwaysWrap endpoint")
}
}
func TestSometimesWrapped(t *testing.T) {
ctx := context.Background()
resp, err := middlewareEndpoints.SometimesWrapped(ctx, &pb.Empty{})
if err != nil {
t.Fatal(err)
}
if !resp.Always {
t.Error("Always middleware did not wrap SometimesWrapped endpoint")
}
if resp.NotSometimes {
t.Error("NotSometimes middleware did wrap SomtimesWrapped endpoint")
}
}

Просмотреть файл

@ -0,0 +1,34 @@
package test
import (
"os"
"testing"
pb "github.com/TuneLab/go-truss/cmd/_integration-tests/middlewares/middlewarestest-service"
svc "github.com/TuneLab/go-truss/cmd/_integration-tests/middlewares/middlewarestest-service/generated"
handler "github.com/TuneLab/go-truss/cmd/_integration-tests/middlewares/middlewarestest-service/handlers/server"
"github.com/TuneLab/go-truss/cmd/_integration-tests/middlewares/middlewarestest-service/middlewares"
)
var middlewareEndpoints svc.Endpoints
func TestMain(m *testing.M) {
var service pb.MiddlewaresTestServer
{
service = handler.NewService()
}
// Endpoint domain.
alwaysWrapped := svc.MakeAlwaysWrappedEndpoint(service)
sometimesWrapped := svc.MakeSometimesWrappedEndpoint(service)
middlewareEndpoints = svc.Endpoints{
AlwaysWrappedEndpoint: alwaysWrapped,
SometimesWrappedEndpoint: sometimesWrapped,
}
middlewareEndpoints = middlewares.WrapEndpoints(middlewareEndpoints)
os.Exit(m.Run())
}

Просмотреть файл

@ -58,6 +58,8 @@ func TestNewEndpointMiddleware(t *testing.T) {
package middlewares
import (
_ "github.com/go-kit/kit/endpoint"
svc "github.com/TuneLab/go-truss/gengokit/general-service/generated"
)
@ -65,12 +67,16 @@ func TestNewEndpointMiddleware(t *testing.T) {
// set of middlewares can be wrapped around every middleware (e.g., access
// logging and instrumentation), and others wrapped selectively around some
// endpoints and not others (e.g., endpoints requiring authenticated access).
// Note that the final middleware applied will be the outermost middleware
// (i.e. applied first)
func WrapEndpoints(in svc.Endpoints) svc.Endpoints {
// Pass in the middlewares you want applied to every endpoint.
in.WrapAll(/* ...endpoint.Middleware */)
// optionally pass in endpoints by name that you want to be excluded
// e.g.
// in.WrapAll(authMiddleware, "Status", "Ping")
// How to apply a middleware selectively.
// How to apply a middleware to a single endpoint.
// in.ExampleEndpoint = authMiddleware(in.ExampleEndpoint)
return in

Просмотреть файл

@ -4,6 +4,8 @@ const EndpointsBase = `
package middlewares
import (
_ "github.com/go-kit/kit/endpoint"
svc "{{.ImportPath -}} /generated"
)
@ -11,12 +13,16 @@ import (
// set of middlewares can be wrapped around every middleware (e.g., access
// logging and instrumentation), and others wrapped selectively around some
// endpoints and not others (e.g., endpoints requiring authenticated access).
// Note that the final middleware applied will be the outermost middleware
// (i.e. applied first)
func WrapEndpoints(in svc.Endpoints) svc.Endpoints {
// Pass in the middlewares you want applied to every endpoint.
in.WrapAll(/* ...endpoint.Middleware */)
// optionally pass in endpoints by name that you want to be excluded
// e.g.
// in.WrapAll(authMiddleware, "Status", "Ping")
// How to apply a middleware selectively.
// How to apply a middleware to a single endpoint.
// in.ExampleEndpoint = authMiddleware(in.ExampleEndpoint)
return in

Просмотреть файл

@ -6,6 +6,7 @@ package svc
// formats. It also includes endpoint middlewares.
import (
"fmt"
"golang.org/x/net/context"
"github.com/go-kit/kit/endpoint"
@ -25,8 +26,7 @@ import (
//
// In a client, it's useful to collect individually constructed endpoints into a
// single type that implements the Service interface. For example, you might
// construct individual endpoints using transport/http.NewClient, combine them
// into an Endpoints, and return it to the caller as a Service.
// construct individual endpoints using transport/http.NewClient, combine them into an Endpoints, and return it to the caller as a Service.
type Endpoints struct {
{{range $i := .Service.Methods}}
{{$i.Name}}Endpoint endpoint.Endpoint
@ -63,14 +63,27 @@ type Endpoints struct {
// WrapAll wraps each Endpoint field of struct Endpoints with a
// go-kit/kit/endpoint.Middleware.
// Use this for applying a set of middlewares to every endpoint in the service.
// The middlewares will be applied in the order passed, with the first
// middleware being the outermost middleware.
func (e *Endpoints) WrapAll(middlewares ...endpoint.Middleware) {
if len(middlewares) == 0 {
return
// Optionally, endpoints can be passed in by name to be excluded from being wrapped.
// WrapAll(middleware, "Status", "Ping")
func (e *Endpoints) WrapAll(middleware endpoint.Middleware, excluded ...string) {
included := map[string]struct{}{
{{- range $i := .Service.Methods}}
"{{$i.Name}}": struct{}{},
{{- end}}
}
for _, ex := range excluded {
if _, ok := included[ex]; !ok {
panic(fmt.Sprintf("Excluded endpoint '%s' does not exist; see middlewares/endpoints.go", ex))
}
delete(included, ex)
}
for inc, _ := range included {
{{- range $i := .Service.Methods}}
if inc == "{{$i.Name}}" {
e.{{$i.Name}}Endpoint = middleware(e.{{$i.Name}}Endpoint)
}
{{- end}}
}
m := endpoint.Chain(middlewares[0], middlewares[1:]...)
{{range $i := .Service.Methods}}
e.{{$i.Name}}Endpoint = m(e.{{$i.Name}}Endpoint)
{{- end}}
}

Различия файлов скрыты, потому что одна или несколько строк слишком длинны