feat(go-swagger): Refactoring to use go-swagger templating system for most of the work
continuous-integration/drone/push Build is failing
continuous-integration/drone/push Build is failing
This commit is contained in:
29 files changed
+538
-21
No files matched your search
@@ -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"
|
||||
@@ -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 }}
|
||||
}
|
||||
@@ -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{}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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.")
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Whitespace-only changes.
Whitespace-only changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Reference in new issue
Block a user