feat(go-swagger): Refactoring to use go-swagger templating system for most of the work
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
60
templates/go-swagger/app/config.yaml
Normal file
60
templates/go-swagger/app/config.yaml
Normal file
@ -0,0 +1,60 @@
|
||||
layout:
|
||||
application:
|
||||
- name: configure
|
||||
source: asset:serverConfigureapi
|
||||
target: "{{ joinFilePath .Target .ServerPackage }}"
|
||||
file_name: "configure_{{ .Name }}.go"
|
||||
- name: main
|
||||
source: asset:serverMain
|
||||
target: "{{ joinFilePath .Target \"cmd\" (dasherize (pascalize .Name)) }}-server"
|
||||
file_name: "main.go"
|
||||
skip_exists: true
|
||||
- name: embedded_spec
|
||||
source: asset:swaggerJsonEmbed
|
||||
target: "{{ joinFilePath .Target .ServerPackage }}"
|
||||
file_name: "embedded_spec.go"
|
||||
- name: server
|
||||
source: asset:serverServer
|
||||
target: "{{ joinFilePath .Target .ServerPackage }}"
|
||||
file_name: "server.go"
|
||||
- name: builder
|
||||
source: asset:serverBuilder
|
||||
target: "{{ joinFilePath .Target .ServerPackage .Package }}"
|
||||
file_name: "{{ snakize (pascalize .Name) }}_api.go"
|
||||
- name: doc
|
||||
source: asset:serverDoc
|
||||
target: "{{ joinFilePath .Target .ServerPackage }}"
|
||||
file_name: "doc.go"
|
||||
models:
|
||||
- name: definition
|
||||
source: asset:model
|
||||
target: "{{ joinFilePath .Target .ModelPackage }}"
|
||||
file_name: "{{ (snakize (pascalize .Name)) }}.go"
|
||||
operations:
|
||||
- name: parameters
|
||||
source: asset:serverParameter
|
||||
target: "{{ if gt (len .Tags) 0 }}{{ joinFilePath .Target .ServerPackage .APIPackage .Package }}{{ else }}{{ joinFilePath .Target .ServerPackage .Package }}{{ end }}"
|
||||
file_name: "{{ (snakize (pascalize .Name)) }}_parameters.go"
|
||||
- name: responses
|
||||
source: asset:serverResponses
|
||||
target: "{{ if gt (len .Tags) 0 }}{{ joinFilePath .Target .ServerPackage .APIPackage .Package }}{{ else }}{{ joinFilePath .Target .ServerPackage .Package }}{{ end }}"
|
||||
file_name: "{{ (snakize (pascalize .Name)) }}_responses.go"
|
||||
- name: handler
|
||||
source: asset:serverOperation
|
||||
target: "{{ if gt (len .Tags) 0 }}{{ joinFilePath .Target .ServerPackage .APIPackage .Package }}{{ else }}{{ joinFilePath .Target .ServerPackage .Package }}{{ end }}"
|
||||
file_name: "{{ (snakize (pascalize .Name)) }}.go"
|
||||
- name: apihandler
|
||||
source: asset:apiHandler
|
||||
target: "{{ joinFilePath .Target \"internal\" \"api\" .Package }}"
|
||||
file_name: "{{ (snakize (pascalize .Name)) }}.go"
|
||||
skip_exists: true
|
||||
operation_groups:
|
||||
- name: cmdtag
|
||||
source: asset:serverTag
|
||||
target: "{{ joinFilePath .Target \"cmd\" .MainPackage }}"
|
||||
file_name: "tag_{{ (snakize (pascalize .Name)) }}.go"
|
||||
skip_exists: true
|
||||
- name: apitag
|
||||
source: asset:apiTag
|
||||
target: "{{ joinFilePath .Target \"internal\" \"api\" (snakize (pascalize .Name)) }}"
|
||||
file_name: "{{ (snakize (pascalize .Name)) }}.go"
|
33
templates/go-swagger/app/templates/api/handler.gotmpl
Normal file
33
templates/go-swagger/app/templates/api/handler.gotmpl
Normal file
@ -0,0 +1,33 @@
|
||||
package {{ .Package }}
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"{{ index .DefaultImports "models"}}"
|
||||
|
||||
api "{{ index .DefaultImports .Package }}"
|
||||
)
|
||||
|
||||
// {{ pascalize .Name }} will {{ .Summary }}.
|
||||
//
|
||||
// {{ .Method }} {{ .Path }}
|
||||
func (m {{ pascalize .Package }}) {{ pascalize .Name }}(ctx context.Context, params api.{{ pascalize .Name }}Params) middleware.Responder {
|
||||
{{ if eq .Name "getMonPing" }}
|
||||
return api.NewGetMonPingOK().WithPayload(
|
||||
&models.MonPing{
|
||||
Details: &models.MonPingDetails{
|
||||
AppVersion: "",
|
||||
GitHash: "",
|
||||
},
|
||||
Status: &models.MonPingStatus{
|
||||
Application: true,
|
||||
Database: true,
|
||||
},
|
||||
},
|
||||
)
|
||||
{{ else }}
|
||||
return middleware.NotImplemented("Not Implemented")
|
||||
{{ end }}
|
||||
}
|
6
templates/go-swagger/app/templates/api/tag.gotmpl
Normal file
6
templates/go-swagger/app/templates/api/tag.gotmpl
Normal file
@ -0,0 +1,6 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package {{ (snakize (pascalize .Name)) }}
|
||||
|
||||
// {{ pascalize .Name }} implement the {{ pascalize .Name }}API interface from restapi.
|
||||
type {{ pascalize .Name }} struct{}
|
222
templates/go-swagger/app/templates/server/configureapi.gotmpl
Normal file
222
templates/go-swagger/app/templates/server/configureapi.gotmpl
Normal file
@ -0,0 +1,222 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
|
||||
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
|
||||
|
||||
|
||||
package {{ .APIPackage }}
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"log"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/loads"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/runtime/security"
|
||||
|
||||
{{ imports .DefaultImports }}
|
||||
{{ imports .Imports }}
|
||||
)
|
||||
{{ $package := .Package }}
|
||||
|
||||
type contextKey string
|
||||
|
||||
const AuthKey contextKey = "Auth"
|
||||
|
||||
{{ range .OperationGroups -}}
|
||||
//go:generate mockery -name {{ pascalize .Name}}API -inpkg
|
||||
|
||||
/* {{ pascalize .Name }}API {{ .Description }} */
|
||||
type {{ pascalize .Name }}API interface {
|
||||
{{ range .Operations -}}
|
||||
{{ if .Summary -}}
|
||||
/* {{ pascalize .Name }} {{ .Summary }} */
|
||||
{{ else if .Description -}}
|
||||
/* {{ pascalize .Name }} {{ .Description }} */
|
||||
{{ end -}}
|
||||
{{ pascalize .Name }}(ctx context.Context, params {{.Package}}.{{ pascalize .Name }}Params) middleware.Responder
|
||||
|
||||
{{ end -}}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// Config is configuration for Handler
|
||||
type Config struct {
|
||||
{{ range .OperationGroups -}}
|
||||
{{ pascalize .Name }}API
|
||||
{{ end -}}
|
||||
Logger func(string, ...interface{})
|
||||
// InnerMiddleware is for the handler executors. These do not apply to the swagger.json document.
|
||||
// The middleware executes after routing but before authentication, binding and validation
|
||||
InnerMiddleware func(http.Handler) http.Handler
|
||||
|
||||
// Authorizer is used to authorize a request after the Auth function was called using the "Auth*" functions
|
||||
// and the principal was stored in the context in the "AuthKey" context value.
|
||||
Authorizer func(*http.Request) error
|
||||
|
||||
{{ range .SecurityDefinitions -}}
|
||||
{{ if .IsBasicAuth -}}
|
||||
// Auth{{ pascalize .ID }} for basic authentication
|
||||
Auth{{ pascalize .ID }} func(user string, pass string) ({{ if .PrincipalIsNullable }}*{{ end }}{{ .Principal }}, error)
|
||||
{{ end -}}
|
||||
{{ if .IsAPIKeyAuth -}}
|
||||
// Auth{{ pascalize .ID }} Applies when the "{{ .Name }}" {{ .Source }} is set
|
||||
Auth{{ pascalize .ID }} func(token string) ({{ if .PrincipalIsNullable }}*{{ end }}{{ .Principal }}, error)
|
||||
{{ end }}
|
||||
{{ if .IsOAuth2 -}}
|
||||
// Auth{{ pascalize .ID }} For OAuth2 authentication
|
||||
Auth{{ pascalize .ID }} func(token string, scopes []string) ({{ if .PrincipalIsNullable }}*{{ end }}{{ .Principal }}, error)
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
|
||||
// Authenticator to use for all APIKey authentication
|
||||
APIKeyAuthenticator func(string, string, security.TokenAuthentication) runtime.Authenticator
|
||||
// Authenticator to use for all Bearer authentication
|
||||
BasicAuthenticator func(security.UserPassAuthentication) runtime.Authenticator
|
||||
// Authenticator to use for all Basic authentication
|
||||
BearerAuthenticator func(string, security.ScopedTokenAuthentication) runtime.Authenticator
|
||||
|
||||
{{ range .Consumes -}}
|
||||
{{ if .Implementation -}}
|
||||
// {{ pascalize .Name }}Consumer is a {{ .Name }} consumer that will replace the default if not nil.
|
||||
{{ pascalize .Name }}Consumer runtime.Consumer
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
}
|
||||
|
||||
// Handler returns an http.Handler given the handler configuration
|
||||
// It mounts all the business logic implementers in the right routing.
|
||||
func Handler(c Config) (http.Handler, error) {
|
||||
h, _, err := HandlerAPI(c)
|
||||
return h, err
|
||||
}
|
||||
|
||||
// HandlerAPI returns an http.Handler given the handler configuration
|
||||
// and the corresponding *{{ pascalize .Name }} instance.
|
||||
// It mounts all the business logic implementers in the right routing.
|
||||
func HandlerAPI(c Config) (http.Handler, *{{.Package}}.{{ pascalize .Name }}API, error) {
|
||||
spec, err := loads.Analyzed(swaggerCopy(SwaggerJSON), "")
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("analyze swagger: %v", err)
|
||||
}
|
||||
api := {{.Package}}.New{{ pascalize .Name }}API(spec)
|
||||
api.ServeError = errors.ServeError
|
||||
api.Logger = c.Logger
|
||||
|
||||
if c.APIKeyAuthenticator != nil {
|
||||
api.APIKeyAuthenticator = c.APIKeyAuthenticator
|
||||
}
|
||||
if c.BasicAuthenticator != nil {
|
||||
api.BasicAuthenticator = c.BasicAuthenticator
|
||||
}
|
||||
if c.BearerAuthenticator != nil {
|
||||
api.BearerAuthenticator = c.BearerAuthenticator
|
||||
}
|
||||
|
||||
{{ range .Consumes -}}
|
||||
if c.{{ pascalize .Name }}Consumer != nil {
|
||||
api.{{ pascalize .Name }}Consumer = c.{{ pascalize .Name }}Consumer
|
||||
} else {
|
||||
{{ if .Implementation -}}
|
||||
api.{{ pascalize .Name }}Consumer = {{ .Implementation }}
|
||||
{{ else }}
|
||||
api.{{ pascalize .Name }}Consumer = runtime.ConsumerFunc(func(r io.Reader, target interface{}) error {
|
||||
return errors.NotImplemented("{{.Name}} consumer has not yet been implemented")
|
||||
})
|
||||
{{ end -}}
|
||||
}
|
||||
{{ end -}}
|
||||
{{ range .Produces -}}
|
||||
{{ if .Implementation -}}
|
||||
api.{{ pascalize .Name }}Producer = {{ .Implementation }}
|
||||
{{ else -}}
|
||||
api.{{ pascalize .Name }}Producer = runtime.ProducerFunc(func(w io.Writer, data interface{}) error {
|
||||
return errors.NotImplemented("{{.Name}} producer has not yet been implemented")
|
||||
})
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
|
||||
{{ range .SecurityDefinitions -}}
|
||||
{{ if .IsBasicAuth -}}
|
||||
api.{{ pascalize .ID }}Auth = func(user string, pass string) ({{if .PrincipalIsNullable }}*{{ end }}{{.Principal}}, error) {
|
||||
if c.Auth{{ pascalize .ID }} == nil {
|
||||
{{- if eq .Principal "interface{}" }}
|
||||
return "", nil
|
||||
{{- else }}
|
||||
panic("you specified a custom principal type, but did not provide the authenticator to provide this")
|
||||
{{- end }}
|
||||
}
|
||||
return c.Auth{{ pascalize .ID }}(user, pass)
|
||||
}
|
||||
{{ end -}}
|
||||
{{ if .IsAPIKeyAuth -}}
|
||||
api.{{ pascalize .ID }}Auth = func(token string) ({{ if .PrincipalIsNullable }}*{{ end }}{{.Principal}}, error) {
|
||||
if c.Auth{{ pascalize .ID }} == nil {
|
||||
{{- if eq .Principal "interface{}" }}
|
||||
return token, nil
|
||||
{{- else }}
|
||||
panic("you specified a custom principal type, but did not provide the authenticator to provide this")
|
||||
{{- end }}
|
||||
}
|
||||
return c.Auth{{ pascalize .ID }}(token)
|
||||
}
|
||||
{{ end }}
|
||||
{{ if .IsOAuth2 -}}
|
||||
api.{{ pascalize .ID }}Auth = func(token string, scopes []string) ({{ if .PrincipalIsNullable }}*{{ end }}{{.Principal}}, error) {
|
||||
if c.Auth{{ pascalize .ID }} == nil {
|
||||
{{- if eq .Principal "interface{}" }}
|
||||
return token, nil
|
||||
{{- else }}
|
||||
panic("you specified a custom principal type, but did not provide the authenticator to provide this")
|
||||
{{- end }}
|
||||
}
|
||||
return c.Auth{{ pascalize .ID }}(token, scopes)
|
||||
}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
|
||||
{{ if .SecurityDefinitions -}}
|
||||
api.APIAuthorizer = authorizer(c.Authorizer)
|
||||
{{ end -}}
|
||||
|
||||
{{ range .Operations -}}
|
||||
api.{{if ne .Package $package}}{{pascalize .Package}}{{end}}{{ pascalize .Name }}Handler =
|
||||
{{- .PackageAlias }}.{{ pascalize .Name }}HandlerFunc(func(params {{.PackageAlias}}.{{ pascalize .Name }}Params{{if .Authorized}}, principal {{ if .PrincipalIsNullable }}*{{ end }}{{ .Principal }}{{end}}) middleware.Responder {
|
||||
ctx := params.HTTPRequest.Context()
|
||||
{{ if .Authorized -}}
|
||||
ctx = storeAuth(ctx, principal)
|
||||
{{ end -}}
|
||||
return c.{{pascalize .Package}}API.{{pascalize .Name}}(ctx, params)
|
||||
})
|
||||
{{ end -}}
|
||||
|
||||
api.ServerShutdown = func() { }
|
||||
return api.Serve(c.InnerMiddleware), api, nil
|
||||
}
|
||||
|
||||
// swaggerCopy copies the swagger json to prevent data races in runtime
|
||||
func swaggerCopy(orig json.RawMessage) json.RawMessage {
|
||||
c := make(json.RawMessage, len(orig))
|
||||
copy(c, orig)
|
||||
return c
|
||||
}
|
||||
|
||||
// authorizer is a helper function to implement the runtime.Authorizer interface.
|
||||
type authorizer func(*http.Request) error
|
||||
|
||||
func (a authorizer) Authorize(req *http.Request, principal interface{}) error {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
ctx := storeAuth(req.Context(), principal)
|
||||
return a(req.WithContext(ctx))
|
||||
}
|
||||
|
||||
func storeAuth(ctx context.Context, principal interface{}) context.Context {
|
||||
return context.WithValue(ctx, AuthKey, principal)
|
||||
}
|
92
templates/go-swagger/app/templates/server/main.gotmpl
Normal file
92
templates/go-swagger/app/templates/server/main.gotmpl
Normal file
@ -0,0 +1,92 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"git.dev.m-and-m.ovh/mderasse/gocommon/log"
|
||||
"git.dev.m-and-m.ovh/mderasse/gocommon/tracing"
|
||||
|
||||
"{{ index .DefaultImports "restapi"}}"
|
||||
)
|
||||
|
||||
// initTag is the signature of the function that tag initialization should respect.
|
||||
type initTag func(*restapi.Config) error
|
||||
|
||||
// tagswill be fulfill by init function in tag_XXX.go.
|
||||
var tags = map[string]initTag{}
|
||||
|
||||
func main() {
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Initialize logs
|
||||
logger, err := log.Init()
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("fail to initialize gocommon.log")
|
||||
}
|
||||
|
||||
// Initialize tracing
|
||||
logger.Info("Initializing tracing")
|
||||
tp, err := tracing.Init()
|
||||
if err != nil {
|
||||
logger.WithError(err).Fatal("fail to initialize gocommon.tracing")
|
||||
}
|
||||
|
||||
// Configure go-swagger api
|
||||
logger.Info("Configure go-swagger API")
|
||||
conf := restapi.Config{
|
||||
Logger: logger.Printf,
|
||||
}
|
||||
|
||||
// Initialize Tags by calling initTag function that have been stored in tags variable (with init() in tag_XXX.go)
|
||||
logger.Info("Initializing Tags")
|
||||
for tag, initTagFunction := range tags {
|
||||
if err := initTagFunction(&conf); err != nil {
|
||||
logger.WithError(err).Fatalf("Failed to initialize tag %q", tag)
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize API Handlers
|
||||
logger.Info("Initializing API handlers")
|
||||
h, _, err := restapi.HandlerAPI(conf)
|
||||
if err != nil {
|
||||
logger.WithError(err).Fatal("Failed to initialize API Handlers")
|
||||
}
|
||||
|
||||
srv := http.Server{
|
||||
Addr: ":8081",
|
||||
Handler: h,
|
||||
IdleTimeout: 30 * time.Second,
|
||||
ReadHeaderTimeout: 2 * time.Second,
|
||||
ReadTimeout: 1 * time.Second,
|
||||
WriteTimeout: 1 * time.Second,
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := srv.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
||||
logger.WithError(err).Fatalf("HTTP server error: %v", err)
|
||||
}
|
||||
logger.Info("Stopped serving new connections.")
|
||||
}()
|
||||
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-sigChan
|
||||
|
||||
shutdownCtx, shutdownRelease := context.WithTimeout(ctx, 10*time.Second)
|
||||
defer shutdownRelease()
|
||||
|
||||
if err := srv.Shutdown(shutdownCtx); err != nil {
|
||||
logger.WithError(err).Fatalf("HTTP shutdown error: %v", err)
|
||||
}
|
||||
tp.Shutdown(ctx)
|
||||
logger.Info("Graceful shutdown complete.")
|
||||
}
|
9
templates/go-swagger/app/templates/server/server.gotmpl
Normal file
9
templates/go-swagger/app/templates/server/server.gotmpl
Normal file
@ -0,0 +1,9 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
|
||||
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
|
||||
|
||||
|
||||
package {{ .APIPackage }}
|
||||
|
||||
// this file is intentionally empty. Otherwise go-swagger will generate a server which we don't want
|
13
templates/go-swagger/app/templates/server/tag.gotmpl
Normal file
13
templates/go-swagger/app/templates/server/tag.gotmpl
Normal file
@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"{{ .GenCommon.TargetImportPath }}/internal/api/{{ (dasherize (pascalize .Name)) }}"
|
||||
"{{ .GenCommon.TargetImportPath }}/restapi"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tags["{{ pascalize .Name }}"] = func(c *restapi.Config) error {
|
||||
c.{{ pascalize .Name }}API = {{ (snakize (pascalize .Name)) }}.{{ pascalize .Name }}{}
|
||||
return nil
|
||||
}
|
||||
}
|
0
templates/go-swagger/cmd/main.go.tmpl
Normal file
0
templates/go-swagger/cmd/main.go.tmpl
Normal file
0
templates/go-swagger/cmd/operation_group.go.tmpl
Normal file
0
templates/go-swagger/cmd/operation_group.go.tmpl
Normal file
Reference in New Issue
Block a user