wi2l / fizz Goto Github PK
View Code? Open in Web Editor NEW:lemon: Gin wrapper with OpenAPI 3 spec generation
Home Page: https://pkg.go.dev/github.com/wI2L/fizz
License: MIT License
:lemon: Gin wrapper with OpenAPI 3 spec generation
Home Page: https://pkg.go.dev/github.com/wI2L/fizz
License: MIT License
Hi,
We're returning a large ndjson stream on some calls, and the problem we have is that we can't figure out how to get fizz go generate an openapi spec that indicates that ndjson is returned as the output, so instead the spec indicates nothing is returned.
We're essentially writing x-ndjson directly to the Writer on the context.
We tried using a fizz.Response(200...) but we can't override that.
I have an API that has one field that can return an arbitrary data type. Normally, I would avoid that scenario as much as possible, but in my case it does make sense to have this behavior.
On my output struct, I have a field result interface{}
, but when generating the OpenAPI 3.0 schema, it's completely dropped. I think ideally, this would generate a jsonschema of "type":["number","string","boolean","object","array", "null"]
Is there to express this? If not, do you mind if I make a PR to add this behavior?
Prerequisites:
type args struct {
Id []int64 `path:"id" validate:"dive,required" explode:"1"`
}
fizz.GET("/:id", []fizz.OperationOption{/*...*//}, tonic.Handler(func(ctx *gin.Context, args *args) error {
fmt.Println(args)
return nil
}, 200))
Then, when I make a request: curl "http://localhost:8080/1,2,3"
, I get error: binding error on field 'Id' of type 'args': strconv.ParseInt: parsing "1,2,3": invalid syntax
.
With the 1.18 version of Go, we can have this:
type MyStruct[T ObjA | ObjB] struct {
Result T `json:"result"`
}
But unfortunately the fizz cannot generate the spec for this :(
go version go1.11.1 darwin/amd64
The OpenAPI generation generates a bad syntax for a path which have an additionnal response with the status code not found (404)
. Instead of having 404
in the OpenAPI JSON structure, we have a ฦ
. See below.
Here is my code for a router:
// Defines groups of routes
lightsGroup := f.Group("/lights", "Lights", "")
// Defines Lights group's routes
lightsGroup.GET("/", []fizz.OperationOption{
fizz.Summary("Gets a list of lights corresponding to the selector."),
fizz.Description("Returns a list of lights with their informations."),
fizz.Response(string(http.StatusNotFound), "cannot find lights corresponding to the selector.", nil, nil),
}, tonic.Handler(api.getDevices, http.StatusOK))
Here is the OpenAPI generation for this path:
{
"paths":{
"/lights/":{
"get":{
"tags":[
"Lights"
],
"summary":"Gets a list of lights corresponding to the selector.",
"description":"Returns a list of lights with their information.",
"operationId":"getDevices)-fm",
"parameters":[
{
"name":"selector",
"in":"query",
"description":"The selector to limit which lights are controlled. More informations about format here: https://api.developer.lifx.com/docs/selectors",
"schema":{
"type":"string",
"description":"The selector to limit which lights are controlled. More informations about format here: https://api.developer.lifx.com/docs/selectors",
"default":"all"
}
}
],
"responses":{
"200":{
"description":"OK",
"content":{
"application/json":{
"schema":{
"type":"array",
"items":{
"$ref":"#/components/schemas/LifxLifx"
}
}
}
}
},
"ฦ":{
"description":"cannot find lights corresponding to the selector."
}
}
}
},
...
You can see "ฦ"
instead of "404"
.
Is it possible to have parameters and the body passed to a function?
For example let's say my endpoint is POST /game/{name}/question
.
Where the defintion looks like:
fizzApp.Group("/game", "game", "Related to managing the game types.")
// ...
func route(grp *fizz.RouterGroup) {
/ ...
grp.POST("/:name/question", []fizz.OperationOption{
fizz.Summary("Add a new question to a game type."),
}, tonic.Handler(controllers.AddQuestion, http.StatusOK))
}
and the function definition looks something like:
func AddQuestion(_ *gin.Context, params *models.GameParams, question *models.NewQuestion) (struct{}, error)
However I get the following error message:
incorrect number of input parameters for handler ontrollers.AddQuestion expected 1 or 2, got 3
Is there another way to get the body and the parameters passed to a function ?
Thanks
I'm using [email protected] and trying to generate openapi documentation by the following code:
app.Generator().API().Components.SecuritySchemes = map[string]*openapi.SecuritySchemeOrRef{
"basic_auth": {
SecurityScheme: &openapi.SecurityScheme{
Type: "http",
Scheme: "basic",
},
},
}
app.Generator().SetSecurityRequirement(&openapi.SecurityRequirement{"basic_auth": []string{}})
However, swagger UI shows it does not apply security scheme to APIs. Look into the generated openapi.json, security is:
{
"security": {
"basic_auth": []
}
}
After I change the security format from dictionary to array, it works as expected:
{
"security": [
{
"basic_auth": []
}
]
}
I would like to generate the OpenAPI specification for the endpoint that receives the request body as multipart/form-data
. Is this possible?
I'm pasting my attempts below, but all of them resulted in the request body to be documented as application/json
.
Code:
package main
import (
"fmt"
"mime/multipart"
"net/http"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/loopfz/gadgeto/tonic"
"github.com/wI2L/fizz"
)
type CreateForm struct {
Message string `form:"message"`
File *multipart.FileHeader `form:"file"`
}
// ATTEMPT 1: tried to bind with the f parameter: marks is as JSON
func handler(c *gin.Context, f *CreateForm) error {
var form CreateForm
if err := c.ShouldBindWith(&form, binding.FormMultipart); err != nil {
return err
}
// more implementation here
return nil
}
func main() {
f := fizz.NewFromEngine(gin.New())
f.GET("/openapi", nil, f.OpenAPI(nil, "yaml"))
f.POST(
"/create",
[]fizz.OperationOption{
// ATTEMPT 2: try to bind with the fizz.InputModel: marks is as JSON
fizz.InputModel(CreateForm{}),
},
tonic.Handler(
handler,
201,
),
)
fmt.Println("Errors:", f.Errors())
srv := &http.Server{Addr: ":8090", Handler: f}
srv.ListenAndServe()
}
Generated OpenAPI:
openapi: 3.0.1
info: null
paths:
/create:
post:
operationId: handler
requestBody:
content:
application/json:
schema:
type: object
properties:
File:
$ref: '#/components/schemas/MultipartFileHeader'
Message:
type: string
responses:
"201":
description: Created
components:
schemas:
MultipartFileHeader:
type: object
properties:
Filename:
type: string
Header:
type: object
additionalProperties:
type: array
items:
type: string
Size:
type: integer
format: int64
I have following router declaration:
appRoutes.GET("/:domain/:name/:version", []fizz.OperationOption{fizz.ID("GetApplication")}, hateoas.HandlerFindOneBy(appRepo))
appRoutes.DELETE("/:domain/:name/:version", []fizz.OperationOption{fizz.ID("DeleteApplication")}, hateoas.HandlerRemoveOneBy(appRepo))
appRoutes.PUT("/:domain/:name/:version", []fizz.OperationOption{fizz.ID("PutApplication")}, application.HandlerCreate(appRepo))
Generated spec embeds parameter declaration only on the PUT
route:
'/api/v1/applications/{domain}/{name}/{version}':
get:
tags:
- applications
operationId: GetApplication
responses:
'200':
description: OK
put:
tags:
- applications
operationId: PutApplication
parameters:
- name: domain
in: path
required: true
schema:
type: string
- name: name
in: path
required: true
schema:
type: string
- name: version
in: path
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
type: object
properties:
'':
type: integer
format: int32
_createdAt:
type: string
format: date-time
_links:
type: array
items:
$ref: '#/components/schemas/HateoasResourceLink'
_updatedAt:
type: string
format: date-time
manifest:
$ref: '#/components/schemas/PostgresJsonb'
delete:
tags:
- applications
operationId: DeleteApplication
And so OpenAPI parser claims some errors:
Semantic error at
paths./api/v1/applications/{domain}/{name}/{version}
Declared path parameter "domain" needs to be defined within every operation in the path (missing in "get", "delete"), or moved to the path-level parameters object
Jump to line 71Semantic error at
paths./api/v1/applications/{domain}/{name}/{version}
Declared path parameter "name" needs to be defined within every operation in the path (missing in "get", "delete"), or moved to the path-level parameters object
Jump to line 71Semantic error at
paths./api/v1/applications/{domain}/{name}/{version}
Declared path parameter "version" needs to be defined within every operation in the path (missing in "get", "delete"), or moved to the path-level parameters object
Jump to line 71
For example, I have a function with such signature
type NodePatchParam struct {
ID uint `path:"id"`
models.Node
}
func PatchNode(c *gin.Context, nodePatchParam *NodePatchParam) (*NodeResponse, error)
So the schemas in generated spec will show many schemas, including NodePatchParam and NodeResponse.
What if I don't want to show these types ? How to omit these types ?
Hi, is it possible to use endpoints that have a :
in the path?
Such as:
/game/{name}:enable
If I try:
grp.PUT("/:name:enabled", []fizz.OperationOption{
....
}, tonic.Handler(controllers.DisableGameType, http.StatusOK))
I get the following error
panic: error while generating OpenAPI spec on operation PUT /:name:disable: semantic error for path /game/{name:disable}: declared path parameter name:disable needs to be defined at operation level
Thanks
While I use fizz to server the api with generic responses, I got the issue $ref values must be RFC3986-compliant percent-encoded URIs
And this is my final api doc.
openapi.json.txt
Hello.
I'm in the middle of implementing Fizz for an API. I saw there is a mandatory dependency on tonic
and I was wondering why, and if this can be made optional. The reason I believe it should be optional is mainly because tonic seems to be a small project with not a lot of traction. It also seems to be a bundle of utils and in most cases, not all dependencies are required.
My biggest concern right now is about a dependency mismatch between Gin and tonic on the validator package. I'm aware that not using tonic would require me to define more Operation information manually, but I'm absolutely fine with it.
I was wondering if you could consider breaking the dependency and making it optional to use tonic on the handler.
Thanks
How can I add x-readme
section to the config?
there is an XTagGroup
but neither a more generic way nor a more specific way to specify the x-readme
section.
Hi I know you've already provided me with a lot of help.
I have another question, I know this might not be directly related to Fizz, however I cannot seem to
work out how to get it working with Fizz/Tonic. However others can get it work with Gin directly.
It is around input validation errors, I know validators is used to check the incoming data. So if I do
curl -X POST http://localhost:8080/game -H "Content-Type: application/json" -d "{\"name\":\"quibly\",\"ruls_url\":\"https://gitlab.com/banter-bus/games\"}"
# Output
{
"message": "binding error: Key: 'NewGame.RulesURL' Error:Field validation for 'RulesURL' failed on the 'required' tag"
}
Where the expected data is modelled like:
type NewGame struct {
Name string `json:"name" validate:"required"`
RulesURL string `json:"rules_url" validate:"required"`
}
I would like to use just get the json field names i.e. rules_url
and create my own error message to return back to the client.
for _, err := range e.(tonic.BindError).ValidationErrors() {
fmt.Println(err.Namespace())
fmt.Println(err.Field())
fmt.Println(err.StructNamespace())
fmt.Println(err.StructField())
fmt.Println(err.Tag())
fmt.Println(err.ActualTag())
fmt.Println(err.Kind())
fmt.Println(err.Type())
fmt.Println(err.Value())
fmt.Println(err.Param())
fmt.Println()
}
Which outputs:
NewGame.RulesURL
RulesURL
NewGame.RulesURL
RulesURL
required
required
string
string
I've tried to add a custom validator to gin as Validators maintainer suggests here
binding.Validator = new(defaultValidator)
engine := gin.New()
engine.Use(cors.Default())
fizzApp := fizz.NewFromEngine(engine)
// ....
infos := &openapi.Info{
Title: "Banter Bus",
Description: "The API definition for the Banter Bus server.",
Version: "1.0.0",
}
Where the defaultValidator
looks like:
import (
"reflect"
"strings"
"sync"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/validator/v10"
)
type defaultValidator struct {
once sync.Once
validate *validator.Validate
}
var _ binding.StructValidator = &defaultValidator{}
func (v *defaultValidator) ValidateStruct(obj interface{}) error {
if kindOfData(obj) == reflect.Struct {
v.lazyinit()
if err := v.validate.Struct(obj); err != nil {
return err
}
}
return nil
}
func (v *defaultValidator) Engine() interface{} {
v.lazyinit()
return v.validate
}
func (v *defaultValidator) lazyinit() {
v.once.Do(func() {
v.validate = validator.New()
v.validate.SetTagName("binding") // Print JSON name on validator.FieldError.Field()
v.validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
return name
})
})
}
func kindOfData(data interface{}) reflect.Kind {
value := reflect.ValueOf(data)
valueType := value.Kind()
if valueType == reflect.Ptr {
valueType = value.Elem().Kind()
}
return valueType
}
I was wondering if you could suggest a way I could get the JSON tag (`rules_url) from the struct rather than the struct field name(RulesURL). I've tried debugging it myself with breakpoints but I cannot seem to work it out. Any help would be appreciated thanks.
P.S. Sorry for the information overload.
In order to inject dependencies in my tonic handlers, i use some closure to declare them:
func HandlerFindByPage(repository PageableRepository) gin.HandlerFunc {
return tonic.Handler(func(c *gin.Context) (*PagedResources, error) {
...
}, http.StatusPartialContent)
}
// HandlerRemoveAll removes a whole collection
func HandlerRemoveAll(repository TruncatableRepository) gin.HandlerFunc {
return tonic.Handler(func(c *gin.Context) error {
return repository.Truncate()
}, http.StatusNoContent)
}
The associated fizz router group looks like:
appRoutes := router.Group("/applications", "applications", "Applications management")
appRoutes.GET("/", nil, hateoas.HandlerFindByPage(appRepo))
appRoutes.DELETE("/", nil, hateoas.HandlerRemoveAll(appRepo))
In such a case, fizz panics at runtime with following error and trace:
panic: error while generating OpenAPI spec on operation DELETE /api/v1/applications/: ID func1 is already used by another operation
goroutine 1 [running]:
stash.ovh.net/urba/appcatalog/api/vendor/github.com/wI2L/fizz.(*RouterGroup).Handle(0xc4202b1ec0, 0xc4202d0c00, 0x15, 0x1a63c61, 0x6, 0x0, 0x0, 0x0, 0xc4202bc1f8, 0x1, ...)
/Users/fmeurill/Workspaces/go/src/stash.ovh.net/urba/appcatalog/api/vendor/github.com/wI2L/fizz/fizz.go:196 +0xa43
stash.ovh.net/urba/appcatalog/api/vendor/github.com/wI2L/fizz.(*RouterGroup).DELETE(0xc4202b1ec0, 0x1a615e6, 0x1, 0x0, 0x0, 0x0, 0xc4202bc1f8, 0x1, 0x1, 0x0)
/Users/fmeurill/Workspaces/go/src/stash.ovh.net/urba/appcatalog/api/vendor/github.com/wI2L/fizz/fizz.go:138 +0xb8
stash.ovh.net/urba/appcatalog/api/v1/routing.registerRoutes(0xc4202b1e60, 0xc4202bc1b8, 0xc4202bc1c0, 0xc4202bc1c8, 0xc4202b4850)
/Users/fmeurill/Workspaces/go/src/stash.ovh.net/urba/appcatalog/api/v1/routing/routes.go:16 +0x27a
stash.ovh.net/urba/appcatalog/api/v1/routing.Init(0xc420306090, 0xc4202b1e60)
/Users/fmeurill/Workspaces/go/src/stash.ovh.net/urba/appcatalog/api/v1/routing/routes.go:48 +0xb2
stash.ovh.net/urba/appcatalog/api/routing.NewRouter(0xc420306090, 0x1a62916, 0x5, 0x1a61f62, 0x4, 0xc4202be301, 0xc4202b83c0, 0x0)
/Users/fmeurill/Workspaces/go/src/stash.ovh.net/urba/appcatalog/api/routing/routes.go:38 +0x55d
main.main()
/Users/fmeurill/Workspaces/go/src/stash.ovh.net/urba/appcatalog/api/cmd/appcatalog/main.go:91 +0xa4d
While sending an audio file within the request body, I'm receiving the following error:
{
"error": "binding error: error parsing request body: invalid character 'R' looking for beginning of value"
}
As I understand, the audio file is parsed as a string
and not the binary file -- the "R" is from string "RIFF" file header. The question is whether I could bind it as an audio/wav
type as described below?
The request looks like this:
curl --location 'http://0.0.0.0:8080/test?userId=some-id' \
--header 'accept: application/json' \
--header 'Content-Type: audio/wav' \
--data '@/audio_file.wav'
So I send userId
as a param and binary audio file in the body as audio/wav
content type.
Code-wise, the handler functions look like this:
// Bind returns router parameters
func (h *Handler) Bind(router *fizz.Fizz) {
router.POST("/test", []fizz.OperationOption{
fizz.ID("test"),
}, tonic.Handler(h.handle, http.StatusOK))
}
func (h *Handler) handle(c *gin.Context, _ *Evaluation) (*Response, error) {
var request Request
err := c.ShouldBindQuery(&request)
/.../
audio, err := io.ReadAll(c.Request.Body)
/.../
input := Evaluation{
UserID: request.UserID,
Audio: audio,
}
eval := h.Evaluate(c, input)
/.../
return &Response{Eval: eval}, nil
and the structs:
type Evaluation struct {
UserID string `query:"userId" validate:"required" description:"Id of user"`
Audio []byte `format:"binary"`
}
type Request struct {
UserID string `query:"userId" validate:"required" description:"Id of user"`
}
type Response struct {
Eval string `json:"eval"`
}
BTW While generating OpenAPI YAML, the part I'm interested in looks like this:
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/TestInput'
components:
schemas:
TestInput:
type: object
properties:
Audio:
type: string
format: binary
and I need this:
requestBody:
content:
audio/wav:
schema:
type: string
format: binary
How define example values for parameters?
How can I specify multiple error responses, say I have the following code:
func routes(grp *fizz.RouterGroup) {
grp.POST("", []fizz.OperationOption{
fizz.Summary("Create a new game type."),
fizz.Response(fmt.Sprint(http.StatusBadRequest), "Bad request", nil, nil),
fizz.Response(fmt.Sprint(http.StatusConflict), "Does not exist", nil, nil),
}, tonic.Handler(controllers.CreateGameType, http.StatusOK))
}
How can I specify within my CreateGameType
to either return a 400
error or a 409
error.
Any error I return (such as using juju as shown in the example, errors.AlreadyExistsf("conflict")
,
always returns a 400
error.
func CreateGameType(c *gin.Context, game *models.NewGame) (struct{}, error) {
....
}
Thanks
Is this project still maintained? Looks like the last tag was quite some time ago. Thanks!
Hi!
I was wondering if Fizz currently supports a way to add Open API extensions to the autogenerated documentation?
Especially, the things I had in mind are the following:
x-logo
under info
.x-tagGroups
at top-level.x-codeSamples
under each endpoint definition.I checked and I got the felling this is not supported. If this is true, how do we fell about adding this? Any ideas how the support could be added in a general way so all extensions would be supported?
If general supports turns out infeasible, how do we feel about adding support for this case-by-case? I check for the logos case, and extending the Info
struct to add the logo information seems easy. Similarly for tag groups. But before I'd tackle the implementation, I wanted to check how do we even feel about adding support for extensions? Would this be welcome, or does it contradicts the idea of Fizz?
Hi.
Having method with same name on different object generate the same ID for the OperationInfo witch give such error:
panic: error while generating OpenAPI spec on operation POST /: ID Create-fm is already used by another operation
Overwriting the id for each object because tedious since we have lot of method named Create for example.
We would like to include the package name and the struct name in the Operation ID.
A PR will follow
Hi, I try to use fizz to work with tonic, but I found HeaderTag not working for input struct. Because fizz not support OpenAPI security, so I tried to use header tag to some goal.
For example:
type Info struct {
Authorization string `header:"Authorization" description:"token" example:"Bear xxxxxxxx"`
Name string `json:"name" validate:"required" example:"banana"`
Origin string `json:"origin" validate:"required" description:"Country of origin of the fruit" enum:"ecuador,france,senegal,china,spain"`
Price float64 `json:"price" validate:"required" description:"Price in euros" example:"5.13"`
AddedAt time.Time `json:"-" binding:"-" description:"Date of addition of the fruit to the market"`
}
The result become to :
RestInfo:
type: object
properties:
Authorization:
type: string
description: Token
example: Bear xxxxxxxx
name:
type: string
example: banana
origin:
type: string
description: Country of origin of the fruit
enum:
- ecuador
- france
- senegal
- china
- spain
price:
type: number
description: Price in euros
format: double
example: 5.13
required:
- name
- origin
- price
We need to be able to ingest values like 0
. If make the parameter a pointer and tag it required
we get the following output
/v1/foo/{fooId}:
get:
...
- name: fooId
in: path
required: true
schema:
type: string
nullable: true
...
As you can see, fooId is both nullable and required.
https://pkg.go.dev/github.com/go-playground/validator/v10#hdr-Required states;For slices, maps, pointers, interfaces, channels and functions ensures the value is not nil. For structs ensures value is not the zero value when using WithRequiredStructEnabled.
Any thoughts on how to correct the output so the param is not marked nullable
?
How to keep sorting for tags, operations, struct fields?
These must be arrays according to the spec https://json-schema.org/draft/2020-12/json-schema-core#section-10.2.1.3, but aren't?
Lines 156 to 158 in fe54d35
I'm trying to inline the GeoJSON spec from an JSON file, but unmarshalling fails due to this.
Would be interested to hear about the reasons if this is deliberate.
Is there an easy way to define an API Group like /api/v1/
or /v1/
to all paths ? Or do we have to simply add to every group in the "routes" file
thanks
I am trying to use fizz.Response
to add additional errors in my documentation. However, the description seems does not work as expected. For example, add one of the following fizz options:
fizz.Response(fmt.Sprint(http.StatusNotFound), "my error desc", nil, nil, nil)
fizz.ResponseWithExamples(fmt.Sprint(http.StatusNotFound), "my error desc", nil, nil, nil)
I expect the description of the error should be my error desc
, but it only shows Not Found
in openapi.json.
I have some path and query fields mapped to well-defined types like int
and string
. However, I want to read the request body as []byte
or ideally, as map[string][]string
. It seems c.Request.Body
is non-empty in middleware but it is empty inside my gin routes wrapped in Fizz. What's the best way to access the body inside the handler?
Hello,
I am currently writing my API swagger doc so for example on this route:
router.GET("/tl/:id", []fizz.OperationOption{
fizz.ID("get_by_id"),
fizz.Description("Get by its id."),
fizz.Response("200",
"Example of a successful call by id.<br>",
dedicated.TlResponse{}, nil, GetByIdSuccessExemple),
}, tonic.Handler(handler.GetById, http.StatusOK))
But if do that I get an error because examples for status 200 are already generated by tonic, so I've added example tags to my response struct:
but some fields of my struct are type interface{}
because I can't know the exact type of object I'm getting from my API.
So if I use the example tag on an interface{}
type, in my swaggers the example for this field says "string"
But it's not clear for someone who reads this and don't know the project, so I tried using the fizz.Response on another status and I can put some object example values in those interface{}
fields...
I could use "default" status code but I think its still not clear in the swagger to have status 200 example with "string" for a field and under it, having an object for the same field :/
Is their a way to override the 200 status/default example ?
Thanks :)
Although in OpenAPI, it supports multiple tags in the tags field of each path operation. However, only a single tag can be set using the Group()
method. It would be great to add a new OperationOption to set additional tags.
We have the following setup:
For us there would be 2 valid solutions:
Is there a way to provide example values for fields with custom types? I've implemented a custom type to change the format of a time.Time
field and spotted that example value became {}
. Is there a workaround for this?
For the purposes of modifying the format in which we return time.time
field (e.g. let's say I don't want to return the timezone information), I've implemented a custom type:
type MyTime time.Time
func (t MyTime) MarshalJSON() ([]byte, error) {
stamp := fmt.Sprintf("\"%s\"", time.Time(t).Format("2006-01-02T15:04:05"))
return []byte(stamp), nil
When I use the this type on my struct:
type Item struct {
ID int64 `gorm:"primaryKey,column:id" json:"-"`
DateOld time.Time `gorm:"column:item_timestamp" json:"dateOld" example:"2021-07-12T12:20:48Z" description:"The time of the item"`
DateNew MyTime `gorm:"column:item_timestamp" json:"dateNew" example:"2021-07-12T12:20:48" description:"The time of the item"`
}
When I look into rendered open api spec I see this:
Item:
type: object
properties:
dateNew:
$ref: '#/components/schemas/MyTime'
dateOld:
type: string
description: The time of the item
format: date-time
components:
schemas:
MyTime:
type: object
description: The time of the item
And consequently also the Elements library we use to rended the docs shows empty dictionary {}
instead of an actual example value:
I assume the issue for this is that custom objects are not handled in the parseExampleValue
function, which cannot handle my type.
Is there any known workaround for providing examples when using custom types? If not, I'm happy to try to work on this. Any ideas how to approach this? Should we perhaps introduce an interface which custom structs would need to implement in order to provide examples?
Fizz currently inlines a request's body schema in the operation's definition. When the resulting OpenAPI spec is fed into a code generator such as openapi-generator
, this produces input types with generic names which are hard to use.
I'll illustrate the issue with the following example code:
func main() {
router := fizz.New()
router.POST("/foo",
[]fizz.OperationOption{},
tonic.Handler(foo, http.StatusCreated))
...
etc
...
}
type FooInput struct {
Name string `json:"name"`
}
type FooOutput struct {
Message string `json:"message"`
}
func foo(c *gin.Context, in *FooInput) (*FooOutput, error) {
return &FooOutput{"OK"}, nil
}
This server results in the following openAPI schema:
paths:
/foo:
post:
operationId: foo
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
type: string
responses:
"201":
description: Created
content:
application/json:
schema:
$ref: '#/components/schemas/FooOutput'
components:
schemas:
FooOutput:
type: object
properties:
message:
type: string
Which in turn, produces the following client code (running openapi-generator generate -i spec.yaml -g go
):
package openapi
// InlineObject struct for InlineObject
type InlineObject struct {
Name string `json:"name,omitempty"`
}
type DefaultApiService service
// FooOpts Optional parameters for the method 'Foo'
type FooOpts struct {
InlineObject optional.Interface
}
func (a *DefaultApiService) Foo(ctx _context.Context, localVarOptionals *FooOpts) (FooOutput, *_nethttp.Response, error) {
...
etc
...
}
A more desirable behaviour would be to generate a named schema for the request body, such as:
paths:
/foo:
post:
operationId: foo
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/FooInput'
responses:
"201":
description: Created
content:
application/json:
schema:
$ref: '#/components/schemas/FooOutput'
components:
schemas:
FooInput:
type: object
properties:
name:
type: string
FooOutput:
type: object
properties:
message:
type: string
Which results in the following generated code:
package openapi
// FooInput struct for FooInput
type FooInput struct {
Name string `json:"name,omitempty"`
}
type DefaultApiService service
// FooOpts Optional parameters for the method 'Foo'
type FooOpts struct {
FooInput optional.Interface
}
func (a *DefaultApiService) Foo(ctx _context.Context, localVarOptionals *FooOpts) (FooOutput, *_nethttp.Response, error) {
...
etc
...
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.