go.mod: Update oapi-codegen and kin-openapi

This commit is contained in:
sanne 2022-01-11 19:00:14 +01:00 committed by Sanne Raymaekers
parent add17bba45
commit a83cf95d5b
156 changed files with 29663 additions and 2248 deletions

View file

@ -18,14 +18,14 @@ import (
"bufio"
"bytes"
"fmt"
"go/format"
"regexp"
"runtime/debug"
"sort"
"strings"
"text/template"
"github.com/getkin/kin-openapi/openapi3"
"github.com/pkg/errors"
"golang.org/x/tools/imports"
"github.com/deepmap/oapi-codegen/pkg/codegen/templates"
)
@ -37,8 +37,9 @@ type Options struct {
GenerateClient bool // GenerateClient specifies whether to generate client boilerplate
GenerateTypes bool // GenerateTypes specifies whether to generate type definitions
EmbedSpec bool // Whether to embed the swagger spec in the generated code
SkipFmt bool // Whether to skip go fmt on the generated code
SkipFmt bool // Whether to skip go imports on the generated code
SkipPrune bool // Whether to skip pruning unused components on the generated code
AliasTypes bool // Whether to alias types if possible
IncludeTags []string // Only include operations that have one of these tags. Ignored when empty.
ExcludeTags []string // Exclude operations that have one of these tags. Ignored when empty.
UserTemplates map[string]string // Override built-in templates from user-provided files
@ -46,54 +47,38 @@ type Options struct {
ExcludeSchemas []string // Exclude from generation schemas with given names. Ignored when empty.
}
// goImport represents a go package to be imported in the generated code
type goImport struct {
lookFor string
alias string
packageName string
Name string // package name
Path string // package path
}
func (i goImport) String() string {
if i.alias != "" {
return fmt.Sprintf("%s %q", i.alias, i.packageName)
// String returns a go import statement
func (gi goImport) String() string {
if gi.Name != "" {
return fmt.Sprintf("%s %q", gi.Name, gi.Path)
}
return fmt.Sprintf("%q", i.packageName)
return fmt.Sprintf("%q", gi.Path)
}
type goImports []goImport
// importMap maps external OpenAPI specifications files/urls to external go packages
type importMap map[string]goImport
var (
allGoImports = goImports{
{lookFor: "base64\\.", packageName: "encoding/base64"},
{lookFor: "bytes\\.", packageName: "bytes"},
{lookFor: "chi\\.", packageName: "github.com/go-chi/chi"},
{lookFor: "context\\.", packageName: "context"},
{lookFor: "echo\\.", packageName: "github.com/labstack/echo/v4"},
{lookFor: "errors\\.", packageName: "github.com/pkg/errors"},
{lookFor: "fmt\\.", packageName: "fmt"},
{lookFor: "gzip\\.", packageName: "compress/gzip"},
{lookFor: "http\\.", packageName: "net/http"},
{lookFor: "io\\.", packageName: "io"},
{lookFor: "ioutil\\.", packageName: "io/ioutil"},
{lookFor: "json\\.", packageName: "encoding/json"},
{lookFor: "openapi3\\.", packageName: "github.com/getkin/kin-openapi/openapi3"},
{lookFor: "openapi_types\\.", alias: "openapi_types", packageName: "github.com/deepmap/oapi-codegen/pkg/types"},
{lookFor: "path\\.", packageName: "path"},
{lookFor: "runtime\\.", packageName: "github.com/deepmap/oapi-codegen/pkg/runtime"},
{lookFor: "strings\\.", packageName: "strings"},
{lookFor: "time\\.Duration", packageName: "time"},
{lookFor: "time\\.Time", packageName: "time"},
{lookFor: "url\\.", packageName: "net/url"},
{lookFor: "xml\\.", packageName: "encoding/xml"},
{lookFor: "yaml\\.", packageName: "gopkg.in/yaml.v2"},
// GoImports returns a slice of go import statements
func (im importMap) GoImports() []string {
goImports := make([]string, 0, len(im))
for _, v := range im {
goImports = append(goImports, v.String())
}
return goImports
}
importMapping = map[string]goImport{}
)
var importMapping importMap
func constructImportMapping(input map[string]string) map[string]goImport {
func constructImportMapping(input map[string]string) importMap {
var (
nameToAlias = map[string]string{}
result = map[string]goImport{}
pathToName = map[string]string{}
result = importMap{}
)
{
@ -103,14 +88,14 @@ func constructImportMapping(input map[string]string) map[string]goImport {
}
sort.Strings(packagePaths)
for _, packageName := range packagePaths {
if _, ok := nameToAlias[packageName]; !ok {
nameToAlias[packageName] = fmt.Sprintf("externalRef%d", len(nameToAlias))
for _, packagePath := range packagePaths {
if _, ok := pathToName[packagePath]; !ok {
pathToName[packagePath] = fmt.Sprintf("externalRef%d", len(pathToName))
}
}
}
for urlOrPath, packageName := range input {
result[urlOrPath] = goImport{alias: nameToAlias[packageName], packageName: packageName}
for specPath, packagePath := range input {
result[specPath] = goImport{Name: pathToName[packagePath], Path: packagePath}
}
return result
}
@ -118,7 +103,7 @@ func constructImportMapping(input map[string]string) map[string]goImport {
// Uses the Go templating engine to generate all of our server wrappers from
// the descriptions we've built up above from the schema objects.
// opts defines
func Generate(swagger *openapi3.Swagger, packageName string, opts Options) (string, error) {
func Generate(swagger *openapi3.T, packageName string, opts Options) (string, error) {
importMapping = constructImportMapping(opts.ImportMapping)
filterOperationsByTag(swagger, opts)
@ -127,6 +112,7 @@ func Generate(swagger *openapi3.Swagger, packageName string, opts Options) (stri
}
// This creates the golang templates text package
TemplateFunctions["opts"] = func() Options { return opts }
t := template.New("oapi-codegen").Funcs(TemplateFunctions)
// This parses all of our own template files into the template object
// above
@ -150,12 +136,18 @@ func Generate(swagger *openapi3.Swagger, packageName string, opts Options) (stri
return "", errors.Wrap(err, "error creating operation definitions")
}
var typeDefinitions string
var typeDefinitions, constantDefinitions string
if opts.GenerateTypes {
typeDefinitions, err = GenerateTypeDefinitions(t, swagger, ops, opts.ExcludeSchemas)
if err != nil {
return "", errors.Wrap(err, "error generating type definitions")
}
constantDefinitions, err = GenerateConstants(t, ops)
if err != nil {
return "", errors.Wrap(err, "error generating constants")
}
}
var echoServerOut string
@ -192,49 +184,17 @@ func Generate(swagger *openapi3.Swagger, packageName string, opts Options) (stri
var inlinedSpec string
if opts.EmbedSpec {
inlinedSpec, err = GenerateInlinedSpec(t, swagger)
inlinedSpec, err = GenerateInlinedSpec(t, importMapping, swagger)
if err != nil {
return "", errors.Wrap(err, "error generating Go handlers for Paths")
}
}
// Imports needed for the generated code to compile
var imports []string
for _, importGo := range importMapping {
imports = append(imports, importGo.String())
}
var buf bytes.Buffer
w := bufio.NewWriter(&buf)
// Based on module prefixes, figure out which optional imports are required.
pkgs := make(map[string]int)
for _, str := range []string{typeDefinitions, chiServerOut, echoServerOut, clientOut, clientWithResponsesOut, inlinedSpec} {
for _, line := range strings.Split(strings.TrimSpace(str), "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "//") {
continue
}
for _, goImport := range allGoImports {
match, err := regexp.MatchString(fmt.Sprintf("[^a-zA-Z0-9_]%s", goImport.lookFor), line)
if err != nil {
return "", errors.Wrap(err, "error figuring out imports")
}
if match {
pkgs[goImport.String()]++
}
}
}
}
for k := range pkgs {
imports = append(imports, k)
}
importsOut, err := GenerateImports(t, imports, packageName)
externalImports := importMapping.GoImports()
importsOut, err := GenerateImports(t, externalImports, packageName)
if err != nil {
return "", errors.Wrap(err, "error generating imports")
}
@ -244,6 +204,11 @@ func Generate(swagger *openapi3.Swagger, packageName string, opts Options) (stri
return "", errors.Wrap(err, "error writing imports")
}
_, err = w.WriteString(constantDefinitions)
if err != nil {
return "", errors.Wrap(err, "error writing constants")
}
_, err = w.WriteString(typeDefinitions)
if err != nil {
return "", errors.Wrap(err, "error writing type definitions")
@ -290,12 +255,13 @@ func Generate(swagger *openapi3.Swagger, packageName string, opts Options) (stri
// remove any byte-order-marks which break Go-Code
goCode := SanitizeCode(buf.String())
// The generation code produces unindented horrors. Use the Go formatter
// The generation code produces unindented horrors. Use the Go Imports
// to make it all pretty.
if opts.SkipFmt {
return goCode, nil
}
outBytes, err := format.Source([]byte(goCode))
outBytes, err := imports.Process(packageName+".go", []byte(goCode), nil)
if err != nil {
fmt.Println(goCode)
return "", errors.Wrap(err, "error formatting Go code")
@ -303,7 +269,7 @@ func Generate(swagger *openapi3.Swagger, packageName string, opts Options) (stri
return string(outBytes), nil
}
func GenerateTypeDefinitions(t *template.Template, swagger *openapi3.Swagger, ops []OperationDefinition, excludeSchemas []string) (string, error) {
func GenerateTypeDefinitions(t *template.Template, swagger *openapi3.T, ops []OperationDefinition, excludeSchemas []string) (string, error) {
schemaTypes, err := GenerateTypesForSchemas(t, swagger.Components.Schemas, excludeSchemas)
if err != nil {
return "", errors.Wrap(err, "error generating Go types for component schemas")
@ -332,6 +298,11 @@ func GenerateTypeDefinitions(t *template.Template, swagger *openapi3.Swagger, op
return "", errors.Wrap(err, "error generating Go types for operation parameters")
}
enumsOut, err := GenerateEnums(t, allTypes)
if err != nil {
return "", errors.Wrap(err, "error generating code for type enums")
}
typesOut, err := GenerateTypes(t, allTypes)
if err != nil {
return "", errors.Wrap(err, "error generating code for type definitions")
@ -342,10 +313,50 @@ func GenerateTypeDefinitions(t *template.Template, swagger *openapi3.Swagger, op
return "", errors.Wrap(err, "error generating allOf boilerplate")
}
typeDefinitions := strings.Join([]string{typesOut, paramTypesOut, allOfBoilerplate}, "")
typeDefinitions := strings.Join([]string{enumsOut, typesOut, paramTypesOut, allOfBoilerplate}, "")
return typeDefinitions, nil
}
// Generates operation ids, context keys, paths, etc. to be exported as constants
func GenerateConstants(t *template.Template, ops []OperationDefinition) (string, error) {
var buf bytes.Buffer
w := bufio.NewWriter(&buf)
constants := Constants{
SecuritySchemeProviderNames: []string{},
}
providerNameMap := map[string]struct{}{}
for _, op := range ops {
for _, def := range op.SecurityDefinitions {
providerName := SanitizeGoIdentity(def.ProviderName)
providerNameMap[providerName] = struct{}{}
}
}
var providerNames []string
for providerName := range providerNameMap {
providerNames = append(providerNames, providerName)
}
sort.Strings(providerNames)
for _, providerName := range providerNames {
constants.SecuritySchemeProviderNames = append(constants.SecuritySchemeProviderNames, providerName)
}
err := t.ExecuteTemplate(w, "constants.tmpl", constants)
if err != nil {
return "", fmt.Errorf("error generating server interface: %s", err)
}
err = w.Flush()
if err != nil {
return "", fmt.Errorf("error flushing output buffer for server interface: %s", err)
}
return buf.String(), nil
}
// Generates type definitions for any custom types defined in the
// components/schemas section of the Swagger spec.
func GenerateTypesForSchemas(t *template.Template, schemas map[string]*openapi3.SchemaRef, excludeSchemas []string) ([]TypeDefinition, error) {
@ -509,18 +520,64 @@ func GenerateTypes(t *template.Template, types []TypeDefinition) (string, error)
return buf.String(), nil
}
// Generate our import statements and package definition.
func GenerateImports(t *template.Template, imports []string, packageName string) (string, error) {
sort.Strings(imports)
func GenerateEnums(t *template.Template, types []TypeDefinition) (string, error) {
var buf bytes.Buffer
w := bufio.NewWriter(&buf)
c := Constants{
EnumDefinitions: []EnumDefinition{},
}
for _, tp := range types {
if len(tp.Schema.EnumValues) > 0 {
wrapper := ""
if tp.Schema.GoType == "string" {
wrapper = `"`
}
c.EnumDefinitions = append(c.EnumDefinitions, EnumDefinition{
Schema: tp.Schema,
TypeName: tp.TypeName,
ValueWrapper: wrapper,
})
}
}
err := t.ExecuteTemplate(w, "constants.tmpl", c)
if err != nil {
return "", errors.Wrap(err, "error generating enums")
}
err = w.Flush()
if err != nil {
return "", errors.Wrap(err, "error flushing output buffer for enums")
}
return buf.String(), nil
}
// Generate our import statements and package definition.
func GenerateImports(t *template.Template, externalImports []string, packageName string) (string, error) {
var buf bytes.Buffer
w := bufio.NewWriter(&buf)
// Read build version for incorporating into generated files
var modulePath string
var moduleVersion string
if bi, ok := debug.ReadBuildInfo(); ok {
modulePath = bi.Main.Path
moduleVersion = bi.Main.Version
} else {
// Unit tests have ok=false, so we'll just use "unknown" for the
// version if we can't read this.
modulePath = "unknown module path"
moduleVersion = "unknown version"
}
context := struct {
Imports []string
PackageName string
ExternalImports []string
PackageName string
ModuleName string
Version string
}{
Imports: imports,
PackageName: packageName,
ExternalImports: externalImports,
PackageName: packageName,
ModuleName: modulePath,
Version: moduleVersion,
}
err := t.ExecuteTemplate(w, "imports.tmpl", context)
if err != nil {