From a9497eca2057f602d0cb1156bce4e09b00dc5b7a Mon Sep 17 00:00:00 2001 From: Antoine GIRARD Date: Sun, 22 May 2022 19:37:29 +0200 Subject: [PATCH 1/5] treat any and error as interface (empty type) --- parser.go | 3 +++ schema.go | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/parser.go b/parser.go index 815b2f8c6..fc4c23099 100644 --- a/parser.go +++ b/parser.go @@ -868,6 +868,9 @@ func convertFromSpecificToPrimitive(typeName string) (string, error) { } func (parser *Parser) getTypeSchema(typeName string, file *ast.File, ref bool) (*spec.Schema, error) { + if IsInterfaceLike(typeName) { + return &spec.Schema{}, nil + } if IsGolangPrimitiveType(typeName) { return PrimitiveSchema(TransToValidSchemeType(typeName)), nil } diff --git a/schema.go b/schema.go index a23d21b36..c7f2ec416 100644 --- a/schema.go +++ b/schema.go @@ -26,6 +26,8 @@ const ( STRING = "string" // FUNC represent a function value. FUNC = "func" + // ERROR represent a error value. + ERROR = "error" // INTERFACE represent a interface value. INTERFACE = "interface{}" // ANY represent a any value. @@ -63,6 +65,11 @@ func IsPrimitiveType(typeName string) bool { return false } +// IsInterfaceLike determines whether the swagger type name is an go named interface type like error type. +func IsInterfaceLike(typeName string) bool { + return typeName == ERROR || typeName == ANY +} + // IsNumericType determines whether the swagger type name is a numeric type. func IsNumericType(typeName string) bool { return typeName == INTEGER || typeName == NUMBER @@ -106,8 +113,7 @@ func IsGolangPrimitiveType(typeName string) bool { "float32", "float64", "bool", - "string", - "any": + "string": return true } From b3f0c90e1c4e8371ff12c9ee7168b899122e5108 Mon Sep 17 00:00:00 2001 From: Antoine GIRARD Date: Sun, 22 May 2022 20:18:36 +0200 Subject: [PATCH 2/5] add basic test --- schema_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/schema_test.go b/schema_test.go index 0fe60e816..58135b81a 100644 --- a/schema_test.go +++ b/schema_test.go @@ -142,6 +142,15 @@ func TestIsNumericType(t *testing.T) { assert.Equal(t, IsNumericType(STRING), false) } +func TestIsInterfaceLike(t *testing.T) { + t.Parallel() + + assert.Equal(t, IsInterfaceLike(ERROR), true) + assert.Equal(t, IsInterfaceLike(ANY), true) + + assert.Equal(t, IsInterfaceLike(STRING), false) +} + func TestTypeDocName(t *testing.T) { t.Parallel() From 2ab70ed50a3ffb53d83ba945efcaef7b218aa9be Mon Sep 17 00:00:00 2001 From: Antoine GIRARD Date: Sun, 22 May 2022 20:29:50 +0200 Subject: [PATCH 3/5] validate that interface{} = any = error result --- gen/gen_test.go | 42 ++++++++++++++++++++ testdata/error/api/api.go | 23 +++++++++++ testdata/error/errors/errors.go | 14 +++++++ testdata/error/expected.json | 69 +++++++++++++++++++++++++++++++++ testdata/error/main.go | 27 +++++++++++++ testdata/error/web/handler.go | 7 ++++ 6 files changed, 182 insertions(+) create mode 100644 testdata/error/api/api.go create mode 100644 testdata/error/errors/errors.go create mode 100644 testdata/error/expected.json create mode 100644 testdata/error/main.go create mode 100644 testdata/error/web/handler.go diff --git a/gen/gen_test.go b/gen/gen_test.go index 62a26c2d3..ef0b0f611 100644 --- a/gen/gen_test.go +++ b/gen/gen_test.go @@ -779,3 +779,45 @@ func TestGen_Debugger(t *testing.T) { _ = os.Remove(expectedFile) } } + +func TestGen_ErrorAndInterface(t *testing.T) { + config := &Config{ + SearchDir: "../testdata/error", + MainAPIFile: "./main.go", + OutputDir: "../testdata/error/docs", + OutputTypes: outputTypes, + PropNamingStrategy: "", + } + + assert.NoError(t, New().Build(config)) + + expectedFiles := []string{ + filepath.Join(config.OutputDir, "docs.go"), + filepath.Join(config.OutputDir, "swagger.json"), + filepath.Join(config.OutputDir, "swagger.yaml"), + } + t.Cleanup(func() { + for _, expectedFile := range expectedFiles { + _ = os.Remove(expectedFile) + } + }) + + // check files + for _, expectedFile := range expectedFiles { + if _, err := os.Stat(expectedFile); os.IsNotExist(err) { + require.NoError(t, err) + } + } + + // check content + jsonOutput, err := ioutil.ReadFile(filepath.Join(config.OutputDir, "swagger.json")) + if err != nil { + require.NoError(t, err) + } + expectedJSON, err := ioutil.ReadFile(filepath.Join(config.SearchDir, "expected.json")) + if err != nil { + require.NoError(t, err) + } + + assert.JSONEq(t, string(expectedJSON), string(jsonOutput)) +} diff --git a/testdata/error/api/api.go b/testdata/error/api/api.go new file mode 100644 index 000000000..8c59da5c7 --- /dev/null +++ b/testdata/error/api/api.go @@ -0,0 +1,23 @@ +package api + +import ( + "net/http" + + . "github.com/swaggo/swag/testdata/error/errors" + _ "github.com/swaggo/swag/testdata/error/web" +) + +// Upload do something +// @Summary Upload file +// @Description Upload file +// @ID file.upload +// @Accept multipart/form-data +// @Produce json +// @Param file formData file true "this is a test file" +// @Success 200 {string} string "ok" +// @Failure 400 {object} web.CrossErrors "Abort !!" +// @Router /file/upload [post] +func Upload(w http.ResponseWriter, r *http.Request) { + //write your code + _ = Errors{} +} diff --git a/testdata/error/errors/errors.go b/testdata/error/errors/errors.go new file mode 100644 index 000000000..60349c12e --- /dev/null +++ b/testdata/error/errors/errors.go @@ -0,0 +1,14 @@ +package errors + +// CustomInterface some interface +type CustomInterface interface { + Error() string +} + +// Errors errors and interfaces +type Errors struct { + Error error + ErrorInterface CustomInterface + Interface interface{} + Any any +} diff --git a/testdata/error/expected.json b/testdata/error/expected.json new file mode 100644 index 000000000..a188df22f --- /dev/null +++ b/testdata/error/expected.json @@ -0,0 +1,69 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server.", + "title": "Swagger Example API", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "API Support", + "url": "http://www.swagger.io/support", + "email": "support@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "1.0" + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "paths": { + "/file/upload": { + "post": { + "description": "Upload file", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "summary": "Upload file", + "operationId": "file.upload", + "parameters": [ + { + "type": "file", + "description": "this is a test file", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Abort !!", + "schema": { + "$ref": "#/definitions/web.CrossErrors" + } + } + } + } + } + }, + "definitions": { + "web.CrossErrors": { + "type": "object", + "properties": { + "any": {}, + "error": {}, + "errorInterface": {}, + "interface": {} + } + } + } +} \ No newline at end of file diff --git a/testdata/error/main.go b/testdata/error/main.go new file mode 100644 index 000000000..249c0bb85 --- /dev/null +++ b/testdata/error/main.go @@ -0,0 +1,27 @@ +package main + +import ( + "net/http" + + "github.com/swaggo/swag/testdata/error/api" +) + +// @title Swagger Example API +// @version 1.0 +// @description This is a sample server Petstore server. +// @termsOfService http://swagger.io/terms/ + +// @contact.name API Support +// @contact.url http://www.swagger.io/support +// @contact.email support@swagger.io + +// @license.name Apache 2.0 +// @license.url http://www.apache.org/licenses/LICENSE-2.0.html + +// @host petstore.swagger.io +// @BasePath /v2 + +func main() { + http.HandleFunc("/testapi/upload", api.Upload) + http.ListenAndServe(":8080", nil) +} diff --git a/testdata/error/web/handler.go b/testdata/error/web/handler.go new file mode 100644 index 000000000..c46f90a9d --- /dev/null +++ b/testdata/error/web/handler.go @@ -0,0 +1,7 @@ +package web + +import ( + "github.com/swaggo/swag/testdata/error/errors" +) + +type CrossErrors errors.Errors From cfec0b633e4aff16cdf83eaf71a0001041424e9b Mon Sep 17 00:00:00 2001 From: Antoine GIRARD Date: Wed, 25 May 2022 10:39:01 +0200 Subject: [PATCH 4/5] :white_check_mark: add test (similar to gen) in parser package --- parser_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/parser_test.go b/parser_test.go index 07ac66518..725e7a1b6 100644 --- a/parser_test.go +++ b/parser_test.go @@ -826,6 +826,20 @@ func TestParseSimpleApi1(t *testing.T) { assert.JSONEq(t, string(expected), string(b)) } +func TestParseInterfaceError(t *testing.T) { + t.Parallel() + + expected, err := ioutil.ReadFile("testdata/error/expected.json") + assert.NoError(t, err) + searchDir := "testdata/error" + p := New() + err = p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth) + assert.NoError(t, err) + + b, _ := json.MarshalIndent(p.swagger, "", " ") + assert.JSONEq(t, string(expected), string(b)) +} + func TestParseSimpleApi_ForSnakecase(t *testing.T) { t.Parallel() From 1e6f665b9e97639b3561f4488d76ff17e3fa6edd Mon Sep 17 00:00:00 2001 From: Antoine GIRARD Date: Wed, 25 May 2022 10:39:50 +0200 Subject: [PATCH 5/5] ... --- parser_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser_test.go b/parser_test.go index 725e7a1b6..447e622b7 100644 --- a/parser_test.go +++ b/parser_test.go @@ -826,7 +826,7 @@ func TestParseSimpleApi1(t *testing.T) { assert.JSONEq(t, string(expected), string(b)) } -func TestParseInterfaceError(t *testing.T) { +func TestParseInterfaceAndError(t *testing.T) { t.Parallel() expected, err := ioutil.ReadFile("testdata/error/expected.json")