Coder Social home page Coder Social logo

fizz's People

Contributors

bluemoon avatar denispalnitsky avatar ericlingit avatar fabienm avatar fritterhoff avatar hmajid2301 avatar inozuma avatar ipfans avatar lukap3 avatar mcorbin avatar nikicc avatar rbeuque74 avatar rw-access avatar vladshub avatar wayt avatar wi2l avatar ybriffa avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fizz's Issues

Params and Body

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

Providing Examples for Custom Types

Question

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?

Details

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:
Screenshot 2022-02-07 at 15 31 51

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?

Specify Multiple error responses?

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

'Any' type with interface{}

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?

Make tonic wrapping optional

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

Endpoints with colon(:)

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

How to document multipart/form-data?

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

Add API Group

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

Fizz does not work with generic structs

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 :(

No scopes unavailable in OAuth2 flow

Hello, If an application does not use the scopes property in an oauth2 security flow, the property should be present in the output openapi.yaml to not cause a parsing error in Swagger.

image

It should be represented as an empty object:

scopes: {}

Enum values can't be added when DataType interface has been implemented

We have the following setup:

  • Enums with values that are based on ints (generated with https://github.com/klippa-app/go-enum)
  • The enums values have a string representation that we use for JSON, generated/parsed by MarshalJSON and UnmarshalJSON
  • We use the DataType interface to tell OpenAPI it's actually a string and not an int
  • When you do this, an enum consisting of string values can't be added to the struct property, because the validation logic will use the original type for validation, and not the overriden type with the DataType interface.

For us there would be 2 valid solutions:

  • Make the validator aware of DataType and use that to check the enum value
  • Make another interface that returns the possible enum values for a type, this would actually make it very easy for us because then we can just add that to the go-enum generator

pointer, required, not nullable openapi output

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?

Security should be an array instead of dictionary

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": []
      }
  ]
}

Support for Open API extensions?

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:

  • Adding a logo: would need x-logo under info.
  • Adding tag groups: would need x-tagGroups at top-level.
  • Adding code samples: would need 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?

Are Schema's anyOf/allOf/oneOf incorrectly typed?

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?

fizz/openapi/spec.go

Lines 156 to 158 in fe54d35

AllOf *SchemaOrRef `json:"allOf,omitempty" yaml:"allOf,omitempty"`
OneOf *SchemaOrRef `json:"oneOf,omitempty" yaml:"oneOf,omitempty"`
AnyOf *SchemaOrRef `json:"anyOf,omitempty" yaml:"anyOf,omitempty"`

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.

Do we have method to not show all struct in param and return type ?

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 ?

Add additional tags for a path operation

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.

A way to override default example

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:
image
image

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"
image

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 :)

Inline requestBody schema results in uglier client code

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
}

Before

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
  ...
}

After

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
  ...
}

Array parameter in path

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.

error while generating OpenAPI spec on operation: ID func1 is already used by another operation

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

HeaderTag now working in input struct

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

How to read the input body?

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?

Example tag

How define example values for parameters?

[binding error] Parsing audio file sent in the request body together with query params

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

About generic schemas

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.
image
openapi.json.txt

Additionnal response and OpenAPI generation

  • go version: go version go1.11.1 darwin/amd64
  • fizz version (or commit ref): d320b2d

Description

What ?

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.

How ?

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".

Input Validation Get JSON tags

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.

Keep sorting

How to keep sorting for tags, operations, struct fields?

How to specify the return type for an API that writes to the output directly via stream

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.

desc in Response and ResponseWithExamples does not work

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.

Given i declare two similar parameterized routes with different verbs, parameters are generated on a single verb

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 71

Semantic 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 71

Semantic 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

Method with same name on different struct produce same openAPI ID

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.