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'.
|
# Select the Go version to target. The default is '1.13'.
|
||||||
go: "1.18.5"
|
go: "1.18.5"
|
||||||
|
|
||||||
misspell:
|
misspell: {}
|
||||||
# Correct spellings using locale preferences for US or UK.
|
# Correct spellings using locale preferences for US or UK.
|
||||||
# Default is to use a neutral variety of English.
|
# Default is to use a neutral variety of English.
|
||||||
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
|
# 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 (
|
require (
|
||||||
github.com/blang/semver v3.5.1+incompatible
|
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/go-swagger/go-swagger v0.30.3
|
||||||
github.com/jessevdk/go-flags v1.5.0
|
github.com/jessevdk/go-flags v1.5.0
|
||||||
github.com/juju/errors v1.0.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/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.6.0 // 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/errors v0.20.3 // indirect
|
||||||
github.com/go-openapi/inflect v0.19.0 // indirect
|
github.com/go-openapi/inflect v0.19.0 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.20.0 // 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/runtime v0.24.2 // indirect
|
||||||
github.com/go-openapi/spec v0.20.7 // indirect
|
github.com/go-openapi/spec v0.20.7 // indirect
|
||||||
github.com/go-openapi/strfmt v0.21.3 // 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.
|
// 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.
|
// that file will always be deleted after generation.
|
||||||
const mergeYamlFileName = "api-temp.yaml"
|
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
|
package go_swagger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/juju/errors"
|
"github.com/juju/errors"
|
||||||
|
|
||||||
"git.dev.m-and-m.ovh/mderasse/gouick/helpers"
|
"git.dev.m-and-m.ovh/mderasse/gouick/helpers"
|
||||||
@ -13,13 +16,34 @@ import (
|
|||||||
func (a APIType) GenerateAPI(path string, config *models.Config) error {
|
func (a APIType) GenerateAPI(path string, config *models.Config) error {
|
||||||
log.Debugf("Starting %s - %s", a.GetName(), helpers.GetCurrentFuncName())
|
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
|
// Launch Go Swagger
|
||||||
err := a.executeGoSwagger(path)
|
err = a.executeGoSwagger(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Fail to Execute Go Swagger")
|
log.Error("Fail to Execute Go Swagger")
|
||||||
return errors.Trace(err)
|
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
|
// Delete api yaml temporary file
|
||||||
err = a.deleteTempApiYaml(path)
|
err = a.deleteTempApiYaml(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -29,3 +53,44 @@ func (a APIType) GenerateAPI(path string, config *models.Config) error {
|
|||||||
|
|
||||||
return nil
|
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 {
|
func (a APIType) GenerateDockerfile(path string, config *models.Config) error {
|
||||||
log.Debugf("Starting %s - %s", a.GetName(), helpers.GetCurrentFuncName())
|
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")
|
savePath := filepath.Join(path, "Dockerfile")
|
||||||
|
|
||||||
data, err := a.getStdTemplate(path, config)
|
data, err := a.getStdTemplate(path, config)
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
func (a APIType) GenerateLauncher(path string, config *models.Config) error {
|
func (a APIType) GenerateLauncher(path string, config *models.Config) error {
|
||||||
log.Debugf("Starting %s - %s", a.GetName(), helpers.GetCurrentFuncName())
|
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")
|
savePath := filepath.Join(path, "launcher.sh")
|
||||||
|
|
||||||
data, err := a.getStdTemplate(path, config)
|
data, err := a.getStdTemplate(path, config)
|
||||||
|
@ -16,7 +16,7 @@ import (
|
|||||||
func (a APIType) GenerateMakefile(path string, config *models.Config) error {
|
func (a APIType) GenerateMakefile(path string, config *models.Config) error {
|
||||||
log.Debugf("Starting %s - %s", a.GetName(), helpers.GetCurrentFuncName())
|
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")
|
savePath := filepath.Join(path, "Makefile")
|
||||||
|
|
||||||
data, err := a.getStdTemplate(path, config)
|
data, err := a.getStdTemplate(path, config)
|
||||||
|
@ -16,7 +16,7 @@ import (
|
|||||||
func (a APIType) GenerateReadme(path string, config *models.Config) error {
|
func (a APIType) GenerateReadme(path string, config *models.Config) error {
|
||||||
log.Debugf("Starting %s - %s", a.GetName(), helpers.GetCurrentFuncName())
|
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")
|
savePath := filepath.Join(path, "Readme.md")
|
||||||
|
|
||||||
data, err := a.getStdTemplate(path, config)
|
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.
|
// goSwaggerMixin will call go-swagger code to merge api yaml files.
|
||||||
func (a APIType) goSwaggerMixin(path string, primaryFile string, mixinFiles []string) ([]string, error) {
|
func (a APIType) goSwaggerMixin(path string, primaryFile string, mixinFiles []string) ([]string, error) {
|
||||||
|
log.Debugf("Starting %s - %s", a.GetName(), helpers.GetCurrentFuncName())
|
||||||
|
|
||||||
mixinSpec := commands.MixinSpec{
|
mixinSpec := commands.MixinSpec{
|
||||||
Compact: false,
|
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.
|
// goSwaggerGenerate will generate all app files as go-swagger binary will.
|
||||||
func (a APIType) goSwaggerGenerate(path string) error {
|
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{
|
generateCmd := commands.Generate{
|
||||||
Server: &generate.Server{},
|
Server: &generate.Server{},
|
||||||
}
|
}
|
||||||
generateCmd.Server.Shared.Spec = flags.Filename(filepath.Join(path, mergeYamlFileName))
|
generateCmd.Server.Shared.Spec = flags.Filename(filepath.Join(path, mergeYamlFileName))
|
||||||
//generateCmd.Server.Shared.Target = flags.Filename(path)
|
|
||||||
generateCmd.Server.ServerPackage = "restapi"
|
generateCmd.Server.ServerPackage = "restapi"
|
||||||
generateCmd.Server.Models.ModelPackage = "models"
|
generateCmd.Server.Models.ModelPackage = "models"
|
||||||
generateCmd.Server.Operations.APIPackage = "operations"
|
generateCmd.Server.Operations.APIPackage = "operations"
|
||||||
generateCmd.Server.Shared.StrictResponders = true
|
generateCmd.Server.Shared.StrictResponders = false
|
||||||
generateCmd.Server.Shared.Template = "stratoscale"
|
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)
|
return generateCmd.Server.Execute(nil)
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/juju/errors"
|
"github.com/juju/errors"
|
||||||
|
|
||||||
|
"git.dev.m-and-m.ovh/mderasse/gouick/helpers"
|
||||||
"git.dev.m-and-m.ovh/mderasse/gouick/models"
|
"git.dev.m-and-m.ovh/mderasse/gouick/models"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@ -14,6 +15,7 @@ import (
|
|||||||
|
|
||||||
// getProcesses will read cmd directory and guess the app processes.
|
// getProcesses will read cmd directory and guess the app processes.
|
||||||
func (a APIType) getProcesses(path string) ([]*models.Process, error) {
|
func (a APIType) getProcesses(path string) ([]*models.Process, error) {
|
||||||
|
log.Debugf("Starting %s - %s", a.GetName(), helpers.GetCurrentFuncName())
|
||||||
|
|
||||||
cmdPath := filepath.Join(path, "cmd")
|
cmdPath := filepath.Join(path, "cmd")
|
||||||
cmdDirectories, err := filepath.Glob(filepath.Join(cmdPath, "*"))
|
cmdDirectories, err := filepath.Glob(filepath.Join(cmdPath, "*"))
|
||||||
|
@ -25,15 +25,15 @@ func (a APIType) createDefaultAPIYamls(path string, config *models.Config) error
|
|||||||
templateFileList := []templateFileStruct{
|
templateFileList := []templateFileStruct{
|
||||||
{
|
{
|
||||||
savePath: filepath.Join(path, "api.yaml"),
|
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"),
|
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"),
|
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)
|
tempFilePath := filepath.Join(path, mergeYamlFileName)
|
||||||
|
|
||||||
// delete
|
exist, err := helpers.FileExists(tempFilePath, false)
|
||||||
exist, err := helpers.FileExists(tempFilePath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Trace(err)
|
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.
|
// IsGouickProject will check if a gouick config file exist and readeable.
|
||||||
func IsGouickProject(path string) (bool, error) {
|
func IsGouickProject(path string) (bool, error) {
|
||||||
exist, err := FileExists(filepath.Join(path, configFile))
|
exist, err := FileExists(filepath.Join(path, configFile), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, errors.Trace(err)
|
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.
|
// 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)
|
fileInfo, err := os.Stat(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, fs.ErrPermission) {
|
if errors.Is(err, fs.ErrPermission) {
|
||||||
@ -53,7 +53,7 @@ func FileExists(path string) (bool, error) {
|
|||||||
return false, errors.Trace(err)
|
return false, errors.Trace(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if fileInfo.IsDir() {
|
if fileInfo.IsDir() && !allowDirectory {
|
||||||
return false, errors.NotValidf("%s is actually a directory", path)
|
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).
|
// IsGoProject check if we are in a golang project (have go.mod).
|
||||||
func IsGoProject(path string) (bool, error) {
|
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 {
|
if err != nil {
|
||||||
return false, errors.Trace(err)
|
return false, errors.Trace(err)
|
||||||
}
|
}
|
||||||
@ -19,7 +19,7 @@ func IsGoProject(path string) (bool, error) {
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
exist, err = FileExists(filepath.Join(path, "go.sum"))
|
exist, err = FileExists(filepath.Join(path, "go.sum"), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, errors.Trace(err)
|
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