Skip to content

Instantly share code, notes, and snippets.

@peterhellberg
Created January 11, 2023 16:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save peterhellberg/2d64be86f1b2abb844f05f3041905b3e to your computer and use it in GitHub Desktop.
Save peterhellberg/2d64be86f1b2abb844f05f3041905b3e to your computer and use it in GitHub Desktop.
Oto template which include otohttp inline instead of importing it.
// Code generated by oto; DO NOT EDIT.
package <%= def.PackageName %>
import (
"compress/gzip"
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"strings"
<%= for (importPath, name) in def.Imports { %>
<%= name %> "<%= importPath %>"
<% } %>
)
<%= for (service) in def.Services { %>
<%= format_comment_text(service.Comment) %>type <%= service.Name %> interface {
<%= for (method) in service.Methods { %>
<%= format_comment_text(method.Comment) %><%= method.Name %>(context.Context, <%= method.InputObject.TypeName %>) (*<%= method.OutputObject.TypeName %>, error)<% } %>
}
<% } %>
<%= for (service) in def.Services { %>
type <%= camelize_down(service.Name) %>Server struct {
server *Server
<%= camelize_down(service.Name) %> <%= service.Name %>
}
// Register adds the <%= service.Name %> to the otohttp.Server.
func Register<%= service.Name %>(server *Server, <%= camelize_down(service.Name) %> <%= service.Name %>) {
handler := &<%= camelize_down(service.Name) %>Server{
server: server,
<%= camelize_down(service.Name) %>: <%= camelize_down(service.Name) %>,
}
<%= for (method) in service.Methods { %>server.Register("<%= service.Name %>", "<%= method.Name %>", handler.handle<%= method.Name %>)
<% } %>}
<%= for (method) in service.Methods { %>
func (s *<%= camelize_down(service.Name) %>Server) handle<%= method.Name %>(w http.ResponseWriter, r *http.Request) {
var request <%= method.InputObject.TypeName %>
if err := Decode(r, &request); err != nil {
s.server.OnErr(w, r, err)
return
}
response, err := s.<%= camelize_down(service.Name) %>.<%= method.Name %>(r.Context(), request)
if err != nil {
s.server.OnErr(w, r, err)
return
}
if err := Encode(w, r, http.StatusOK, response); err != nil {
s.server.OnErr(w, r, err)
return
}
}
<% } %>
<% } %>
<%= for (object) in def.Objects { %>
<%= format_comment_text(object.Comment) %>type <%= object.Name %> struct {
<%= for (field) in object.Fields { %><%= format_comment_text(field.Comment) %><%= field.Name %> <%= if (field.Type.Multiple == true) { %>[]<% } %><%= field.Type.TypeName %> `json:"<%= field.NameLowerCamel %><%= if (field.OmitEmpty) { %>,omitempty<% } %>"`
<% } %>
}
<% } %>
// Server handles oto requests.
type Server struct {
// Basepath is the path prefix to match.
// Default: /oto/
Basepath string
routes map[string]http.Handler
// NotFound is the http.Handler to use when a resource is
// not found.
NotFound http.Handler
// OnErr is called when there is an error.
OnErr func(w http.ResponseWriter, r *http.Request, err error)
}
// NewServer makes a new Server.
func NewServer() *Server {
return &Server{
Basepath: "/oto/",
routes: make(map[string]http.Handler),
OnErr: func(w http.ResponseWriter, r *http.Request, err error) {
errObj := struct {
Error string `json:"error"`
}{
Error: err.Error(),
}
if err := Encode(w, r, http.StatusInternalServerError, errObj); err != nil {
log.Printf("failed to encode error: %s\n", err)
}
},
NotFound: http.NotFoundHandler(),
}
}
// Register adds a handler for the specified service method.
func (s *Server) Register(service, method string, h http.HandlerFunc) {
s.routes[fmt.Sprintf("%s%s.%s", s.Basepath, service, method)] = h
}
// ServeHTTP serves the request.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
s.NotFound.ServeHTTP(w, r)
return
}
h, ok := s.routes[r.URL.Path]
if !ok {
s.NotFound.ServeHTTP(w, r)
return
}
h.ServeHTTP(w, r)
}
// Encode writes the response.
func Encode(w http.ResponseWriter, r *http.Request, status int, v interface{}) error {
b, err := json.Marshal(v)
if err != nil {
return fmt.Errorf("encode json: %w", err)
}
var out io.Writer = w
if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
w.Header().Set("Content-Encoding", "gzip")
gzw := gzip.NewWriter(w)
out = gzw
defer gzw.Close()
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(status)
if _, err := out.Write(b); err != nil {
return err
}
return nil
}
// Decode unmarshals the object in the request into v.
func Decode(r *http.Request, v interface{}) error {
bodyBytes, err := io.ReadAll(io.LimitReader(r.Body, 1024*1024))
if err != nil {
return fmt.Errorf("Decode: read body: %w", err)
}
err = json.Unmarshal(bodyBytes, v)
if err != nil {
return fmt.Errorf("Decode: json.Unmarshal: %w", err)
}
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment