Giter Site home page Giter Site logo

swaggest / openapi-go Goto Github PK

View Code? Open in Web Editor NEW
204.0 204.0 17.0 345 KB

OpenAPI structures for Go

Home Page: https://pkg.go.dev/github.com/swaggest/openapi-go/openapi3

License: MIT License

Makefile 0.97% Go 99.03%
code-generation documentation-generator hacktoberfest json-schema openapi openapi3

openapi-go's People

Contributors

cussrox avatar vearutop avatar xobotyi 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

openapi-go's Issues

pathItem has a wrong key when using regexes.

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

Changes to UUID schema generation

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

Doc page gets 404 when getting swagger-ui assets

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)

How would you set a field as required?

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.

Improving the regular expression used to remove gorilla.mux-style regexp in paths

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.

Issue with self-referencing entites

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.

Binary response content should not have schema

    "responses":{
     "200":{"description":"OK","content":{"text/vnd.graphviz":{"schema":{}}}},
     "404":{
      "description":"Not Found",
      "content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestErrResponse"}}}
     }

Security schemes validation error

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

Suggestion: support exported `json:`-tag-less fields to fully reflect / align-with Go's actual `json.Marshal` behavior

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

Support OAS3.1 ?

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.

  • Example -> Examples
  • Nullable -> type list containing null as element
  • GET and HEAD requests payloads allowed. (Still need to implement the ForceRequestBody() function)
  • File Upload Payload descriptions

Is there any intention of handling these changes soon (or at all)?

Error with UnmarshalYAML() using http/bearer auth scheme

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

Method designed without params/placeholders throws "missing path parameter placeholder in url"?

Using v0.2.42 here, go getted 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 errors 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").
  • So then 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.

req/resp struct using generic could cause semantic error in openapi output

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 โค๏ธ

[Question] Is there a way to select if a struct schema is a ref or not ?

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.

Set response and request schemas without declared structure?

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

Pointer primitives does not end up nullable

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

Better discriminator support?

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!

How to use custom x- tags in a schema

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?

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.