storj/private/apigen/api.go
Vitalii Shpital 07c71e34c2 satellite/console{gen}: GetUsersProjects endpoint
Initial implementation of auto-generated GetUsersProjects endpoint

Change-Id: If41bff2ea3ff9cfc87afeda9e5e5b3f586cbab33
2022-02-10 08:03:02 +00:00

205 lines
4.6 KiB
Go

// Copyright (C) 2022 Storj Labs, Inc.
// See LICENSE for copying information.
package apigen
import (
"fmt"
"go/format"
"os"
"reflect"
"strings"
"github.com/zeebo/errs"
"storj.io/storj/private/api"
)
// API represents specific API's configuration.
type API struct {
Version string
Description string
PackageName string
Auth api.Auth
EndpointGroups []*EndpointGroup
}
// Group adds new endpoints group to API.
func (a *API) Group(name, prefix string) *EndpointGroup {
group := &EndpointGroup{
Name: name,
Prefix: prefix,
Endpoints: make(map[PathMethod]*Endpoint),
}
a.EndpointGroups = append(a.EndpointGroups, group)
return group
}
// MustWrite writes generated code into a file.
func (a *API) MustWrite(path string) {
generated, err := a.generateGo()
if err != nil {
panic(errs.Wrap(err))
}
err = os.WriteFile(path+"api.gen.go", generated, 0755)
if err != nil {
panic(errs.Wrap(err))
}
}
// generateGo generates api code and returns an output.
func (a *API) generateGo() ([]byte, error) {
var result string
p := func(format string, a ...interface{}) {
result += fmt.Sprintf(format+"\n", a...)
}
getPackageName := func(path string) string {
pathPackages := strings.Split(path, "/")
return pathPackages[len(pathPackages)-1]
}
p("// AUTOGENERATED BY private/apigen")
p("// DO NOT EDIT.")
p("")
p("package %s", a.PackageName)
p("")
p("import (")
p(`"context"`)
p(`"encoding/json"`)
p(`"net/http"`)
p("")
p(`"github.com/gorilla/mux"`)
p(`"github.com/zeebo/errs"`)
p(`"go.uber.org/zap"`)
p("")
p(`"storj.io/storj/private/api"`)
for _, group := range a.EndpointGroups {
for _, method := range group.Endpoints {
if method.Request != nil {
path := reflect.TypeOf(method.Request).Elem().PkgPath()
pn := getPackageName(path)
if pn == a.PackageName {
continue
}
p(`"%s"`, path)
}
if method.Response != nil {
path := reflect.TypeOf(method.Response).Elem().PkgPath()
pn := getPackageName(path)
if pn == a.PackageName {
continue
}
p(`"%s"`, path)
}
}
p(")")
p("")
p("var Err%sAPI = errs.Class(\"%s %s api\")", strings.Title(group.Prefix), a.PackageName, group.Prefix)
p("")
p("type %sService interface {", group.Name)
for _, method := range group.Endpoints {
responseType := reflect.TypeOf(method.Response)
if strings.Contains(responseType.String(), a.PackageName) {
p("%s(context.Context) (%s, api.HTTPError)", method.MethodName, responseType.Elem().Name())
} else {
p("%s(context.Context) (%s, api.HTTPError)", method.MethodName, responseType)
}
}
p("}")
p("")
p("type Handler struct {")
p("log *zap.Logger")
p("service %sService", group.Name)
p("auth api.Auth")
p("}")
p("")
p(
"func New%s(log *zap.Logger, service %sService, router *mux.Router) *Handler {",
group.Name,
group.Name,
)
p("handler := &Handler{")
p("log: log,")
p("service: service,")
p("}")
p("")
p("%sRouter := router.PathPrefix(\"/api/v0/%s\").Subrouter()", group.Prefix, group.Prefix)
for pathMethod, endpoint := range group.Endpoints {
handlerName := "handle" + endpoint.MethodName
p("%sRouter.HandleFunc(\"%s\", handler.%s).Methods(\"%s\")", group.Prefix, pathMethod.Path, handlerName, pathMethod.Method)
}
p("")
p("return handler")
p("}")
for _, endpoint := range group.Endpoints {
p("")
handlerName := "handle" + endpoint.MethodName
p("func (h *Handler) %s(w http.ResponseWriter, r *http.Request) {", handlerName)
p("ctx := r.Context()")
p("var err error")
p("defer mon.Task()(&ctx)(&err)")
p("")
p("w.Header().Set(\"Content-Type\", \"application/json\")")
p("")
if !endpoint.NoCookieAuth {
p("err = h.auth.IsAuthenticated(r)")
p("if err != nil {")
p("api.ServeError(h.log, w, http.StatusUnauthorized, err)")
p("return")
p("}")
p("")
}
methodFormat := "retVal, httpErr := h.service.%s(ctx"
args := []string{endpoint.MethodName}
// TODO to be implemented
// if !endpoint.NoAPIAuth {}
methodFormat += ")"
interfaceArgs := make([]interface{}, len(args))
for i, v := range args {
interfaceArgs[i] = v
}
p(methodFormat, interfaceArgs...)
p("if err != nil {")
p("api.ServeError(h.log, w, httpErr.Status, httpErr.Err)")
p("return")
p("}")
p("")
p("err = json.NewEncoder(w).Encode(retVal)")
p("if err != nil {")
p("h.log.Debug(\"failed to write json %s response\", zap.Error(Err%sAPI.Wrap(err)))", endpoint.MethodName, strings.Title(group.Prefix))
p("}")
p("}")
p("")
}
}
output, err := format.Source([]byte(result))
if err != nil {
return nil, err
}
return output, nil
}