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

Use early overrides to allow converting unsupported types. #1209

Merged
merged 12 commits into from Jun 17, 2022
2 changes: 2 additions & 0 deletions example/override/.swaggo
@@ -0,0 +1,2 @@
replace sql.NullString string
replace sql.NullInt64 int64
89 changes: 89 additions & 0 deletions 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)
}
66 changes: 66 additions & 0 deletions 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"
}
}
}
}
}
45 changes: 45 additions & 0 deletions 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"
30 changes: 30 additions & 0 deletions 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
}
24 changes: 24 additions & 0 deletions 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)
}
5 changes: 5 additions & 0 deletions parser.go
Expand Up @@ -898,6 +898,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 IsGolangPrimitiveType(typeName) {
return PrimitiveSchema(TransToValidSchemeType(typeName)), nil
}
Expand Down
30 changes: 29 additions & 1 deletion parser_test.go
Expand Up @@ -77,6 +77,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()

Expand Down Expand Up @@ -478,7 +506,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()
Expand Down