135 lines
4.3 KiB
Go
135 lines
4.3 KiB
Go
|
package addsvc
|
||
|
|
||
|
// This file contains methods to make individual endpoints from services,
|
||
|
// request and response types to serve those endpoints, as well as encoders and
|
||
|
// decoders for those types, for all of our supported transport serialization
|
||
|
// formats. It also includes endpoint middlewares.
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"fmt"
|
||
|
"time"
|
||
|
|
||
|
"github.com/go-kit/kit/endpoint"
|
||
|
"github.com/go-kit/kit/log"
|
||
|
"github.com/go-kit/kit/metrics"
|
||
|
)
|
||
|
|
||
|
// Endpoints collects all of the endpoints that compose an add service. It's
|
||
|
// meant to be used as a helper struct, to collect all of the endpoints into a
|
||
|
// single parameter.
|
||
|
//
|
||
|
// In a server, it's useful for functions that need to operate on a per-endpoint
|
||
|
// basis. For example, you might pass an Endpoints to a function that produces
|
||
|
// an http.Handler, with each method (endpoint) wired up to a specific path. (It
|
||
|
// is probably a mistake in design to invoke the Service methods on the
|
||
|
// Endpoints struct in a server.)
|
||
|
//
|
||
|
// 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.
|
||
|
type Endpoints struct {
|
||
|
SumEndpoint endpoint.Endpoint
|
||
|
ConcatEndpoint endpoint.Endpoint
|
||
|
}
|
||
|
|
||
|
// Sum implements Service. Primarily useful in a client.
|
||
|
func (e Endpoints) Sum(ctx context.Context, a, b int) (int, error) {
|
||
|
request := sumRequest{A: a, B: b}
|
||
|
response, err := e.SumEndpoint(ctx, request)
|
||
|
if err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
return response.(sumResponse).V, response.(sumResponse).Err
|
||
|
}
|
||
|
|
||
|
// Concat implements Service. Primarily useful in a client.
|
||
|
func (e Endpoints) Concat(ctx context.Context, a, b string) (string, error) {
|
||
|
request := concatRequest{A: a, B: b}
|
||
|
response, err := e.ConcatEndpoint(ctx, request)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
return response.(concatResponse).V, response.(concatResponse).Err
|
||
|
}
|
||
|
|
||
|
// MakeSumEndpoint returns an endpoint that invokes Sum on the service.
|
||
|
// Primarily useful in a server.
|
||
|
func MakeSumEndpoint(s Service) endpoint.Endpoint {
|
||
|
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
|
||
|
sumReq := request.(sumRequest)
|
||
|
v, err := s.Sum(ctx, sumReq.A, sumReq.B)
|
||
|
if err == ErrIntOverflow {
|
||
|
return nil, err // special case; see comment on ErrIntOverflow
|
||
|
}
|
||
|
return sumResponse{
|
||
|
V: v,
|
||
|
Err: err,
|
||
|
}, nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MakeConcatEndpoint returns an endpoint that invokes Concat on the service.
|
||
|
// Primarily useful in a server.
|
||
|
func MakeConcatEndpoint(s Service) endpoint.Endpoint {
|
||
|
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
|
||
|
concatReq := request.(concatRequest)
|
||
|
v, err := s.Concat(ctx, concatReq.A, concatReq.B)
|
||
|
return concatResponse{
|
||
|
V: v,
|
||
|
Err: err,
|
||
|
}, nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// EndpointInstrumentingMiddleware returns an endpoint middleware that records
|
||
|
// the duration of each invocation to the passed histogram. The middleware adds
|
||
|
// a single field: "success", which is "true" if no error is returned, and
|
||
|
// "false" otherwise.
|
||
|
func EndpointInstrumentingMiddleware(duration metrics.Histogram) endpoint.Middleware {
|
||
|
return func(next endpoint.Endpoint) endpoint.Endpoint {
|
||
|
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
|
||
|
|
||
|
defer func(begin time.Time) {
|
||
|
duration.With("success", fmt.Sprint(err == nil)).Observe(time.Since(begin).Seconds())
|
||
|
}(time.Now())
|
||
|
return next(ctx, request)
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// EndpointLoggingMiddleware returns an endpoint middleware that logs the
|
||
|
// duration of each invocation, and the resulting error, if any.
|
||
|
func EndpointLoggingMiddleware(logger log.Logger) endpoint.Middleware {
|
||
|
return func(next endpoint.Endpoint) endpoint.Endpoint {
|
||
|
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
|
||
|
|
||
|
defer func(begin time.Time) {
|
||
|
logger.Log("error", err, "took", time.Since(begin))
|
||
|
}(time.Now())
|
||
|
return next(ctx, request)
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// These types are unexported because they only exist to serve the endpoint
|
||
|
// domain, which is totally encapsulated in this package. They are otherwise
|
||
|
// opaque to all callers.
|
||
|
|
||
|
type sumRequest struct{ A, B int }
|
||
|
|
||
|
type sumResponse struct {
|
||
|
V int
|
||
|
Err error
|
||
|
}
|
||
|
|
||
|
type concatRequest struct{ A, B string }
|
||
|
|
||
|
type concatResponse struct {
|
||
|
V string
|
||
|
Err error
|
||
|
}
|