swaggest / openapi-go Goto Github PK
View Code? Open in Web Editor NEWOpenAPI structures for Go
Home Page: https://pkg.go.dev/github.com/swaggest/openapi-go/openapi3
License: MIT License
OpenAPI structures for Go
Home Page: https://pkg.go.dev/github.com/swaggest/openapi-go/openapi3
License: MIT License
Hi,
Following this issue, there is a problem when using regexes on path.
Let's say I have 2 routes : GET /users/{userID:[^/]+}
and POST /users/{userID:[^/]+}
.
In helper.go
, in Spec.SetupOperation()
, we get the pathItem (l.40) from s.Paths.MapOfPathItemValues
by using the path as key (so in this case, /users/{userID:[^/]+}
). But at the end of the method, we set the new pathItem under the key /users/{userID}
(the path without the regex). When we setup the operation for the next method, we get the pathItem under the key /users/{userID:[^/]+}
, which is empty, so the first routes is overridden completely by the second one.
I think getting the pathItem
after the regex substitution should solve the issue (so after l. 51, maybe at l.54 because the first use of pathItem
is at line 55).
Describe the bug
After upgrading from openapi-go v0.2.25 to openapi-go v0.2.30, I noticed that now google/uuid
is generated as a byte array:
UuidUUID: # v0.2.25
type: string
UuidUUID:
items:
minimum: 0
type: integer
nullable: true
type: array
I can easily work around it with another intercept on openapi3.Reflector.DefaultOptions
:
jsonschema.InterceptType(func(v reflect.Value, s *jsonschema.Schema) (stop bool, err error) {
if s.ReflectType == reflect.TypeOf(uuid.New()) {
s.Type = &jsonschema.Type{SimpleTypes: pointers.New(jsonschema.SimpleType("string"))}
s.Items = &jsonschema.Items{}
}
return false, nil
})
but I was wondering if this is an intended change (indeed it's type UUID [16]byte
, but somehow it was generated as string
in 0.2.25). Maybe a short mention plus this example in the docs is useful, since everyone will want to use the string version of their own uuid library by default I would think.
Additional context
Current go.mod
github.com/swaggest/openapi-go v0.2.30
github.com/swaggest/jsonschema-go v0.3.50 // indirect
github.com/swaggest/refl v1.1.0 // indirect
In the console of the docs page, it attempts to GET a few js/css files (from Cloudflare) and fails (404). The result is the page renders white. Is there a way to to use an alternative?
Here is the output from my console.
GET https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.19.1/swagger-ui.css net::ERR_ABORTED 404
docs:34 GET https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.19.1/swagger-ui-standalone-preset.js net::ERR_ABORTED 404
docs:33 GET https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.19.1/swagger-ui-bundle.js net::ERR_ABORTED 404
docs:63 Uncaught ReferenceError: SwaggerUIBundle is not defined
at window.onload (docs:63:5)
I can see that path automatically gets the required property, but what about other fields? Is there any required/not_nullable tag that can be used?
I tried looking through the documentation but I don't really see anything that can help me with this.
Hello,
Imagine I have the 2 routes in my API : /users/{userID:[^/]+}
and /users/{userID:[^/]+}/books/{bookID:.+}
.
The regexp are [^/]+
because without it, calling users/toto/books/openapi
, matches with /users/{userID:[^/]+}
with userID being toto/books/openapi
.
The problem is that during Spec.SetupOperation()
, {userID:[^/]+}
is not matched by regexFindPathParameter
({([^}:]+)(:[^/]+)?(?:})
).
I think changing the regex to {([^}:]+)(:[^}]+)?(?:})
would solve the problem.
Describe the bug
When a sub-entity of a request has a field of its own type, the resulting schema doesn't reference itself. It works well if the request (the top-level type) references itself, but not for sub-entities.
To Reproduce
Playground
type SubEntity struct {
Self *SubEntity `json:"self"`
}
type Req struct {
SubEntity *SubEntity `json:"subentity"`
Self *Req `json:"self"`
}
func main() {
reflector := openapi3.Reflector{}
reflector.Spec = &openapi3.Spec{Openapi: "3.1.0"}
putOp := openapi3.Operation{}
reflector.SetRequest(&putOp, Req{}, http.MethodPut)
reflector.Spec.AddOperation(http.MethodPut, "/things/{id}", putOp)
schema, err := reflector.Spec.MarshalYAML()
if err != nil {
log.Fatal(err)
}
// Req schema references itself on self field, but SubEntity schema has no reference on self field
fmt.Println(string(schema))
}
Expected behavior
components.schemas.SubEntity.properties.self
should have a $ref
field with the value '#/components/schemas/SubEntity'
.
Also SubEntity.self
has nullable
set as true
but I am not sure if it is normal, as Req.self
has not.
"responses":{
"200":{"description":"OK","content":{"text/vnd.graphviz":{"schema":{}}}},
"404":{
"description":"Not Found",
"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestErrResponse"}}}
}
E.g. x-codeSamples
is a extension for OpenAPI, it is not standard.
There are some extension tags that very useful for generate docs from code. So it should be a new feature or just I missed something that can do this?
Consider something like:
var s openapi3.Spec
spec := `openapi: 3.0.3
info:
description: description
license:
name: Apache-2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
title: title
version: 2.0.0
servers:
- url: /v2
paths:
/user:
put:
summary: updates the user by id
operationId: UpdateUser
requestBody:
content:
application/json:
schema:
type: string
description: Updated user object
required: true
responses:
"404":
description: User not found
components:
securitySchemes:
api_key:
in: header
name: x-api-key
type: apiKey
bearer_auth:
type: http
scheme: bearer
bearerFormat: JWT`
if err := s.UnmarshalYAML([]byte(spec)); err != nil {
log.Fatal(err)
}
this results in:
oneOf constraint failed for SecuritySchemeOrRef with 0 valid results: map[SecurityScheme:oneOf constraint failed for SecurityScheme with 0 valid results: map[APIKeySecurityScheme:required key missing: name HTTPSecurityScheme:oneOf constraint failed for HTTPSecurityScheme with 2 valid results: map[] OAuth2SecurityScheme:required key missing: flows OpenIDConnectSecurityScheme:required key missing: openIdConnectUrl] SecuritySchemeReference:required key missing: $ref]
when there really shouldn't be any error. It's coming from the generated openapi3/entities.go
but when I use the source json schema directly it gives no errors as it should, so I there must be something wrong with the generated validation code
Version: v0.2.24
Glad this exists and is in such mature shape!
Would you be open to (implementing, or a PR implementing) support for json:
-tag-less struct fields for request/response structs?
There are surely a few encoding/json
users other than me out there who are quite fine with the standard upper-case naming being Marshal
ed for exported fields without json:
tags, and don't need or want to ensure the more-common "js(on) field camelCasing aesthetic", and don't want to busy themselves with the endless chore of field-tag maintenance (linter-assisted or not).
Right now, for openapi31
to emit something (that shows up in the swagger-ui-ish rendition of editor-next.swagger.io) for one's AddReqStructure
/AddRespStructure
calls, those tags are necessary โ imho unnecessarily.
OpenAPI does support XML data https://swagger.io/docs/specification/data-models/representing-xml/ but this library currently only has SetJSONResponse methods.
Would be good to see XML support
Having any plan to rewrite this?
As described in this article by the OpenAPI Initiative , there are a few changes from OAS3.0 to OAS3.1.
I might be wrong but it seems that the majority of them should not be that hard to implement.
Is there any intention of handling these changes soon (or at all)?
Is there any support for oneOf, anyOf etc... If so how would we do it. I feel like one or two examples would help clarify this.
Overall, great project!
Describe the bug
I get an error when unmarshaling an openapi v3 spec that includes "bearer" as the scheme and has a bearerFormat value:
oneOf constraint failed for SecuritySchemeOrReference with 0 valid results: map[Reference:required key missing: $ref SecurityScheme:oneOf constraint failed for SecurityScheme with 2 valid results: map[APIKey:required key missing: name MutualTLS:bad const value for "type" ("mutualTLS" expected, "http" received) Oauth2:required key missing: flows Oidc:required key missing: openIdConnectUrl]]
To Reproduce
Run github.com/swaggest/openapi-go/openapi31.Schema.Unmarshal()
with this input:
openapi: 3.1.0
info:
title: MyProject
description: "My Project Description"
version: v1.0.0
# 1) Define the security scheme type (HTTP bearer)
components:
securitySchemes:
bearerAuth: # arbitrary name for the security scheme
type: http
scheme: bearer
bearerFormat: JWT # optional, arbitrary value for documentation purposes
# 2) Apply the security globally to all operations
security:
- bearerAuth: [] # use the same name as above
Go Playground: https://go.dev/play/p/TpF1raEIANk
Expected behavior
I expected the scheme to parse without an error and have these assertions hold true of the resulting schema:
s.Components.SecuritySchemes["bearerAuth"].SecurityScheme.HTTP != nil
s.Components.SecuritySchemes["bearerAuth"].SecurityScheme.HTTP.Scheme == "bearer"
s.Components.SecuritySchemes["bearerAuth"].SecurityScheme.HTTPBearer != nil
s.Components.SecuritySchemes["bearerAuth"].SecurityScheme.HTTPBearer.BearerFormat == "JWT"
Additional context
github.com/swaggest/openapi-go v0.2.47
sudorandom/protoc-gen-connect-openapi#5
Using v0.2.42
here, go get
ted just today.
My error-raising request-struct looks like this: struct { Id yodb.I64 }
which actually equates structurally/mem-wise to struct { Id int64 }
.
(It's an unnamed type so I guess reflect.Type.Name
will be empty, but not sure. But that's not the cause โ just double-checked by temporarily naming it.)
My Add
-ing code: op.AddReqStructure(reflect.New(ty_args).Interface(), openapi.WithContentType("application/json"))
This error
s out with:
validate path params post /_/postDelete: missing path parameter placeholder in url: Id", link:
Nothing after "link:". What's the idea? The underlying method expects a {"Id": number}
payload and does not offer any url placeholdering/parametering. How and why is the assumption there?
(Don't tell me the _
in /_/postDelete
is interpreted as placeholder? That's a url-prefix for all our api-method paths, so that any non-_/*
path after /
is free as a user-claimable vanity-name/base-path, even sth. like apis
๐ )
From what I can tell:
AddOperation
calls SetupOperation
calls SanitizeMethodPath
which receives the pathPattern
of "/_/postDelete"
. I'd expect pathParametersSubmatches
to be empty for your {([^}:]+)(:[^}]+)?(?:})
regex (tried out quickly at regex101.com which said "no match").SetupOperation
should receive pathParams
as empty/nil, and yet clearly it doesn't โ else the subsequent validatePathParams
wouldn't throw like I quoted above... mystifies me right now.Describe the bug
If req/resp struct using generic, like type req[T any] struct {}
, generated openapi output will contains schema with name like Req[Blabla]
, and Swagger Editor complains it as semantic error
To Reproduce
Take the example code in README, I modify req/resp to use generic:
package main
import (
"fmt"
"github.com/swaggest/openapi-go/openapi3"
"log"
"net/http"
"time"
)
func handleError(err error) {
if err != nil {
panic(err)
}
}
func main() {
reflector := openapi3.Reflector{}
reflector.Spec = &openapi3.Spec{Openapi: "3.0.3"}
reflector.Spec.Info.
WithTitle("Things API").
WithVersion("1.2.3").
WithDescription("Put something here")
type req[T any] struct {
ID string `path:"id" example:"XXX-XXXXX"`
Locale string `query:"locale" pattern:"^[a-z]{2}-[A-Z]{2}$"`
Title string `json:"string"`
Amount uint `json:"amount"`
Items []struct {
Count uint `json:"count"`
Name string `json:"name"`
} `json:"items"`
}
type resp[T any] struct {
ID string `json:"id" example:"XXX-XXXXX"`
Amount uint `json:"amount"`
Items []struct {
Count uint `json:"count"`
Name string `json:"name"`
} `json:"items"`
UpdatedAt time.Time `json:"updated_at"`
}
putOp := openapi3.Operation{}
handleError(reflector.SetRequest(&putOp, new(req[time.Time]), http.MethodPut))
handleError(reflector.SetJSONResponse(&putOp, new(resp[time.Time]), http.StatusOK))
handleError(reflector.SetJSONResponse(&putOp, new([]resp[time.Time]), http.StatusConflict))
handleError(reflector.Spec.AddOperation(http.MethodPut, "/things/{id}", putOp))
getOp := openapi3.Operation{}
handleError(reflector.SetRequest(&getOp, new(req[time.Time]), http.MethodGet))
handleError(reflector.SetJSONResponse(&getOp, new(resp[time.Time]), http.StatusOK))
handleError(reflector.Spec.AddOperation(http.MethodGet, "/things/{id}", getOp))
schema, err := reflector.Spec.MarshalYAML()
if err != nil {
log.Fatal(err)
}
fmt.Println(string(schema))
}
which will output:
openapi: 3.0.3
info:
description: Put something here
title: Things API
version: 1.2.3
paths:
/things/{id}:
get:
parameters:
- in: query
name: locale
schema:
pattern: ^[a-z]{2}-[A-Z]{2}$
type: string
- in: path
name: id
required: true
schema:
example: XXX-XXXXX
type: string
responses:
"200":
content:
application/json:
schema:
$ref: '#/components/schemas/Resp[TimeTime]'
description: OK
put:
parameters:
- in: query
name: locale
schema:
pattern: ^[a-z]{2}-[A-Z]{2}$
type: string
- in: path
name: id
required: true
schema:
example: XXX-XXXXX
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Req[TimeTime]'
responses:
"200":
content:
application/json:
schema:
$ref: '#/components/schemas/Resp[TimeTime]'
description: OK
"409":
content:
application/json:
schema:
items:
$ref: '#/components/schemas/Resp[TimeTime]'
type: array
description: Conflict
components:
schemas:
Req[TimeTime]:
properties:
amount:
minimum: 0
type: integer
items:
items:
properties:
count:
minimum: 0
type: integer
name:
type: string
type: object
nullable: true
type: array
string:
type: string
type: object
Resp[TimeTime]:
properties:
amount:
minimum: 0
type: integer
id:
example: XXX-XXXXX
type: string
items:
items:
properties:
count:
minimum: 0
type: integer
name:
type: string
type: object
nullable: true
type: array
updated_at:
format: date-time
type: string
type: object
Then paste it to https://editor.swagger.io/, it will complain Component names can only contain the characters A-Z a-z 0-9 - . _
, although req/resp displayed correctly
Expected behavior
Output schema name should not contain bracket
Additional context
Nothing else and thanks for your library โค๏ธ
Hello,
I noticed that every schema is put in reflector.Spec.Components.Schemas.MapOfSchemaOrRefValues
, even structs that are fields of other structs, which are referenced in reflector.Spec.Components.Schemas.MapOfSchemaOrRefValues["Struct"].Schema.Properties["Field"].Schema.Items.SchemaReference
.
Is there some options that can be used so schemas of fields of structs are set in reflector.Spec.Components.Schemas.MapOfSchemaOrRefValues["Struct"].Schema.Properties["Field"].Schema.Items.Schema
directly ?
It is just a question, no problem if it is not possible.
Hi, very nice lib!
My app knows about response format only during runtime(it takes it from config file). So I cant declare response format with struct.
Here is any way how I can set response schema with json, yaml, map or something else?
getOp, _ := reflector.NewOperationContext(http.MethodGet, "/user/{id}")
getOp.AddReqStructure(new(struct {
ID string `path:"id"`
}))
getOp.AddRespStructure() // <- put json, yaml, map or something else here
type Req struct {
Sector *int `query:"sector"`
}
Generates to
parameters:
- in: query
name: sector
schema:
type: integer
I've expected pointer fields to be described as nullable
Hi there, thanks for this family of modules.
I'm using this module to generate schema near existing HTTP handler code (like a minimal version of swaggest/rest). Trying to use oneOf in a request body which works fine using JSONSchemaOneOf. But I can't seem to crack an easy or straightforward way to set discriminator
as described here.
I see that Schema has Discriminator on it but I'm not quite sure how to get there from something like this.
I tried implementing jsonschema.Preparer and Exposer but don't think I can influence discriminator that way.
Assuming I'm not missing something (apologies if I am!), would some better support for this be good? I could see discriminator
tag support somewhere to set propertyName
. But it would probably also be good to support mapping
as well.
Happy to try putting a PR together if there's a direction you'd like to go.
Thanks again!
If a schema like
components:
schemas:
CreateSomethingRequest:
type: object
required:
- something
properties:
something:
type: number
example: 1
x-oapi-codegen-extra-tags:
validate: gte=0
was required, how can this x-oapi-codegen-extra-tags field be specified?
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.