Giter Site home page Giter Site logo

swaggest / rest Goto Github PK

View Code? Open in Web Editor NEW
319.0 319.0 15.0 6.17 MB

Web services with OpenAPI and JSON Schema done quick in Go

Home Page: https://pkg.go.dev/github.com/swaggest/rest

License: MIT License

Makefile 0.99% Go 99.01%
go golang hacktoberfest json-schema openapi openapi3 rest-api swagger

rest's People

Contributors

brianwilkinson avatar cussrox avatar haimgel avatar iaincalderfh avatar pboguslawski avatar vearutop 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  avatar

rest's Issues

Custom error message is not used for response validation errors

Describe the bug

Custom error message struct is not used for response validation errors.

To Reproduce

Try calling /validation API method from advanced example with X-Input = 44; returned error is

{
  "status": "INTERNAL",
  "error": "internal: bad response: validation failed",
  "context": {
    "header:X-Output": [
      "#: must be <= 20/1 but found 44"
    ]
  }
}

Expected behavior
Error message should be generated using customErr.

Additional context
Input validation errors are formatted as expected:

$ curl --silent -X 'POST' 'http://localhost:8011/validation'  -H 'accept: application/json'  -H 'X-Input: 4'  -H 'Content-Type: application/json'  -d '{ "data": { "value": "string"  } }'| jq
{
  "msg": "invalid argument: validation failed",
  "details": {
    "header:X-Input": [
      "#: must be >= 10/1 but found 4"
    ]
  }
}

uuid.UUID howto

Is there a way to support uuid.UUID as a string with format uuid? I couldn't find an example on how some of the more advanced reflect stuff worked.

