Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement error (and any) like interface #1212

Merged
merged 5 commits into from Jun 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
42 changes: 42 additions & 0 deletions gen/gen_test.go
Expand Up @@ -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))
}
3 changes: 3 additions & 0 deletions parser.go
Expand Up @@ -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
}
Comment on lines +871 to +873
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to cover this with unit tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should already be covered by TestGen_ErrorAndInterface and TestIsInterfaceLike but I can recheck.

Copy link
Contributor Author

@sapk sapk May 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a test similar to TestGen_ErrorAndInterface based on testdata in the root package.

if IsGolangPrimitiveType(typeName) {
return PrimitiveSchema(TransToValidSchemeType(typeName)), nil
}
Expand Down
14 changes: 14 additions & 0 deletions parser_test.go
Expand Up @@ -826,6 +826,20 @@ func TestParseSimpleApi1(t *testing.T) {
assert.JSONEq(t, string(expected), string(b))
}

func TestParseInterfaceAndError(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()

Expand Down
10 changes: 8 additions & 2 deletions schema.go
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -106,8 +113,7 @@ func IsGolangPrimitiveType(typeName string) bool {
"float32",
"float64",
"bool",
"string",
"any":
"string":
return true
}

Expand Down
9 changes: 9 additions & 0 deletions schema_test.go
Expand Up @@ -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()

Expand Down
23 changes: 23 additions & 0 deletions 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{}
}
14 changes: 14 additions & 0 deletions 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
}
69 changes: 69 additions & 0 deletions 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": {}
}
}
}
}
27 changes: 27 additions & 0 deletions 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)
}
7 changes: 7 additions & 0 deletions testdata/error/web/handler.go
@@ -0,0 +1,7 @@
package web

import (
"github.com/swaggo/swag/testdata/error/errors"
)

type CrossErrors errors.Errors