diff --git a/example/override/.swaggo b/example/override/.swaggo new file mode 100644 index 000000000..07fc329cf --- /dev/null +++ b/example/override/.swaggo @@ -0,0 +1,2 @@ +replace sql.NullString string +replace sql.NullInt64 int64 diff --git a/example/override/docs/docs.go b/example/override/docs/docs.go new file mode 100644 index 000000000..7b849a658 --- /dev/null +++ b/example/override/docs/docs.go @@ -0,0 +1,89 @@ +// Package docs GENERATED BY SWAG; DO NOT EDIT +// This file was generated by swaggo/swag +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "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": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/testapi/update-product/{product_id}": { + "post": { + "consumes": [ + "application/json" + ], + "summary": "Update product attributes", + "operationId": "update-product", + "parameters": [ + { + "type": "integer", + "description": "Product ID", + "name": "product_id", + "in": "path", + "required": true + }, + { + "description": " ", + "name": "_", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/main.ProductUpdates" + } + } + ], + "responses": {} + } + } + }, + "definitions": { + "main.ProductUpdates": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "stock": { + "type": "integer" + }, + "type": { + "type": "string" + } + } + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "1.0", + Host: "product_info.swagger.io", + BasePath: "/v2", + Schemes: []string{}, + Title: "Swagger Example API", + Description: "This is a sample server for updating product information.", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/example/override/docs/swagger.json b/example/override/docs/swagger.json new file mode 100644 index 000000000..479b774b4 --- /dev/null +++ b/example/override/docs/swagger.json @@ -0,0 +1,66 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server for updating product information.", + "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": "product_info.swagger.io", + "basePath": "/v2", + "paths": { + "/testapi/update-product/{product_id}": { + "post": { + "consumes": [ + "application/json" + ], + "summary": "Update product attributes", + "operationId": "update-product", + "parameters": [ + { + "type": "integer", + "description": "Product ID", + "name": "product_id", + "in": "path", + "required": true + }, + { + "description": " ", + "name": "_", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/main.ProductUpdates" + } + } + ], + "responses": {} + } + } + }, + "definitions": { + "main.ProductUpdates": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "stock": { + "type": "integer" + }, + "type": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/example/override/docs/swagger.yaml b/example/override/docs/swagger.yaml new file mode 100644 index 000000000..091007419 --- /dev/null +++ b/example/override/docs/swagger.yaml @@ -0,0 +1,45 @@ +basePath: /v2 +definitions: + main.ProductUpdates: + properties: + description: + type: string + stock: + type: integer + type: + type: string + type: object +host: product_info.swagger.io +info: + contact: + email: support@swagger.io + name: API Support + url: http://www.swagger.io/support + description: This is a sample server for updating product information. + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html + termsOfService: http://swagger.io/terms/ + title: Swagger Example API + version: "1.0" +paths: + /testapi/update-product/{product_id}: + post: + consumes: + - application/json + operationId: update-product + parameters: + - description: Product ID + in: path + name: product_id + required: true + type: integer + - description: ' ' + in: body + name: _ + required: true + schema: + $ref: '#/definitions/main.ProductUpdates' + responses: {} + summary: Update product attributes +swagger: "2.0" diff --git a/example/override/handler.go b/example/override/handler.go new file mode 100644 index 000000000..4f9e67003 --- /dev/null +++ b/example/override/handler.go @@ -0,0 +1,30 @@ +package main + +import ( + "database/sql" + "encoding/json" + "net/http" +) + +type ProductUpdates struct { + Type sql.NullString `json:"type"` + Description sql.NullString `json:"description"` + Stock sql.NullInt64 `json:"stock"` +} + +// UpdateProduct example +// @Summary Update product attributes +// @ID update-product +// @Accept json +// @Param product_id path int true "Product ID" +// @Param _ body ProductUpdates true " " +// @Router /testapi/update-product/{product_id} [post] +func UpdateProduct(w http.ResponseWriter, r *http.Request) { + var pUpdates ProductUpdates + if err := json.NewDecoder(r.Body).Decode(&pUpdates); err != nil { + // write your code + return + } + + // write your code +} diff --git a/example/override/main.go b/example/override/main.go new file mode 100644 index 000000000..c19711ea4 --- /dev/null +++ b/example/override/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "net/http" +) + +// @title Swagger Example API +// @version 1.0 +// @description This is a sample server with null types overridden with primitive types. +// @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 product_info.swagger.io +// @BasePath /v2 +func main() { + http.HandleFunc("/testapi/update-product", UpdateProduct) + http.ListenAndServe(":8080", nil) +} diff --git a/parser.go b/parser.go index e284b594b..20f825f2e 100644 --- a/parser.go +++ b/parser.go @@ -871,6 +871,11 @@ func convertFromSpecificToPrimitive(typeName string) (string, error) { } func (parser *Parser) getTypeSchema(typeName string, file *ast.File, ref bool) (*spec.Schema, error) { + if override, ok := parser.Overrides[typeName]; ok { + parser.debug.Printf("Override detected for %s: using %s instead", typeName, override) + typeName = override + } + if IsInterfaceLike(typeName) { return &spec.Schema{}, nil } diff --git a/parser_test.go b/parser_test.go index 447e622b7..87c9099ab 100644 --- a/parser_test.go +++ b/parser_test.go @@ -78,6 +78,34 @@ func TestSetOverrides(t *testing.T) { assert.Equal(t, overrides, p.Overrides) } +func TestOverrides_getTypeSchema(t *testing.T) { + t.Parallel() + + overrides := map[string]string{ + "sql.NullString": "string", + } + + p := New(SetOverrides(overrides)) + + t.Run("Override sql.NullString by string", func(t *testing.T) { + t.Parallel() + + s, err := p.getTypeSchema("sql.NullString", nil, false) + if assert.NoError(t, err) { + assert.Truef(t, s.Type.Contains("string"), "type sql.NullString should be overridden by string") + } + }) + + t.Run("Missing Override for sql.NullInt64", func(t *testing.T) { + t.Parallel() + + _, err := p.getTypeSchema("sql.NullInt64", nil, false) + if assert.Error(t, err) { + assert.Equal(t, "cannot find type definition: sql.NullInt64", err.Error()) + } + }) +} + func TestParser_ParseDefinition(t *testing.T) { p := New() @@ -479,7 +507,7 @@ func TestParser_ParseProduceComment(t *testing.T) { assert.Equal(t, parser.swagger.Produces, expected) } -func TestParser_ParseGeneralAPIInfoCollectionFromat(t *testing.T) { +func TestParser_ParseGeneralAPIInfoCollectionFormat(t *testing.T) { t.Parallel() parser := New()