swaggo / swag

Automatically generate RESTful API documentation with Swagger 2.0 for Go.
MIT License
10.82k stars 1.2k forks source link

Explicitly add enum to generated definitions #1603

Open OSRSBlue opened 1 year ago

OSRSBlue commented 1 year ago

Is your feature request related to a problem? Please describe. I'm trying to add my enum for my API error codes to my generated definition, but since it does not have a relationship it does not get generated with my other definitions.

Describe the solution you'd like I would like to be able to explicitly add an enum to the generated definitions if they do not have a relationship.

Describe alternatives you've considered I have tried creating a custom struct that matches my HTTPAPIError interface for each API so that I can specifiy my error codes as my custom enum, but this feels hacky.

Additional context

Current Currently, I would expect to be able to explicitly add my API error code enum to the generated definitions without referencing them like below, but currently I cannot.

// GuideAPIErrorCode model info
// @Description an Guide API Error Code
type GuideAPIErrorCode string // @name GuideAPIErrorCode

const (
    UnhandledError GuideAPIErrorCode = "UnhandledError"
    InvalidSession GuideAPIErrorCode = "InvalidSession"
    NotFound       GuideAPIErrorCode = "NotFound"
    GuideExists    GuideAPIErrorCode = "GuideExists"
)

var (
    ErrUnhandled          = internalerrors.NewHTTPAPIError(http.StatusInternalServerError, string(UnhandledError), "Something went wrong.")
    ErrAuthSession        = internalerrors.NewHTTPAPIError(http.StatusUnauthorized, string(InvalidSession), "Invalid session.")
    ErrGuideNotFound      = internalerrors.NewHTTPAPIError(http.StatusNotFound, string(NotFound), "Guide not found.")
    ErrGuideAlreadyExists = internalerrors.NewHTTPAPIError(http.StatusForbidden, string(GuideExists), "Guide name already exists for group and category.")
)

Workaround

To work around and force my type to be generated I create a wrapper struct for each HTTP API that matches my HTTPAPIError type and update my controllers to reference my "wrapper" to create a relationship to generate the definitions for my errors codes:

// GuideAPIErrorCode model info
// @Description an Guide API Error Code
type GuideAPIErrorCode string // @name GuideAPIErrorCode

const (
    UnhandledError GuideAPIErrorCode = "UnhandledError"
    InvalidSession GuideAPIErrorCode = "InvalidSession"
    NotFound       GuideAPIErrorCode = "NotFound"
    GuideExists    GuideAPIErrorCode = "GuideExists"
)

// GuideHTTPAPIError model info
// @Description an HTTPAPIError
type GuideHTTPAPIError struct {
    Status  int    `json:"-"`
    Code    GuideAPIErrorCode `json:"code" validate:"required"`
    Message string `json:"message" validate:"required"`
} //@name GuideHTTPAPIError

var (
    ErrUnhandled          = internalerrors.NewHTTPAPIError(http.StatusInternalServerError, string(UnhandledError), "Something went wrong.")
    ErrAuthSession        = internalerrors.NewHTTPAPIError(http.StatusUnauthorized, string(InvalidSession), "Invalid session.")
    ErrGuideNotFound      = internalerrors.NewHTTPAPIError(http.StatusNotFound, string(NotFound), "Guide not found.")
    ErrGuideAlreadyExists = internalerrors.NewHTTPAPIError(http.StatusForbidden, string(GuideExists), "Guide name already exists for group and category.")
)

And then update my controller failure default to reference my GuideHTTPAPIError instead of my HTTPAPIError so I can get the definiton generated. This feels hacky and I would prefer to be able to someone add my enum for my API Error code explicitly somehow.

// @Summary Get Guide
// @Schemes
// @Description Get a guide
// @Tags Guide
// @Accept json
// @Produce json
// @Param request body GetGuideRequest true "query params"
// @Failure default {object}  GuideHTTPAPIError
// @Success 200 {object} EnhancedGuide
// @Router /guides [post]
func (h *GuidesController) GetGuide(c *gin.Context) {
    var request GetGuideRequest
    if err := c.BindJSON(&request); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{
            "message": "Invalid request body.",
        })

        return
    }

    guide, err := h.guideUseCase.GetGuide(c, request.Group, request.Category, request.ID)
    if err != nil {
        c.Error(err)
        return
    }

    enhancedGuide := EnhancedGuide{
        Guide: guide,
        User: EnhancedUser{
            Username: "Blue",
        },
    }
    c.JSON(http.StatusOK, enhancedGuide)
}

Has anyone experienced this before and have tips on how to get this to work properly?

rickywei commented 1 year ago

same problem :(