swag icon indicating copy to clipboard operation
swag copied to clipboard

Support `null` Package and Data-Types

Open naftulikay opened this issue 2 years ago • 13 comments

Describe the bug

swag init fails when compiling models which contain structs from the gopkg.in/guregu/null.v4 package.

To Reproduce

I have a PATCH endpoint where every field is optional, so my struct looks like this:

import "gopkg.in/guregu/null.v4"

type Request struct {
    Email null.String
    IsAdmin null.Bool
}

Having marked up my endpoint like so:

// UserUpdateHandler godoc
// ...
// @Param user body models.Request true "Updated user data."
// ...

When I run swag init, it chokes on items in the gopkg.in/guregu/null.v4 package:

2021/08/25 15:50:45 ParseComment error in file pkg/routes/sadmin/users/root.go :cannot find type definition: null.Bool

Expected behavior

I expected for swag to understand that null.* are simply nullable types.

Screenshots If applicable, add screenshots to help explain your problem.

Your swag version

1.7.1

Your go version

go version go1.16.6 linux/amd64

Desktop (please complete the following information):

  • OS: elementary OS Loki (Ubuntu 16.04 LTS) amd64

Additional context

If I shouldn't be using the null.* times for optional values in models, what should I be using?

naftulikay avatar Aug 25 '21 22:08 naftulikay

Try instead of

import "gopkg.in/guregu/null.v4"

type Request struct {
    Email null.String
    IsAdmin null.Bool
}

this one

import null "gopkg.in/guregu/null.v4"

type Request struct {
    Email null.String
    IsAdmin null.Bool
}

SmartPhoneJava avatar Sep 01 '21 09:09 SmartPhoneJava

@naftulikay, have you figured it out?

ubogdan avatar Sep 24 '21 21:09 ubogdan

@SmartPhoneJava why would changing the imported package name work? Is there some logic within swaggo that will understand it if I force the package name? The code above does compile.

@ubogdan I haven't tested this yet because I don't see how it would work differently. I'll attempt it at some point. Can you link to code or documentation that covers how this works?

naftulikay avatar Sep 25 '21 16:09 naftulikay

@naftulikay If the suggested workaround works it means there is an issue in the swag parser related to strings.Split. Swagger is Open Source. Feel free to look at the source code.

Please test and let us know.

ubogdan avatar Sep 25 '21 17:09 ubogdan

@naftulikay swaggo has different strategies to parse named and unnamed imports for detecting imported types.

You can see it in https://github.com/swaggo/swag/blob/master/packages.go#L245

I have an assumption that in the case of an unnamed import, swaggo uses the part after the slash as the package name. For gopkg.in/guregu/null.v4 it is null.v4. Accordingly, instead of null.String, swaggo expects null.v4.String, which is incorrect.

Check whether the import naming will work and everything will become clear :)

SmartPhoneJava avatar Sep 25 '21 18:09 SmartPhoneJava

I tried it out, and no dice:

pkg/routes/login/root.go:

package login

import (
    // ...
   null "gopkg.in/guregu/null.v4"
)

// LoginHandler godoc
// @Summary Login
// @Tags auth
// @Description Log a user in using email and password.
// @Accept json
// @Param login body views.LoginRequest true "Login Credentials"
// @Produce json
// @Success 200 {object} views.Response{data=views.LoginResponse}
// @Failure 400 {object} views.Response
// @Failure 403 {object} views.Response
// @Router /login [post]
func LoginHandler(w http.ResponsWriter, r *http.Request) {
    // ...
    resp := views.LoginRequest {
        // ...
    }

    if err := json.NewEncoder(w).Encode(resp); err != nil {
        // ...
    }
}

pkg/views/root.go:

package views

import (
    // ...
    null "gopkg.in/guregu/null.v4"
)

type Response struct {
	StatusCode uint16        `json:"status_code"`
	Error      null.String   `json:"error"`
	Data       interface{}   `json:"data"`
	Links      ResponseLinks `json:"_links"`
}

type ResponseLinks struct {
	Self string      `json:"_self"`
	Prev null.String `json:"_prev,omitempty"`
	Next null.String `json:"_next,omitempty"`
}

If I add swaggertype:"string" to each of the null.String fields, it does, in fact, work.

@ubogdan @SmartPhoneJava is there anything else I should try?

naftulikay avatar Oct 02 '21 21:10 naftulikay

@naftulikay Thanks for letting us know about the issue.

A quick dirty workaround is to use "swaggertype" to override the value with a primitive as you described above.

ubogdan avatar Oct 03 '21 08:10 ubogdan

@ubogdan Is there a swagger struct field tag that I can use to mark a struct field as optional? I'm using swaggertype:"string" for my null.String values, but is there a way that I can inform the user that this is an optional value and does not need to be included?

naftulikay avatar Oct 16 '21 00:10 naftulikay

@ubogdan ping on this: is there a tag that I can use to let Swagger know the field can be null?

naftulikay avatar Oct 19 '21 20:10 naftulikay

You can use validate:"required" to signal that field is not optional :).

ubogdan avatar Oct 19 '21 22:10 ubogdan

Is there a validate: "not-required" to signal that a field is indeed optional? Right now with swaggertype:"string" it shows all fields as required when any null.* type should be optional.

naftulikay avatar Oct 22 '21 20:10 naftulikay

Hi, I am getting those structs from https://github.com/volatiletech/sqlboiler, so I cannot really add those tags or the way it imports null. Is there a solution to this?

bluebrown avatar Dec 11 '21 18:12 bluebrown

Hello, I also meet a similar problem, and here is my code:

// api comment
// @Param       data  body      api.Problem  true  "comment"
import "mime/multipart"

type Problem struct {
	Input        *multipart.FileHeader `form:"input"`
	Output       *multipart.FileHeader `form:"output"`
}

And when I run swag init, the error occurs:

ParseComment error in file :cannot find type definition: multipart.FileHeader

So the only solution now is to use swaggertype or swaggerignore?

Matrix53 avatar Mar 02 '22 09:03 Matrix53