diff --git a/internal/test/parameters/shared_anyof/doc.go b/internal/test/parameters/shared_anyof/doc.go deleted file mode 100644 index b460b9fb8..000000000 --- a/internal/test/parameters/shared_anyof/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Package sharedanyof is a regression test for issue-2090: a path-level anyOf -// parameter shared by multiple methods on a path (and on webhooks/callbacks) -// must have its union member types declared once for the path item, not once -// per method. The committed generated file compiling as part of the test -// module is the guard. -package sharedanyof - -//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config=config.yaml spec.yaml diff --git a/internal/test/parameters/shared_anyof/shared_anyof.gen.go b/internal/test/parameters/shared_anyof/shared_anyof.gen.go deleted file mode 100644 index 75ed74d7a..000000000 --- a/internal/test/parameters/shared_anyof/shared_anyof.gen.go +++ /dev/null @@ -1,1639 +0,0 @@ -// Package sharedanyof provides primitives to interact with the openapi HTTP API. -// -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT. -package sharedanyof - -import ( - "context" - "encoding/json" - "fmt" - "io" - "net/http" - "net/url" - "strings" - - "github.com/labstack/echo/v4" - "github.com/oapi-codegen/runtime" - openapi_types "github.com/oapi-codegen/runtime/types" -) - -// WidgetId defines model for WidgetId. -type WidgetId struct { - union json.RawMessage -} - -// WidgetId0 defines model for WidgetId.0. -type WidgetId0 = openapi_types.UUID - -// WidgetId1 defines model for WidgetId.1. -type WidgetId1 = string - -// Id0 defines parameters for DeleteResource. -type Id0 = openapi_types.UUID - -// Id1 defines parameters for DeleteResource. -type Id1 = string - -// OnWebhookGetParams defines parameters for OnWebhookGet. -type OnWebhookGetParams struct { - Whid struct { - union json.RawMessage - } `form:"whid" json:"whid"` -} - -// Whid0 defines parameters for OnWebhookGet. -type Whid0 = openapi_types.UUID - -// Whid1 defines parameters for OnWebhookGet. -type Whid1 = string - -// OnWebhookPostParams defines parameters for OnWebhookPost. -type OnWebhookPostParams struct { - Whid struct { - union json.RawMessage - } `form:"whid" json:"whid"` -} - -// OnResourceGetParams defines parameters for OnResourceGet. -type OnResourceGetParams struct { - Cbid struct { - union json.RawMessage - } `form:"cbid" json:"cbid"` -} - -// Cbid0 defines parameters for OnResourceGet. -type Cbid0 = openapi_types.UUID - -// Cbid1 defines parameters for OnResourceGet. -type Cbid1 = string - -// OnResourcePostParams defines parameters for OnResourcePost. -type OnResourcePostParams struct { - Cbid struct { - union json.RawMessage - } `form:"cbid" json:"cbid"` -} - -// AsWidgetId0 returns the union data inside the WidgetId as a WidgetId0 -func (t WidgetId) AsWidgetId0() (WidgetId0, error) { - var body WidgetId0 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromWidgetId0 overwrites any union data inside the WidgetId as the provided WidgetId0 -func (t *WidgetId) FromWidgetId0(v WidgetId0) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeWidgetId0 performs a merge with any union data inside the WidgetId, using the provided WidgetId0 -func (t *WidgetId) MergeWidgetId0(v WidgetId0) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsWidgetId1 returns the union data inside the WidgetId as a WidgetId1 -func (t WidgetId) AsWidgetId1() (WidgetId1, error) { - var body WidgetId1 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromWidgetId1 overwrites any union data inside the WidgetId as the provided WidgetId1 -func (t *WidgetId) FromWidgetId1(v WidgetId1) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeWidgetId1 performs a merge with any union data inside the WidgetId, using the provided WidgetId1 -func (t *WidgetId) MergeWidgetId1(v WidgetId1) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t WidgetId) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *WidgetId) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// RequestEditorFn is the function signature for the RequestEditor callback function -type RequestEditorFn func(ctx context.Context, req *http.Request) error - -// Doer performs HTTP requests. -// -// The standard http.Client implements this interface. -type HttpRequestDoer interface { - Do(req *http.Request) (*http.Response, error) -} - -// Client which conforms to the OpenAPI3 specification for this service. -type Client struct { - // The endpoint of the server conforming to this interface, with scheme, - // https://api.deepmap.com for example. This can contain a path relative - // to the server, such as https://api.deepmap.com/dev-test, and all the - // paths in the swagger spec will be appended to the server. - Server string - - // Doer for performing requests, typically a *http.Client with any - // customized settings, such as certificate chains. - Client HttpRequestDoer - - // A list of callbacks for modifying requests which are generated before sending over - // the network. - RequestEditors []RequestEditorFn -} - -// ClientOption allows setting custom parameters during construction -type ClientOption func(*Client) error - -// Creates a new Client, with reasonable defaults -func NewClient(server string, opts ...ClientOption) (*Client, error) { - // create a client with sane default values - client := Client{ - Server: server, - } - // mutate client and add all optional params - for _, o := range opts { - if err := o(&client); err != nil { - return nil, err - } - } - // ensure the server URL always has a trailing slash - if !strings.HasSuffix(client.Server, "/") { - client.Server += "/" - } - // create httpClient, if not already present - if client.Client == nil { - client.Client = &http.Client{} - } - return &client, nil -} - -// WithHTTPClient allows overriding the default Doer, which is -// automatically created using http.Client. This is useful for tests. -func WithHTTPClient(doer HttpRequestDoer) ClientOption { - return func(c *Client) error { - c.Client = doer - return nil - } -} - -// WithRequestEditorFn allows setting up a callback function, which will be -// called right before sending the request. This can be used to mutate the request. -func WithRequestEditorFn(fn RequestEditorFn) ClientOption { - return func(c *Client) error { - c.RequestEditors = append(c.RequestEditors, fn) - return nil - } -} - -// The interface specification for the client above. -type ClientInterface interface { - - // GetGadget performs a GET /gadgets/{wid} (the `GetGadget` operationId) request. - GetGadget(ctx context.Context, wid WidgetId, reqEditors ...RequestEditorFn) (*http.Response, error) - - // Subscribe performs a POST /subscribe (the `Subscribe` operationId) request. - Subscribe(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) - - // DeleteResource performs a DELETE /v1/resource/{id} (the `DeleteResource` operationId) request. - DeleteResource(ctx context.Context, id struct { - union json.RawMessage - }, reqEditors ...RequestEditorFn) (*http.Response, error) - - // GetResource performs a GET /v1/resource/{id} (the `GetResource` operationId) request. - GetResource(ctx context.Context, id struct { - union json.RawMessage - }, reqEditors ...RequestEditorFn) (*http.Response, error) - - // PostResource performs a POST /v1/resource/{id} (the `PostResource` operationId) request. - PostResource(ctx context.Context, id struct { - union json.RawMessage - }, reqEditors ...RequestEditorFn) (*http.Response, error) - - // GetWidget performs a GET /widgets/{wid} (the `GetWidget` operationId) request. - GetWidget(ctx context.Context, wid WidgetId, reqEditors ...RequestEditorFn) (*http.Response, error) -} - -// GetGadget performs a GET /gadgets/{wid} (the `GetGadget` operationId) request. -func (c *Client) GetGadget(ctx context.Context, wid WidgetId, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewGetGadgetRequest(c.Server, wid) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -// Subscribe performs a POST /subscribe (the `Subscribe` operationId) request. -func (c *Client) Subscribe(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewSubscribeRequest(c.Server) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -// DeleteResource performs a DELETE /v1/resource/{id} (the `DeleteResource` operationId) request. -func (c *Client) DeleteResource(ctx context.Context, id struct { - union json.RawMessage -}, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteResourceRequest(c.Server, id) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -// GetResource performs a GET /v1/resource/{id} (the `GetResource` operationId) request. -func (c *Client) GetResource(ctx context.Context, id struct { - union json.RawMessage -}, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewGetResourceRequest(c.Server, id) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -// PostResource performs a POST /v1/resource/{id} (the `PostResource` operationId) request. -func (c *Client) PostResource(ctx context.Context, id struct { - union json.RawMessage -}, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostResourceRequest(c.Server, id) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -// GetWidget performs a GET /widgets/{wid} (the `GetWidget` operationId) request. -func (c *Client) GetWidget(ctx context.Context, wid WidgetId, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewGetWidgetRequest(c.Server, wid) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -// NewGetGadgetRequest constructs an http.Request for the GetGadget method -func NewGetGadgetRequest(server string, wid WidgetId) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "wid", wid, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/gadgets/%s", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewSubscribeRequest constructs an http.Request for the Subscribe method -func NewSubscribeRequest(server string) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/subscribe") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewDeleteResourceRequest constructs an http.Request for the DeleteResource method -func NewDeleteResourceRequest(server string, id struct { - union json.RawMessage -}) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/resource/%s", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewGetResourceRequest constructs an http.Request for the GetResource method -func NewGetResourceRequest(server string, id struct { - union json.RawMessage -}) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/resource/%s", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewPostResourceRequest constructs an http.Request for the PostResource method -func NewPostResourceRequest(server string, id struct { - union json.RawMessage -}) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/resource/%s", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewGetWidgetRequest constructs an http.Request for the GetWidget method -func NewGetWidgetRequest(server string, wid WidgetId) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "wid", wid, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/widgets/%s", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { - for _, r := range c.RequestEditors { - if err := r(ctx, req); err != nil { - return err - } - } - for _, r := range additionalEditors { - if err := r(ctx, req); err != nil { - return err - } - } - return nil -} - -// ClientWithResponses builds on ClientInterface to offer response payloads -type ClientWithResponses struct { - ClientInterface -} - -// NewClientWithResponses creates a new ClientWithResponses, which wraps -// Client with return type handling -func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) { - client, err := NewClient(server, opts...) - if err != nil { - return nil, err - } - return &ClientWithResponses{client}, nil -} - -// WithBaseURL overrides the baseURL. -func WithBaseURL(baseURL string) ClientOption { - return func(c *Client) error { - newBaseURL, err := url.Parse(baseURL) - if err != nil { - return err - } - c.Server = newBaseURL.String() - return nil - } -} - -// ClientWithResponsesInterface is the interface specification for the client with responses above. -type ClientWithResponsesInterface interface { - - // GetGadgetWithResponse performs a GET /gadgets/{wid} (the `GetGadget` operationId) request. - // - // Returns a wrapper object for the known response body format(s). - GetGadgetWithResponse(ctx context.Context, wid WidgetId, reqEditors ...RequestEditorFn) (*GetGadgetResponse, error) - - // SubscribeWithResponse performs a POST /subscribe (the `Subscribe` operationId) request. - // - // Returns a wrapper object for the known response body format(s). - SubscribeWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*SubscribeResponse, error) - - // DeleteResourceWithResponse performs a DELETE /v1/resource/{id} (the `DeleteResource` operationId) request. - // - // Returns a wrapper object for the known response body format(s). - DeleteResourceWithResponse(ctx context.Context, id struct { - union json.RawMessage - }, reqEditors ...RequestEditorFn) (*DeleteResourceResponse, error) - - // GetResourceWithResponse performs a GET /v1/resource/{id} (the `GetResource` operationId) request. - // - // Returns a wrapper object for the known response body format(s). - GetResourceWithResponse(ctx context.Context, id struct { - union json.RawMessage - }, reqEditors ...RequestEditorFn) (*GetResourceResponse, error) - - // PostResourceWithResponse performs a POST /v1/resource/{id} (the `PostResource` operationId) request. - // - // Returns a wrapper object for the known response body format(s). - PostResourceWithResponse(ctx context.Context, id struct { - union json.RawMessage - }, reqEditors ...RequestEditorFn) (*PostResourceResponse, error) - - // GetWidgetWithResponse performs a GET /widgets/{wid} (the `GetWidget` operationId) request. - // - // Returns a wrapper object for the known response body format(s). - GetWidgetWithResponse(ctx context.Context, wid WidgetId, reqEditors ...RequestEditorFn) (*GetWidgetResponse, error) -} - -type GetGadgetResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// GetBody returns the raw response body bytes -func (r GetGadgetResponse) GetBody() []byte { - return r.Body -} - -// Status returns HTTPResponse.Status -func (r GetGadgetResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r GetGadgetResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r GetGadgetResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type SubscribeResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// GetBody returns the raw response body bytes -func (r SubscribeResponse) GetBody() []byte { - return r.Body -} - -// Status returns HTTPResponse.Status -func (r SubscribeResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r SubscribeResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r SubscribeResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type DeleteResourceResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// GetBody returns the raw response body bytes -func (r DeleteResourceResponse) GetBody() []byte { - return r.Body -} - -// Status returns HTTPResponse.Status -func (r DeleteResourceResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r DeleteResourceResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r DeleteResourceResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type GetResourceResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// GetBody returns the raw response body bytes -func (r GetResourceResponse) GetBody() []byte { - return r.Body -} - -// Status returns HTTPResponse.Status -func (r GetResourceResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r GetResourceResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r GetResourceResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type PostResourceResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// GetBody returns the raw response body bytes -func (r PostResourceResponse) GetBody() []byte { - return r.Body -} - -// Status returns HTTPResponse.Status -func (r PostResourceResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r PostResourceResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r PostResourceResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type GetWidgetResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// GetBody returns the raw response body bytes -func (r GetWidgetResponse) GetBody() []byte { - return r.Body -} - -// Status returns HTTPResponse.Status -func (r GetWidgetResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r GetWidgetResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r GetWidgetResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -// GetGadgetWithResponse performs a GET /gadgets/{wid} (the `GetGadget` operationId) request. -// -// Returns a wrapper object for the known response body format(s). -func (c *ClientWithResponses) GetGadgetWithResponse(ctx context.Context, wid WidgetId, reqEditors ...RequestEditorFn) (*GetGadgetResponse, error) { - rsp, err := c.GetGadget(ctx, wid, reqEditors...) - if err != nil { - return nil, err - } - return ParseGetGadgetResponse(rsp) -} - -// SubscribeWithResponse performs a POST /subscribe (the `Subscribe` operationId) request. -// -// Returns a wrapper object for the known response body format(s). -func (c *ClientWithResponses) SubscribeWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*SubscribeResponse, error) { - rsp, err := c.Subscribe(ctx, reqEditors...) - if err != nil { - return nil, err - } - return ParseSubscribeResponse(rsp) -} - -// DeleteResourceWithResponse performs a DELETE /v1/resource/{id} (the `DeleteResource` operationId) request. -// -// Returns a wrapper object for the known response body format(s). -func (c *ClientWithResponses) DeleteResourceWithResponse(ctx context.Context, id struct { - union json.RawMessage -}, reqEditors ...RequestEditorFn) (*DeleteResourceResponse, error) { - rsp, err := c.DeleteResource(ctx, id, reqEditors...) - if err != nil { - return nil, err - } - return ParseDeleteResourceResponse(rsp) -} - -// GetResourceWithResponse performs a GET /v1/resource/{id} (the `GetResource` operationId) request. -// -// Returns a wrapper object for the known response body format(s). -func (c *ClientWithResponses) GetResourceWithResponse(ctx context.Context, id struct { - union json.RawMessage -}, reqEditors ...RequestEditorFn) (*GetResourceResponse, error) { - rsp, err := c.GetResource(ctx, id, reqEditors...) - if err != nil { - return nil, err - } - return ParseGetResourceResponse(rsp) -} - -// PostResourceWithResponse performs a POST /v1/resource/{id} (the `PostResource` operationId) request. -// -// Returns a wrapper object for the known response body format(s). -func (c *ClientWithResponses) PostResourceWithResponse(ctx context.Context, id struct { - union json.RawMessage -}, reqEditors ...RequestEditorFn) (*PostResourceResponse, error) { - rsp, err := c.PostResource(ctx, id, reqEditors...) - if err != nil { - return nil, err - } - return ParsePostResourceResponse(rsp) -} - -// GetWidgetWithResponse performs a GET /widgets/{wid} (the `GetWidget` operationId) request. -// -// Returns a wrapper object for the known response body format(s). -func (c *ClientWithResponses) GetWidgetWithResponse(ctx context.Context, wid WidgetId, reqEditors ...RequestEditorFn) (*GetWidgetResponse, error) { - rsp, err := c.GetWidget(ctx, wid, reqEditors...) - if err != nil { - return nil, err - } - return ParseGetWidgetResponse(rsp) -} - -// ParseGetGadgetResponse parses an HTTP response from a GetGadgetWithResponse call -func ParseGetGadgetResponse(rsp *http.Response) (*GetGadgetResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &GetGadgetResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseSubscribeResponse parses an HTTP response from a SubscribeWithResponse call -func ParseSubscribeResponse(rsp *http.Response) (*SubscribeResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &SubscribeResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseDeleteResourceResponse parses an HTTP response from a DeleteResourceWithResponse call -func ParseDeleteResourceResponse(rsp *http.Response) (*DeleteResourceResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &DeleteResourceResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseGetResourceResponse parses an HTTP response from a GetResourceWithResponse call -func ParseGetResourceResponse(rsp *http.Response) (*GetResourceResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &GetResourceResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParsePostResourceResponse parses an HTTP response from a PostResourceWithResponse call -func ParsePostResourceResponse(rsp *http.Response) (*PostResourceResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &PostResourceResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseGetWidgetResponse parses an HTTP response from a GetWidgetWithResponse call -func ParseGetWidgetResponse(rsp *http.Response) (*GetWidgetResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &GetWidgetResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// WebhookInitiator sends OpenAPI 3.1 webhook requests to target URLs. -// Modeled on the generated Client, but with no stored Server -- the full -// target URL is provided per-call by the caller (typically discovered -// from a subscription registration). -type WebhookInitiator struct { - // Doer for performing requests, typically a *http.Client with any - // customized settings, such as certificate chains. - Client HttpRequestDoer - - // A list of callbacks for modifying requests which are generated before sending over - // the network. - RequestEditors []RequestEditorFn -} - -// WebhookInitiatorOption allows setting custom parameters during construction. -type WebhookInitiatorOption func(*WebhookInitiator) error - -// NewWebhookInitiator creates a new WebhookInitiator with reasonable defaults. -func NewWebhookInitiator(opts ...WebhookInitiatorOption) (*WebhookInitiator, error) { - initiator := WebhookInitiator{} - for _, o := range opts { - if err := o(&initiator); err != nil { - return nil, err - } - } - if initiator.Client == nil { - initiator.Client = &http.Client{} - } - return &initiator, nil -} - -// WithWebhookHTTPClient allows overriding the default Doer, which is -// automatically created using http.Client. This is useful for tests. -func WithWebhookHTTPClient(doer HttpRequestDoer) WebhookInitiatorOption { - return func(p *WebhookInitiator) error { - p.Client = doer - return nil - } -} - -// WithWebhookRequestEditorFn allows setting up a callback function, which -// will be called right before sending the webhook request. This can be -// used to mutate the request, e.g. to add signature headers. -func WithWebhookRequestEditorFn(fn RequestEditorFn) WebhookInitiatorOption { - return func(p *WebhookInitiator) error { - p.RequestEditors = append(p.RequestEditors, fn) - return nil - } -} - -func (p *WebhookInitiator) applyWebhookEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { - for _, r := range p.RequestEditors { - if err := r(ctx, req); err != nil { - return err - } - } - for _, r := range additionalEditors { - if err := r(ctx, req); err != nil { - return err - } - } - return nil -} - -// WebhookInitiatorInterface is the interface specification for the webhook initiator. -type WebhookInitiatorInterface interface { - // OnWebhookGet fires the resourceWebhook webhook - OnWebhookGet(ctx context.Context, targetURL string, params *OnWebhookGetParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // OnWebhookPost fires the resourceWebhook webhook - OnWebhookPost(ctx context.Context, targetURL string, params *OnWebhookPostParams, reqEditors ...RequestEditorFn) (*http.Response, error) -} - -func (p *WebhookInitiator) OnWebhookGet(ctx context.Context, targetURL string, params *OnWebhookGetParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewOnWebhookGetWebhookRequest(targetURL, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := p.applyWebhookEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return p.Client.Do(req) -} - -func (p *WebhookInitiator) OnWebhookPost(ctx context.Context, targetURL string, params *OnWebhookPostParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewOnWebhookPostWebhookRequest(targetURL, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := p.applyWebhookEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return p.Client.Do(req) -} - -// NewOnWebhookGetWebhookRequest builds a GET request for the resourceWebhook webhook -func NewOnWebhookGetWebhookRequest(targetURL string, params *OnWebhookGetParams) (*http.Request, error) { - var err error - _ = err - - reqURL, err := url.Parse(targetURL) - if err != nil { - return nil, err - } - - if params != nil { - queryValues := reqURL.Query() - var rawQueryFragments []string - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "whid", params.Whid, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - reqURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodGet, reqURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewOnWebhookPostWebhookRequest builds a POST request for the resourceWebhook webhook -func NewOnWebhookPostWebhookRequest(targetURL string, params *OnWebhookPostParams) (*http.Request, error) { - var err error - _ = err - - reqURL, err := url.Parse(targetURL) - if err != nil { - return nil, err - } - - if params != nil { - queryValues := reqURL.Query() - var rawQueryFragments []string - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "whid", params.Whid, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - reqURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodPost, reqURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// CallbackInitiator sends OpenAPI callback requests to target URLs. -// Modeled on the generated Client, but with no stored Server -- the full -// target URL is provided per-call by the caller (typically discovered -// from a callback expression on the parent operation's request body, -// e.g. `{$request.body#/callbackUrl}`). -type CallbackInitiator struct { - // Doer for performing requests, typically a *http.Client with any - // customized settings, such as certificate chains. - Client HttpRequestDoer - - // A list of callbacks for modifying requests which are generated before sending over - // the network. - RequestEditors []RequestEditorFn -} - -// CallbackInitiatorOption allows setting custom parameters during construction. -type CallbackInitiatorOption func(*CallbackInitiator) error - -// NewCallbackInitiator creates a new CallbackInitiator with reasonable defaults. -func NewCallbackInitiator(opts ...CallbackInitiatorOption) (*CallbackInitiator, error) { - initiator := CallbackInitiator{} - for _, o := range opts { - if err := o(&initiator); err != nil { - return nil, err - } - } - if initiator.Client == nil { - initiator.Client = &http.Client{} - } - return &initiator, nil -} - -// WithCallbackHTTPClient allows overriding the default Doer, which is -// automatically created using http.Client. This is useful for tests. -func WithCallbackHTTPClient(doer HttpRequestDoer) CallbackInitiatorOption { - return func(p *CallbackInitiator) error { - p.Client = doer - return nil - } -} - -// WithCallbackRequestEditorFn allows setting up a callback function, which -// will be called right before sending the callback request. This can be -// used to mutate the request, e.g. to add signature headers. -func WithCallbackRequestEditorFn(fn RequestEditorFn) CallbackInitiatorOption { - return func(p *CallbackInitiator) error { - p.RequestEditors = append(p.RequestEditors, fn) - return nil - } -} - -func (p *CallbackInitiator) applyCallbackEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { - for _, r := range p.RequestEditors { - if err := r(ctx, req); err != nil { - return err - } - } - for _, r := range additionalEditors { - if err := r(ctx, req); err != nil { - return err - } - } - return nil -} - -// CallbackInitiatorInterface is the interface specification for the callback initiator. -type CallbackInitiatorInterface interface { - // OnResourceGet fires the resourceEvent callback - OnResourceGet(ctx context.Context, targetURL string, params *OnResourceGetParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // OnResourcePost fires the resourceEvent callback - OnResourcePost(ctx context.Context, targetURL string, params *OnResourcePostParams, reqEditors ...RequestEditorFn) (*http.Response, error) -} - -func (p *CallbackInitiator) OnResourceGet(ctx context.Context, targetURL string, params *OnResourceGetParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewOnResourceGetCallbackRequest(targetURL, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := p.applyCallbackEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return p.Client.Do(req) -} - -func (p *CallbackInitiator) OnResourcePost(ctx context.Context, targetURL string, params *OnResourcePostParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewOnResourcePostCallbackRequest(targetURL, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := p.applyCallbackEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return p.Client.Do(req) -} - -// NewOnResourceGetCallbackRequest builds a GET request for the resourceEvent callback -func NewOnResourceGetCallbackRequest(targetURL string, params *OnResourceGetParams) (*http.Request, error) { - var err error - _ = err - - reqURL, err := url.Parse(targetURL) - if err != nil { - return nil, err - } - - if params != nil { - queryValues := reqURL.Query() - var rawQueryFragments []string - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cbid", params.Cbid, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - reqURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodGet, reqURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewOnResourcePostCallbackRequest builds a POST request for the resourceEvent callback -func NewOnResourcePostCallbackRequest(targetURL string, params *OnResourcePostParams) (*http.Request, error) { - var err error - _ = err - - reqURL, err := url.Parse(targetURL) - if err != nil { - return nil, err - } - - if params != nil { - queryValues := reqURL.Query() - var rawQueryFragments []string - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cbid", params.Cbid, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - reqURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodPost, reqURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// ServerInterface represents all server handlers. -type ServerInterface interface { - - // (GET /gadgets/{wid}) - GetGadget(ctx echo.Context, wid WidgetId) error - - // (POST /subscribe) - Subscribe(ctx echo.Context) error - - // (DELETE /v1/resource/{id}) - DeleteResource(ctx echo.Context, id struct { - union json.RawMessage - }) error - - // (GET /v1/resource/{id}) - GetResource(ctx echo.Context, id struct { - union json.RawMessage - }) error - - // (POST /v1/resource/{id}) - PostResource(ctx echo.Context, id struct { - union json.RawMessage - }) error - - // (GET /widgets/{wid}) - GetWidget(ctx echo.Context, wid WidgetId) error -} - -// ServerInterfaceWrapper converts echo contexts to parameters. -type ServerInterfaceWrapper struct { - Handler ServerInterface -} - -// GetGadget converts echo context to params. -func (w *ServerInterfaceWrapper) GetGadget(ctx echo.Context) error { - var err error - // ------------- Path parameter "wid" ------------- - var wid WidgetId - - err = runtime.BindStyledParameterWithOptions("simple", "wid", ctx.Param("wid"), &wid, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter wid: %s", err)) - } - - // Invoke the callback with all the unmarshaled arguments - err = w.Handler.GetGadget(ctx, wid) - return err -} - -// Subscribe converts echo context to params. -func (w *ServerInterfaceWrapper) Subscribe(ctx echo.Context) error { - var err error - - // Invoke the callback with all the unmarshaled arguments - err = w.Handler.Subscribe(ctx) - return err -} - -// DeleteResource converts echo context to params. -func (w *ServerInterfaceWrapper) DeleteResource(ctx echo.Context) error { - var err error - // ------------- Path parameter "id" ------------- - var id struct { - union json.RawMessage - } - - err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) - } - - // Invoke the callback with all the unmarshaled arguments - err = w.Handler.DeleteResource(ctx, id) - return err -} - -// GetResource converts echo context to params. -func (w *ServerInterfaceWrapper) GetResource(ctx echo.Context) error { - var err error - // ------------- Path parameter "id" ------------- - var id struct { - union json.RawMessage - } - - err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) - } - - // Invoke the callback with all the unmarshaled arguments - err = w.Handler.GetResource(ctx, id) - return err -} - -// PostResource converts echo context to params. -func (w *ServerInterfaceWrapper) PostResource(ctx echo.Context) error { - var err error - // ------------- Path parameter "id" ------------- - var id struct { - union json.RawMessage - } - - err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) - } - - // Invoke the callback with all the unmarshaled arguments - err = w.Handler.PostResource(ctx, id) - return err -} - -// GetWidget converts echo context to params. -func (w *ServerInterfaceWrapper) GetWidget(ctx echo.Context) error { - var err error - // ------------- Path parameter "wid" ------------- - var wid WidgetId - - err = runtime.BindStyledParameterWithOptions("simple", "wid", ctx.Param("wid"), &wid, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "", Format: "", ValueIsUnescaped: ctx.Request().URL.RawPath == ""}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter wid: %s", err)) - } - - // Invoke the callback with all the unmarshaled arguments - err = w.Handler.GetWidget(ctx, wid) - return err -} - -// This is a simple interface which specifies echo.Route addition functions which -// are present on both echo.Echo and echo.Group, since we want to allow using -// either of them for path registration -type EchoRouter interface { - CONNECT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route - DELETE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route - GET(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route - HEAD(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route - OPTIONS(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route - PATCH(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route - POST(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route - PUT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route - TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route -} - -// RegisterHandlersOptions configures RegisterHandlersWithOptions. -type RegisterHandlersOptions struct { - // BaseURL is prepended to every registered path so the API can be served - // under a prefix. - BaseURL string - // OperationMiddlewares lets the caller attach per-operation middleware at - // registration time. The map key is the OpenAPI `operationId` value as it - // appears in the spec (the raw, un-normalized form). Operations that have - // no entry are registered with no extra middleware. A nil map disables - // per-operation middleware entirely. - OperationMiddlewares map[string][]echo.MiddlewareFunc -} - -// RegisterHandlers adds each server route to the EchoRouter. -func RegisterHandlers(router EchoRouter, si ServerInterface) { - RegisterHandlersWithOptions(router, si, RegisterHandlersOptions{}) -} - -// RegisterHandlersWithBaseURL registers handlers and prepends BaseURL to the -// paths so the API can be served under a prefix. -func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) { - RegisterHandlersWithOptions(router, si, RegisterHandlersOptions{BaseURL: baseURL}) -} - -// RegisterHandlersWithOptions registers handlers using the supplied options, -// including any per-operation middleware. -func RegisterHandlersWithOptions(router EchoRouter, si ServerInterface, options RegisterHandlersOptions) { - - wrapper := ServerInterfaceWrapper{ - Handler: si, - } - - router.DELETE(options.BaseURL+"/v1/resource/:id", wrapper.DeleteResource, options.OperationMiddlewares["deleteResource"]...) - router.GET(options.BaseURL+"/v1/resource/:id", wrapper.GetResource, options.OperationMiddlewares["getResource"]...) - router.POST(options.BaseURL+"/v1/resource/:id", wrapper.PostResource, options.OperationMiddlewares["postResource"]...) - router.POST(options.BaseURL+"/subscribe", wrapper.Subscribe, options.OperationMiddlewares["subscribe"]...) - router.GET(options.BaseURL+"/widgets/:wid", wrapper.GetWidget, options.OperationMiddlewares["getWidget"]...) - router.GET(options.BaseURL+"/gadgets/:wid", wrapper.GetGadget, options.OperationMiddlewares["getGadget"]...) - -} - -// WebhookReceiverInterface represents handlers for receiving inbound -// webhook requests. Each webhook becomes a Handle*Webhook -// method that the implementation fills in. The caller mounts the per- -// webhook echo.HandlerFunc returned by {Op}WebhookHandler at -// whatever URL path they advertise to senders. -type WebhookReceiverInterface interface { - - // HandleOnWebhookGetWebhook handles the GET webhook for resourceWebhook. - HandleOnWebhookGetWebhook(ctx echo.Context, params OnWebhookGetParams) error - - // HandleOnWebhookPostWebhook handles the POST webhook for resourceWebhook. - HandleOnWebhookPostWebhook(ctx echo.Context, params OnWebhookPostParams) error -} - -// OnWebhookGetWebhookHandler returns the echo.HandlerFunc for the resourceWebhook webhook. -// Mount this at the URL path advertised to webhook senders. Errors -// during parameter binding are returned via echo.NewHTTPError so echo's -// error-handling chain reports them as 400. Middlewares are applied in -// the order provided -- the last argument becomes the outermost wrapper. -func OnWebhookGetWebhookHandler(si WebhookReceiverInterface, middlewares ...echo.MiddlewareFunc) echo.HandlerFunc { - h := echo.HandlerFunc(func(ctx echo.Context) error { - var err error - _ = err - - // Parameter object where we will unmarshal all parameters from the context. - var params OnWebhookGetParams - - // ------------- Required query parameter "whid" ------------- - - err = runtime.BindQueryParameterWithOptions("form", true, true, "whid", ctx.QueryParams(), ¶ms.Whid, runtime.BindQueryParameterOptions{Type: "", Format: ""}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter whid: %s", err)) - } - - return si.HandleOnWebhookGetWebhook(ctx, params) - }) - for _, mw := range middlewares { - h = mw(h) - } - return h -} - -// OnWebhookPostWebhookHandler returns the echo.HandlerFunc for the resourceWebhook webhook. -// Mount this at the URL path advertised to webhook senders. Errors -// during parameter binding are returned via echo.NewHTTPError so echo's -// error-handling chain reports them as 400. Middlewares are applied in -// the order provided -- the last argument becomes the outermost wrapper. -func OnWebhookPostWebhookHandler(si WebhookReceiverInterface, middlewares ...echo.MiddlewareFunc) echo.HandlerFunc { - h := echo.HandlerFunc(func(ctx echo.Context) error { - var err error - _ = err - - // Parameter object where we will unmarshal all parameters from the context. - var params OnWebhookPostParams - - // ------------- Required query parameter "whid" ------------- - - err = runtime.BindQueryParameterWithOptions("form", true, true, "whid", ctx.QueryParams(), ¶ms.Whid, runtime.BindQueryParameterOptions{Type: "", Format: ""}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter whid: %s", err)) - } - - return si.HandleOnWebhookPostWebhook(ctx, params) - }) - for _, mw := range middlewares { - h = mw(h) - } - return h -} - -// CallbackReceiverInterface represents handlers for receiving inbound -// callback requests. Each callback becomes a Handle*Callback -// method that the implementation fills in. The caller mounts the per- -// callback echo.HandlerFunc returned by {Op}CallbackHandler at -// whatever URL path they advertise to senders. -type CallbackReceiverInterface interface { - - // HandleOnResourceGetCallback handles the GET callback for resourceEvent. - HandleOnResourceGetCallback(ctx echo.Context, params OnResourceGetParams) error - - // HandleOnResourcePostCallback handles the POST callback for resourceEvent. - HandleOnResourcePostCallback(ctx echo.Context, params OnResourcePostParams) error -} - -// OnResourceGetCallbackHandler returns the echo.HandlerFunc for the resourceEvent callback. -// Mount this at the URL path advertised to callback senders. Errors -// during parameter binding are returned via echo.NewHTTPError so echo's -// error-handling chain reports them as 400. Middlewares are applied in -// the order provided -- the last argument becomes the outermost wrapper. -func OnResourceGetCallbackHandler(si CallbackReceiverInterface, middlewares ...echo.MiddlewareFunc) echo.HandlerFunc { - h := echo.HandlerFunc(func(ctx echo.Context) error { - var err error - _ = err - - // Parameter object where we will unmarshal all parameters from the context. - var params OnResourceGetParams - - // ------------- Required query parameter "cbid" ------------- - - err = runtime.BindQueryParameterWithOptions("form", true, true, "cbid", ctx.QueryParams(), ¶ms.Cbid, runtime.BindQueryParameterOptions{Type: "", Format: ""}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cbid: %s", err)) - } - - return si.HandleOnResourceGetCallback(ctx, params) - }) - for _, mw := range middlewares { - h = mw(h) - } - return h -} - -// OnResourcePostCallbackHandler returns the echo.HandlerFunc for the resourceEvent callback. -// Mount this at the URL path advertised to callback senders. Errors -// during parameter binding are returned via echo.NewHTTPError so echo's -// error-handling chain reports them as 400. Middlewares are applied in -// the order provided -- the last argument becomes the outermost wrapper. -func OnResourcePostCallbackHandler(si CallbackReceiverInterface, middlewares ...echo.MiddlewareFunc) echo.HandlerFunc { - h := echo.HandlerFunc(func(ctx echo.Context) error { - var err error - _ = err - - // Parameter object where we will unmarshal all parameters from the context. - var params OnResourcePostParams - - // ------------- Required query parameter "cbid" ------------- - - err = runtime.BindQueryParameterWithOptions("form", true, true, "cbid", ctx.QueryParams(), ¶ms.Cbid, runtime.BindQueryParameterOptions{Type: "", Format: ""}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter cbid: %s", err)) - } - - return si.HandleOnResourcePostCallback(ctx, params) - }) - for _, mw := range middlewares { - h = mw(h) - } - return h -} diff --git a/internal/test/parameters/shared_anyof/shared_anyof_test.go b/internal/test/parameters/shared_anyof/shared_anyof_test.go deleted file mode 100644 index b11be615b..000000000 --- a/internal/test/parameters/shared_anyof/shared_anyof_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package sharedanyof - -import ( - "testing" - - openapi_types "github.com/oapi-codegen/runtime/types" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// TestSharedAnyOfParamTypesDeclaredOnce is the issue-2090 regression guard: a -// path-level anyOf parameter shared by several methods, and a component -// parameter referenced from several paths, each have their union member types -// declared exactly once. The blank declarations below only build because each -// type exists (and is not redeclared); the generated package failing to build -// as part of the test module would also catch a regression. -func TestSharedAnyOfParamTypesDeclaredOnce(t *testing.T) { - var ( - _ Id0 // path parameter, shared by get/post/delete - _ Id1 // - _ Whid0 // webhook query parameter - _ Cbid0 // callback query parameter - _ WidgetId0 // component parameter, $ref'd from two paths - _ WidgetId1 // - ) - - // The component parameter's union type carries its accessors once. - u := openapi_types.UUID{} - var w WidgetId - require.NoError(t, w.FromWidgetId0(u)) - got, err := w.AsWidgetId0() - require.NoError(t, err) - assert.Equal(t, u, got) -} diff --git a/internal/test/parameters/shared_anyof/spec.yaml b/internal/test/parameters/shared_anyof/spec.yaml deleted file mode 100644 index 62ef86497..000000000 --- a/internal/test/parameters/shared_anyof/spec.yaml +++ /dev/null @@ -1,63 +0,0 @@ -openapi: 3.1.0 -info: - title: shared-anyof-param - version: "1.0.0" -# Path-level anyOf parameters shared across multiple methods. The union member -# types (Id0/Id1, ...) are declared once for the path item rather than once per -# method, so this compiles (issue #2090). Distinct parameter names across the -# regular path, webhook, and callback avoid a cross-path collision. -paths: - /v1/resource/{id}: - parameters: - - name: id - in: path - required: true - schema: - anyOf: - - {type: string, format: uuid} - - {type: string, pattern: '^[a-z0-9_-]+$'} - get: {operationId: getResource, responses: {'200': {description: ok}}} - post: {operationId: postResource, responses: {'200': {description: ok}}} - delete: {operationId: deleteResource, responses: {'200': {description: ok}}} - /subscribe: - post: - operationId: subscribe - responses: {'200': {description: ok}} - callbacks: - resourceEvent: - '{$request.body#/callbackUrl}': - parameters: - - name: cbid - in: query - required: true - schema: - anyOf: [{type: string, format: uuid}, {type: string, pattern: '^[a-z0-9_-]+$'}] - post: {operationId: onResourcePost, responses: {'200': {description: ok}}} - get: {operationId: onResourceGet, responses: {'200': {description: ok}}} - # A component parameter with an anyOf schema, referenced from two different - # paths: its union member types are declared once with the component, so the - # two references do not collide (issue #2090). - /widgets/{wid}: - parameters: [{$ref: '#/components/parameters/WidgetId'}] - get: {operationId: getWidget, responses: {'200': {description: ok}}} - /gadgets/{wid}: - parameters: [{$ref: '#/components/parameters/WidgetId'}] - get: {operationId: getGadget, responses: {'200': {description: ok}}} -components: - parameters: - WidgetId: - name: wid - in: path - required: true - schema: - anyOf: [{type: string, format: uuid}, {type: string, pattern: '^[a-z0-9_-]+$'}] -webhooks: - resourceWebhook: - parameters: - - name: whid - in: query - required: true - schema: - anyOf: [{type: string, format: uuid}, {type: string, pattern: '^[a-z0-9_-]+$'}] - post: {operationId: onWebhookPost, responses: {'200': {description: ok}}} - get: {operationId: onWebhookGet, responses: {'200': {description: ok}}} diff --git a/internal/test/parameters/shared_anyof/config.yaml b/internal/test/parameters/shared_collision/config.yaml similarity index 55% rename from internal/test/parameters/shared_anyof/config.yaml rename to internal/test/parameters/shared_collision/config.yaml index 3b67ae177..61c9942fb 100644 --- a/internal/test/parameters/shared_anyof/config.yaml +++ b/internal/test/parameters/shared_collision/config.yaml @@ -1,8 +1,6 @@ # yaml-language-server: $schema=../../../../configuration-schema.json -# From issue-2090 -package: sharedanyof +package: parameterssharedcollision +output: shared_collision.gen.go generate: - echo-server: true - client: true models: true -output: shared_anyof.gen.go + client: true diff --git a/internal/test/parameters/shared_collision/doc.go b/internal/test/parameters/shared_collision/doc.go new file mode 100644 index 000000000..cc0d763b3 --- /dev/null +++ b/internal/test/parameters/shared_collision/doc.go @@ -0,0 +1,19 @@ +// Package parameterssharedcollision is a regression fixture for the naming of +// helper types hoisted by shared (path-item-level) parameters (issue #2090). +// +// A parameter declared at the path-item level is inherited by every method on +// the path, so a helper type it hoists (e.g. an anyOf member) was declared once +// per operation and redeclared — "Id0 redeclared in this block" — on any path +// with more than one method, and clashed again across sibling paths that reuse +// the same parameter. The spec exercises each case: a parameter shared across +// two methods of one path (declared once, bare), the same parameter reused by +// two sibling paths (hash-disambiguated per path), a non-colliding single-method +// parameter (unchanged), and the equivalent shared parameters on a webhook and a +// callback (the emit-once paths in WebhookOperationDefinitions and +// CallbackOperationDefinitions). +// +// The generated file compiling as part of this package is the primary guard: +// the redeclaration bug would fail the build. +package parameterssharedcollision + +//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config=config.yaml spec.yaml diff --git a/internal/test/parameters/shared_collision/shared_collision.gen.go b/internal/test/parameters/shared_collision/shared_collision.gen.go new file mode 100644 index 000000000..e237ca344 --- /dev/null +++ b/internal/test/parameters/shared_collision/shared_collision.gen.go @@ -0,0 +1,1295 @@ +// Package parameterssharedcollision provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT. +package parameterssharedcollision + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + + "github.com/oapi-codegen/runtime" + openapi_types "github.com/oapi-codegen/runtime/types" +) + +// Hd147675Id0 is a helper type for the shared "id" parameter of "/gadget/{id}", prefixed with a per-path hash to disambiguate it from the same-named parameter on another path. +type Hd147675Id0 = openapi_types.UUID + +// Hd147675Id1 is a helper type for the shared "id" parameter of "/gadget/{id}", prefixed with a per-path hash to disambiguate it from the same-named parameter on another path. +type Hd147675Id1 = string + +// Hdf81445Id0 is a helper type for the shared "id" parameter of "/gadget/{id}/part", prefixed with a per-path hash to disambiguate it from the same-named parameter on another path. +type Hdf81445Id0 = openapi_types.UUID + +// Hdf81445Id1 is a helper type for the shared "id" parameter of "/gadget/{id}/part", prefixed with a per-path hash to disambiguate it from the same-named parameter on another path. +type Hdf81445Id1 = string + +// Ref0 defines parameters for GetSprocket. +type Ref0 = openapi_types.UUID + +// Ref1 defines parameters for GetSprocket. +type Ref1 = string + +// Wid0 defines parameters for DeleteWidget. +type Wid0 = openapi_types.UUID + +// Wid1 defines parameters for DeleteWidget. +type Wid1 = string + +// OnEventGetParams defines parameters for OnEventGet. +type OnEventGetParams struct { + Whid *struct { + union json.RawMessage + } `form:"whid,omitempty" json:"whid,omitempty"` +} + +// Whid0 defines parameters for OnEventGet. +type Whid0 = openapi_types.UUID + +// Whid1 defines parameters for OnEventGet. +type Whid1 = string + +// OnEventPostParams defines parameters for OnEventPost. +type OnEventPostParams struct { + Whid *struct { + union json.RawMessage + } `form:"whid,omitempty" json:"whid,omitempty"` +} + +// OnDataGetParams defines parameters for OnDataGet. +type OnDataGetParams struct { + Cbid *struct { + union json.RawMessage + } `form:"cbid,omitempty" json:"cbid,omitempty"` +} + +// Cbid0 defines parameters for OnDataGet. +type Cbid0 = openapi_types.UUID + +// Cbid1 defines parameters for OnDataGet. +type Cbid1 = string + +// OnDataPostParams defines parameters for OnDataPost. +type OnDataPostParams struct { + Cbid *struct { + union json.RawMessage + } `form:"cbid,omitempty" json:"cbid,omitempty"` +} + +// RequestEditorFn is the function signature for the RequestEditor callback function +type RequestEditorFn func(ctx context.Context, req *http.Request) error + +// Doer performs HTTP requests. +// +// The standard http.Client implements this interface. +type HttpRequestDoer interface { + Do(req *http.Request) (*http.Response, error) +} + +// Client which conforms to the OpenAPI3 specification for this service. +type Client struct { + // The endpoint of the server conforming to this interface, with scheme, + // https://api.deepmap.com for example. This can contain a path relative + // to the server, such as https://api.deepmap.com/dev-test, and all the + // paths in the swagger spec will be appended to the server. + Server string + + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A list of callbacks for modifying requests which are generated before sending over + // the network. + RequestEditors []RequestEditorFn +} + +// ClientOption allows setting custom parameters during construction +type ClientOption func(*Client) error + +// Creates a new Client, with reasonable defaults +func NewClient(server string, opts ...ClientOption) (*Client, error) { + // create a client with sane default values + client := Client{ + Server: server, + } + // mutate client and add all optional params + for _, o := range opts { + if err := o(&client); err != nil { + return nil, err + } + } + // ensure the server URL always has a trailing slash + if !strings.HasSuffix(client.Server, "/") { + client.Server += "/" + } + // create httpClient, if not already present + if client.Client == nil { + client.Client = &http.Client{} + } + return &client, nil +} + +// WithHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithHTTPClient(doer HttpRequestDoer) ClientOption { + return func(c *Client) error { + c.Client = doer + return nil + } +} + +// WithRequestEditorFn allows setting up a callback function, which will be +// called right before sending the request. This can be used to mutate the request. +func WithRequestEditorFn(fn RequestEditorFn) ClientOption { + return func(c *Client) error { + c.RequestEditors = append(c.RequestEditors, fn) + return nil + } +} + +// The interface specification for the client above. +type ClientInterface interface { + + // DeleteGadget performs a DELETE /gadget/{id} (the `DeleteGadget` operationId) request. + DeleteGadget(ctx context.Context, id struct { + union json.RawMessage + }, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetGadgetPart performs a GET /gadget/{id}/part (the `GetGadgetPart` operationId) request. + GetGadgetPart(ctx context.Context, id struct { + union json.RawMessage + }, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetSprocket performs a GET /sprocket/{ref} (the `GetSprocket` operationId) request. + GetSprocket(ctx context.Context, ref struct { + union json.RawMessage + }, reqEditors ...RequestEditorFn) (*http.Response, error) + + // Subscribe performs a POST /subscribe (the `Subscribe` operationId) request. + Subscribe(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // DeleteWidget performs a DELETE /widget/{wid} (the `DeleteWidget` operationId) request. + DeleteWidget(ctx context.Context, wid struct { + union json.RawMessage + }, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetWidget performs a GET /widget/{wid} (the `GetWidget` operationId) request. + GetWidget(ctx context.Context, wid struct { + union json.RawMessage + }, reqEditors ...RequestEditorFn) (*http.Response, error) +} + +// DeleteGadget performs a DELETE /gadget/{id} (the `DeleteGadget` operationId) request. +func (c *Client) DeleteGadget(ctx context.Context, id struct { + union json.RawMessage +}, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteGadgetRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetGadgetPart performs a GET /gadget/{id}/part (the `GetGadgetPart` operationId) request. +func (c *Client) GetGadgetPart(ctx context.Context, id struct { + union json.RawMessage +}, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetGadgetPartRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetSprocket performs a GET /sprocket/{ref} (the `GetSprocket` operationId) request. +func (c *Client) GetSprocket(ctx context.Context, ref struct { + union json.RawMessage +}, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetSprocketRequest(c.Server, ref) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// Subscribe performs a POST /subscribe (the `Subscribe` operationId) request. +func (c *Client) Subscribe(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSubscribeRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// DeleteWidget performs a DELETE /widget/{wid} (the `DeleteWidget` operationId) request. +func (c *Client) DeleteWidget(ctx context.Context, wid struct { + union json.RawMessage +}, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteWidgetRequest(c.Server, wid) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetWidget performs a GET /widget/{wid} (the `GetWidget` operationId) request. +func (c *Client) GetWidget(ctx context.Context, wid struct { + union json.RawMessage +}, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetWidgetRequest(c.Server, wid) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// NewDeleteGadgetRequest constructs an http.Request for the DeleteGadget method +func NewDeleteGadgetRequest(server string, id struct { + union json.RawMessage +}) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/gadget/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetGadgetPartRequest constructs an http.Request for the GetGadgetPart method +func NewGetGadgetPartRequest(server string, id struct { + union json.RawMessage +}) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/gadget/%s/part", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetSprocketRequest constructs an http.Request for the GetSprocket method +func NewGetSprocketRequest(server string, ref struct { + union json.RawMessage +}) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/sprocket/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewSubscribeRequest constructs an http.Request for the Subscribe method +func NewSubscribeRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/subscribe") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewDeleteWidgetRequest constructs an http.Request for the DeleteWidget method +func NewDeleteWidgetRequest(server string, wid struct { + union json.RawMessage +}) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "wid", wid, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/widget/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetWidgetRequest constructs an http.Request for the GetWidget method +func NewGetWidgetRequest(server string, wid struct { + union json.RawMessage +}) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "wid", wid, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/widget/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { + for _, r := range c.RequestEditors { + if err := r(ctx, req); err != nil { + return err + } + } + for _, r := range additionalEditors { + if err := r(ctx, req); err != nil { + return err + } + } + return nil +} + +// ClientWithResponses builds on ClientInterface to offer response payloads +type ClientWithResponses struct { + ClientInterface +} + +// NewClientWithResponses creates a new ClientWithResponses, which wraps +// Client with return type handling +func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) { + client, err := NewClient(server, opts...) + if err != nil { + return nil, err + } + return &ClientWithResponses{client}, nil +} + +// WithBaseURL overrides the baseURL. +func WithBaseURL(baseURL string) ClientOption { + return func(c *Client) error { + newBaseURL, err := url.Parse(baseURL) + if err != nil { + return err + } + c.Server = newBaseURL.String() + return nil + } +} + +// ClientWithResponsesInterface is the interface specification for the client with responses above. +type ClientWithResponsesInterface interface { + + // DeleteGadgetWithResponse performs a DELETE /gadget/{id} (the `DeleteGadget` operationId) request. + // + // Returns a wrapper object for the known response body format(s). + DeleteGadgetWithResponse(ctx context.Context, id struct { + union json.RawMessage + }, reqEditors ...RequestEditorFn) (*DeleteGadgetResponse, error) + + // GetGadgetPartWithResponse performs a GET /gadget/{id}/part (the `GetGadgetPart` operationId) request. + // + // Returns a wrapper object for the known response body format(s). + GetGadgetPartWithResponse(ctx context.Context, id struct { + union json.RawMessage + }, reqEditors ...RequestEditorFn) (*GetGadgetPartResponse, error) + + // GetSprocketWithResponse performs a GET /sprocket/{ref} (the `GetSprocket` operationId) request. + // + // Returns a wrapper object for the known response body format(s). + GetSprocketWithResponse(ctx context.Context, ref struct { + union json.RawMessage + }, reqEditors ...RequestEditorFn) (*GetSprocketResponse, error) + + // SubscribeWithResponse performs a POST /subscribe (the `Subscribe` operationId) request. + // + // Returns a wrapper object for the known response body format(s). + SubscribeWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*SubscribeResponse, error) + + // DeleteWidgetWithResponse performs a DELETE /widget/{wid} (the `DeleteWidget` operationId) request. + // + // Returns a wrapper object for the known response body format(s). + DeleteWidgetWithResponse(ctx context.Context, wid struct { + union json.RawMessage + }, reqEditors ...RequestEditorFn) (*DeleteWidgetResponse, error) + + // GetWidgetWithResponse performs a GET /widget/{wid} (the `GetWidget` operationId) request. + // + // Returns a wrapper object for the known response body format(s). + GetWidgetWithResponse(ctx context.Context, wid struct { + union json.RawMessage + }, reqEditors ...RequestEditorFn) (*GetWidgetResponse, error) +} + +type DeleteGadgetResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r DeleteGadgetResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r DeleteGadgetResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteGadgetResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteGadgetResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetGadgetPartResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r GetGadgetPartResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetGadgetPartResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetGadgetPartResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetGadgetPartResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetSprocketResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r GetSprocketResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetSprocketResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetSprocketResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetSprocketResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type SubscribeResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r SubscribeResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r SubscribeResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r SubscribeResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r SubscribeResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DeleteWidgetResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r DeleteWidgetResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r DeleteWidgetResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteWidgetResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteWidgetResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetWidgetResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r GetWidgetResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetWidgetResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetWidgetResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetWidgetResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +// DeleteGadgetWithResponse performs a DELETE /gadget/{id} (the `DeleteGadget` operationId) request. +// +// Returns a wrapper object for the known response body format(s). +func (c *ClientWithResponses) DeleteGadgetWithResponse(ctx context.Context, id struct { + union json.RawMessage +}, reqEditors ...RequestEditorFn) (*DeleteGadgetResponse, error) { + rsp, err := c.DeleteGadget(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteGadgetResponse(rsp) +} + +// GetGadgetPartWithResponse performs a GET /gadget/{id}/part (the `GetGadgetPart` operationId) request. +// +// Returns a wrapper object for the known response body format(s). +func (c *ClientWithResponses) GetGadgetPartWithResponse(ctx context.Context, id struct { + union json.RawMessage +}, reqEditors ...RequestEditorFn) (*GetGadgetPartResponse, error) { + rsp, err := c.GetGadgetPart(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetGadgetPartResponse(rsp) +} + +// GetSprocketWithResponse performs a GET /sprocket/{ref} (the `GetSprocket` operationId) request. +// +// Returns a wrapper object for the known response body format(s). +func (c *ClientWithResponses) GetSprocketWithResponse(ctx context.Context, ref struct { + union json.RawMessage +}, reqEditors ...RequestEditorFn) (*GetSprocketResponse, error) { + rsp, err := c.GetSprocket(ctx, ref, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetSprocketResponse(rsp) +} + +// SubscribeWithResponse performs a POST /subscribe (the `Subscribe` operationId) request. +// +// Returns a wrapper object for the known response body format(s). +func (c *ClientWithResponses) SubscribeWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*SubscribeResponse, error) { + rsp, err := c.Subscribe(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseSubscribeResponse(rsp) +} + +// DeleteWidgetWithResponse performs a DELETE /widget/{wid} (the `DeleteWidget` operationId) request. +// +// Returns a wrapper object for the known response body format(s). +func (c *ClientWithResponses) DeleteWidgetWithResponse(ctx context.Context, wid struct { + union json.RawMessage +}, reqEditors ...RequestEditorFn) (*DeleteWidgetResponse, error) { + rsp, err := c.DeleteWidget(ctx, wid, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteWidgetResponse(rsp) +} + +// GetWidgetWithResponse performs a GET /widget/{wid} (the `GetWidget` operationId) request. +// +// Returns a wrapper object for the known response body format(s). +func (c *ClientWithResponses) GetWidgetWithResponse(ctx context.Context, wid struct { + union json.RawMessage +}, reqEditors ...RequestEditorFn) (*GetWidgetResponse, error) { + rsp, err := c.GetWidget(ctx, wid, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetWidgetResponse(rsp) +} + +// ParseDeleteGadgetResponse parses an HTTP response from a DeleteGadgetWithResponse call +func ParseDeleteGadgetResponse(rsp *http.Response) (*DeleteGadgetResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteGadgetResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetGadgetPartResponse parses an HTTP response from a GetGadgetPartWithResponse call +func ParseGetGadgetPartResponse(rsp *http.Response) (*GetGadgetPartResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetGadgetPartResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetSprocketResponse parses an HTTP response from a GetSprocketWithResponse call +func ParseGetSprocketResponse(rsp *http.Response) (*GetSprocketResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetSprocketResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseSubscribeResponse parses an HTTP response from a SubscribeWithResponse call +func ParseSubscribeResponse(rsp *http.Response) (*SubscribeResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &SubscribeResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseDeleteWidgetResponse parses an HTTP response from a DeleteWidgetWithResponse call +func ParseDeleteWidgetResponse(rsp *http.Response) (*DeleteWidgetResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteWidgetResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetWidgetResponse parses an HTTP response from a GetWidgetWithResponse call +func ParseGetWidgetResponse(rsp *http.Response) (*GetWidgetResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetWidgetResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// WebhookInitiator sends OpenAPI 3.1 webhook requests to target URLs. +// Modeled on the generated Client, but with no stored Server -- the full +// target URL is provided per-call by the caller (typically discovered +// from a subscription registration). +type WebhookInitiator struct { + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A list of callbacks for modifying requests which are generated before sending over + // the network. + RequestEditors []RequestEditorFn +} + +// WebhookInitiatorOption allows setting custom parameters during construction. +type WebhookInitiatorOption func(*WebhookInitiator) error + +// NewWebhookInitiator creates a new WebhookInitiator with reasonable defaults. +func NewWebhookInitiator(opts ...WebhookInitiatorOption) (*WebhookInitiator, error) { + initiator := WebhookInitiator{} + for _, o := range opts { + if err := o(&initiator); err != nil { + return nil, err + } + } + if initiator.Client == nil { + initiator.Client = &http.Client{} + } + return &initiator, nil +} + +// WithWebhookHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithWebhookHTTPClient(doer HttpRequestDoer) WebhookInitiatorOption { + return func(p *WebhookInitiator) error { + p.Client = doer + return nil + } +} + +// WithWebhookRequestEditorFn allows setting up a callback function, which +// will be called right before sending the webhook request. This can be +// used to mutate the request, e.g. to add signature headers. +func WithWebhookRequestEditorFn(fn RequestEditorFn) WebhookInitiatorOption { + return func(p *WebhookInitiator) error { + p.RequestEditors = append(p.RequestEditors, fn) + return nil + } +} + +func (p *WebhookInitiator) applyWebhookEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { + for _, r := range p.RequestEditors { + if err := r(ctx, req); err != nil { + return err + } + } + for _, r := range additionalEditors { + if err := r(ctx, req); err != nil { + return err + } + } + return nil +} + +// WebhookInitiatorInterface is the interface specification for the webhook initiator. +type WebhookInitiatorInterface interface { + // OnEventGet fires the onEvent webhook + OnEventGet(ctx context.Context, targetURL string, params *OnEventGetParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // OnEventPost fires the onEvent webhook + OnEventPost(ctx context.Context, targetURL string, params *OnEventPostParams, reqEditors ...RequestEditorFn) (*http.Response, error) +} + +func (p *WebhookInitiator) OnEventGet(ctx context.Context, targetURL string, params *OnEventGetParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewOnEventGetWebhookRequest(targetURL, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := p.applyWebhookEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return p.Client.Do(req) +} + +func (p *WebhookInitiator) OnEventPost(ctx context.Context, targetURL string, params *OnEventPostParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewOnEventPostWebhookRequest(targetURL, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := p.applyWebhookEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return p.Client.Do(req) +} + +// NewOnEventGetWebhookRequest builds a GET request for the onEvent webhook +func NewOnEventGetWebhookRequest(targetURL string, params *OnEventGetParams) (*http.Request, error) { + var err error + _ = err + + reqURL, err := url.Parse(targetURL) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := reqURL.Query() + var rawQueryFragments []string + + if params.Whid != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "whid", *params.Whid, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + reqURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, reqURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewOnEventPostWebhookRequest builds a POST request for the onEvent webhook +func NewOnEventPostWebhookRequest(targetURL string, params *OnEventPostParams) (*http.Request, error) { + var err error + _ = err + + reqURL, err := url.Parse(targetURL) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := reqURL.Query() + var rawQueryFragments []string + + if params.Whid != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "whid", *params.Whid, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + reqURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodPost, reqURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// CallbackInitiator sends OpenAPI callback requests to target URLs. +// Modeled on the generated Client, but with no stored Server -- the full +// target URL is provided per-call by the caller (typically discovered +// from a callback expression on the parent operation's request body, +// e.g. `{$request.body#/callbackUrl}`). +type CallbackInitiator struct { + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A list of callbacks for modifying requests which are generated before sending over + // the network. + RequestEditors []RequestEditorFn +} + +// CallbackInitiatorOption allows setting custom parameters during construction. +type CallbackInitiatorOption func(*CallbackInitiator) error + +// NewCallbackInitiator creates a new CallbackInitiator with reasonable defaults. +func NewCallbackInitiator(opts ...CallbackInitiatorOption) (*CallbackInitiator, error) { + initiator := CallbackInitiator{} + for _, o := range opts { + if err := o(&initiator); err != nil { + return nil, err + } + } + if initiator.Client == nil { + initiator.Client = &http.Client{} + } + return &initiator, nil +} + +// WithCallbackHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithCallbackHTTPClient(doer HttpRequestDoer) CallbackInitiatorOption { + return func(p *CallbackInitiator) error { + p.Client = doer + return nil + } +} + +// WithCallbackRequestEditorFn allows setting up a callback function, which +// will be called right before sending the callback request. This can be +// used to mutate the request, e.g. to add signature headers. +func WithCallbackRequestEditorFn(fn RequestEditorFn) CallbackInitiatorOption { + return func(p *CallbackInitiator) error { + p.RequestEditors = append(p.RequestEditors, fn) + return nil + } +} + +func (p *CallbackInitiator) applyCallbackEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { + for _, r := range p.RequestEditors { + if err := r(ctx, req); err != nil { + return err + } + } + for _, r := range additionalEditors { + if err := r(ctx, req); err != nil { + return err + } + } + return nil +} + +// CallbackInitiatorInterface is the interface specification for the callback initiator. +type CallbackInitiatorInterface interface { + // OnDataGet fires the onData callback + OnDataGet(ctx context.Context, targetURL string, params *OnDataGetParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // OnDataPost fires the onData callback + OnDataPost(ctx context.Context, targetURL string, params *OnDataPostParams, reqEditors ...RequestEditorFn) (*http.Response, error) +} + +func (p *CallbackInitiator) OnDataGet(ctx context.Context, targetURL string, params *OnDataGetParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewOnDataGetCallbackRequest(targetURL, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := p.applyCallbackEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return p.Client.Do(req) +} + +func (p *CallbackInitiator) OnDataPost(ctx context.Context, targetURL string, params *OnDataPostParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewOnDataPostCallbackRequest(targetURL, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := p.applyCallbackEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return p.Client.Do(req) +} + +// NewOnDataGetCallbackRequest builds a GET request for the onData callback +func NewOnDataGetCallbackRequest(targetURL string, params *OnDataGetParams) (*http.Request, error) { + var err error + _ = err + + reqURL, err := url.Parse(targetURL) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := reqURL.Query() + var rawQueryFragments []string + + if params.Cbid != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cbid", *params.Cbid, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + reqURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, reqURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewOnDataPostCallbackRequest builds a POST request for the onData callback +func NewOnDataPostCallbackRequest(targetURL string, params *OnDataPostParams) (*http.Request, error) { + var err error + _ = err + + reqURL, err := url.Parse(targetURL) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := reqURL.Query() + var rawQueryFragments []string + + if params.Cbid != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cbid", *params.Cbid, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + reqURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodPost, reqURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} diff --git a/internal/test/parameters/shared_collision/shared_collision_test.go b/internal/test/parameters/shared_collision/shared_collision_test.go new file mode 100644 index 000000000..705740a63 --- /dev/null +++ b/internal/test/parameters/shared_collision/shared_collision_test.go @@ -0,0 +1,56 @@ +package parameterssharedcollision + +import ( + "os" + "regexp" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// Referencing these names asserts, at compile time, that non-colliding shared +// (path-item-level) parameters keep their historical undecorated form, so +// existing code is unaffected (issue #2090). The generated file compiling is +// itself the primary guard — the "redeclared in this block" bug would fail the +// build. +// +// - Wid0/Wid1: shared across two methods of /widget/{wid}. Declared once for +// the path item (not once per method), and bare because the name is unique. +// - Ref0/Ref1: the single-method /sprocket/{ref}, also unique and bare. +// - Whid0/Whid1: shared across the two methods of the onEvent webhook, and +// Cbid0/Cbid1 across the two methods of the onData callback — the webhook +// and callback emit-once paths, declared once each rather than per method. +var ( + _ Wid0 + _ Wid1 + _ Ref0 + _ Ref1 + _ Whid0 + _ Whid1 + _ Cbid0 + _ Cbid1 +) + +// TestSharedParamCollisionHashed asserts that the same {id} parameter, reused by +// the two sibling paths /gadget/{id} and /gadget/{id}/part, is disambiguated by +// a per-path hash prefix rather than colliding on a bare name. We read the +// generated source because the hash values are derived from the paths and +// shouldn't be hard-coded into the test. +func TestSharedParamCollisionHashed(t *testing.T) { + src, err := os.ReadFile("shared_collision.gen.go") + require.NoError(t, err) + + // The bare, colliding names must NOT be declared. + assert.NotRegexp(t, regexp.MustCompile(`(?m)^type Id0 `), string(src)) + assert.NotRegexp(t, regexp.MustCompile(`(?m)^type Id1 `), string(src)) + + // Exactly two distinct hash-prefixed variants of the id union member must be + // declared — one per colliding path. + variants := regexp.MustCompile(`(?m)^type (H[0-9a-f]+Id0) `).FindAllStringSubmatch(string(src), -1) + seen := map[string]bool{} + for _, m := range variants { + seen[m[1]] = true + } + assert.Len(t, seen, 2, "expected two per-path hash-disambiguated id types, got %v", seen) +} diff --git a/internal/test/parameters/shared_collision/spec.yaml b/internal/test/parameters/shared_collision/spec.yaml new file mode 100644 index 000000000..68a0b8220 --- /dev/null +++ b/internal/test/parameters/shared_collision/spec.yaml @@ -0,0 +1,111 @@ +openapi: 3.1.0 +info: + title: shared-path-parameter-collision + version: "1.0" +paths: + # Within-path, unique name: a path-item-level `anyOf` parameter shared by two + # methods. Its helper types are declared once for the path item (not once per + # method, which was the "Wid0 redeclared" bug), and since the name is unique + # across the spec it keeps its bare, undecorated form. + /widget/{wid}: + parameters: + - name: wid + in: path + required: true + schema: + anyOf: + - { type: string, format: uuid } + - { type: string } + get: + operationId: getWidget + responses: { '200': { description: ok } } + delete: + operationId: deleteWidget + responses: { '200': { description: ok } } + + # Cross-path collision: two sibling single-method paths reuse the same `{id}` + # parameter. The bare names would collide across the two paths, so each path's + # parameter is prefixed with a short, stable hash of the path to disambiguate. + /gadget/{id}: + parameters: + - name: id + in: path + required: true + schema: + anyOf: + - { type: string, format: uuid } + - { type: string } + delete: + operationId: deleteGadget + responses: { '200': { description: ok } } + /gadget/{id}/part: + parameters: + - name: id + in: path + required: true + schema: + anyOf: + - { type: string, format: uuid } + - { type: string } + get: + operationId: getGadgetPart + responses: { '200': { description: ok } } + + # No collision: a single-method path whose shared parameter hoists a name used + # nowhere else keeps its historical undecorated names (Ref0/Ref1), so existing + # generated code is unaffected. + /sprocket/{ref}: + parameters: + - name: ref + in: path + required: true + schema: + anyOf: + - { type: string, format: uuid } + - { type: string } + get: + operationId: getSprocket + responses: { '200': { description: ok } } + + # Callback with a parameter shared across the callback path item's methods. + # Exercises the shared-parameter emit-once path in CallbackOperationDefinitions + # (cbid produces Cbid0/Cbid1 once, not once per callback method). + /subscribe: + post: + operationId: subscribe + responses: { '200': { description: ok } } + callbacks: + onData: + '{$request.body#/callbackUrl}': + parameters: + - name: cbid + in: query + schema: + anyOf: + - { type: string, format: uuid } + - { type: string } + get: + operationId: onDataGet + responses: { '200': { description: ok } } + post: + operationId: onDataPost + responses: { '200': { description: ok } } + +# Webhook with a parameter shared across the webhook path item's methods. +# Exercises the shared-parameter emit-once path in WebhookOperationDefinitions +# (whid produces Whid0/Whid1 once, not once per webhook method). +webhooks: + onEvent: + parameters: + - name: whid + in: query + schema: + anyOf: + - { type: string, format: uuid } + - { type: string } + get: + operationId: onEventGet + responses: { '200': { description: ok } } + post: + operationId: onEventPost + responses: { '200': { description: ok } } diff --git a/internal/test/spec_validation/spec_validation_test.go b/internal/test/spec_validation/spec_validation_test.go index f355d3192..3f5441639 100644 --- a/internal/test/spec_validation/spec_validation_test.go +++ b/internal/test/spec_validation/spec_validation_test.go @@ -31,8 +31,6 @@ func TestValidateSpecRejects(t *testing.T) { "x_go_type_semicolon.yaml": "x-go-type", "ref_sibling_x_go_type.yaml": "x-go-type", "security_scope_quote.yaml": "scope", - - "shared_path_param_type_collision.yaml": "path-level parameter", } for file, want := range cases { diff --git a/internal/test/spec_validation/testdata/shared_path_param_type_collision.yaml b/internal/test/spec_validation/testdata/shared_path_param_type_collision.yaml deleted file mode 100644 index 8392c683f..000000000 --- a/internal/test/spec_validation/testdata/shared_path_param_type_collision.yaml +++ /dev/null @@ -1,27 +0,0 @@ -openapi: "3.0.0" -info: {title: t, version: "1.0.0"} -# Two different path items each declare a path-level anyOf parameter named -# "id". Each path declares the union member types (Id0, Id1) once, so across -# the two paths they are declared twice and collide (issue #2090). ValidateSpec -# should reject this before code generation. -paths: - /a/{id}: - parameters: - - name: id - in: path - required: true - schema: - anyOf: [{type: string, format: uuid}, {type: string, pattern: '^[a-z]+$'}] - get: - operationId: getA - responses: {'200': {description: ok}} - /b/{id}: - parameters: - - name: id - in: path - required: true - schema: - anyOf: [{type: string, format: uuid}, {type: string, pattern: '^[a-z]+$'}] - get: - operationId: getB - responses: {'200': {description: ok}} diff --git a/internal/test/spec_validation/testdata/valid.yaml b/internal/test/spec_validation/testdata/valid.yaml index 442a57fcb..8724b4a0d 100644 --- a/internal/test/spec_validation/testdata/valid.yaml +++ b/internal/test/spec_validation/testdata/valid.yaml @@ -10,25 +10,6 @@ paths: schema: {$ref: '#/components/schemas/Thing'} responses: '200': {description: ok} - # A path-level anyOf parameter shared by several methods on ONE path is - # declared once for the path item, so it is accepted (no collision). - /resource/{id}: - parameters: - - name: id - in: path - required: true - schema: - anyOf: - - {type: string, format: uuid} - - {type: string, pattern: '^[a-z]+$'} - get: - operationId: getResource - responses: - '200': {description: ok} - delete: - operationId: deleteResource - responses: - '200': {description: ok} components: schemas: Thing: diff --git a/pkg/codegen/codegen.go b/pkg/codegen/codegen.go index 54dee5e99..90ced55a3 100644 --- a/pkg/codegen/codegen.go +++ b/pkg/codegen/codegen.go @@ -1144,6 +1144,11 @@ func GenerateTypesForParameters(t *template.Template, params map[string]*openapi for _, paramName := range SortedMapKeys(params) { paramOrRef := params[paramName] + goType, err := paramToGoType(paramOrRef.Value, nil) + if err != nil { + return nil, fmt.Errorf("error generating Go type for schema in parameter %s: %w", paramName, err) + } + goTypeName, err := renameParameter(paramName, paramOrRef) if err != nil { return nil, fmt.Errorf("error making name for components/parameters/%s: %w", paramName, err) @@ -1153,16 +1158,6 @@ func GenerateTypesForParameters(t *template.Template, params map[string]*openapi goTypeName = resolved } - // Root the schema path at the parameter's Go type name so any helper - // types the schema needs (e.g. anyOf union members) are named after the - // parameter — GoTypeName0, GoTypeName1 — matching the union accessors - // generated on the parameter type, rather than an unrelated - // numeric-derived name (issue #2090). - goType, err := paramToGoType(paramOrRef.Value, []string{goTypeName}) - if err != nil { - return nil, fmt.Errorf("error generating Go type for schema in parameter %s: %w", paramName, err) - } - typeDef := TypeDefinition{ JsonName: paramName, Schema: goType, @@ -1179,10 +1174,6 @@ func GenerateTypesForParameters(t *template.Template, params map[string]*openapi } types = append(types, typeDef) - // Declare the parameter's helper types (union members, ...) once here, - // with the component, so operations that reference the parameter do not - // each redeclare them (issue #2090). - types = append(types, goType.AdditionalTypes...) } return types, nil } diff --git a/pkg/codegen/operations.go b/pkg/codegen/operations.go index 6d4f65d3d..ccb9c39d7 100644 --- a/pkg/codegen/operations.go +++ b/pkg/codegen/operations.go @@ -18,6 +18,7 @@ import ( "bytes" "cmp" "fmt" + "hash/fnv" "maps" "slices" "strconv" @@ -38,9 +39,9 @@ type ParameterDefinition struct { Schema Schema // Shared is true for a parameter declared at the path-item level, which - // is inherited by every method on the path. Its helper types are - // generated once for the path item rather than once per operation, so - // operation type collection skips them (issue #2090). + // is inherited by every method on the path. Its helper types are declared + // once for the path item rather than once per operation, so operation type + // collection skips them (issue #2090). Shared bool } @@ -290,20 +291,260 @@ func DescribeParameters(params openapi3.Parameters, path []string) ([]ParameterD return nil, fmt.Errorf("error dereferencing (%s) for param (%s): %s", paramOrRef.Ref, param.Name, err) } - // The parameter references a component parameter, whose type and - // any helper types (e.g. anyOf union members) are declared once - // with the component. Point at it and drop the inline schema this - // operation would otherwise redeclare per operation (issue #2090). pd.Schema.GoType = goType - pd.Schema.RefType = goType - pd.Schema.AdditionalTypes = nil - pd.Schema.UnionElements = nil } outParams = append(outParams, pd) } return outParams, nil } +// paramNeedsHoisting reports whether a parameter's schema produces a named +// helper type — an anyOf/oneOf union member, an inline object, etc. — as +// opposed to a bare primitive. Only such parameters can cause the redeclaration +// collision fixed for issue #2090, so only they participate in collision +// detection. The check is exact: it describes the parameter and asks whether it +// hoisted anything, rather than second-guessing which schema shapes hoist. +func paramNeedsHoisting(paramRef *openapi3.ParameterRef) (bool, error) { + if paramRef == nil || paramRef.Value == nil { + return false, nil + } + described, err := DescribeParameters(openapi3.Parameters{paramRef}, nil) + if err != nil { + return false, err + } + for _, pd := range described { + if len(pd.Schema.AdditionalTypes) > 0 { + return true, nil + } + } + return false, nil +} + +// sharedParamScope is one path item whose path-item-level parameters are shared +// by every method on it. hashKey is a string unique to the scope, hashed to +// disambiguate colliding names; source is a human-readable identifier for the +// scope used in generated doc comments. +type sharedParamScope struct { + item *openapi3.PathItem + hashKey string + source string +} + +// enumerateSharedParamScopes returns every path item in the spec that can +// declare path-item-level (shared) parameters: regular paths, webhooks, and +// callback path items. All three share the one global Go type namespace, so +// they must be considered together when detecting and resolving collisions. +func enumerateSharedParamScopes(swagger *openapi3.T) []sharedParamScope { + var scopes []sharedParamScope + + if swagger.Paths != nil { + for _, requestPath := range SortedMapKeys(swagger.Paths.Map()) { + item := swagger.Paths.Value(requestPath) + if item != nil { + scopes = append(scopes, sharedParamScope{item: item, hashKey: requestPath, source: requestPath}) + } + } + } + for _, webhookName := range SortedMapKeys(swagger.Webhooks) { + if item := swagger.Webhooks[webhookName]; item != nil { + scopes = append(scopes, sharedParamScope{item: item, hashKey: "webhook:" + webhookName, source: "webhook " + webhookName}) + } + } + if swagger.Paths != nil { + for _, requestPath := range SortedMapKeys(swagger.Paths.Map()) { + pathItem := swagger.Paths.Value(requestPath) + if pathItem == nil { + continue + } + for _, parentMethod := range SortedMapKeys(pathItem.Operations()) { + parentOp := pathItem.Operations()[parentMethod] + for _, cbName := range SortedMapKeys(parentOp.Callbacks) { + cbRef := parentOp.Callbacks[cbName] + if cbRef == nil || cbRef.Value == nil { + continue + } + cb := cbRef.Value + cbKeys := append([]string(nil), cb.Keys()...) + slices.Sort(cbKeys) + for _, urlExpr := range cbKeys { + if item := cb.Value(urlExpr); item != nil { + scopes = append(scopes, sharedParamScope{ + item: item, + hashKey: "callback:" + requestPath + ":" + parentMethod + ":" + cbName + ":" + urlExpr, + source: "callback " + cbName + " " + urlExpr, + }) + } + } + } + } + } + } + return scopes +} + +// resolveSharedParameters is the pre-pass for shared (path-item-level) +// parameter naming (issue #2090). It describes each scope's shared parameters +// once and returns them keyed by path item, ready for the operation loops to +// attach. +// +// A shared parameter's helper types are declared once for its path item, so +// two methods on the same path never collide. Names still collide *across* +// path items, though — the same `{id}` reused by sibling paths is the common +// case — so a helper-type name produced by more than one scope is disambiguated +// by prefixing every colliding scope's parameters with a short, stable hash of +// the scope (git-style: extended to the full hash only if two scopes' short +// hashes clash). A parameter that doesn't collide keeps its historical +// undecorated name, so existing generated code is unaffected. +func resolveSharedParameters(swagger *openapi3.T) (map[*openapi3.PathItem][]ParameterDefinition, error) { + scopes := enumerateSharedParamScopes(swagger) + + // Count, per shared-parameter name, how many scopes hoist a helper type + // under it. A name produced by two or more scopes collides. + nameCounts := map[string]int{} + for _, scope := range scopes { + if len(scope.item.Operations()) == 0 { + continue + } + for _, paramRef := range scope.item.Parameters { + hoists, err := paramNeedsHoisting(paramRef) + if err != nil { + return nil, err + } + if hoists { + nameCounts[paramRef.Value.Name]++ + } + } + } + colliding := map[string]bool{} + for name, n := range nameCounts { + if n >= 2 { + colliding[name] = true + } + } + + // Assign a disambiguating token to every scope that carries a colliding + // parameter. Tokens are hashes of the scope key; a short prefix is used + // unless two scopes' short prefixes clash, in which case both fall back to + // the full hash. + tokenScopeKeys := map[*openapi3.PathItem]string{} + var needToken []string + for _, scope := range scopes { + if len(scope.item.Operations()) == 0 { + continue + } + for _, paramRef := range scope.item.Parameters { + if paramRef.Value != nil && colliding[paramRef.Value.Name] { + tokenScopeKeys[scope.item] = scope.hashKey + needToken = append(needToken, scope.hashKey) + break + } + } + } + tokens := assignScopeTokens(needToken) + + result := map[*openapi3.PathItem][]ParameterDefinition{} + for _, scope := range scopes { + if _, done := result[scope.item]; done { + continue + } + token := "" + if key, ok := tokenScopeKeys[scope.item]; ok { + token = tokens[key] + } + described, err := describeSharedParameters(scope.item.Parameters, scope.source, token, colliding) + if err != nil { + return nil, err + } + markShared(described) + result[scope.item] = described + } + return result, nil +} + +// assignScopeTokens maps each scope key to a stable identifier token derived +// from an FNV hash of the key. It uses a short prefix of the hash, extending +// any keys whose short prefixes collide to the full hash (git-style). The +// tokens are lowercase so they camel-case cleanly when threaded through type +// naming (e.g. "a1b2c3d" becomes the "A1b2c3d" prefix of "A1b2c3dId0"). +func assignScopeTokens(keys []string) map[string]string { + const shortLen = 7 + full := map[string]string{} + short := map[string]string{} + for _, key := range keys { + h := fnv.New64a() + _, _ = h.Write([]byte(key)) + sum := fmt.Sprintf("h%x", h.Sum64()) + full[key] = sum + short[key] = sum[:min(shortLen+1, len(sum))] + } + shortCounts := map[string]int{} + for _, key := range keys { + shortCounts[short[key]]++ + } + tokens := map[string]string{} + for _, key := range keys { + if shortCounts[short[key]] > 1 { + tokens[key] = full[key] + } else { + tokens[key] = short[key] + } + } + return tokens +} + +// describeSharedParameters describes a scope's shared parameters once. A +// parameter whose name collides across scopes is prefixed with the scope's +// disambiguating token so its helper types are uniquely named; a parameter +// that does not collide keeps its historical undecorated name (issue #2090). +// For the disambiguated types, a doc comment is set explaining the otherwise +// opaque hash prefix and pointing back to the source path. +func describeSharedParameters(params openapi3.Parameters, source, token string, colliding map[string]bool) ([]ParameterDefinition, error) { + out := make([]ParameterDefinition, 0, len(params)) + for _, paramRef := range params { + collides := token != "" && paramRef.Value != nil && colliding[paramRef.Value.Name] + var path []string + if collides { + path = []string{token} + } + described, err := DescribeParameters(openapi3.Parameters{paramRef}, path) + if err != nil { + return nil, err + } + if collides { + for i := range described { + for j := range described[i].Schema.AdditionalTypes { + td := &described[i].Schema.AdditionalTypes[j] + td.Comment = fmt.Sprintf( + "// %s is a helper type for the shared %q parameter of %q, prefixed with a per-path hash to disambiguate it from the same-named parameter on another path.", + td.TypeName, paramRef.Value.Name, source) + } + } + } + out = append(out, described...) + } + return out, nil +} + +// markShared flags every parameter as shared at the path-item level, so its +// helper types are declared once for the path item rather than once per +// operation (issue #2090). +func markShared(params []ParameterDefinition) { + for i := range params { + params[i].Shared = true + } +} + +// sharedParameterTypeDefs returns the helper TypeDefinitions produced by +// path-item-level parameters. These are emitted once for the path item +// (attributed to its first operation) instead of once per operation. +func sharedParameterTypeDefs(sharedParams []ParameterDefinition) []TypeDefinition { + var typeDefs []TypeDefinition + for _, param := range sharedParams { + typeDefs = append(typeDefs, param.Schema.AdditionalTypes...) + } + return typeDefs +} + type SecurityDefinition struct { ProviderName string Scopes []string @@ -364,11 +605,11 @@ type OperationDefinition struct { // appear in the spec rather than sorted (issue #1887). Zero when the // source location is unavailable (e.g. a programmatically-built spec), // in which case route registration falls back to the default order. - SpecOrder int - Spec *openapi3.Operation - IsAlias bool // True when this path is a $ref alias of another path item - AliasTarget string // When IsAlias is true, this is the OperationId of the canonical operation (for route registration to reference the correct wrapper) - PathItemRef string // The path item's $ref (if any); used to qualify externally-loaded schemas referenced from this operation's responses + SpecOrder int + Spec *openapi3.Operation + IsAlias bool // True when this path is a $ref alias of another path item + AliasTarget string // When IsAlias is true, this is the OperationId of the canonical operation (for route registration to reference the correct wrapper) + PathItemRef string // The path item's $ref (if any); used to qualify externally-loaded schemas referenced from this operation's responses // IsWebhook is true when this OperationDefinition was sourced from // spec.Webhooks (OpenAPI 3.1+). Webhook operations have no path @@ -1112,21 +1353,23 @@ func OperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) { // when multiple paths $ref the same path item. aliasCounters := map[string]int{} + // Resolve path-item-level (shared) parameters once for the whole spec, so + // their helper types are declared once per path item and colliding names + // across paths are disambiguated (issue #2090). + sharedParams, err := resolveSharedParameters(swagger) + if err != nil { + return nil, err + } + for _, requestPath := range SortedMapKeys(swagger.Paths.Map()) { pathItem := swagger.Paths.Value(requestPath) // Source line of this path's key, so route registration can follow // spec declaration order (issue #1887). Zero when unavailable. pathSpecOrder := pathItemSourceLine(pathItem) - // These are parameters defined for all methods on a given path. They - // are shared by all methods, so their helper types are declared once - // for the path item (on its first operation) rather than once per - // operation (issue #2090). - globalParams, err := DescribeParameters(pathItem.Parameters, nil) - if err != nil { - return nil, fmt.Errorf("error describing global parameters for %s: %s", - requestPath, err) - } - markShared(globalParams) + // Parameters defined for all methods on this path, resolved by the + // pre-pass. Their helper types are emitted once for the path item (on + // its first operation) rather than once per operation (issue #2090). + globalParams := sharedParams[pathItem] sharedParamTypeDefs := sharedParameterTypeDefs(globalParams) sharedTypeDefsEmitted := false @@ -1307,20 +1550,21 @@ func WebhookOperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, er return operations, nil } + sharedParams, err := resolveSharedParameters(swagger) + if err != nil { + return nil, err + } + for _, webhookName := range SortedMapKeys(swagger.Webhooks) { pathItem := swagger.Webhooks[webhookName] if pathItem == nil { continue } - // Path-item-level parameters apply to every method on the - // webhook (rare for webhooks, but honored defensively). Their helper - // types are declared once for the path item (issue #2090). - globalParams, err := DescribeParameters(pathItem.Parameters, nil) - if err != nil { - return nil, fmt.Errorf("error describing webhook %q parameters: %w", webhookName, err) - } - markShared(globalParams) + // Path-item-level parameters apply to every method on the webhook + // (rare for webhooks, but honored defensively). Their helper types are + // declared once for the path item (issue #2090). + globalParams := sharedParams[pathItem] sharedParamTypeDefs := sharedParameterTypeDefs(globalParams) sharedTypeDefsEmitted := false @@ -1427,6 +1671,11 @@ func CallbackOperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, e return operations, nil } + sharedParams, err := resolveSharedParameters(swagger) + if err != nil { + return nil, err + } + for _, requestPath := range SortedMapKeys(swagger.Paths.Map()) { pathItem := swagger.Paths.Value(requestPath) if pathItem == nil { @@ -1457,14 +1706,10 @@ func CallbackOperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, e if cbPathItem == nil { continue } - globalParams, err := DescribeParameters(cbPathItem.Parameters, nil) - if err != nil { - return nil, fmt.Errorf("error describing callback %q parameters: %w", callbackName, err) - } - - // Shared callback path-item parameters: declare helper - // types once for the path item (issue #2090). - markShared(globalParams) + // Path-item-level parameters shared by every method on + // the callback path item; helper types declared once + // (issue #2090). + globalParams := sharedParams[cbPathItem] sharedParamTypeDefs := sharedParameterTypeDefs(globalParams) sharedTypeDefsEmitted := false @@ -1852,26 +2097,6 @@ func GenerateResponseDefinitions(operationID string, responses map[string]*opena return responseDefinitions, nil } -// markShared flags every parameter as shared at the path-item level, so its -// helper types are declared once for the path item rather than once per -// operation (issue #2090). -func markShared(params []ParameterDefinition) { - for i := range params { - params[i].Shared = true - } -} - -// sharedParameterTypeDefs returns the helper TypeDefinitions produced by -// path-item-level parameters. These are emitted once for the path item -// (attributed to its first operation) instead of once per operation. -func sharedParameterTypeDefs(sharedParams []ParameterDefinition) []TypeDefinition { - var typeDefs []TypeDefinition - for _, param := range sharedParams { - typeDefs = append(typeDefs, param.Schema.AdditionalTypes...) - } - return typeDefs -} - func GenerateTypeDefsForOperation(op OperationDefinition) []TypeDefinition { var typeDefs []TypeDefinition // Start with the params object itself diff --git a/pkg/codegen/schema.go b/pkg/codegen/schema.go index e8d6500db..e5959d2d3 100644 --- a/pkg/codegen/schema.go +++ b/pkg/codegen/schema.go @@ -303,6 +303,13 @@ type TypeDefinition struct { // DeprecationReason is the human-readable reason for deprecation, used // when Deprecated is true. If empty, a generic message is emitted. DeprecationReason string + + // Comment, when set, is the full doc comment (including the leading "//") + // emitted above the type in the operation parameter template, replacing the + // default " defines parameters for ." line. Used to explain + // non-obvious generated names, e.g. the per-path hash prefix on a shared + // parameter disambiguated across paths (issue #2090). + Comment string } // DeprecationComment returns a Go-style deprecation comment if the type is diff --git a/pkg/codegen/templates/param-types.tmpl b/pkg/codegen/templates/param-types.tmpl index bb107b8f0..bccf29102 100644 --- a/pkg/codegen/templates/param-types.tmpl +++ b/pkg/codegen/templates/param-types.tmpl @@ -1,6 +1,6 @@ {{range .}}{{$opid := .OperationId}} {{range .TypeDefinitions}} -// {{.TypeName}} defines parameters for {{$opid}}. +{{if .Comment}}{{.Comment}}{{else}}// {{.TypeName}} defines parameters for {{$opid}}.{{end}} type {{.TypeName}} {{if .IsAlias}}={{end}} {{.Schema.TypeDecl}} {{end}} {{end}} diff --git a/pkg/codegen/validate_spec.go b/pkg/codegen/validate_spec.go index ff9e6bf44..d7f31e46c 100644 --- a/pkg/codegen/validate_spec.go +++ b/pkg/codegen/validate_spec.go @@ -4,8 +4,6 @@ import ( "errors" "fmt" "go/token" - "slices" - "strings" "unicode" "github.com/getkin/kin-openapi/openapi3" @@ -32,101 +30,13 @@ func ValidateSpec(spec *openapi3.T) error { if spec == nil { return nil } - v := &specValidator{ - pathParamTypeDecls: map[string]int{}, - pathParamTypeWhere: map[string][]string{}, - } + v := &specValidator{} v.walkDocument(spec) - v.checkSharedPathParamTypeCollisions() return errors.Join(v.errs...) } type specValidator struct { errs []error - - // A path-item-level parameter whose inline schema needs a helper Go type - // (an anyOf/oneOf union, an enum, or an inline object) has that type - // declared once for the path item under a name derived only from the - // parameter. When two different path items declare a parameter of the - // same name, both emit the same type name and Go rejects the duplicate - // (issue #2090). These maps count, per generated type name, how many path - // items declare it and where, so checkSharedPathParamTypeCollisions can - // turn that into a clear pre-generation error. - pathParamTypeDecls map[string]int - pathParamTypeWhere map[string][]string -} - -// recordPathLevelParams accounts for the helper types generated by a path -// item's shared (path-level) parameters. Each type is declared once for the -// path item, so a name declared by more than one path item collides. -func (v *specValidator) recordPathLevelParams(item *openapi3.PathItem, where string) { - if item == nil || len(item.Operations()) == 0 { - return - } - for _, p := range item.Parameters { - name, ok := generatedParamTypeName(p) - if !ok { - continue - } - v.pathParamTypeDecls[name]++ - v.pathParamTypeWhere[name] = append(v.pathParamTypeWhere[name], where) - } -} - -// checkSharedPathParamTypeCollisions reports a generated helper type name that -// is produced by a path-level parameter on more than one path item, which -// would declare the same type twice. -func (v *specValidator) checkSharedPathParamTypeCollisions() { - for _, name := range SortedMapKeys(v.pathParamTypeDecls) { - if v.pathParamTypeDecls[name] <= 1 { - continue - } - wheres := dedupeStrings(v.pathParamTypeWhere[name]) - v.addf("the Go type %q is generated for a path-level parameter declared on more than one path (%s), so it would be declared more than once and the output will not compile; define the parameter once under #/components/parameters and $ref it from each path, give it a distinct name with the x-go-type-name extension or a spec overlay, or move it into each operation", - name, strings.Join(wheres, ", ")) - } -} - -// generatedParamTypeName returns the base Go type name a parameter's inline -// schema would generate, and whether it generates a named helper type that can -// collide across path items. An inline anyOf/oneOf produces union member types -// named from the parameter alone (Id0, Id1, ...); plain scalars, enums and -// objects used in a parameter generate no such shared named type, and a $ref -// schema references an already-declared one. -func generatedParamTypeName(ref *openapi3.ParameterRef) (string, bool) { - if ref == nil || ref.Ref != "" || ref.Value == nil { - return "", false - } - sr := ref.Value.Schema - if sr == nil || sr.Ref != "" || sr.Value == nil { - return "", false - } - s := sr.Value - if len(s.AnyOf) == 0 && len(s.OneOf) == 0 { - return "", false - } - // An x-go-type-name (on the schema) overrides the generated name, so - // collisions are keyed by that instead of the parameter name. - if raw, ok := s.Extensions[extGoTypeName]; ok { - if n, err := extTypeName(raw); err == nil { - return n, true - } - } - return SchemaNameToTypeName(ref.Value.Name), true -} - -func dedupeStrings(items []string) []string { - seen := map[string]struct{}{} - var uniq []string - for _, it := range items { - if _, ok := seen[it]; ok { - continue - } - seen[it] = struct{}{} - uniq = append(uniq, it) - } - slices.Sort(uniq) - return uniq } func (v *specValidator) addf(format string, args ...any) { @@ -320,7 +230,6 @@ func (v *specValidator) walkPathItem(item *openapi3.PathItem, where string) { if item == nil { return } - v.recordPathLevelParams(item, where) for _, p := range item.Parameters { v.walkParameterRef(p, where) }