Definition builder fails reference on generic struct
Given a struct created with generics the swagger doc compiles correctly but will fail to reference the definition correctly.
Example:
type Response[T any] struct{
MetaData MetaData `json:"meta"`
Objects []T `json:"objects"
}
type MyObject struct{
Foo string `json:"foo"`
Bar int `json:"bar"`
}
Passing in a variable of type module.Response[module.MyObject]{} will yield a definition for module.Response[module.MyObject] as well as module.MyObject. But the reference to this definition will use the key #/definitions/module.Response%5Bmodule.MyObject%5D.
The proposed change would be to sanitize the definition keys to be [->%5B and ] ->%5D
Thank you for reporting this issue. It is the first time I learn about someone using generics with this pkg. I will try to come up with a solution.
On Thu, 20 Jul 2023 at 01:12, tlyons-cs @.***> wrote:
Given a struct created with generics the swagger doc compiles correctly but will fail to reference the definition correctly.
Example:
type Response[T any] struct{ MetaData MetaData
json:"meta"Objects []T `json:"objects" }type MyObject struct{ Foo string
json:"foo"Bar intjson:"bar"}Passing in a variable of type module.Response[module.MyObject]{} will yield a definition for module.Response[module.MyObject] as well as module.MyObject. But the reference to this definition will use the key #/definitions/module.Response%5Bmodule.MyObject%5D.
The proposed change would be to sanitize the definition keys to be [->%5B and ] ->%5D
— Reply to this email directly, view it on GitHub https://github.com/emicklei/go-restful-openapi/issues/109, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAFHRDGZ2TTBK54EDY6NU6LXRBSXDANCNFSM6AAAAAA2QTGDIU . You are receiving this because you are subscribed to this thread.Message ID: @.***>
-- Met vriendelijke groet, Kind regards,
Ernest Micklei
Try out my music project Melrōse https://melrōse.org
@emicklei I have also encountered a similar problem. After using go generics, reflect.Type.Name() displays the passed type object as the full pathname, which causes swagger ui to not resolve "/" when using (schema.$ref)reference.
Example:
package v1
type APIResponse[T any] struct {
Code int `json:"code"`
Message string `json:"message"`
Result T `json:"result"`
}
type AuthData struct {
Roles map[string]string `json:"roles"`
Token string `json:"token"`
Status AuthStatus `json:"status"`
}
package sgxxx
a.Ws.Route(a.Ws.POST("/auth").To(auth.Login).
Doc("登陆").
Metadata(restfulspec.KeyOpenAPITags, tags).
Reads(v1.AuthUserLoginReq{}).
Returns(200, "ok", v1.APIResponse[v1.AuthData]{}),
)
build swagger doc result:
paths:
/v1/apis/auth:
post:
........
'200':
description: ok
schema:
$ref: '#/definitions/v1.APIResponse%5Bgithub.com/xxx/pkg/v1.AuthData%5D'
definitions:
v1.APIResponse[github.com/xxx/pkg/v1.AuthData]:
Swagger UI ERROR INFO:
Semantic error at paths./v1/apis/auth.post.responses.200.schema.$ref
$refs must reference a valid location in the document
FIX: I change "/" to "." Swagger UI does not report errors
paths:
/v1/apis/auth:
.........
responses:
'200':
description: ok
schema:
$ref: '#/definitions/v1.APIResponse%5Bgithub.com.xxx.pkg.v1.AuthData%5D'
definitions:
v1.APIResponse[github.com.xxx.pkg.v1.AuthData]:
can I submit a PR to fix it, and modify the corresponding content when the keyFrom() is parsed.
thank you for reporting this. Yes, please help get this fixed so that generics are supported too.
@emicklei I have also encountered a similar problem. After using go generics, reflect.Type.Name() displays the passed type object as the full pathname, which causes swagger ui to not resolve "/" when using (schema.$ref)reference.
Example:
package v1 type APIResponse[T any] struct { Code int `json:"code"` Message string `json:"message"` Result T `json:"result"` } type AuthData struct { Roles map[string]string `json:"roles"` Token string `json:"token"` Status AuthStatus `json:"status"` }package sgxxx a.Ws.Route(a.Ws.POST("/auth").To(auth.Login). Doc("登陆"). Metadata(restfulspec.KeyOpenAPITags, tags). Reads(v1.AuthUserLoginReq{}). Returns(200, "ok", v1.APIResponse[v1.AuthData]{}), )build swagger doc result:
paths: /v1/apis/auth: post: ........ '200': description: ok schema: $ref: '#/definitions/v1.APIResponse%5Bgithub.com/xxx/pkg/v1.AuthData%5D' definitions: v1.APIResponse[github.com/xxx/pkg/v1.AuthData]:Swagger UI ERROR INFO:
Semantic error at paths./v1/apis/auth.post.responses.200.schema.$ref $refs must reference a valid location in the documentFIX: I change "/" to "." Swagger UI does not report errors
paths: /v1/apis/auth: ......... responses: '200': description: ok schema: $ref: '#/definitions/v1.APIResponse%5Bgithub.com.xxx.pkg.v1.AuthData%5D' definitions: v1.APIResponse[github.com.xxx.pkg.v1.AuthData]:can I submit a PR to fix it, and modify the corresponding content when the keyFrom() is parsed.
@emicklei
I found that in the library, I can resolve this by customizing the ModelTypeNameHandler method. This is how I addressed it.
config := restfulspec.Config{
Host: "127.0.0.1",
APIPath: "/apidoc.json",
WebServices: resources.Default.RegisteredWebServices(),
DisableCORS: false,
PostBuildSwaggerObjectHandler: enrichSwaggerObject,
ModelTypeNameHandler: enrichModelTypeName,
}
func enrichModelTypeName(st reflect.Type) (string, bool) {
var output string
key := st.String() //v1.APIResponse[github.com.HugoWw/x_apiserver/pkg/resource/v1.AuthData]
pattern := `\[(.*?)\]`
re := regexp.MustCompile(pattern)
match := re.FindStringSubmatch(key)
if len(match) > 0 {
// get submatch
// Example:
// match[1] = github.com.HugoWw/x_apiserver/pkg/resource/v1.AuthData
content := match[1]
end := strings.LastIndex(content, "/")
replaceContent := content[end+1:]
output = re.ReplaceAllString(key, "["+replaceContent+"]")
} else {
return "", false
}
return output, true
}
The generated swag documents are as follows
paths:
/v1/apis/auth:
post:
...........
responses:
'200':
description: ok
schema:
$ref: '#/definitions/v1.APIResponse%5Bv1.AuthData%5D'
definitions:
v1.APIResponse[v1.AuthData]:
........