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:
parent
d997c0035d
commit
65add2a61d
@ -155,7 +155,7 @@ linters-settings:
|
||||
# Select the Go version to target. The default is '1.13'.
|
||||
go: "1.18.5"
|
||||
|
||||
misspell:
|
||||
misspell: {}
|
||||
# Correct spellings using locale preferences for US or UK.
|
||||
# Default is to use a neutral variety of English.
|
||||
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
|
||||
|
4
go.mod
4
go.mod
@ -4,6 +4,8 @@ go 1.19
|
||||
|
||||
require (
|
||||
github.com/blang/semver v3.5.1+incompatible
|
||||
github.com/go-openapi/analysis v0.21.4
|
||||
github.com/go-openapi/loads v0.21.2
|
||||
github.com/go-swagger/go-swagger v0.30.3
|
||||
github.com/jessevdk/go-flags v1.5.0
|
||||
github.com/juju/errors v1.0.0
|
||||
@ -19,12 +21,10 @@ require (
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
|
||||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/go-openapi/analysis v0.21.4 // indirect
|
||||
github.com/go-openapi/errors v0.20.3 // indirect
|
||||
github.com/go-openapi/inflect v0.19.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.0 // indirect
|
||||
github.com/go-openapi/loads v0.21.2 // indirect
|
||||
github.com/go-openapi/runtime v0.24.2 // indirect
|
||||
github.com/go-openapi/spec v0.20.7 // indirect
|
||||
github.com/go-openapi/strfmt v0.21.3 // indirect
|
||||
|
@ -6,3 +6,10 @@ const templateDirectory = "templates/go-swagger"
|
||||
// mergeYamlFileName is the name of the file that will contain the final yaml spec of the API.
|
||||
// that file will always be deleted after generation.
|
||||
const mergeYamlFileName = "api-temp.yaml"
|
||||
|
||||
// regeneratedFiles is a list of files / directory that are automatically regenerated by go-swagger.
|
||||
// They will be automatically deleted by the generate api process.
|
||||
var regeneratedFiles = []string{
|
||||
"restapi",
|
||||
"models",
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
package go_swagger
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/juju/errors"
|
||||
|
||||
"git.dev.m-and-m.ovh/mderasse/gouick/helpers"
|
||||
@ -13,13 +16,34 @@ import (
|
||||
func (a APIType) GenerateAPI(path string, config *models.Config) error {
|
||||
log.Debugf("Starting %s - %s", a.GetName(), helpers.GetCurrentFuncName())
|
||||
|
||||
// delete file that need to be regenerated
|
||||
err := a.deleteRegeneratedFiles(path)
|
||||
if err != nil {
|
||||
log.Error("Fail to delete Files that will be regenerated by Go Swagger")
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
err = a.createDirectories(path)
|
||||
if err != nil {
|
||||
log.Error("Fail to create project directories")
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
// Launch Go Swagger
|
||||
err := a.executeGoSwagger(path)
|
||||
err = a.executeGoSwagger(path)
|
||||
if err != nil {
|
||||
log.Error("Fail to Execute Go Swagger")
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
// No use atm
|
||||
// Generate files from spec
|
||||
// err = a.generateFromSpec(path)
|
||||
// if err != nil {
|
||||
// log.Error("Fail to generate files from spec")
|
||||
// return errors.Trace(err)
|
||||
// }
|
||||
|
||||
// Delete api yaml temporary file
|
||||
err = a.deleteTempApiYaml(path)
|
||||
if err != nil {
|
||||
@ -29,3 +53,44 @@ func (a APIType) GenerateAPI(path string, config *models.Config) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// deleteRegeneratedFiles will delete files that will be regenerated by go swagger.
|
||||
func (a APIType) deleteRegeneratedFiles(path string) error {
|
||||
log.Debugf("Starting %s - %s", a.GetName(), helpers.GetCurrentFuncName())
|
||||
|
||||
for _, file := range regeneratedFiles {
|
||||
tempFilePath := filepath.Join(path, file)
|
||||
|
||||
exist, err := helpers.FileExists(tempFilePath, true)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
if !exist {
|
||||
log.Debug("File/Directory doesn't exist already")
|
||||
return nil
|
||||
}
|
||||
|
||||
err = os.RemoveAll(tempFilePath)
|
||||
if err != nil {
|
||||
log.Errorf("fail to delete file/dir in %s", tempFilePath)
|
||||
return errors.Trace(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// // generateFromSpec will read the swagger spec and create cmd server, handlers, test ...
|
||||
// func (a APIType) generateFromSpec(path string) error {
|
||||
// log.Debugf("Starting %s - %s", a.GetName(), helpers.GetCurrentFuncName())
|
||||
|
||||
// tempFilePath := filepath.Join(path, mergeYamlFileName)
|
||||
// specData, err := spec.NewSpec(tempFilePath)
|
||||
// if err != nil {
|
||||
// return errors.Trace(err)
|
||||
// }
|
||||
// fmt.Printf("%#v", specData.GetOperationGroups())
|
||||
|
||||
// return nil
|
||||
// }
|
||||
|
@ -15,7 +15,7 @@ import (
|
||||
func (a APIType) GenerateDockerfile(path string, config *models.Config) error {
|
||||
log.Debugf("Starting %s - %s", a.GetName(), helpers.GetCurrentFuncName())
|
||||
|
||||
templatePath := filepath.Join(templateDirectory, "Dockerfile.tmpl")
|
||||
templatePath := filepath.Join(templateDirectory, "custom/Dockerfile.tmpl")
|
||||
savePath := filepath.Join(path, "Dockerfile")
|
||||
|
||||
data, err := a.getStdTemplate(path, config)
|
||||
|
@ -15,7 +15,7 @@ import (
|
||||
func (a APIType) GenerateLauncher(path string, config *models.Config) error {
|
||||
log.Debugf("Starting %s - %s", a.GetName(), helpers.GetCurrentFuncName())
|
||||
|
||||
templatePath := filepath.Join(templateDirectory, "launcher.sh.tmpl")
|
||||
templatePath := filepath.Join(templateDirectory, "custom/launcher.sh.tmpl")
|
||||
savePath := filepath.Join(path, "launcher.sh")
|
||||
|
||||
data, err := a.getStdTemplate(path, config)
|
||||
|
@ -16,7 +16,7 @@ import (
|
||||
func (a APIType) GenerateMakefile(path string, config *models.Config) error {
|
||||
log.Debugf("Starting %s - %s", a.GetName(), helpers.GetCurrentFuncName())
|
||||
|
||||
templatePath := filepath.Join(templateDirectory, "Makefile.tmpl")
|
||||
templatePath := filepath.Join(templateDirectory, "custom/Makefile.tmpl")
|
||||
savePath := filepath.Join(path, "Makefile")
|
||||
|
||||
data, err := a.getStdTemplate(path, config)
|
||||
|
@ -16,7 +16,7 @@ import (
|
||||
func (a APIType) GenerateReadme(path string, config *models.Config) error {
|
||||
log.Debugf("Starting %s - %s", a.GetName(), helpers.GetCurrentFuncName())
|
||||
|
||||
templatePath := filepath.Join(templateDirectory, "Readme.md.tmpl")
|
||||
templatePath := filepath.Join(templateDirectory, "custom/Readme.md.tmpl")
|
||||
savePath := filepath.Join(path, "Readme.md")
|
||||
|
||||
data, err := a.getStdTemplate(path, config)
|
||||
|
@ -72,6 +72,7 @@ func (a APIType) executeGoSwagger(path string) error {
|
||||
|
||||
// goSwaggerMixin will call go-swagger code to merge api yaml files.
|
||||
func (a APIType) goSwaggerMixin(path string, primaryFile string, mixinFiles []string) ([]string, error) {
|
||||
log.Debugf("Starting %s - %s", a.GetName(), helpers.GetCurrentFuncName())
|
||||
|
||||
mixinSpec := commands.MixinSpec{
|
||||
Compact: false,
|
||||
@ -86,16 +87,24 @@ func (a APIType) goSwaggerMixin(path string, primaryFile string, mixinFiles []st
|
||||
|
||||
// goSwaggerGenerate will generate all app files as go-swagger binary will.
|
||||
func (a APIType) goSwaggerGenerate(path string) error {
|
||||
log.Debugf("Starting %s - %s", a.GetName(), helpers.GetCurrentFuncName())
|
||||
|
||||
execDirectory, err := helpers.GetExecutableDirectory()
|
||||
if err != nil {
|
||||
log.Error("Fail to get Gouick directory")
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
generateCmd := commands.Generate{
|
||||
Server: &generate.Server{},
|
||||
}
|
||||
generateCmd.Server.Shared.Spec = flags.Filename(filepath.Join(path, mergeYamlFileName))
|
||||
//generateCmd.Server.Shared.Target = flags.Filename(path)
|
||||
generateCmd.Server.ServerPackage = "restapi"
|
||||
generateCmd.Server.Models.ModelPackage = "models"
|
||||
generateCmd.Server.Operations.APIPackage = "operations"
|
||||
generateCmd.Server.Shared.StrictResponders = true
|
||||
generateCmd.Server.Shared.Template = "stratoscale"
|
||||
generateCmd.Server.Shared.StrictResponders = false
|
||||
generateCmd.Server.Shared.TemplateDir = flags.Filename(filepath.Join(execDirectory, "templates/go-swagger/app/templates/"))
|
||||
generateCmd.Server.Shared.ConfigFile = flags.Filename(filepath.Join(execDirectory, "templates/go-swagger/app/config.yaml"))
|
||||
|
||||
return generateCmd.Server.Execute(nil)
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/juju/errors"
|
||||
|
||||
"git.dev.m-and-m.ovh/mderasse/gouick/helpers"
|
||||
"git.dev.m-and-m.ovh/mderasse/gouick/models"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
@ -14,6 +15,7 @@ import (
|
||||
|
||||
// getProcesses will read cmd directory and guess the app processes.
|
||||
func (a APIType) getProcesses(path string) ([]*models.Process, error) {
|
||||
log.Debugf("Starting %s - %s", a.GetName(), helpers.GetCurrentFuncName())
|
||||
|
||||
cmdPath := filepath.Join(path, "cmd")
|
||||
cmdDirectories, err := filepath.Glob(filepath.Join(cmdPath, "*"))
|
||||
|
@ -25,15 +25,15 @@ func (a APIType) createDefaultAPIYamls(path string, config *models.Config) error
|
||||
templateFileList := []templateFileStruct{
|
||||
{
|
||||
savePath: filepath.Join(path, "api.yaml"),
|
||||
templatePath: filepath.Join(templateDirectory, "api.yaml.tmpl"),
|
||||
templatePath: filepath.Join(templateDirectory, "custom/api.yaml.tmpl"),
|
||||
},
|
||||
{
|
||||
savePath: filepath.Join(path, "api/001-general.yaml"),
|
||||
templatePath: filepath.Join(templateDirectory, "api/001-general.yaml.tmpl"),
|
||||
templatePath: filepath.Join(templateDirectory, "custom/api/001-general.yaml.tmpl"),
|
||||
},
|
||||
{
|
||||
savePath: filepath.Join(path, "api/001-monitoring.yaml"),
|
||||
templatePath: filepath.Join(templateDirectory, "api/002-monitoring.yaml.tmpl"),
|
||||
templatePath: filepath.Join(templateDirectory, "custom/api/002-monitoring.yaml.tmpl"),
|
||||
},
|
||||
}
|
||||
|
||||
@ -60,8 +60,7 @@ func (a APIType) deleteTempApiYaml(path string) error {
|
||||
|
||||
tempFilePath := filepath.Join(path, mergeYamlFileName)
|
||||
|
||||
// delete
|
||||
exist, err := helpers.FileExists(tempFilePath)
|
||||
exist, err := helpers.FileExists(tempFilePath, false)
|
||||
if err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ func IsGouickDirectory(path string) (bool, error) {
|
||||
|
||||
// IsGouickProject will check if a gouick config file exist and readeable.
|
||||
func IsGouickProject(path string) (bool, error) {
|
||||
exist, err := FileExists(filepath.Join(path, configFile))
|
||||
exist, err := FileExists(filepath.Join(path, configFile), false)
|
||||
if err != nil {
|
||||
return false, errors.Trace(err)
|
||||
}
|
||||
@ -42,7 +42,7 @@ func IsGouickProject(path string) (bool, error) {
|
||||
}
|
||||
|
||||
// FileExists will check if a file exist and is readeable.
|
||||
func FileExists(path string) (bool, error) {
|
||||
func FileExists(path string, allowDirectory bool) (bool, error) {
|
||||
fileInfo, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if errors.Is(err, fs.ErrPermission) {
|
||||
@ -53,7 +53,7 @@ func FileExists(path string) (bool, error) {
|
||||
return false, errors.Trace(err)
|
||||
}
|
||||
}
|
||||
if fileInfo.IsDir() {
|
||||
if fileInfo.IsDir() && !allowDirectory {
|
||||
return false, errors.NotValidf("%s is actually a directory", path)
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
|
||||
// IsGoProject check if we are in a golang project (have go.mod).
|
||||
func IsGoProject(path string) (bool, error) {
|
||||
exist, err := FileExists(filepath.Join(path, "go.mod"))
|
||||
exist, err := FileExists(filepath.Join(path, "go.mod"), false)
|
||||
if err != nil {
|
||||
return false, errors.Trace(err)
|
||||
}
|
||||
@ -19,7 +19,7 @@ func IsGoProject(path string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
exist, err = FileExists(filepath.Join(path, "go.sum"))
|
||||
exist, err = FileExists(filepath.Join(path, "go.sum"), false)
|
||||
if err != nil {
|
||||
return false, errors.Trace(err)
|
||||
}
|
||||
|
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
Loading…
Reference in New Issue
Block a user