Move http transport templates for consolidation

This commit is contained in:
Adam Ryman 2016-10-24 15:02:05 -07:00
Родитель 6553cb50e1
Коммит 7f984bfdff
7 изменённых файлов: 287 добавлений и 261 удалений

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

@ -325,7 +325,6 @@ func applyTemplate(templBytes []byte, templName string, executor *templateExecut
templateString := string(templBytes)
codeTemplate, err := template.New(templName).Funcs(executor.funcMap).Parse(templateString)
if err != nil {
return nil, errors.Wrap(err, "could not create template")
}

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

@ -24,6 +24,8 @@ import (
type Helper struct {
Methods []*Method
PathParamsBuilder string
ServerTemplate func(interface{}) (string, error)
ClientTemplate func(interface{}) (string, error)
}
// NewHelper builds a helper struct from a service declaration. The other
@ -35,6 +37,8 @@ func NewHelper(svc *deftree.ProtoService) *Helper {
pp := FormatCode(HTTPAssistFuncs)
rv := Helper{
PathParamsBuilder: pp,
ServerTemplate: GenServerTemplate,
ClientTemplate: GenClientTemplate,
}
for _, meth := range svc.Methods {
if len(meth.HttpBindings) > 0 {
@ -148,6 +152,24 @@ func NewBinding(i int, meth *deftree.ServiceMethod) *Binding {
return &nBinding
}
func GenServerTemplate(exec interface{}) (string, error) {
code, err := ApplyTemplate("ServerTemplate", serverTemplate, exec, TemplateFuncs)
if err != nil {
return "", err
}
code = FormatCode(code)
return code, nil
}
func GenClientTemplate(exec interface{}) (string, error) {
code, err := ApplyTemplate("ClientTemplate", clientTemplate, exec, TemplateFuncs)
if err != nil {
return "", err
}
code = FormatCode(code)
return code, nil
}
// GenServerDecode returns the generated code for the server-side decoding of
// an http request into its request struct.
func (b *Binding) GenServerDecode() (string, error) {

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

@ -4,36 +4,36 @@ package httptransport
// function for a particular Binding.
var ServerDecodeTemplate = `
{{ with $binding := .}}
// DecodeHTTP{{$binding.Label}}Request is a transport/http.DecodeRequestFunc that
// decodes a JSON-encoded {{ToLower $binding.Parent.Name}} request from the HTTP request
// body. Primarily useful in a server.
func DecodeHTTP{{$binding.Label}}Request(_ context.Context, r *http.Request) (interface{}, error) {
var req pb.{{GoName $binding.Parent.RequestType}}
err := json.NewDecoder(r.Body).Decode(&req)
// err = io.EOF if r.Body was empty
if err != nil && err != io.EOF {
return nil, errors.Wrap(err, "decoding body of http request")
}
// DecodeHTTP{{$binding.Label}}Request is a transport/http.DecodeRequestFunc that
// decodes a JSON-encoded {{ToLower $binding.Parent.Name}} request from the HTTP request
// body. Primarily useful in a server.
func DecodeHTTP{{$binding.Label}}Request(_ context.Context, r *http.Request) (interface{}, error) {
var req pb.{{GoName $binding.Parent.RequestType}}
err := json.NewDecoder(r.Body).Decode(&req)
// err = io.EOF if r.Body was empty
if err != nil && err != io.EOF {
return nil, errors.Wrap(err, "decoding body of http request")
}
pathParams, err := PathParams(r.URL.Path, "{{$binding.PathTemplate}}")
_ = pathParams
if err != nil {
fmt.Printf("Error while reading path params: %v\n", err)
return nil, errors.Wrap(err, "couldn't unmarshal path parameters")
pathParams, err := PathParams(r.URL.Path, "{{$binding.PathTemplate}}")
_ = pathParams
if err != nil {
fmt.Printf("Error while reading path params: %v\n", err)
return nil, errors.Wrap(err, "couldn't unmarshal path parameters")
}
queryParams, err := QueryParams(r.URL.Query())
_ = queryParams
if err != nil {
fmt.Printf("Error while reading query params: %v\n", err)
return nil, errors.Wrapf(err, "Error while reading query params: %v", r.URL.Query())
}
{{range $field := $binding.Fields}}
{{if ne $field.Location "body"}}
{{$field.GenQueryUnmarshaler}}
{{end}}
{{end}}
return &req, err
}
queryParams, err := QueryParams(r.URL.Query())
_ = queryParams
if err != nil {
fmt.Printf("Error while reading query params: %v\n", err)
return nil, errors.Wrapf(err, "Error while reading query params: %v", r.URL.Query())
}
{{range $field := $binding.Fields}}
{{if ne $field.Location "body"}}
{{$field.GenQueryUnmarshaler}}
{{end}}
{{end}}
return &req, err
}
{{end}}
`
@ -66,30 +66,30 @@ func EncodeHTTP{{$binding.Label}}Request(_ context.Context, r *http.Request, req
values := r.URL.Query()
var tmp []byte
_ = tmp
{{- range $field := $binding.Fields }}
{{- if eq $field.Location "query"}}
{{if or (not $field.IsBaseType) $field.Repeated}}
tmp, err = json.Marshal(req.{{$field.CamelName}})
if err != nil {
return errors.Wrap(err, "failed to marshal req.{{$field.CamelName}}")
}
values.Add("{{$field.Name}}", string(tmp))
{{else}}
values.Add("{{$field.Name}}", fmt.Sprint(req.{{$field.CamelName}}))
{{- end }}
{{- end }}
{{- end}}
{{- range $field := $binding.Fields }}
{{- if eq $field.Location "query"}}
{{if or (not $field.IsBaseType) $field.Repeated}}
tmp, err = json.Marshal(req.{{$field.CamelName}})
if err != nil {
return errors.Wrap(err, "failed to marshal req.{{$field.CamelName}}")
}
values.Add("{{$field.Name}}", string(tmp))
{{else}}
values.Add("{{$field.Name}}", fmt.Sprint(req.{{$field.CamelName}}))
{{- end }}
{{- end }}
{{- end}}
r.URL.RawQuery = values.Encode()
// Set the body parameters
var buf bytes.Buffer
toRet := map[string]interface{}{
{{- range $field := $binding.Fields -}}
{{if eq $field.Location "body"}}
"{{$field.CamelName}}" : req.{{$field.CamelName}},
{{end}}
{{- end -}}
{{- range $field := $binding.Fields -}}
{{if eq $field.Location "body"}}
"{{$field.CamelName}}" : req.{{$field.CamelName}},
{{end}}
{{- end -}}
}
if err := json.NewEncoder(&buf).Encode(toRet); err != nil {
return errors.Wrapf(err, "couldn't encode body as json %v", toRet)
@ -187,3 +187,206 @@ func QueryParams(vals url.Values) (map[string]string, error) {
// for encoding and decoding http request to and from generated protobuf
// structs, and is used within the generated code of each microservice.
var HTTPAssistFuncs = PathParamsTemplate + BuildParamMapTemplate + RemoveBracesTemplate + QueryParamsTemplate
var serverTemplate = `
package svc
// This file provides server-side bindings for the HTTP transport.
// It utilizes the transport/http.Server.
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
"io"
"golang.org/x/net/context"
"github.com/go-kit/kit/log"
"github.com/pkg/errors"
httptransport "github.com/go-kit/kit/transport/http"
// This service
pb "{{.PBImportPath -}}"
)
var (
_ = fmt.Sprint
_ = bytes.Compare
_ = strconv.Atoi
_ = httptransport.NewServer
_ = ioutil.NopCloser
_ = pb.Register{{.Service.GetName}}Server
_ = io.Copy
)
// MakeHTTPHandler returns a handler that makes a set of endpoints available
// on predefined paths.
func MakeHTTPHandler(ctx context.Context, endpoints Endpoints, logger log.Logger) http.Handler {
/*options := []httptransport.ServerOption{
httptransport.ServerErrorEncoder(errorEncoder),
httptransport.ServerErrorLogger(logger),
}*/
m := http.NewServeMux()
{{range $method := .HTTPHelper.Methods}}
{{range $binding := $method.Bindings}}
m.Handle("{{ToLower $binding.BasePath}}", httptransport.NewServer(
ctx,
endpoints.{{$method.Name}}Endpoint,
HttpDecodeLogger(DecodeHTTP{{$binding.Label}}Request, logger),
EncodeHTTPGenericResponse,
//options...,
))
{{- end}}
{{- end}}
return m
}
func HttpDecodeLogger(next httptransport.DecodeRequestFunc, logger log.Logger) httptransport.DecodeRequestFunc {
return func(ctx context.Context, r *http.Request) (interface{}, error) {
logger.Log("method", r.Method, "url", r.URL.String())
rv, err := next(ctx, r)
if err != nil {
logger.Log("method", r.Method, "url", r.URL.String(), "Error", err)
}
return rv, err
}
}
func errorEncoder(_ context.Context, err error, w http.ResponseWriter) {
code := http.StatusInternalServerError
msg := err.Error()
w.WriteHeader(code)
json.NewEncoder(w).Encode(errorWrapper{Error: msg})
}
func errorDecoder(r *http.Response) error {
var w errorWrapper
if err := json.NewDecoder(r.Body).Decode(&w); err != nil {
return err
}
return errors.New(w.Error)
}
type errorWrapper struct {
Error string ` + "`json:\"error\"`\n" + `
}
// Server Decode
{{range $method := .HTTPHelper.Methods}}
{{range $binding := $method.Bindings}}
{{$binding.GenServerDecode}}
{{end}}
{{end}}
// Client Decode
{{range $method := .HTTPHelper.Methods}}
// DecodeHTTP{{$method.Name}} is a transport/http.DecodeResponseFunc that decodes
// a JSON-encoded {{GoName $method.ResponseType}} response from the HTTP response body.
// If the response has a non-200 status code, we will interpret that as an
// error and attempt to decode the specific error message from the response
// body. Primarily useful in a client.
func DecodeHTTP{{$method.Name}}Response(_ context.Context, r *http.Response) (interface{}, error) {
if r.StatusCode != http.StatusOK {
return nil, errorDecoder(r)
}
var resp pb.{{GoName $method.ResponseType}}
err := json.NewDecoder(r.Body).Decode(&resp)
return &resp, err
}
{{end}}
// Client Encode
{{range $method := .HTTPHelper.Methods}}
{{range $binding := $method.Bindings}}
{{$binding.GenClientEncode}}
{{end}}
{{end}}
// EncodeHTTPGenericResponse is a transport/http.EncodeResponseFunc that encodes
// the response as JSON to the response writer. Primarily useful in a server.
func EncodeHTTPGenericResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
return json.NewEncoder(w).Encode(response)
}
// Helper functions
{{.HTTPHelper.PathParamsBuilder}}
`
var clientTemplate = `
// Package http provides an HTTP client for the {{.Service.GetName}} service.
package http
import (
"net/url"
"strings"
"github.com/go-kit/kit/endpoint"
httptransport "github.com/go-kit/kit/transport/http"
// This Service
handler "{{.ImportPath -}} /handlers/server"
svc "{{.ImportPath -}} /generated"
)
var (
_ = endpoint.Chain
_ = httptransport.NewClient
)
// New returns a service backed by an HTTP server living at the remote
// instance. We expect instance to come from a service discovery system, so
// likely of the form "host:port".
func New(instance string) (handler.Service, error) {
//options := []httptransport.ClientOptions{
//httptransport.ClientBefore(),
//}
if !strings.HasPrefix(instance, "http") {
instance = "http://" + instance
}
u, err := url.Parse(instance)
if err != nil {
return nil, err
}
_ = u
{{range $method := .HTTPHelper.Methods}}
{{range $binding := $method.Bindings}}
var {{$binding.Label}}Endpoint endpoint.Endpoint
{
{{$binding.Label}}Endpoint = httptransport.NewClient(
"{{$binding.Verb}}",
copyURL(u, "{{ToLower $binding.BasePath}}"),
svc.EncodeHTTP{{$binding.Label}}Request,
svc.DecodeHTTP{{$method.Name}}Response,
//options...,
).Endpoint()
}
{{- end}}
{{- end}}
return svc.Endpoints{
{{range $method := .HTTPHelper.Methods -}}
{{range $binding := $method.Bindings -}}
{{$method.Name}}Endpoint: {{$binding.Label}}Endpoint,
{{end}}
{{- end}}
}, nil
}
func copyURL(base *url.URL, path string) *url.URL {
next := *base
next.Path = path
return &next
}
`

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

@ -16,8 +16,8 @@ import (
// New returns an service backed by a gRPC client connection. It is the
// responsibility of the caller to dial, and later close, the connection.
func New(conn *grpc.ClientConn) handler.Service {
//options := []grpctransport.ServerOption{
//grpctransport.ServerBefore(),
//options := []grpctransport.ClientOptions{
//grpctransport.ClientBefore(),
//}
{{- with $tE := .}}

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

@ -1,68 +1,2 @@
// Package http provides an HTTP client for the {{.Service.GetName}} service.
package http
import (
"net/url"
"strings"
"github.com/go-kit/kit/endpoint"
httptransport "github.com/go-kit/kit/transport/http"
// This Service
handler "{{.ImportPath -}} /handlers/server"
svc "{{.ImportPath -}} /generated"
)
var (
_ = endpoint.Chain
_ = httptransport.NewClient
)
// New returns a service backed by an HTTP server living at the remote
// instance. We expect instance to come from a service discovery system, so
// likely of the form "host:port".
func New(instance string) (handler.Service, error) {
//options := []httptransport.ServerOption{
//httptransport.ServerBefore(),
//}
if !strings.HasPrefix(instance, "http") {
instance = "http://" + instance
}
u, err := url.Parse(instance)
if err != nil {
return nil, err
}
_ = u
{{range $method := .HTTPHelper.Methods}}
{{range $binding := $method.Bindings}}
var {{$binding.Label}}Endpoint endpoint.Endpoint
{
{{$binding.Label}}Endpoint = httptransport.NewClient(
"{{$binding.Verb}}",
copyURL(u, "{{ToLower $binding.BasePath}}"),
svc.EncodeHTTP{{$binding.Label}}Request,
svc.DecodeHTTP{{$method.Name}}Response,
//options...,
).Endpoint()
}
{{- end}}
{{- end}}
return svc.Endpoints{
{{range $method := .HTTPHelper.Methods -}}
{{range $binding := $method.Bindings -}}
{{$method.Name}}Endpoint: {{$binding.Label}}Endpoint,
{{end}}
{{- end}}
}, nil
}
func copyURL(base *url.URL, path string) *url.URL {
next := *base
next.Path = path
return &next
}
{{/* See go-truss/gengokit/httptransport/template.go for code */}}
{{call .HTTPHelper.ClientTemplate .}}

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

@ -1,134 +1,2 @@
package svc
// This file provides server-side bindings for the HTTP transport.
// It utilizes the transport/http.Server.
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
"io"
"golang.org/x/net/context"
"github.com/go-kit/kit/log"
"github.com/pkg/errors"
httptransport "github.com/go-kit/kit/transport/http"
// This service
pb "{{.PBImportPath -}}"
)
var (
_ = fmt.Sprint
_ = bytes.Compare
_ = strconv.Atoi
_ = httptransport.NewServer
_ = ioutil.NopCloser
_ = pb.Register{{.Service.GetName}}Server
_ = io.Copy
)
// MakeHTTPHandler returns a handler that makes a set of endpoints available
// on predefined paths.
func MakeHTTPHandler(ctx context.Context, endpoints Endpoints, logger log.Logger) http.Handler {
/*options := []httptransport.ServerOption{
httptransport.ServerErrorEncoder(errorEncoder),
httptransport.ServerErrorLogger(logger),
}*/
m := http.NewServeMux()
{{range $method := .HTTPHelper.Methods}}
{{range $binding := $method.Bindings}}
m.Handle("{{ToLower $binding.BasePath}}", httptransport.NewServer(
ctx,
endpoints.{{$method.Name}}Endpoint,
HttpDecodeLogger(DecodeHTTP{{$binding.Label}}Request, logger),
EncodeHTTPGenericResponse,
//options...,
))
{{- end}}
{{- end}}
return m
}
func HttpDecodeLogger(next httptransport.DecodeRequestFunc, logger log.Logger) httptransport.DecodeRequestFunc {
return func(ctx context.Context, r *http.Request) (interface{}, error) {
logger.Log("method", r.Method, "url", r.URL.String())
rv, err := next(ctx, r)
if err != nil {
logger.Log("method", r.Method, "url", r.URL.String(), "Error", err)
}
return rv, err
}
}
func errorEncoder(_ context.Context, err error, w http.ResponseWriter) {
code := http.StatusInternalServerError
msg := err.Error()
w.WriteHeader(code)
json.NewEncoder(w).Encode(errorWrapper{Error: msg})
}
func errorDecoder(r *http.Response) error {
var w errorWrapper
if err := json.NewDecoder(r.Body).Decode(&w); err != nil {
return err
}
return errors.New(w.Error)
}
type errorWrapper struct {
Error string `json:"error"`
}
// Server Decode
{{range $method := .HTTPHelper.Methods}}
{{range $binding := $method.Bindings}}
{{$binding.GenServerDecode}}
{{end}}
{{end}}
// Client Decode
{{range $method := .HTTPHelper.Methods}}
// DecodeHTTP{{$method.Name}} is a transport/http.DecodeResponseFunc that decodes
// a JSON-encoded {{GoName $method.ResponseType}} response from the HTTP response body.
// If the response has a non-200 status code, we will interpret that as an
// error and attempt to decode the specific error message from the response
// body. Primarily useful in a client.
func DecodeHTTP{{$method.Name}}Response(_ context.Context, r *http.Response) (interface{}, error) {
if r.StatusCode != http.StatusOK {
return nil, errorDecoder(r)
}
var resp pb.{{GoName $method.ResponseType}}
err := json.NewDecoder(r.Body).Decode(&resp)
return &resp, err
}
{{end}}
// Client Encode
{{range $method := .HTTPHelper.Methods}}
{{range $binding := $method.Bindings}}
{{$binding.GenClientEncode}}
{{end}}
{{end}}
// EncodeHTTPGenericResponse is a transport/http.EncodeResponseFunc that encodes
// the response as JSON to the response writer. Primarily useful in a server.
func EncodeHTTPGenericResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
return json.NewEncoder(w).Encode(response)
}
// Helper functions
{{.HTTPHelper.PathParamsBuilder}}
{{/* See go-truss/gengokit/httptransport/template.go for code */}}
{{call .HTTPHelper.ServerTemplate .}}

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