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
+1
-1
@@ -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,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)
|
||||
}
|
||||
|
||||
+3
-3
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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