I also don't have access to the type, so i can't add extra tags to the properly because it's part of an underlying framework :'(

Unable to set default values for custom typed strings

Describe the bug
I'm trying to build an enum type that can be used for input. As a part of this, I'd like to be able to set a default value for the enum field as well. I'd like to be able to define this in code (for reuse) rather than as a struct tag (i.e make our actual inputs/outputs match the documentation).

However, when I try to use a custom type with the default struct tags, it seems as though the parser expects a json string/object rather than a string value. You can use a json string to get past this, but then default does something weird. Concrete example below.

To Reproduce

package main_test

import (
	"bytes"
	"context"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/swaggest/rest/web"
	"github.com/swaggest/usecase"
)

type Discover string

const (
	DiscoverAll  Discover = "all"
	DiscoverNone Discover = "none"
)

func (d *Discover) Enum() []any {
	return []any{DiscoverAll, DiscoverNone}
}

func TestFoo(t *testing.T) {
	type NewThing struct {
		DiscoverMode *Discover `json:"discover,omitempty" default:"all"`
	}

	s := web.DefaultService()

	s.Post("/foo", usecase.NewInteractor(func(ctx context.Context, input NewThing, output *string) error {
		disc := string(*input.DiscoverMode)
		*output = disc

		return nil
	}))

	req, err := http.NewRequest(http.MethodPost, "/foo", bytes.NewReader([]byte(`{}`)))
	require.NoError(t, err)
	req.Header.Set("Content-Type", "application/json")
	rw := httptest.NewRecorder()

	s.ServeHTTP(rw, req)
	assert.Equal(t, `"all" `, rw.Body.String())
}

Observed behavior

=== RUN   TestFoo
--- FAIL: TestFoo (0.00s)
panic: DiscoverMode: parsing default as JSON: invalid character 'a' looking for beginning of value [recovered]
        panic: DiscoverMode: parsing default as JSON: invalid character 'a' looking for beginning of value

Expected behavior
The API returns all.

Side note: you can change this so it reads default:"\"all\"" -- then the input validation passes, but your default ends up as the quoted json string "all", not all (i.e. it is not possible to produce the valid enum option).

--

I'm happy to make a contribution here if you can point me in the right direction. I did step through the code a bit. I tried Implementing a TextUn/Marshaller so that maybe the type would properly get set as a string in jsonschema-go/reflect.go, but that didn't seem to make anything better on the version I was using. I believe it's because string is added as "a" type, rather than "the" SimpleType, which causes the checkInlineValue to fall into the default rather than String case (where it does a json.Unmarshal)

How to generate openapi.json file only?

How to generate openapi.json file without using http.ListenAndServe to start the service.
I just want to use 'go run .' to generate an openapi.json file, not start a http service.

Header names case sensitivity

Describe the bug

According to

https://stackoverflow.com/questions/5258977/are-http-headers-case-sensitive

HTTP header names should be compared in a case-insensitive fashion.

With example input header definition

HeaderSecFetchSite string `header:"sec-fetch-site" required:"true"`

request with

Sec-Fetch-Site: same-origin

throws request validation failure with

"header:sec-fetch-site": [
      "missing value"
    ]

Expected behavior
Requests with any of below should be passed:

sec-fetch-site: same-origin
Sec-Fetch-Site: same-origin
sEc-fEtCh-sitE: same-origin

Setting additionalProperties=false in all validations

It's a very common issue that someone might have a typo on an optional API endpoint's query/json body object property/url-encoded form field, and since it doesn't match the correct one, it's ignored but doesn't necessarily need to fail in a noticeable way, therefore causing a silent error.

How can I set this?

I have these additional questions regarding validation:

  • In general, it's not clear to me how to set a validation except when it's a tag on a struct field (e.g. in this case this validation is not bound to a field).
  • Within struct fields, how to set a format validation when the field is a list or a map, e.g. how to define that each element on a slice must be an enum of certain strings.

Broken pipe should not cause panic in gzip writer

Describe the bug
When HTTP request in canceled on client side, write in progress may fail with broken pipe error.
GZIP middleware propagates this error using panic.

To Reproduce
Open a browser page that makes many http requests to your server and close the window before the page is fully loaded.

 panic: BUG: cannot close gzip writer: write tcp 127.0.0.1:8008->127.0.0.1:33666: write: broken pipe
 
 -> github.com/swaggest/rest/response/gzip.Middleware.func1.1
 ->   /home/vearutop/go/pkg/mod/github.com/swaggest/[email protected]/response/gzip/middleware.go:34

    github.com/swaggest/rest/response/gzip.Middleware.func1
      /home/vearutop/go/pkg/mod/github.com/swaggest/[email protected]/response/gzip/middleware.go:40
    net/http.HandlerFunc.ServeHTTP
      /home/vearutop/sdk/gotip/src/net/http/server.go:2166
    github.com/bool64/brick.NewBaseLocator.HTTPTraceTransaction.func6.1
      /home/vearutop/go/pkg/mod/github.com/bool64/[email protected]/log/http.go:30
    net/http.HandlerFunc.ServeHTTP
      /home/vearutop/sdk/gotip/src/net/http/server.go:2166
    go.opencensus.io/plugin/ochttp.(*Handler).ServeHTTP
      /home/vearutop/go/pkg/mod/[email protected]/plugin/ochttp/server.go:92
    github.com/bool64/brick/opencensus.Middleware.WithRouteTag.func2
      /home/vearutop/go/pkg/mod/[email protected]/plugin/ochttp/route.go:40
....

Expected behavior
Probably such an error should be silently "swallowed", as there is nothing you can do with client resets.

Response headers defined in embedded struct are ignored

Describe the bug

When Test response header is defined using embedded struct like this

type MyEmbedded struct {
    Test string          `header:"Test" json:"-"`
}

type MyResponse struct {
    MyEmbedded
    Other string          `json:"other"`
}

such header is not present in API response. No such problem when header is defined directly i.e.

type MyResponse struct {
    Test string          `header:"Test" json:"-"`
    Other string          `json:"other"`
}

Expected behavior
Response header defined using embedded struct should be present in response.

Middlewares not working?

Hi,

I started to use this module in a project but I can't get most of the middlewares to work with the chi wrapper.

When I use the default chi router it works without problem (e.g with CORS and StipSlashes middlewares):

r := chi.NewRouter()
r.Use(
middleware.StripSlashes,
cors.Handler(cors.Options{
			AllowedOrigins:   "*",
			AllowedMethods:   []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
			AllowedHeaders:   []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
			AllowCredentials: false,
			MaxAge:           300,
		})
)

But with the wrapper it does not, as I am getting CORSed on preflight requests and end slashes are not stripped:

r :=  chirouter.NewWrapper(chi.NewRouter())
r.Use(...) //Same as above

Do you have any idea why?

[Feature] Possibility to define multiple success status. Add HTTPMultiResponder

[There was a discussion about having the ability to define multiple success status. As it was proposed in discussion and it seemed to possibly be forgotten, I'll add this here as an issue.]

It's easy to specify multiple error statuses with u.SetExpectedErrors(...), and I can override the "success" status with nethttp.SuccessStatus(http.StatusCreated) when creating the handler. But: my API needs to return either 200 or 201 status based on some logic, with essentially the same JSON response.

I cannot find a way to do that nicely: I don't want to override the whole response serialization, and I don't think treating 200 or 201 as an "error" is the right approach. Any ideas?

Originally posted by @haimgel in #153

Cannot validate incorrect types

Describe the bug

When validating an input with an incorrect type we get an failed to decode json: json: cannot unmarshall... error instead of a validation failed error with a context object pointing to the issue. This is because the un-marshalling fails before the validator is even run.

To Reproduce

package main

import (
	"bytes"
	"context"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/swaggest/rest/web"
	"github.com/swaggest/usecase"
)

func TestFoo(t *testing.T) {
	type TestInput struct {
		TestString string `json:"testString" default:"valid" required:"true" minLength:"5" maxLength:"10" pattern:"^[a-z]+$"`
	}

	s := web.DefaultService()

	s.Post("/foo", usecase.NewInteractor(func(ctx context.Context, input TestInput, output *string) error {
		*output = input.TestString

		return nil
	}))

	req, err := http.NewRequest(http.MethodPost, "/foo", bytes.NewReader([]byte(`{"testString":77}`)))
	require.NoError(t, err)
	req.Header.Set("Content-Type", "application/json")
	rw := httptest.NewRecorder()

	s.ServeHTTP(rw, req)
	assert.Contains(t, rw.Body.String(), "validation failed")
	assert.Equal(t, http.StatusBadRequest, rw.Code)
}

Expected behavior

I would expect a type mismatch issue to be reported in the same way as any other validation error to the end user

Missing security operation

I'm trying to add a security operation to a subset of routes. However, the security op isn't set on the paths.

	s.Route("/github", func(r chi.Router) {
		r.Group(func(r chi.Router) {
			r.Use(sessionMiddleware, nethttp.SecurityMiddleware(s.OpenAPICollector, "User", openapi3.SecurityScheme{
				APIKeySecurityScheme: &openapi3.APIKeySecurityScheme{
					In:   "cookie",
					Name: appcfg.CookieName,
				},
			}))
			r.Method(http.MethodGet, "/connect", nethttp.NewHandler(controllers.Connect(appcfg.GitHubAppSlug), nethttp.SuccessStatus(http.StatusTemporaryRedirect)))
			r.Method(http.MethodGet, "/install", nethttp.NewHandler(controllers.Install(orgManager, clientCreator), nethttp.SuccessfulResponseContentType("text/html")))
		})
	})

Output of json (no security :()

    "/github/connect": {
      "get": {
        "summary": "Connect",
        "description": "Connect to GitHub",
        "operationId": "function/controllers.Connect",
        "responses": {
          "307": {
            "description": "Temporary Redirect",
            "headers": {
              "Location": {
                "style": "simple",
                "description": "Redirect to GitHub",
                "schema": {
                  "type": "string",
                  "description": "Redirect to GitHub"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RestErrResponse"
                }
              }
            }
          }
        }
      }
    },

Response content type values in openapi.json do not match actual response content type values

Describe the bug

Response content type in openapi.json is application\json regardless of DefaultSuccessResponseContentType and DefaultErrorResponseContentType introduced in #116.

To Reproduce

In any API project with success and error reponses set

	response.DefaultSuccessResponseContentType = "application/dummy+json"
	response.DefaultErrorResponseContentType = "application/problem+json"

and see application\json as 200 and error response content type in generated openapi.json.

Expected behavior

In openapi.json 200 response should have content type set with DefaultSuccessResponseContentType and error responses should have content type set with DefaultErrorResponseContentType.

Additional context

Actual 200 and error API response content types are equal to DefaultSuccessResponseContentType and DefaultErrorResponseContentType accordingly so it's fine.

Issue is only about response content types in openapi.json.

Default value not applied when defined using schema.WithDefault

Describe bug
According to #132 (comment) it's possible to define default type value with schema.WithDefault like this

type SortDir string
func (s SortDir) PrepareJSONSchema(schema *jsonschema.Schema) error {
	schema.Enum = []interface{}{"asc", "desc"}
	schema.WithDefault("asc")
	schema.WithExamples("desc")
	return nil
}
type ItemListRequest struct {
[...]
   SortDir SortDir  `query:"sort_dir"`
}

but when

decoderFactory.ApplyDefaults = true

interactor input contains empty SortDir field for request without sort_dir specified (should be default value asc). After changing

-   SortDir SortDir  `query:"sort_dir"`
+   SortDir SortDir  `query:"sort_dir" default:"asc"`

request without sort_dir specified in query generate SortDir = asc in interactor input.

Expected behavior
(1) SortDir default value asc should be applied for requests without sort_dir in query when default value is specified using schema.WithDefault (similar how default field tag works).

(2) When both schema.WithDefault and default field tag are specified, openapi.json does not contain default definition from tag, just schema reference #/components/schemas/SortDir so default definition from field tag should be ignored in such scenario probably but is not (as above).

Mount example doesn't work. what's the right way to have both v1 and v2 on a server?

Hey guys,

Very good project for swagger 3.x, really appreciated your work.
I'd like to create a server with v1 and v2 server url, so was trying to follow https://github.com/swaggest/rest/blob/master/_examples/mount/main.go example to mount endpoints under api/v1, but has following error with service.Mount("/api/v1", apiV1):

panic: reflect API schema for GET /api/v1/sum: operation already exists: get /api/v1/sum

goroutine 1 [running]:
github.com/swaggest/rest/nethttp.OpenAPIMiddleware.func1({0x1012d9600?, 0x14000373920?})
	/Users/267121010/go/pkg/mod/github.com/swaggest/[email protected]/nethttp/openapi.go:35 +0x1a8

I also tried to use

r := openapi31.NewReflector()
r.Spec.WithServers(
	openapi31.Server{
		URL: "/api/v1",
	})
s := web.NewService(r)
s.Route("/data", func(r chi.Router) {
	r.Group(func(r chi.Router) {
		r.Use(serviceTokenAuth, serviceTokenDoc, checkSize)
		r.Method(http.MethodPost, "/", nethttp.NewHandler(handler.GenericPost()))
		r.Method(http.MethodPost, "/file-upload", nethttp.NewHandler(handler.FileUploader()))
	})
})
s.Docs(“/docs”, swgui.New)

with this code I can see server url options in the swagger gui, but the actual endpoint logic is not correctly mapped to server selection. I'd expect to be able to call endpoint <url>/api/v1/data, but the server is actually only listening on <url>/data, the swagger GUI call test does show correct curl example <url>/api/v1/data though.

Invalid request body content types accepted by default

Describe the bug
According to #113 valid JSON Content-Type is application/json but swaggest accepts by default any value matching application/json* pattern, i.e.

application/json; charset=utf-8
application/json123

Expected behavior
Only application/json should be allowed by default; allowing incorrect deviations like application/json; charset=utf-8 may be allowed only when explicitly requested (with option similar to existing tolerateFormData).

Additional context
https://www.hyrumslaw.com/

Add support for cookies in response

Similar to having header field tags in output structure, we can have cookie. However, cookies have more additional parameters that perhaps can be configured as field tag options, e.g. cookie="foo,httponly,path=/foo".

Parse regexp from path parameters

Describe the bug
Currently, when OpenAPI schema is generated, any regular expressions are filtered out from URL pattern to make URL pattern compatible with OpenAPI.

To Reproduce

	r.Get("/{token:^\\.}", usecase.ShortURL(deps))

Expected behavior
Such endpoint should produce token parameter with "pattern":"^\.".

Set response header X-Total-Count without to envelope result

I would like to be able to return (when paginating) an array in the response body and the total count of the query in a header response X-Total-Count.
But If I set a struct for the response like for instance
// Declare output port type.
type helloOutput struct {
TotalCount int64 header:"X-Total-Count" json:"-"
Data []Vehicles json:"vehicles"
}
I need the response to be
[{
vehicle:1
},{
vehicle:2}
]
and NOT
{
data: [{
vehicle:1
},{
vehicle:2}
]
}

Is there any suggestion ?

Struct tags get ignored for net.Prefix

Describe the bug
The tags on netip.Prefix types get ignored when generating the OpenAPI spec.

To Reproduce
Here is a full example:

package main

import (
	"context"
	"net/http"
	"net/netip"

	"github.com/swaggest/rest/web"
	"github.com/swaggest/swgui/v4emb"
	"github.com/swaggest/usecase"
)

type Prefix struct {
	ID     uint64       `json:"id" readonly:"yes"`
	Name   string       `json:"name" required:"true" example:"My Name" description:"Some human-readable name"`
	Owner  string       `json:"owner" example:"[email protected]" format:"idn-email"`
	Prefix netip.Prefix `json:"prefix" required:"true" example:"192.168.0.0/24" description:"Prefix in CIDR notation" format:"cidr"`
}

func getPrefixes() usecase.Interactor {
	u := usecase.NewInteractor(func(ctx context.Context, _ struct{}, output *[]Prefix) error {
		*output = append(*output, Prefix{
			ID:     1,
			Name:   "Example name",
			Owner:  "[email protected]",
			Prefix: netip.MustParsePrefix("10.0.0.0/8"),
		})
		return nil
	})
	return u
}

func main() {
	service := web.DefaultService()

	service.Get("/prefixes", getPrefixes())
	service.Docs("/docs", v4emb.New)

	if err := http.ListenAndServe("localhost:8080", service); err != nil {
		panic(err)
	}
}

When looking at the schema returned by http://localhost:8080/docs/openapi.json I see:

{
  "",
  "components": {
    "schemas": {
      "NetipPrefix": {
        "type": "string"
      },
      "Prefix": {
        "required": [
          "name",
          "prefix"
        ],
        "type": "object",
        "properties": {
          "id": {
            "minimum": 0,
            "type": "integer"
          },
          "name": {
            "type": "string",
            "description": "Some human-readable name",
            "example": "My Name"
          },
          "owner": {
            "type": "string",
            "format": "idn-email",
            "example": "[email protected]"
          },
          "prefix": {
            "$ref": "#/components/schemas/NetipPrefix"
          }
        }
      }
    }
  }
}

Expected behaviour
I expected to see my example, description and format tags represented in the schema output.

I also didn't expect that the netip.Prefix would be represented as

"NetipPrefix": {
  "type": "string"
}

but that's just an implementation detail I guess.

My preferred output would be to have the prefix field documented as

"prefix": {
  "type": "string",
  "format": "cidr",
  "description": "Prefix in CIDR notation",
  "example": "[email protected]"
}

Not obvious correlation between field name exporting and corresponding parameter visibility in API

Describe the bug

If you move example album handling stuff from https://dev.to/vearutop/tutorial-developing-a-restful-api-with-go-json-schema-validation-and-openapi-docs-2490 to separate package say mypackage and then (after exporting function names with capital letter) try to register interactors from main package with

	service.Get("/albums", mypackage.GetAlbums())
	service.Get("/albums/{id}", mypackage.GetAlbumByID())
	service.Post("/albums", mypackage.PostAlbums(), nethttp.SuccessStatus(http.StatusCreated))

everything works ok. But if you decide to change getAlbumByIDInput.ID to unexported getAlbumByIDInput.id than you'll get panic on app start saying

panic: failed to reflect API schema for GET /albums/{id}: undefined path parameter: id

If you restore getAlbumByIDInput.ID and add another query param say

	type getAlbumByIDInput struct {
		ID string `path:"id"`
		important string `query:"important"`
	}

no panics/warnings/errors will be thrown on start and important param won't be visible in OpenAPI nor defined in interactor even if present in request url.

If it's expected - consider adding info in manual about correlation between field name exporting and corresponding parameter visibility in API.

OpenAPI Collector not traversing child nodes, or parsing their mountpoints

I'm using this framework in some projects at work and really love it, but the rough edge I've run up could be me missing something.

In the go-chi world, it's common to have different routers mounted to parent routers. This is a way of separating out / differentiating the middlewares as part of the radix tree. Thus your middlewares can be applied closer to your domain objects in a way that makes sense.

With this library, I've struggled with this, specifically because of the documentation side. If I create a default router:

baseRouter := web.DefaultService()

But then try to add a sub router to it (for endpoints that require authentication via JWT for example)

baseRouter := web.DefaultService()
authenticated := web.DefaultService()

baseRouter.Mount("/authenticated",authenticated)

Will work but without documentation. If I force the sub router to have the same collector as the parent during construction the docs are present but missing the /authenticated prefix from the mount.

I wrote Usecase as a workaround by basically allowing this kind of setup as the UseCase implementation layer as well to allow for HTTP level globals and the further down the stack to a more implementation specific one.

Is there something I'm just missing about how the collector is supposed to be traversing nodes to generate the docs?

Nullable on lists

Describe the bug
When declaring struct with list i.e.

	Strings []string `json:"strings"`
	Ints    []int    `json:"ints"`

final openapi.json has nullable: true on these fields.

If it's intentional: how to declare list field that must exist in json and when without items must be like "strings": []?

Nullable issue

After configuring the UUID as follows:

	uuidDef := jsonschema.Schema{}
	uuidDef.AddType(jsonschema.String)
	uuidDef.WithFormat("uuid")
	uuidDef.WithExamples("248df4b7-aa70-47b8-a036-33ac447e668d")
	s.OpenAPICollector.Reflector().AddTypeMapping(uuid.UUID{}, uuidDef)
	s.OpenAPICollector.Reflector().InlineDefinition(uuid.UUID{})

The second occurrence of a UUID in the schemes has a nullable property.

First Occurrence which is working fine:

type Organisation struct {
	es.BaseAggregateSourced

	Avatar string `json:"avatar" required:"true"`
	Name   string `json:"name" required:"true"`
	Url    string `json:"url" required:"true"`
}

      "AggregatesOrganisation": {
        "required": [
          "id",
          "namespace",
          "avatar",
          "name",
          "url"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "example": "248df4b7-aa70-47b8-a036-33ac447e668d"
          },

Second occurrence which is wrong

type Repository struct {
	es.BaseAggregateSourced

	OrganisationId uuid.UUID `json:"organisation_id" format:"uuid" required:"true"`
	Name           string    `json:"name" required:"true"`
	FullName       string    `json:"full_name" required:"true"`
	DefaultBranch  string    `json:"default_branch" required:"true"`
	CreatedAt      time.Time `json:"created_at" required:"true"`
	Url            string    `json:"url" required:"true"`

	ClusterId uuid.UUID `json:"cluster_id" format:"uuid" required:"true"`
	PlanId    uuid.UUID `json:"plan_id" format:"uuid" required:"true"`
}

      "AggregatesRepository": {
        "required": [
          "id",
          "namespace",
          "organisation_id",
          "name",
          "full_name",
          "default_branch",
          "created_at",
          "url",
          "cluster_id",
          "plan_id"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "example": "248df4b7-aa70-47b8-a036-33ac447e668d"
          },

SetExpectedErrors allows only one error per status code

An endpoint may fail for different reasons with the same status code, e.g.:

Screenshot 2022-04-14 at 18 27 30

Screenshot 2022-04-14 at 18 27 52

It would be useful to be able to document multiple reasons an endpoint request can fail with the same status code. In my use case, the client can create a "prediction" via a JSON blob, and there are a million things that can be wrong with it, but most of them will be a BadRequest.

OpenAPI v3 supports this feature via oneOf: https://stackoverflow.com/questions/36576447/swagger-specify-two-responses-with-same-code-based-on-optional-parameter

As a separate easier question, the interface for SetExpectedErrors takes a list of errors, but there are implicit requirements for those errors to actually appear on the docs, e.g. a .Status() int function. Would you be able to clarify what exactly do I need to implement to make this work? Cheers.

Validation Middleware panics with *uuid.UUID

Describe the bug

When using a *uuid.UUID type in a body request the Validator Middleware is panicing. (See stack trace below)

It only happens with a pointer, when using uuid.UUID it works fine.

This bug was introduced between:
github.com/swaggest/openapi-go v0.2.44 => v0.2.43
^^ found this after creating the bug might need to move this to the other repo?

To Reproduce
Add a pointer to a uuid.UUID
In my models I have

type NewThing struct {
	ParentId      *uuid.UUID              `json:"parent_id" required:"false"`
}

Expected behavior
It shouldn't panic, and the field should not be validated.

Additional context

Exception has occurred: panic
"reflect: Zero(nil)"
Stack:
	 2  0x0000000000525ba5 in reflect.Zero
	     at /usr/local/go/src/reflect/value.go:3222
	 3  0x00000000012c5a90 in github.com/swaggest/openapi-go/internal.ReflectRequestBody.func3
	     at /home/chris/go/pkg/mod/github.com/swaggest/[email protected]/internal/json_schema.go:127
	 4  0x00000000012a3a95 in github.com/swaggest/jsonschema-go.InterceptNullability.func1.1
	     at /home/chris/go/pkg/mod/github.com/swaggest/[email protected]/context.go:106
	 5  0x00000000012bc0b5 in github.com/swaggest/jsonschema-go.checkNullability.func1
	     at /home/chris/go/pkg/mod/github.com/swaggest/[email protected]/reflect.go:1183
	 7  0x00000000012bbfb3 in github.com/swaggest/jsonschema-go.checkNullability
	     at /home/chris/go/pkg/mod/github.com/swaggest/[email protected]/reflect.go:1233
	 8  0x00000000012b9abd in github.com/swaggest/jsonschema-go.(*Reflector).walkProperties
	     at /home/chris/go/pkg/mod/github.com/swaggest/[email protected]/reflect.go:1007
	 9  0x00000000012b7065 in github.com/swaggest/jsonschema-go.(*Reflector).kindSwitch
	     at /home/chris/go/pkg/mod/github.com/swaggest/[email protected]/reflect.go:721
	10  0x00000000012b31d6 in github.com/swaggest/jsonschema-go.(*Reflector).reflect
	     at /home/chris/go/pkg/mod/github.com/swaggest/[email protected]/reflect.go:510
	11  0x00000000012b0446 in github.com/swaggest/jsonschema-go.(*Reflector).Reflect
	     at /home/chris/go/pkg/mod/github.com/swaggest/[email protected]/reflect.go:284
	12  0x00000000012c2fb3 in github.com/swaggest/openapi-go/internal.ReflectRequestBody
	     at /home/chris/go/pkg/mod/github.com/swaggest/[email protected]/internal/json_schema.go:172
	13  0x000000000134cbcc in github.com/swaggest/openapi-go/openapi3.(*Reflector).parseRequestBody
	     at /home/chris/go/pkg/mod/github.com/swaggest/[email protected]/openapi3/reflect.go:323
	14  0x000000000134bc5b in github.com/swaggest/openapi-go/openapi3.(*Reflector).setupRequest
	     at /home/chris/go/pkg/mod/github.com/swaggest/[email protected]/openapi3/reflect.go:246
	15  0x000000000135209d in github.com/swaggest/openapi-go/openapi3.(*Reflector).WalkRequestJSONSchemas
	     at /home/chris/go/pkg/mod/github.com/swaggest/[email protected]/openapi3/walk_schema.go:108
	16  0x00000000013afd84 in github.com/swaggest/rest/openapi.(*Collector).ProvideRequestJSONSchemas
	     at /home/chris/go/pkg/mod/github.com/swaggest/[email protected]/openapi/collector.go:516
	17  0x00000000013d8f97 in github.com/swaggest/rest/jsonschema.Factory.MakeRequestValidator
	     at /home/chris/go/pkg/mod/github.com/swaggest/[email protected]/jsonschema/validator.go:58
	18  0x00000000013db2e9 in github.com/swaggest/rest/jsonschema.(*Factory).MakeRequestValidator
	     at <autogenerated>:1
	19  0x00000000013f6417 in github.com/swaggest/rest/request.ValidatorMiddleware.func1
	     at /home/chris/go/pkg/mod/github.com/swaggest/[email protected]/request/middleware.go:87
	20  0x00000000013b3347 in github.com/swaggest/rest/nethttp.WrapHandler
	     at /home/chris/go/pkg/mod/github.com/swaggest/[email protected]/nethttp/wrap.go:16
	21  0x00000000013b6a4c in github.com/swaggest/rest/chirouter.(*Wrapper).prepareHandler
	     at /home/chris/go/pkg/mod/github.com/swaggest/[email protected]/chirouter/wrapper.go:223
	22  0x00000000013b5fff in github.com/swaggest/rest/chirouter.(*Wrapper).Method
	     at /home/chris/go/pkg/mod/github.com/swaggest/[email protected]/chirouter/wrapper.go:147
	23  0x00000000013fc716 in github.com/swaggest/rest/web.(*Service).Post
	     at /home/chris/go/pkg/mod/github.com/swaggest/[email protected]/web/service.go:152
	24  0x000000000140637e in function/handler.NewHandler
	     at /home/chris/workspace/getnoops/dev/config/handler/handler.go:111
	25  0x0000000001724f8e in main.main
	     at /home/chris/workspace/getnoops/dev/config/main.go:19

Data Schema's not showing on swagger docs site

Describe the bug
Data Schema's not showing on swagger docs site. Usually the schema is near the bottom. The json schema looks correct
image

To Reproduce

git clone https://github.com/swaggest/rest.git
cd /home/david/src/swaggest_ex/_examples/advanced-generic-openapi31
go get ./..
go run .

Expected behavior
At the bottom of the docs site there should be a section for schema

Nullable on required lists

Describe the bug
When declaring struct with list i.e.

Comments []string `json:"comments" required:"true"`
Keys     []int    `json:"ints" required:"true" minItems:"1"`

final openapi.json has nullable: true on these fields. Using usecase.Interactor it seems that there is no way to indicate those fields as non-nullable.

Expected behavior
Required lists should not be nullable.

How to handle JWT Tokens and oauth2?

Hi first let me say thank you for building these awesome api toolkits (rest & openapi)!
Great work :)

Right now I can't find out how describe that some endpoints are protected with JWT Bearer tokens and that I offer several URLs for oauth2 flows.

Are there any examples that show how to secure an API using swaggest/rest and generate the corresponding openapi specs etc?

nethttp.RequestMapping not work for json input data

I want to seperate concerns between usecases and transport it, so I tried nethttp.RequestMapping() as shown on README.md.
But I found it doesn't work for json body, and I started debug the package.

package rest has 6 const ParamIn

const (
	// ParamInPath indicates path parameters, such as `/users/{id}`.
	ParamInPath = ParamIn("path")

	// ParamInQuery indicates query parameters, such as `/users?page=10`.
	ParamInQuery = ParamIn("query")

	// ParamInBody indicates body value, such as `{"id": 10}`.
	ParamInBody = ParamIn("body")

	// ParamInFormData indicates body form parameters.
	ParamInFormData = ParamIn("formData")

	// ParamInCookie indicates cookie parameters, which are passed ParamIn the `Cookie` header,
	// such as `Cookie: debug=0; gdpr=2`.
	ParamInCookie = ParamIn("cookie")

	// ParamInHeader indicates header parameters, such as `X-Header: value`.
	ParamInHeader = ParamIn("header")
)

But in RequestMapping

// RequestMapping creates rest.RequestMapping from struct tags.
//
// This can be used to decouple mapping from usecase input with additional struct.
func RequestMapping(v interface{}) func(h *Handler) {
	return func(h *Handler) {
		m := make(rest.RequestMapping)

		for _, in := range []rest.ParamIn{
			rest.ParamInFormData,
			rest.ParamInQuery,
			rest.ParamInHeader,
			rest.ParamInPath,
			rest.ParamInCookie,
		} {
			mm := make(map[string]string)

			refl.WalkTaggedFields(reflect.ValueOf(v), func(v reflect.Value, sf reflect.StructField, tag string) {
				mm[sf.Name] = tag
			}, string(in))

			if len(mm) > 0 {
				m[in] = mm
			}
		}

		if len(m) > 0 {
			h.ReqMapping = m
		}
	}
}

only 5 of them are used without body for json input. I wonder is this intended.

Feature Request: Specification Extensions through IOInteractor

I've been prototyping a project using this rest framework, but we've noticed that there's no support for OpenAPI specification extensions. These extensions allow service owners to add additional fields to the OpenAPI spec, provided that they are prefixed by x- (for example x-internal) and are quite useful when integrating an OpenAPI spec with other platforms and tools.

https://swagger.io/specification/#specification-extensions

In my use case, I have some automation that we want to use with my generated openapi spec file, however this automation relies on the existence of some specification extensions. Would it be possible to extend the IOInteractor with basic support for extensions?

CSV/custom type response rendering

In the process of migrating a project over to use this collection of modules, and was wondering if you may have any recommendations for handling non-json response types. So far it looks like I may have to build my own response encoder to take the output interface and render it properly, but I feel like that's boiling the ocean.

Any insight would be much appreciated, this module has solved so many problems for our project!

Question: validation errors

Hi! I'm trying to build a custom error middleware and I noticed that I'm getting some validation errors squashed into one item.

I got:

[
  "#: missing properties: \"foo\", \"bar\""
]

while I was expecting:

[
  "missing property: \"foo\"",
  "missing property:\"bar\""
]

when calling Fields(). Is that something I could easily override or setup?

Response header validation case sensitivity

Describe the bug
Accrording to #119 header names should be compared in case insensitive fashion.

When ETag response header is defined with ETag canonical name

ETag string `header:"ETag" json:"-" required:"true"`

response validation error is thrown (missing etag header value) even if ETag field is populated in response struct. No such error when response struct is defined with Etag header name

ETag string `header:"Etag" json:"-" required:"true"`

Expected behavior
No response validation error thrown when header:"ETag" is used in struct definition.

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.