From 7fbbddb6fe4a4bfbfb6596b1fdfa2dc978c0762f Mon Sep 17 00:00:00 2001 From: Bogdan Ungureanu Date: Thu, 6 Apr 2023 01:50:24 +0300 Subject: [PATCH 1/4] chore: refactor code --- cmd/swag/main.go | 2 +- gen/gen.go | 111 +++++++++++++++++--------------------------- gen/genv3.go | 93 ------------------------------------- operationv3_test.go | 9 +++- parser.go | 6 +-- parserv3_test.go | 30 ++++++------ utils.go | 28 ++++++++++- utils_go18.go | 31 ------------- utils_other.go | 47 ------------------- 9 files changed, 95 insertions(+), 262 deletions(-) delete mode 100644 utils_go18.go delete mode 100644 utils_other.go diff --git a/cmd/swag/main.go b/cmd/swag/main.go index f2164e10d..bb08bffbf 100644 --- a/cmd/swag/main.go +++ b/cmd/swag/main.go @@ -207,7 +207,7 @@ func initAction(ctx *cli.Context) error { Tags: ctx.String(tagsFlag), PackageName: ctx.String(packageName), Debugger: logger, - OpenAPIVersion: ctx.Bool(openAPIVersionFlag), + GenerateOpenApi3Doc: ctx.Bool(openAPIVersionFlag), CollectionFormat: collectionFormat, }) } diff --git a/gen/gen.go b/gen/gen.go index 04b2aeb04..ef95c6ac4 100644 --- a/gen/gen.go +++ b/gen/gen.go @@ -17,8 +17,9 @@ import ( jsoniter "github.com/json-iterator/go" - "github.com/go-openapi/spec" - openapi "github.com/sv-tools/openapi/spec" + v2 "github.com/go-openapi/spec" + v3 "github.com/sv-tools/openapi/spec" + "github.com/swaggo/swag" "sigs.k8s.io/yaml" ) @@ -28,16 +29,15 @@ var open = os.Open // DefaultOverridesFile is the location swagger will look for type overrides. const DefaultOverridesFile = ".swaggo" -type genTypeWriter func(*Config, *spec.Swagger) error +type genTypeWriter func(*Config, interface{}) error // Gen presents a generate tool for swag. type Gen struct { - json func(data interface{}) ([]byte, error) - jsonIndent func(data interface{}) ([]byte, error) - jsonToYAML func(data []byte) ([]byte, error) - outputTypeMap map[string]genTypeWriter - outputTypeMapV3 map[string]openAPITypeWriter - debug Debugger + json func(data interface{}) ([]byte, error) + jsonIndent func(data interface{}) ([]byte, error) + jsonToYAML func(data []byte) ([]byte, error) + outputTypeMap map[string]genTypeWriter + debug Debugger } // Debugger is the interface that wraps the basic Printf method. @@ -50,25 +50,17 @@ func New() *Gen { gen := Gen{ json: json.Marshal, jsonIndent: func(data interface{}) ([]byte, error) { - var json = jsoniter.ConfigCompatibleWithStandardLibrary - return json.MarshalIndent(&data, "", " ") + return jsoniter.ConfigCompatibleWithStandardLibrary.MarshalIndent(&data, "", " ") }, jsonToYAML: yaml.JSONToYAML, debug: log.New(os.Stdout, "", log.LstdFlags), } gen.outputTypeMap = map[string]genTypeWriter{ - "go": gen.writeDocSwagger, - "json": gen.writeJSONSwagger, - "yaml": gen.writeYAMLSwagger, - "yml": gen.writeYAMLSwagger, - } - - gen.outputTypeMapV3 = map[string]openAPITypeWriter{ - "go": gen.writeDocOpenAPI, - "json": gen.writeJSONOpenAPI, - "yaml": gen.writeYAMLOpenAPI, - "yml": gen.writeYAMLOpenAPI, + "go": gen.writeDoc, + "json": gen.writeJSON, + "yaml": gen.writeYAML, + "yml": gen.writeYAML, } return &gen @@ -140,7 +132,7 @@ type Config struct { Tags string // if true, OpenAPI V3.1 spec will be generated - OpenAPIVersion bool + GenerateOpenApi3Doc bool // PackageName defines package name of generated `docs.go` PackageName string @@ -196,7 +188,7 @@ func (g *Gen) Build(config *Config) error { swag.SetOverrides(overrides), swag.ParseUsingGoList(config.ParseGoList), swag.SetTags(config.Tags), - swag.SetOpenAPIVersion(config.OpenAPIVersion), + swag.GenerateOpenApi3Docs(config.GenerateOpenApi3Doc), swag.SetCollectionFormat(config.CollectionFormat), ) @@ -213,45 +205,18 @@ func (g *Gen) Build(config *Config) error { return err } - if config.OpenAPIVersion { - openAPI := p.GetOpenAPI() - err := g.writeOpenAPI(config, openAPI) - if err != nil { - return err - } - - return nil + if config.GenerateOpenApi3Doc { + return g.writeOpenAPI(config, p.GetOpenAPI()) } - swagger := p.GetSwagger() - err := g.writeSwagger(config, swagger) - if err != nil { - return err - } - - return nil + return g.writeOpenAPI(config, p.GetSwagger()) } -func (g *Gen) writeOpenAPI(config *Config, o *openapi.OpenAPI) error { - for _, outputType := range config.OutputTypes { - outputType = strings.ToLower(strings.TrimSpace(outputType)) - if typeWriter, ok := g.outputTypeMapV3[outputType]; ok { - if err := typeWriter(config, o); err != nil { - return err - } - } else { - log.Printf("output type '%s' not supported", outputType) - } - } - - return nil -} - -func (g *Gen) writeSwagger(config *Config, swagger *spec.Swagger) error { +func (g *Gen) writeOpenAPI(config *Config, doc interface{}) error { for _, outputType := range config.OutputTypes { outputType = strings.ToLower(strings.TrimSpace(outputType)) if typeWriter, ok := g.outputTypeMap[outputType]; ok { - if err := typeWriter(config, swagger); err != nil { + if err := typeWriter(config, doc); err != nil { return err } } else { @@ -262,7 +227,7 @@ func (g *Gen) writeSwagger(config *Config, swagger *spec.Swagger) error { return nil } -func (g *Gen) writeDocSwagger(config *Config, swagger *spec.Swagger) error { +func (g *Gen) writeDoc(config *Config, doc interface{}) error { var filename = "docs.go" if config.InstanceName != swag.Name { @@ -291,17 +256,25 @@ func (g *Gen) writeDocSwagger(config *Config, swagger *spec.Swagger) error { defer docs.Close() // Write doc - err = g.writeGoDoc(packageName, docs, swagger, config) - if err != nil { - return err - } + switch spec := doc.(type) { + case *v2.Swagger: + err = g.writeGoDoc(packageName, docs, spec, config) + if err != nil { + return err + } + case *v3.OpenAPI: + err = g.writeGoDocV3(packageName, docs, spec, config) + if err != nil { + return nil + } + } g.debug.Printf("create docs.go at %+v", docFileName) return nil } -func (g *Gen) writeJSONSwagger(config *Config, swagger *spec.Swagger) error { +func (g *Gen) writeJSON(config *Config, spec interface{}) error { var filename = "swagger.json" if config.InstanceName != swag.Name { @@ -310,7 +283,7 @@ func (g *Gen) writeJSONSwagger(config *Config, swagger *spec.Swagger) error { jsonFileName := path.Join(config.OutputDir, filename) - b, err := g.jsonIndent(swagger) + b, err := g.jsonIndent(spec) if err != nil { return err } @@ -325,7 +298,7 @@ func (g *Gen) writeJSONSwagger(config *Config, swagger *spec.Swagger) error { return nil } -func (g *Gen) writeYAMLSwagger(config *Config, swagger *spec.Swagger) error { +func (g *Gen) writeYAML(config *Config, swagger interface{}) error { var filename = "swagger.yaml" if config.InstanceName != swag.Name { @@ -421,7 +394,7 @@ func parseOverrides(r io.Reader) (map[string]string, error) { return overrides, nil } -func (g *Gen) writeGoDoc(packageName string, output io.Writer, swagger *spec.Swagger, config *Config) error { +func (g *Gen) writeGoDoc(packageName string, output io.Writer, swagger *v2.Swagger, config *Config) error { generator, err := template.New("swagger_info").Funcs(template.FuncMap{ "printDoc": func(v string) string { // Add schemes @@ -434,16 +407,16 @@ func (g *Gen) writeGoDoc(packageName string, output io.Writer, swagger *spec.Swa return err } - swaggerSpec := &spec.Swagger{ + swaggerSpec := &v2.Swagger{ VendorExtensible: swagger.VendorExtensible, - SwaggerProps: spec.SwaggerProps{ + SwaggerProps: v2.SwaggerProps{ ID: swagger.ID, Consumes: swagger.Consumes, Produces: swagger.Produces, Swagger: swagger.Swagger, - Info: &spec.Info{ + Info: &v2.Info{ VendorExtensible: swagger.Info.VendorExtensible, - InfoProps: spec.InfoProps{ + InfoProps: v2.InfoProps{ Description: "{{escape .Description}}", Title: "{{.Title}}", TermsOfService: swagger.Info.TermsOfService, diff --git a/gen/genv3.go b/gen/genv3.go index 8010660f2..eaf80a22e 100644 --- a/gen/genv3.go +++ b/gen/genv3.go @@ -2,107 +2,14 @@ package gen import ( "bytes" - "fmt" "io" - "os" - "path" - "path/filepath" "strings" "text/template" "time" "github.com/sv-tools/openapi/spec" - "github.com/swaggo/swag" ) -type openAPITypeWriter func(*Config, *spec.OpenAPI) error - -func (g *Gen) writeDocOpenAPI(config *Config, openAPI *spec.OpenAPI) error { - var filename = "docs.go" - - if config.InstanceName != swag.Name { - filename = config.InstanceName + "_" + filename - } - - docFileName := path.Join(config.OutputDir, filename) - - absOutputDir, err := filepath.Abs(config.OutputDir) - if err != nil { - return err - } - - packageName := filepath.Base(absOutputDir) - - docs, err := os.Create(docFileName) - if err != nil { - return err - } - defer docs.Close() - - // Write doc - err = g.writeGoDocV3(packageName, docs, openAPI, config) - if err != nil { - return err - } - - g.debug.Printf("create docs.go at %+v", docFileName) - - return nil -} - -func (g *Gen) writeJSONOpenAPI(config *Config, swagger *spec.OpenAPI) error { - var filename = "swagger.json" - - if config.InstanceName != swag.Name { - filename = config.InstanceName + "_" + filename - } - - jsonFileName := path.Join(config.OutputDir, filename) - - b, err := g.jsonIndent(swagger) - if err != nil { - return err - } - - err = g.writeFile(b, jsonFileName) - if err != nil { - return err - } - - g.debug.Printf("create swagger.json at %+v", jsonFileName) - - return nil -} - -func (g *Gen) writeYAMLOpenAPI(config *Config, swagger *spec.OpenAPI) error { - var filename = "swagger.yaml" - - if config.InstanceName != swag.Name { - filename = config.InstanceName + "_" + filename - } - - yamlFileName := path.Join(config.OutputDir, filename) - - b, err := g.json(swagger) - if err != nil { - return err - } - - y, err := g.jsonToYAML(b) - if err != nil { - return fmt.Errorf("cannot covert json to yaml error: %s", err) - } - - err = g.writeFile(y, yamlFileName) - if err != nil { - return err - } - - g.debug.Printf("create swagger.yaml at %+v", yamlFileName) - - return nil -} - func (g *Gen) writeGoDocV3(packageName string, output io.Writer, openAPI *spec.OpenAPI, config *Config) error { generator, err := template.New("swagger_info").Funcs(template.FuncMap{ "printDoc": func(v string) string { diff --git a/operationv3_test.go b/operationv3_test.go index 5b552caca..5b96ab043 100644 --- a/operationv3_test.go +++ b/operationv3_test.go @@ -234,8 +234,13 @@ func TestParseResponseCommentWithNestedPrimitiveTypeV3(t *testing.T) { allOf := operation.Responses.Spec.Response["200"].Spec.Spec.Content["application/json"].Spec.Schema.Spec.AllOf require.NotNil(t, allOf) assert.Equal(t, 2, len(allOf)) - assert.Equal(t, "#/components/schemas/data", allOf[0].Ref.Ref) - assert.Equal(t, "#/components/schemas/data2", allOf[1].Ref.Ref) + found := map[string]struct{}{} + for _, schema := range allOf { + assert.NotNil(t, schema.Ref.Ref) + found[schema.Ref.Ref] = struct{}{} + } + assert.NotNil(t, found["#/components/schemas/data"]) + assert.NotNil(t, found["#/components/schemas/data2"]) } func TestParseResponseCommentWithNestedPrimitiveArrayTypeV3(t *testing.T) { diff --git a/parser.go b/parser.go index 916a8620c..38bd8b580 100644 --- a/parser.go +++ b/parser.go @@ -370,10 +370,10 @@ func ParseUsingGoList(enabled bool) func(parser *Parser) { } } -// SetOpenAPIVersion parses only those operations which match given extension -func SetOpenAPIVersion(openAPIVersion bool) func(*Parser) { +// GenerateOpenApi3Docs parses only those operations which match given extension +func GenerateOpenApi3Docs(enable bool) func(*Parser) { return func(p *Parser) { - p.openAPIVersion = openAPIVersion + p.openAPIVersion = enable } } diff --git a/parserv3_test.go b/parserv3_test.go index 990b656d4..e948cade9 100644 --- a/parserv3_test.go +++ b/parserv3_test.go @@ -108,7 +108,7 @@ func TestParserParseGeneralApiInfoV3(t *testing.T) { gopath := os.Getenv("GOPATH") assert.NotNil(t, gopath) - p := New(SetOpenAPIVersion(true)) + p := New(GenerateOpenApi3Docs(true)) err := p.ParseGeneralAPIInfo("testdata/v3/main.go") assert.NoError(t, err) @@ -172,7 +172,7 @@ func TestParser_ParseGeneralApiInfoExtensionsV3(t *testing.T) { gopath := os.Getenv("GOPATH") assert.NotNil(t, gopath) - p := New(SetOpenAPIVersion(true)) + p := New(GenerateOpenApi3Docs(true)) err := p.ParseGeneralAPIInfo("testdata/v3/extensionsFail1.go") if assert.Error(t, err) { @@ -188,7 +188,7 @@ func TestParser_ParseGeneralApiInfoExtensionsV3(t *testing.T) { gopath := os.Getenv("GOPATH") assert.NotNil(t, gopath) - p := New(SetOpenAPIVersion(true)) + p := New(GenerateOpenApi3Docs(true)) err := p.ParseGeneralAPIInfo("testdata/v3/extensionsFail2.go") if assert.Error(t, err) { @@ -203,7 +203,7 @@ func TestParserParseGeneralApiInfoWithOpsInSameFileV3(t *testing.T) { gopath := os.Getenv("GOPATH") assert.NotNil(t, gopath) - p := New(SetOpenAPIVersion(true)) + p := New(GenerateOpenApi3Docs(true)) err := p.ParseGeneralAPIInfo("testdata/single_file_api/main.go") assert.NoError(t, err) @@ -216,7 +216,7 @@ func TestParserParseGeneralApiInfoWithOpsInSameFileV3(t *testing.T) { func TestParserParseGeneralAPIInfoMarkdownV3(t *testing.T) { t.Parallel() - p := New(SetMarkdownFileDirectory("testdata"), SetOpenAPIVersion(true)) + p := New(SetMarkdownFileDirectory("testdata"), GenerateOpenApi3Docs(true)) mainAPIFile := "testdata/markdown.go" err := p.ParseGeneralAPIInfo(mainAPIFile) assert.NoError(t, err) @@ -224,7 +224,7 @@ func TestParserParseGeneralAPIInfoMarkdownV3(t *testing.T) { assert.Equal(t, "users", p.openAPI.Tags[0].Spec.Name) assert.Equal(t, "Users Tag Markdown Description", p.openAPI.Tags[0].Spec.Description) - p = New(SetOpenAPIVersion(true)) + p = New(GenerateOpenApi3Docs(true)) err = p.ParseGeneralAPIInfo(mainAPIFile) assert.Error(t, err) @@ -235,14 +235,14 @@ func TestParserParseGeneralApiInfoFailedV3(t *testing.T) { gopath := os.Getenv("GOPATH") assert.NotNil(t, gopath) - p := New(SetOpenAPIVersion(true)) + p := New(GenerateOpenApi3Docs(true)) assert.Error(t, p.ParseGeneralAPIInfo("testdata/noexist.go")) } func TestParserParseGeneralAPIInfoCollectionFormatV3(t *testing.T) { t.Parallel() - parser := New(SetOpenAPIVersion(true)) + parser := New(GenerateOpenApi3Docs(true)) assert.NoError(t, parser.parseGeneralAPIInfoV3([]string{ "@query.collection.format csv", })) @@ -257,7 +257,7 @@ func TestParserParseGeneralAPIInfoCollectionFormatV3(t *testing.T) { func TestParserParseGeneralAPITagGroupsV3(t *testing.T) { t.Parallel() - parser := New(SetOpenAPIVersion(true)) + parser := New(GenerateOpenApi3Docs(true)) assert.NoError(t, parser.parseGeneralAPIInfoV3([]string{ "@x-tagGroups [{\"name\":\"General\",\"tags\":[\"lanes\",\"video-recommendations\"]}]", })) @@ -269,12 +269,12 @@ func TestParserParseGeneralAPITagGroupsV3(t *testing.T) { func TestParserParseGeneralAPITagDocsV3(t *testing.T) { t.Parallel() - parser := New(SetOpenAPIVersion(true)) + parser := New(GenerateOpenApi3Docs(true)) assert.Error(t, parser.parseGeneralAPIInfoV3([]string{ "@tag.name Test", "@tag.docs.description Best example documentation"})) - parser = New(SetOpenAPIVersion(true)) + parser = New(GenerateOpenApi3Docs(true)) err := parser.parseGeneralAPIInfoV3([]string{ "@tag.name test", "@tag.description A test Tag", @@ -293,7 +293,7 @@ func TestGetAllGoFileInfoV3(t *testing.T) { searchDir := "testdata/pet" - p := New(SetOpenAPIVersion(true)) + p := New(GenerateOpenApi3Docs(true)) err := p.getAllGoFileInfo("testdata", searchDir) assert.NoError(t, err) @@ -305,7 +305,7 @@ func TestParser_ParseTypeV3(t *testing.T) { searchDir := "testdata/v3/simple/" - p := New(SetOpenAPIVersion(true)) + p := New(GenerateOpenApi3Docs(true)) err := p.getAllGoFileInfo("testdata", searchDir) assert.NoError(t, err) @@ -322,7 +322,7 @@ func TestParsePet(t *testing.T) { searchDir := "testdata/v3/pet" - p := New(SetOpenAPIVersion(true)) + p := New(GenerateOpenApi3Docs(true)) p.PropNamingStrategy = PascalCase err := p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth) @@ -348,7 +348,7 @@ func TestParseSimpleApiV3(t *testing.T) { t.Parallel() searchDir := "testdata/v3/simple" - p := New(SetOpenAPIVersion(true)) + p := New(GenerateOpenApi3Docs(true)) p.PropNamingStrategy = PascalCase err := p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth) diff --git a/utils.go b/utils.go index df31ff2e1..8272c4cc8 100644 --- a/utils.go +++ b/utils.go @@ -1,6 +1,10 @@ package swag -import "unicode" +import ( + "reflect" + "unicode" + "unicode/utf8" +) // FieldsFunc split a string s by a func splitter into max n parts func FieldsFunc(s string, f func(rune2 rune) bool, n int) []string { @@ -53,3 +57,25 @@ func FieldsFunc(s string, f func(rune2 rune) bool, n int) []string { func FieldsByAnySpace(s string, n int) []string { return FieldsFunc(s, unicode.IsSpace, n) } + +// AppendUtf8Rune appends the UTF-8 encoding of r to the end of p and +// returns the extended buffer. If the rune is out of range, +// it appends the encoding of RuneError. +func AppendUtf8Rune(p []byte, r rune) []byte { + return utf8.AppendRune(p, r) +} + +// CanIntegerValue a wrapper of reflect.Value +type CanIntegerValue struct { + reflect.Value +} + +// CanInt reports whether Uint can be used without panicking. +func (v CanIntegerValue) CanInt() bool { + return v.Value.CanInt() +} + +// CanUint reports whether Uint can be used without panicking. +func (v CanIntegerValue) CanUint() bool { + return v.Value.CanUint() +} diff --git a/utils_go18.go b/utils_go18.go deleted file mode 100644 index 814f93433..000000000 --- a/utils_go18.go +++ /dev/null @@ -1,31 +0,0 @@ -//go:build go1.18 -// +build go1.18 - -package swag - -import ( - "reflect" - "unicode/utf8" -) - -// AppendUtf8Rune appends the UTF-8 encoding of r to the end of p and -// returns the extended buffer. If the rune is out of range, -// it appends the encoding of RuneError. -func AppendUtf8Rune(p []byte, r rune) []byte { - return utf8.AppendRune(p, r) -} - -// CanIntegerValue a wrapper of reflect.Value -type CanIntegerValue struct { - reflect.Value -} - -// CanInt reports whether Uint can be used without panicking. -func (v CanIntegerValue) CanInt() bool { - return v.Value.CanInt() -} - -// CanUint reports whether Uint can be used without panicking. -func (v CanIntegerValue) CanUint() bool { - return v.Value.CanUint() -} diff --git a/utils_other.go b/utils_other.go deleted file mode 100644 index 531c0df12..000000000 --- a/utils_other.go +++ /dev/null @@ -1,47 +0,0 @@ -//go:build !go1.18 -// +build !go1.18 - -package swag - -import ( - "reflect" - "unicode/utf8" -) - -// AppendUtf8Rune appends the UTF-8 encoding of r to the end of p and -// returns the extended buffer. If the rune is out of range, -// it appends the encoding of RuneError. -func AppendUtf8Rune(p []byte, r rune) []byte { - length := utf8.RuneLen(rune(r)) - if length > 0 { - utf8Slice := make([]byte, length) - utf8.EncodeRune(utf8Slice, rune(r)) - p = append(p, utf8Slice...) - } - return p -} - -// CanIntegerValue a wrapper of reflect.Value -type CanIntegerValue struct { - reflect.Value -} - -// CanInt reports whether Uint can be used without panicking. -func (v CanIntegerValue) CanInt() bool { - switch v.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return true - default: - return false - } -} - -// CanUint reports whether Uint can be used without panicking. -func (v CanIntegerValue) CanUint() bool { - switch v.Kind() { - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return true - default: - return false - } -} From 5a1bffdceece9c30fe184ed9f2a600e9db9a3313 Mon Sep 17 00:00:00 2001 From: Bogdan Ungureanu Date: Thu, 6 Apr 2023 02:02:19 +0300 Subject: [PATCH 2/4] chore: lint --- gen/gen.go | 9 +++++---- parser.go | 4 ++-- parserv3_test.go | 30 +++++++++++++++--------------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/gen/gen.go b/gen/gen.go index ef95c6ac4..80bb235f4 100644 --- a/gen/gen.go +++ b/gen/gen.go @@ -131,8 +131,9 @@ type Config struct { // include only tags mentioned when searching, comma separated Tags string - // if true, OpenAPI V3.1 spec will be generated - GenerateOpenApi3Doc bool + // GenerateOpenAPI3Doc if true, OpenAPI V3.1 spec will be generated + GenerateOpenAPI3Doc bool + // PackageName defines package name of generated `docs.go` PackageName string @@ -188,7 +189,7 @@ func (g *Gen) Build(config *Config) error { swag.SetOverrides(overrides), swag.ParseUsingGoList(config.ParseGoList), swag.SetTags(config.Tags), - swag.GenerateOpenApi3Docs(config.GenerateOpenApi3Doc), + swag.GenerateOpenAPI3Doc(config.GenerateOpenAPI3Doc), swag.SetCollectionFormat(config.CollectionFormat), ) @@ -205,7 +206,7 @@ func (g *Gen) Build(config *Config) error { return err } - if config.GenerateOpenApi3Doc { + if config.GenerateOpenAPI3Doc { return g.writeOpenAPI(config, p.GetOpenAPI()) } diff --git a/parser.go b/parser.go index 38bd8b580..0f92110eb 100644 --- a/parser.go +++ b/parser.go @@ -370,8 +370,8 @@ func ParseUsingGoList(enabled bool) func(parser *Parser) { } } -// GenerateOpenApi3Docs parses only those operations which match given extension -func GenerateOpenApi3Docs(enable bool) func(*Parser) { +// GenerateOpenAPI3Doc parses only those operations which match given extension +func GenerateOpenAPI3Doc(enable bool) func(*Parser) { return func(p *Parser) { p.openAPIVersion = enable } diff --git a/parserv3_test.go b/parserv3_test.go index e948cade9..70680722f 100644 --- a/parserv3_test.go +++ b/parserv3_test.go @@ -108,7 +108,7 @@ func TestParserParseGeneralApiInfoV3(t *testing.T) { gopath := os.Getenv("GOPATH") assert.NotNil(t, gopath) - p := New(GenerateOpenApi3Docs(true)) + p := New(GenerateOpenAPI3Doc(true)) err := p.ParseGeneralAPIInfo("testdata/v3/main.go") assert.NoError(t, err) @@ -172,7 +172,7 @@ func TestParser_ParseGeneralApiInfoExtensionsV3(t *testing.T) { gopath := os.Getenv("GOPATH") assert.NotNil(t, gopath) - p := New(GenerateOpenApi3Docs(true)) + p := New(GenerateOpenAPI3Doc(true)) err := p.ParseGeneralAPIInfo("testdata/v3/extensionsFail1.go") if assert.Error(t, err) { @@ -188,7 +188,7 @@ func TestParser_ParseGeneralApiInfoExtensionsV3(t *testing.T) { gopath := os.Getenv("GOPATH") assert.NotNil(t, gopath) - p := New(GenerateOpenApi3Docs(true)) + p := New(GenerateOpenAPI3Doc(true)) err := p.ParseGeneralAPIInfo("testdata/v3/extensionsFail2.go") if assert.Error(t, err) { @@ -203,7 +203,7 @@ func TestParserParseGeneralApiInfoWithOpsInSameFileV3(t *testing.T) { gopath := os.Getenv("GOPATH") assert.NotNil(t, gopath) - p := New(GenerateOpenApi3Docs(true)) + p := New(GenerateOpenAPI3Doc(true)) err := p.ParseGeneralAPIInfo("testdata/single_file_api/main.go") assert.NoError(t, err) @@ -216,7 +216,7 @@ func TestParserParseGeneralApiInfoWithOpsInSameFileV3(t *testing.T) { func TestParserParseGeneralAPIInfoMarkdownV3(t *testing.T) { t.Parallel() - p := New(SetMarkdownFileDirectory("testdata"), GenerateOpenApi3Docs(true)) + p := New(SetMarkdownFileDirectory("testdata"), GenerateOpenAPI3Doc(true)) mainAPIFile := "testdata/markdown.go" err := p.ParseGeneralAPIInfo(mainAPIFile) assert.NoError(t, err) @@ -224,7 +224,7 @@ func TestParserParseGeneralAPIInfoMarkdownV3(t *testing.T) { assert.Equal(t, "users", p.openAPI.Tags[0].Spec.Name) assert.Equal(t, "Users Tag Markdown Description", p.openAPI.Tags[0].Spec.Description) - p = New(GenerateOpenApi3Docs(true)) + p = New(GenerateOpenAPI3Doc(true)) err = p.ParseGeneralAPIInfo(mainAPIFile) assert.Error(t, err) @@ -235,14 +235,14 @@ func TestParserParseGeneralApiInfoFailedV3(t *testing.T) { gopath := os.Getenv("GOPATH") assert.NotNil(t, gopath) - p := New(GenerateOpenApi3Docs(true)) + p := New(GenerateOpenAPI3Doc(true)) assert.Error(t, p.ParseGeneralAPIInfo("testdata/noexist.go")) } func TestParserParseGeneralAPIInfoCollectionFormatV3(t *testing.T) { t.Parallel() - parser := New(GenerateOpenApi3Docs(true)) + parser := New(GenerateOpenAPI3Doc(true)) assert.NoError(t, parser.parseGeneralAPIInfoV3([]string{ "@query.collection.format csv", })) @@ -257,7 +257,7 @@ func TestParserParseGeneralAPIInfoCollectionFormatV3(t *testing.T) { func TestParserParseGeneralAPITagGroupsV3(t *testing.T) { t.Parallel() - parser := New(GenerateOpenApi3Docs(true)) + parser := New(GenerateOpenAPI3Doc(true)) assert.NoError(t, parser.parseGeneralAPIInfoV3([]string{ "@x-tagGroups [{\"name\":\"General\",\"tags\":[\"lanes\",\"video-recommendations\"]}]", })) @@ -269,12 +269,12 @@ func TestParserParseGeneralAPITagGroupsV3(t *testing.T) { func TestParserParseGeneralAPITagDocsV3(t *testing.T) { t.Parallel() - parser := New(GenerateOpenApi3Docs(true)) + parser := New(GenerateOpenAPI3Doc(true)) assert.Error(t, parser.parseGeneralAPIInfoV3([]string{ "@tag.name Test", "@tag.docs.description Best example documentation"})) - parser = New(GenerateOpenApi3Docs(true)) + parser = New(GenerateOpenAPI3Doc(true)) err := parser.parseGeneralAPIInfoV3([]string{ "@tag.name test", "@tag.description A test Tag", @@ -293,7 +293,7 @@ func TestGetAllGoFileInfoV3(t *testing.T) { searchDir := "testdata/pet" - p := New(GenerateOpenApi3Docs(true)) + p := New(GenerateOpenAPI3Doc(true)) err := p.getAllGoFileInfo("testdata", searchDir) assert.NoError(t, err) @@ -305,7 +305,7 @@ func TestParser_ParseTypeV3(t *testing.T) { searchDir := "testdata/v3/simple/" - p := New(GenerateOpenApi3Docs(true)) + p := New(GenerateOpenAPI3Doc(true)) err := p.getAllGoFileInfo("testdata", searchDir) assert.NoError(t, err) @@ -322,7 +322,7 @@ func TestParsePet(t *testing.T) { searchDir := "testdata/v3/pet" - p := New(GenerateOpenApi3Docs(true)) + p := New(GenerateOpenAPI3Doc(true)) p.PropNamingStrategy = PascalCase err := p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth) @@ -348,7 +348,7 @@ func TestParseSimpleApiV3(t *testing.T) { t.Parallel() searchDir := "testdata/v3/simple" - p := New(GenerateOpenApi3Docs(true)) + p := New(GenerateOpenAPI3Doc(true)) p.PropNamingStrategy = PascalCase err := p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth) From 7de8b6d3579fbffca7e8a0a8ac6e387cda2f9c30 Mon Sep 17 00:00:00 2001 From: Bogdan Ungureanu Date: Thu, 6 Apr 2023 02:04:10 +0300 Subject: [PATCH 3/4] chore: lint --- cmd/swag/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/swag/main.go b/cmd/swag/main.go index bb08bffbf..f9f12e710 100644 --- a/cmd/swag/main.go +++ b/cmd/swag/main.go @@ -207,7 +207,7 @@ func initAction(ctx *cli.Context) error { Tags: ctx.String(tagsFlag), PackageName: ctx.String(packageName), Debugger: logger, - GenerateOpenApi3Doc: ctx.Bool(openAPIVersionFlag), + GenerateOpenAPI3Doc: ctx.Bool(openAPIVersionFlag), CollectionFormat: collectionFormat, }) } From f66679490eaf53f8f88bef09e7d0dede3cbf3361 Mon Sep 17 00:00:00 2001 From: Bogdan Ungureanu Date: Thu, 6 Apr 2023 02:31:08 +0300 Subject: [PATCH 4/4] chore: use embed for generated doc --- gen/gen.go | 95 +++++++++++++++++++++++++++++++--------- gen/gen_test.go | 41 ----------------- gen/genv3.go | 109 ---------------------------------------------- gen/src/oas2.tmpl | 23 ++++++++++ gen/src/oas3.tmpl | 20 +++++++++ 5 files changed, 118 insertions(+), 170 deletions(-) delete mode 100644 gen/genv3.go create mode 100644 gen/src/oas2.tmpl create mode 100644 gen/src/oas3.tmpl diff --git a/gen/gen.go b/gen/gen.go index 80bb235f4..1baea3b2a 100644 --- a/gen/gen.go +++ b/gen/gen.go @@ -3,6 +3,7 @@ package gen import ( "bufio" "bytes" + "embed" "encoding/json" "fmt" "go/format" @@ -40,6 +41,9 @@ type Gen struct { debug Debugger } +//go:embed src/*.tmpl +var tmpl embed.FS + // Debugger is the interface that wraps the basic Printf method. type Debugger interface { Printf(format string, v ...interface{}) @@ -396,14 +400,14 @@ func parseOverrides(r io.Reader) (map[string]string, error) { } func (g *Gen) writeGoDoc(packageName string, output io.Writer, swagger *v2.Swagger, config *Config) error { - generator, err := template.New("swagger_info").Funcs(template.FuncMap{ + generator, err := template.New("oas2.tmpl").Funcs(template.FuncMap{ "printDoc": func(v string) string { // Add schemes v = "{\n \"schemes\": {{ marshal .Schemes }}," + v[1:] // Sanitize backticks return strings.Replace(v, "`", "`+\"`\"+`", -1) }, - }).Parse(packageTemplate) + }).ParseFS(tmpl, "src/*.tmpl") if err != nil { return err } @@ -484,27 +488,78 @@ func (g *Gen) writeGoDoc(packageName string, output io.Writer, swagger *v2.Swagg return err } -var packageTemplate = `// Code generated by swaggo/swag{{ if .GeneratedTime }} at {{ .Timestamp }}{{ end }}. DO NOT EDIT. +func (g *Gen) writeGoDocV3(packageName string, output io.Writer, openAPI *v3.OpenAPI, config *Config) error { + generator, err := template.New("oas3.tmpl").Funcs(template.FuncMap{ + "printDoc": func(v string) string { + // Add schemes + v = "{\n \"schemes\": {{ marshal .Schemes }}," + v[1:] + // Sanitize backticks + return strings.Replace(v, "`", "`+\"`\"+`", -1) + }, + }).ParseFS(tmpl, "src/*.tmpl") + if err != nil { + return err + } -package docs + openAPISpec := v3.OpenAPI{ + Components: openAPI.Components, + OpenAPI: openAPI.OpenAPI, + Info: &v3.Extendable[v3.Info]{ + Spec: &v3.Info{ + Description: "{{escape .Description}}", + Title: "{{.Title}}", + Version: "{{.Version}}", + TermsOfService: openAPI.Info.Spec.TermsOfService, + Contact: openAPI.Info.Spec.Contact, + License: openAPI.Info.Spec.License, + Summary: openAPI.Info.Spec.Summary, + }, + Extensions: openAPI.Info.Extensions, + }, + ExternalDocs: openAPI.ExternalDocs, + Paths: openAPI.Paths, + WebHooks: openAPI.WebHooks, + JsonSchemaDialect: openAPI.JsonSchemaDialect, + Security: openAPI.Security, + Tags: openAPI.Tags, + Servers: openAPI.Servers, + } -import "github.com/swaggo/swag" + // crafted docs.json + buf, err := g.jsonIndent(openAPISpec) + if err != nil { + return err + } -const docTemplate{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }} = ` + "`{{ printDoc .Doc}}`" + ` + buffer := &bytes.Buffer{} -// SwaggerInfo{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }} holds exported Swagger Info so clients can modify it -var SwaggerInfo{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }} = &swag.Spec{ - Version: {{ printf "%q" .Version}}, - Host: {{ printf "%q" .Host}}, - BasePath: {{ printf "%q" .BasePath}}, - Schemes: []string{ {{ range $index, $schema := .Schemes}}{{if gt $index 0}},{{end}}{{printf "%q" $schema}}{{end}} }, - Title: {{ printf "%q" .Title}}, - Description: {{ printf "%q" .Description}}, - InfoInstanceName: {{ printf "%q" .InstanceName }}, - SwaggerTemplate: docTemplate{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }}, -} + err = generator.Execute(buffer, struct { + Timestamp time.Time + Doc string + PackageName string + Title string + Description string + Version string + InstanceName string + GeneratedTime bool + }{ + Timestamp: time.Now(), + GeneratedTime: config.GeneratedTime, + Doc: string(buf), + PackageName: packageName, + Title: openAPI.Info.Spec.Title, + Description: openAPI.Info.Spec.Description, + Version: openAPI.Info.Spec.Version, + InstanceName: config.InstanceName, + }) + if err != nil { + return err + } -func init() { - swag.Register(SwaggerInfo{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }}.InstanceName(), SwaggerInfo{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }}) + code := g.formatSource(buffer.Bytes()) + + // write + _, err = output.Write(code) + + return err } -` diff --git a/gen/gen_test.go b/gen/gen_test.go index 62a313e6a..9169dd355 100644 --- a/gen/gen_test.go +++ b/gen/gen_test.go @@ -14,7 +14,6 @@ import ( "strings" "testing" - "github.com/go-openapi/spec" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/swaggo/swag" @@ -516,46 +515,6 @@ func (w *mockWriter) Write(data []byte) (int, error) { return len(data), nil } -func TestGen_writeGoDoc(t *testing.T) { - gen := New() - - swapTemplate := packageTemplate - - packageTemplate = `{{{` - err := gen.writeGoDoc("docs", nil, nil, &Config{}) - assert.Error(t, err) - - packageTemplate = `{{.Data}}` - swagger := &spec.Swagger{ - VendorExtensible: spec.VendorExtensible{}, - SwaggerProps: spec.SwaggerProps{ - Info: &spec.Info{}, - }, - } - - err = gen.writeGoDoc("docs", &mockWriter{}, swagger, &Config{}) - assert.Error(t, err) - - packageTemplate = `{{ if .GeneratedTime }}Fake Time{{ end }}` - err = gen.writeGoDoc("docs", - &mockWriter{ - hook: func(data []byte) { - assert.Equal(t, "Fake Time", string(data)) - }, - }, swagger, &Config{GeneratedTime: true}) - assert.NoError(t, err) - - err = gen.writeGoDoc("docs", - &mockWriter{ - hook: func(data []byte) { - assert.Equal(t, "", string(data)) - }, - }, swagger, &Config{GeneratedTime: false}) - assert.NoError(t, err) - - packageTemplate = swapTemplate -} - func TestGen_GeneratedDoc(t *testing.T) { config := &Config{ SearchDir: searchDir, diff --git a/gen/genv3.go b/gen/genv3.go deleted file mode 100644 index eaf80a22e..000000000 --- a/gen/genv3.go +++ /dev/null @@ -1,109 +0,0 @@ -package gen - -import ( - "bytes" - "io" - "strings" - "text/template" - "time" - - "github.com/sv-tools/openapi/spec" -) - -func (g *Gen) writeGoDocV3(packageName string, output io.Writer, openAPI *spec.OpenAPI, config *Config) error { - generator, err := template.New("swagger_info").Funcs(template.FuncMap{ - "printDoc": func(v string) string { - // Add schemes - v = "{\n \"schemes\": {{ marshal .Schemes }}," + v[1:] - // Sanitize backticks - return strings.Replace(v, "`", "`+\"`\"+`", -1) - }, - }).Parse(packageTemplateV3) - if err != nil { - return err - } - - openAPISpec := spec.OpenAPI{ - Components: openAPI.Components, - OpenAPI: openAPI.OpenAPI, - Info: &spec.Extendable[spec.Info]{ - Spec: &spec.Info{ - Description: "{{escape .Description}}", - Title: "{{.Title}}", - Version: "{{.Version}}", - TermsOfService: openAPI.Info.Spec.TermsOfService, - Contact: openAPI.Info.Spec.Contact, - License: openAPI.Info.Spec.License, - Summary: openAPI.Info.Spec.Summary, - }, - Extensions: openAPI.Info.Extensions, - }, - ExternalDocs: openAPI.ExternalDocs, - Paths: openAPI.Paths, - WebHooks: openAPI.WebHooks, - JsonSchemaDialect: openAPI.JsonSchemaDialect, - Security: openAPI.Security, - Tags: openAPI.Tags, - Servers: openAPI.Servers, - } - - // crafted docs.json - buf, err := g.jsonIndent(openAPISpec) - if err != nil { - return err - } - - buffer := &bytes.Buffer{} - - err = generator.Execute(buffer, struct { - Timestamp time.Time - Doc string - PackageName string - Title string - Description string - Version string - InstanceName string - GeneratedTime bool - }{ - Timestamp: time.Now(), - GeneratedTime: config.GeneratedTime, - Doc: string(buf), - PackageName: packageName, - Title: openAPI.Info.Spec.Title, - Description: openAPI.Info.Spec.Description, - Version: openAPI.Info.Spec.Version, - InstanceName: config.InstanceName, - }) - if err != nil { - return err - } - - code := g.formatSource(buffer.Bytes()) - - // write - _, err = output.Write(code) - - return err -} - -var packageTemplateV3 = `// Code generated by swaggo/swag{{ if .GeneratedTime }} at {{ .Timestamp }}{{ end }}. DO NOT EDIT - -package docs - -import "github.com/swaggo/swag" - -const docTemplate{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }} = ` + "`{{ printDoc .Doc}}`" + ` - -// SwaggerInfo{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }} holds exported Swagger Info so clients can modify it -var SwaggerInfo{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }} = &swag.Spec{ - Version: {{ printf "%q" .Version}}, - Title: {{ printf "%q" .Title}}, - Description: {{ printf "%q" .Description}}, - InfoInstanceName: {{ printf "%q" .InstanceName }}, - SwaggerTemplate: docTemplate{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }}, -} - -func init() { - swag.Register(SwaggerInfo{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }}.InstanceName(), SwaggerInfo{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }}) -} -` diff --git a/gen/src/oas2.tmpl b/gen/src/oas2.tmpl new file mode 100644 index 000000000..b7bce69b9 --- /dev/null +++ b/gen/src/oas2.tmpl @@ -0,0 +1,23 @@ +// Code generated by swaggo/swag{{ if .GeneratedTime }} at {{ .Timestamp }}{{ end }}. DO NOT EDIT. + +package docs + +import "github.com/swaggo/swag" + +const docTemplate{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }} = `{{ printDoc .Doc}}` + +// SwaggerInfo{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }} holds exported Swagger Info so clients can modify it +var SwaggerInfo{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }} = &swag.Spec{ + Version: {{ printf "%q" .Version}}, + Host: {{ printf "%q" .Host}}, + BasePath: {{ printf "%q" .BasePath}}, + Schemes: []string{ {{ range $index, $schema := .Schemes}}{{if gt $index 0}},{{end}}{{printf "%q" $schema}}{{end}} }, + Title: {{ printf "%q" .Title}}, + Description: {{ printf "%q" .Description}}, + InfoInstanceName: {{ printf "%q" .InstanceName }}, + SwaggerTemplate: docTemplate{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }}, +} + +func init() { + swag.Register(SwaggerInfo{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }}.InstanceName(), SwaggerInfo{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }}) +} diff --git a/gen/src/oas3.tmpl b/gen/src/oas3.tmpl new file mode 100644 index 000000000..20cd33af8 --- /dev/null +++ b/gen/src/oas3.tmpl @@ -0,0 +1,20 @@ +// Code generated by swaggo/swag{{ if .GeneratedTime }} at {{ .Timestamp }}{{ end }}. DO NOT EDIT + +package docs + +import "github.com/swaggo/swag" + +const docTemplate{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }} = `{{ printDoc .Doc}}` + +// SwaggerInfo{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }} holds exported Swagger Info so clients can modify it +var SwaggerInfo{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }} = &swag.Spec{ + Version: {{ printf "%q" .Version}}, + Title: {{ printf "%q" .Title}}, + Description: {{ printf "%q" .Description}}, + InfoInstanceName: {{ printf "%q" .InstanceName }}, + SwaggerTemplate: docTemplate{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }}, +} + +func init() { + swag.Register(SwaggerInfo{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }}.InstanceName(), SwaggerInfo{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }}) +} \ No newline at end of file