diff --git a/.github/docs/openapi3.txt b/.github/docs/openapi3.txt index 47aaeadf..61d952ea 100644 --- a/.github/docs/openapi3.txt +++ b/.github/docs/openapi3.txt @@ -90,6 +90,11 @@ var ErrURINotSupported = errors.New("unsupported URI") ErrURINotSupported indicates the ReadFromURIFunc does not know how to handle a given URI. +var IncludeOrigin = false + IncludeOrigin specifies whether to include the origin of the OpenAPI + elements Set this to true before loading a spec to include the origin of the + OpenAPI elements Note it is global and affects all loaders + FUNCTIONS @@ -229,7 +234,7 @@ func (addProps *AdditionalProperties) UnmarshalJSON(data []byte) error type Callback struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` // Has unexported fields. } @@ -332,7 +337,7 @@ type ComponentRef interface { type Components struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Schemas Schemas `json:"schemas,omitempty" yaml:"schemas,omitempty"` Parameters ParametersMap `json:"parameters,omitempty" yaml:"parameters,omitempty"` @@ -364,7 +369,7 @@ func (components *Components) Validate(ctx context.Context, opts ...ValidationOp type Contact struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` URL string `json:"url,omitempty" yaml:"url,omitempty"` @@ -412,7 +417,7 @@ func (content Content) Validate(ctx context.Context, opts ...ValidationOption) e type Discriminator struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` PropertyName string `json:"propertyName" yaml:"propertyName"` // required Mapping StringMap `json:"mapping,omitempty" yaml:"mapping,omitempty"` @@ -435,7 +440,7 @@ func (discriminator *Discriminator) Validate(ctx context.Context, opts ...Valida type Encoding struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` ContentType string `json:"contentType,omitempty" yaml:"contentType,omitempty"` Headers Headers `json:"headers,omitempty" yaml:"headers,omitempty"` @@ -471,7 +476,7 @@ func (encoding *Encoding) WithHeaderRef(name string, ref *HeaderRef) *Encoding type Example struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` @@ -547,7 +552,7 @@ func (examples *Examples) UnmarshalJSON(data []byte) (err error) type ExternalDocs struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` URL string `json:"url,omitempty" yaml:"url,omitempty"` @@ -662,7 +667,7 @@ func (headers *Headers) UnmarshalJSON(data []byte) (err error) type Info struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Title string `json:"title" yaml:"title"` // Required Description string `json:"description,omitempty" yaml:"description,omitempty"` @@ -691,7 +696,7 @@ type IntegerFormatValidator = FormatValidator[int64] type License struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Name string `json:"name" yaml:"name"` // Required URL string `json:"url,omitempty" yaml:"url,omitempty"` @@ -713,7 +718,7 @@ func (license *License) Validate(ctx context.Context, opts ...ValidationOption) type Link struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` OperationRef string `json:"operationRef,omitempty" yaml:"operationRef,omitempty"` OperationID string `json:"operationId,omitempty" yaml:"operationId,omitempty"` @@ -790,9 +795,6 @@ type Loader struct { // IsExternalRefsAllowed enables visiting other files IsExternalRefsAllowed bool - // IncludeOrigin specifies whether to include the origin of the OpenAPI elements - IncludeOrigin bool - // ReadFromURIFunc allows overriding the any file/URL reading func ReadFromURIFunc ReadFromURIFunc @@ -836,7 +838,7 @@ type Location struct { type MediaType struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Schema *SchemaRef `json:"schema,omitempty" yaml:"schema,omitempty"` Example any `json:"example,omitempty" yaml:"example,omitempty"` @@ -914,7 +916,7 @@ type NumberFormatValidator = FormatValidator[float64] type OAuthFlow struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` AuthorizationURL string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"` TokenURL string `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"` @@ -939,7 +941,7 @@ func (flow *OAuthFlow) Validate(ctx context.Context, opts ...ValidationOption) e type OAuthFlows struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Implicit *OAuthFlow `json:"implicit,omitempty" yaml:"implicit,omitempty"` Password *OAuthFlow `json:"password,omitempty" yaml:"password,omitempty"` @@ -964,7 +966,7 @@ func (flows *OAuthFlows) Validate(ctx context.Context, opts ...ValidationOption) type Operation struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` // Optional tags for documentation. Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"` @@ -1037,7 +1039,7 @@ type Origin struct { type Parameter struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` In string `json:"in,omitempty" yaml:"in,omitempty"` @@ -1159,7 +1161,7 @@ func (parametersMap *ParametersMap) UnmarshalJSON(data []byte) (err error) type PathItem struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"` Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` @@ -1199,7 +1201,7 @@ func (pathItem *PathItem) Validate(ctx context.Context, opts ...ValidationOption type Paths struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` // Has unexported fields. } @@ -1289,7 +1291,7 @@ func URIMapCache(reader ReadFromURIFunc) ReadFromURIFunc type Ref struct { Ref string `json:"$ref" yaml:"$ref"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` } Ref is specified by OpenAPI/Swagger 3.0 standard. See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#reference-object @@ -1318,7 +1320,7 @@ func (requestBodies *RequestBodies) UnmarshalJSON(data []byte) (err error) type RequestBody struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` Required bool `json:"required,omitempty" yaml:"required,omitempty"` @@ -1405,7 +1407,7 @@ func (x *RequestBodyRef) Validate(ctx context.Context, opts ...ValidationOption) type Response struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Description *string `json:"description,omitempty" yaml:"description,omitempty"` Headers Headers `json:"headers,omitempty" yaml:"headers,omitempty"` @@ -1548,7 +1550,7 @@ func (responses *Responses) Value(key string) *ResponseRef type Schema struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` OneOf SchemaRefs `json:"oneOf,omitempty" yaml:"oneOf,omitempty"` AnyOf SchemaRefs `json:"anyOf,omitempty" yaml:"anyOf,omitempty"` @@ -1888,7 +1890,7 @@ func (srs *SecurityRequirements) With(securityRequirement SecurityRequirement) * type SecurityScheme struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Type string `json:"type,omitempty" yaml:"type,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` @@ -1995,7 +1997,7 @@ type SerializationMethod struct { type Server struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` URL string `json:"url" yaml:"url"` // Required Description string `json:"description,omitempty" yaml:"description,omitempty"` @@ -2026,7 +2028,7 @@ func (server *Server) Validate(ctx context.Context, opts ...ValidationOption) (e type ServerVariable struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Enum []string `json:"enum,omitempty" yaml:"enum,omitempty"` Default string `json:"default,omitempty" yaml:"default,omitempty"` @@ -2132,7 +2134,7 @@ func (doc *T) Validate(ctx context.Context, opts ...ValidationOption) error type Tag struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` @@ -2244,7 +2246,7 @@ type ValidationOptions struct { type XML struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` diff --git a/go.mod b/go.mod index 48550601..975848f8 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,8 @@ require ( github.com/go-openapi/jsonpointer v0.21.0 github.com/gorilla/mux v1.8.0 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 - github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 - github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 + github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 + github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 github.com/perimeterx/marshmallow v1.1.5 github.com/stretchr/testify v1.9.0 ) diff --git a/go.sum b/go.sum index 422452cf..28e383bf 100644 --- a/go.sum +++ b/go.sum @@ -18,10 +18,10 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 h1:nZspmSkneBbtxU9TopEAE0CY+SBJLxO8LPUlw2vG4pU= -github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80/go.mod h1:7tFDb+Y51LcDpn26GccuUgQXUk6t0CXZsivKjyimYX8= -github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 h1:t05Ww3DxZutOqbMN+7OIuqDwXbhl32HiZGpLy26BAPc= -github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/openapi3/callback.go b/openapi3/callback.go index 3ae6da69..31f4e4be 100644 --- a/openapi3/callback.go +++ b/openapi3/callback.go @@ -9,7 +9,7 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#callback-object type Callback struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` m map[string]*PathItem } diff --git a/openapi3/components.go b/openapi3/components.go index aecf8b64..890671aa 100644 --- a/openapi3/components.go +++ b/openapi3/components.go @@ -25,7 +25,7 @@ type ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#components-object type Components struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Schemas Schemas `json:"schemas,omitempty" yaml:"schemas,omitempty"` Parameters ParametersMap `json:"parameters,omitempty" yaml:"parameters,omitempty"` diff --git a/openapi3/contact.go b/openapi3/contact.go index 57cc801d..2ade3c68 100644 --- a/openapi3/contact.go +++ b/openapi3/contact.go @@ -9,7 +9,7 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#contact-object type Contact struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` URL string `json:"url,omitempty" yaml:"url,omitempty"` diff --git a/openapi3/discriminator.go b/openapi3/discriminator.go index a8ab07b4..6934c170 100644 --- a/openapi3/discriminator.go +++ b/openapi3/discriminator.go @@ -9,7 +9,7 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#discriminator-object type Discriminator struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` PropertyName string `json:"propertyName" yaml:"propertyName"` // required Mapping StringMap `json:"mapping,omitempty" yaml:"mapping,omitempty"` diff --git a/openapi3/encoding.go b/openapi3/encoding.go index 7eb507e5..113eb730 100644 --- a/openapi3/encoding.go +++ b/openapi3/encoding.go @@ -11,7 +11,7 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#encoding-object type Encoding struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` ContentType string `json:"contentType,omitempty" yaml:"contentType,omitempty"` Headers Headers `json:"headers,omitempty" yaml:"headers,omitempty"` diff --git a/openapi3/example.go b/openapi3/example.go index 56aee378..44d5777e 100644 --- a/openapi3/example.go +++ b/openapi3/example.go @@ -10,7 +10,7 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#example-object type Example struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` diff --git a/openapi3/external_docs.go b/openapi3/external_docs.go index f6794141..7ff435ff 100644 --- a/openapi3/external_docs.go +++ b/openapi3/external_docs.go @@ -12,7 +12,7 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#external-documentation-object type ExternalDocs struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` URL string `json:"url,omitempty" yaml:"url,omitempty"` diff --git a/openapi3/info.go b/openapi3/info.go index acca2f4d..ed5710e0 100644 --- a/openapi3/info.go +++ b/openapi3/info.go @@ -10,7 +10,7 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#info-object type Info struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Title string `json:"title" yaml:"title"` // Required Description string `json:"description,omitempty" yaml:"description,omitempty"` diff --git a/openapi3/license.go b/openapi3/license.go index df9d6c44..eb47fc38 100644 --- a/openapi3/license.go +++ b/openapi3/license.go @@ -10,7 +10,7 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#license-object type License struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Name string `json:"name" yaml:"name"` // Required URL string `json:"url,omitempty" yaml:"url,omitempty"` diff --git a/openapi3/link.go b/openapi3/link.go index dfd320f1..c76cf446 100644 --- a/openapi3/link.go +++ b/openapi3/link.go @@ -11,7 +11,7 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#link-object type Link struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` OperationRef string `json:"operationRef,omitempty" yaml:"operationRef,omitempty"` OperationID string `json:"operationId,omitempty" yaml:"operationId,omitempty"` diff --git a/openapi3/loader.go b/openapi3/loader.go index 0f3b8cbd..436a1b3d 100644 --- a/openapi3/loader.go +++ b/openapi3/loader.go @@ -15,6 +15,11 @@ import ( "strings" ) +// IncludeOrigin specifies whether to include the origin of the OpenAPI elements +// Set this to true before loading a spec to include the origin of the OpenAPI elements +// Note it is global and affects all loaders +var IncludeOrigin = false + func foundUnresolvedRef(ref string) error { return fmt.Errorf("found unresolved ref: %q", ref) } @@ -28,9 +33,6 @@ type Loader struct { // IsExternalRefsAllowed enables visiting other files IsExternalRefsAllowed bool - // IncludeOrigin specifies whether to include the origin of the OpenAPI elements - IncludeOrigin bool - // ReadFromURIFunc allows overriding the any file/URL reading func ReadFromURIFunc ReadFromURIFunc @@ -106,7 +108,7 @@ func (loader *Loader) loadSingleElementFromURI(ref string, rootPath *url.URL, el if err != nil { return nil, err } - if err := unmarshal(data, element, loader.IncludeOrigin); err != nil { + if err := unmarshal(data, element, IncludeOrigin); err != nil { return nil, err } @@ -142,7 +144,7 @@ func (loader *Loader) LoadFromIoReader(reader io.Reader) (*T, error) { func (loader *Loader) LoadFromData(data []byte) (*T, error) { loader.resetVisitedPathItemRefs() doc := &T{} - if err := unmarshal(data, doc, loader.IncludeOrigin); err != nil { + if err := unmarshal(data, doc, IncludeOrigin); err != nil { return nil, err } if err := loader.ResolveRefsIn(doc, nil); err != nil { @@ -171,7 +173,7 @@ func (loader *Loader) loadFromDataWithPathInternal(data []byte, location *url.UR doc := &T{} loader.visitedDocuments[uri] = doc - if err := unmarshal(data, doc, loader.IncludeOrigin); err != nil { + if err := unmarshal(data, doc, IncludeOrigin); err != nil { return nil, err } @@ -425,7 +427,7 @@ func (loader *Loader) resolveComponent(doc *T, ref string, path *url.URL, resolv if err2 != nil { return nil, nil, err } - if err2 = unmarshal(data, &cursor, loader.IncludeOrigin); err2 != nil { + if err2 = unmarshal(data, &cursor, IncludeOrigin); err2 != nil { return nil, nil, err } if cursor, err2 = drill(cursor); err2 != nil || cursor == nil { diff --git a/openapi3/media_type.go b/openapi3/media_type.go index d6edaf4d..576826f3 100644 --- a/openapi3/media_type.go +++ b/openapi3/media_type.go @@ -14,7 +14,7 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#media-type-object type MediaType struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Schema *SchemaRef `json:"schema,omitempty" yaml:"schema,omitempty"` Example any `json:"example,omitempty" yaml:"example,omitempty"` diff --git a/openapi3/operation.go b/openapi3/operation.go index 7b57e847..f32836e6 100644 --- a/openapi3/operation.go +++ b/openapi3/operation.go @@ -14,7 +14,7 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object type Operation struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` // Optional tags for documentation. Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"` diff --git a/openapi3/origin.go b/openapi3/origin.go index b0e4a934..d2a51d94 100644 --- a/openapi3/origin.go +++ b/openapi3/origin.go @@ -1,6 +1,6 @@ package openapi3 -const originKey = "origin" +const originKey = "__origin__" // Origin contains the origin of a collection. // Key is the location of the collection itself. diff --git a/openapi3/origin_test.go b/openapi3/origin_test.go index d955056a..2c1d46de 100644 --- a/openapi3/origin_test.go +++ b/openapi3/origin_test.go @@ -7,10 +7,17 @@ import ( "github.com/stretchr/testify/require" ) +func unsetIncludeOrigin() { + IncludeOrigin = false +} + func TestOrigin_Info(t *testing.T) { loader := NewLoader() loader.IsExternalRefsAllowed = true - loader.IncludeOrigin = true + + IncludeOrigin = true + defer unsetIncludeOrigin() + loader.Context = context.Background() doc, err := loader.LoadFromFile("testdata/origin/simple.yaml") @@ -42,7 +49,10 @@ func TestOrigin_Info(t *testing.T) { func TestOrigin_Paths(t *testing.T) { loader := NewLoader() loader.IsExternalRefsAllowed = true - loader.IncludeOrigin = true + + IncludeOrigin = true + defer unsetIncludeOrigin() + loader.Context = context.Background() doc, err := loader.LoadFromFile("testdata/origin/simple.yaml") @@ -78,7 +88,10 @@ func TestOrigin_Paths(t *testing.T) { func TestOrigin_RequestBody(t *testing.T) { loader := NewLoader() loader.IsExternalRefsAllowed = true - loader.IncludeOrigin = true + + IncludeOrigin = true + defer unsetIncludeOrigin() + loader.Context = context.Background() doc, err := loader.LoadFromFile("testdata/origin/request_body.yaml") @@ -105,7 +118,10 @@ func TestOrigin_RequestBody(t *testing.T) { func TestOrigin_Responses(t *testing.T) { loader := NewLoader() loader.IsExternalRefsAllowed = true - loader.IncludeOrigin = true + + IncludeOrigin = true + defer unsetIncludeOrigin() + loader.Context = context.Background() doc, err := loader.LoadFromFile("testdata/origin/simple.yaml") @@ -140,7 +156,10 @@ func TestOrigin_Responses(t *testing.T) { func TestOrigin_Parameters(t *testing.T) { loader := NewLoader() loader.IsExternalRefsAllowed = true - loader.IncludeOrigin = true + + IncludeOrigin = true + defer unsetIncludeOrigin() + loader.Context = context.Background() doc, err := loader.LoadFromFile("testdata/origin/parameters.yaml") @@ -173,7 +192,10 @@ func TestOrigin_Parameters(t *testing.T) { func TestOrigin_SchemaInAdditionalProperties(t *testing.T) { loader := NewLoader() loader.IsExternalRefsAllowed = true - loader.IncludeOrigin = true + + IncludeOrigin = true + defer unsetIncludeOrigin() + loader.Context = context.Background() doc, err := loader.LoadFromFile("testdata/origin/additional_properties.yaml") @@ -201,7 +223,10 @@ func TestOrigin_SchemaInAdditionalProperties(t *testing.T) { func TestOrigin_ExternalDocs(t *testing.T) { loader := NewLoader() loader.IsExternalRefsAllowed = true - loader.IncludeOrigin = true + + IncludeOrigin = true + defer unsetIncludeOrigin() + loader.Context = context.Background() doc, err := loader.LoadFromFile("testdata/origin/external_docs.yaml") @@ -235,7 +260,10 @@ func TestOrigin_ExternalDocs(t *testing.T) { func TestOrigin_Security(t *testing.T) { loader := NewLoader() loader.IsExternalRefsAllowed = true - loader.IncludeOrigin = true + + IncludeOrigin = true + defer unsetIncludeOrigin() + loader.Context = context.Background() doc, err := loader.LoadFromFile("testdata/origin/security.yaml") @@ -283,7 +311,10 @@ func TestOrigin_Security(t *testing.T) { func TestOrigin_Example(t *testing.T) { loader := NewLoader() loader.IsExternalRefsAllowed = true - loader.IncludeOrigin = true + + IncludeOrigin = true + defer unsetIncludeOrigin() + loader.Context = context.Background() doc, err := loader.LoadFromFile("testdata/origin/example.yaml") @@ -319,7 +350,10 @@ func TestOrigin_Example(t *testing.T) { func TestOrigin_XML(t *testing.T) { loader := NewLoader() loader.IsExternalRefsAllowed = true - loader.IncludeOrigin = true + + IncludeOrigin = true + defer unsetIncludeOrigin() + loader.Context = context.Background() doc, err := loader.LoadFromFile("testdata/origin/xml.yaml") @@ -348,3 +382,37 @@ func TestOrigin_XML(t *testing.T) { }, base.Origin.Fields["prefix"]) } + +// TestOrigin_OriginExistsInProperties verifies that loading fails when a specification +// contains a property named "__origin__", highlighting a limitation in the current implementation. +func TestOrigin_OriginExistsInProperties(t *testing.T) { + var data = ` +paths: + /foo: + get: + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/Foo" +components: + schemas: + Foo: + type: object + properties: + __origin__: + type: string +` + + loader := NewLoader() + + IncludeOrigin = true + defer unsetIncludeOrigin() + + _, err := loader.LoadFromData([]byte(data)) + require.Error(t, err) + require.Equal(t, `failed to unmarshal data: json error: invalid character 'p' looking for beginning of value, yaml error: error converting YAML to JSON: yaml: unmarshal errors: + line 0: mapping key "__origin__" already defined at line 17`, err.Error()) +} diff --git a/openapi3/parameter.go b/openapi3/parameter.go index d69bd5f5..23582f7d 100644 --- a/openapi3/parameter.go +++ b/openapi3/parameter.go @@ -73,7 +73,7 @@ func (parameters Parameters) Validate(ctx context.Context, opts ...ValidationOpt // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#parameter-object type Parameter struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` In string `json:"in,omitempty" yaml:"in,omitempty"` diff --git a/openapi3/path_item.go b/openapi3/path_item.go index 2c60472a..98397861 100644 --- a/openapi3/path_item.go +++ b/openapi3/path_item.go @@ -12,7 +12,7 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#path-item-object type PathItem struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"` Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` diff --git a/openapi3/paths.go b/openapi3/paths.go index 80675047..edd8c878 100644 --- a/openapi3/paths.go +++ b/openapi3/paths.go @@ -11,7 +11,7 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#paths-object type Paths struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` m map[string]*PathItem } diff --git a/openapi3/ref.go b/openapi3/ref.go index d24f4c9b..83893b69 100644 --- a/openapi3/ref.go +++ b/openapi3/ref.go @@ -6,5 +6,5 @@ package openapi3 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#reference-object type Ref struct { Ref string `json:"$ref" yaml:"$ref"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` } diff --git a/openapi3/request_body.go b/openapi3/request_body.go index 39288ee7..6c15ba05 100644 --- a/openapi3/request_body.go +++ b/openapi3/request_body.go @@ -10,7 +10,7 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#request-body-object type RequestBody struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` Required bool `json:"required,omitempty" yaml:"required,omitempty"` diff --git a/openapi3/response.go b/openapi3/response.go index d4bfe42e..b87324a2 100644 --- a/openapi3/response.go +++ b/openapi3/response.go @@ -103,7 +103,7 @@ func (responses *Responses) Validate(ctx context.Context, opts ...ValidationOpti // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#response-object type Response struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Description *string `json:"description,omitempty" yaml:"description,omitempty"` Headers Headers `json:"headers,omitempty" yaml:"headers,omitempty"` diff --git a/openapi3/schema.go b/openapi3/schema.go index 10a00eac..75bcf126 100644 --- a/openapi3/schema.go +++ b/openapi3/schema.go @@ -81,7 +81,7 @@ func (s SchemaRefs) JSONLookup(token string) (any, error) { // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#schema-object type Schema struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` OneOf SchemaRefs `json:"oneOf,omitempty" yaml:"oneOf,omitempty"` AnyOf SchemaRefs `json:"anyOf,omitempty" yaml:"anyOf,omitempty"` diff --git a/openapi3/security_scheme.go b/openapi3/security_scheme.go index 7bc889de..676002aa 100644 --- a/openapi3/security_scheme.go +++ b/openapi3/security_scheme.go @@ -12,7 +12,7 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#security-scheme-object type SecurityScheme struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Type string `json:"type,omitempty" yaml:"type,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` @@ -218,7 +218,7 @@ func (ss *SecurityScheme) Validate(ctx context.Context, opts ...ValidationOption // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#oauth-flows-object type OAuthFlows struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Implicit *OAuthFlow `json:"implicit,omitempty" yaml:"implicit,omitempty"` Password *OAuthFlow `json:"password,omitempty" yaml:"password,omitempty"` @@ -320,7 +320,7 @@ func (flows *OAuthFlows) Validate(ctx context.Context, opts ...ValidationOption) // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#oauth-flow-object type OAuthFlow struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` AuthorizationURL string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"` TokenURL string `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"` diff --git a/openapi3/server.go b/openapi3/server.go index e438e920..ea775174 100644 --- a/openapi3/server.go +++ b/openapi3/server.go @@ -51,7 +51,7 @@ func (servers Servers) MatchURL(parsedURL *url.URL) (*Server, []string, string) // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#server-object type Server struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` URL string `json:"url" yaml:"url"` // Required Description string `json:"description,omitempty" yaml:"description,omitempty"` @@ -236,7 +236,7 @@ func (server *Server) Validate(ctx context.Context, opts ...ValidationOption) (e // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#server-variable-object type ServerVariable struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Enum []string `json:"enum,omitempty" yaml:"enum,omitempty"` Default string `json:"default,omitempty" yaml:"default,omitempty"` diff --git a/openapi3/stringmap.go b/openapi3/stringmap.go index 354a4c8e..d1b91ac9 100644 --- a/openapi3/stringmap.go +++ b/openapi3/stringmap.go @@ -18,11 +18,10 @@ func unmarshalStringMapP[V any](data []byte) (map[string]*V, *Origin, error) { return nil, nil, err } - origin, err := deepCast[Origin](m[originKey]) + origin, err := popOrigin(m, originKey) if err != nil { return nil, nil, err } - delete(m, originKey) result := make(map[string]*V, len(m)) for k, v := range m { @@ -43,11 +42,10 @@ func unmarshalStringMap[V any](data []byte) (map[string]V, *Origin, error) { return nil, nil, err } - origin, err := deepCast[Origin](m[originKey]) + origin, err := popOrigin(m, originKey) if err != nil { return nil, nil, err } - delete(m, originKey) result := make(map[string]V, len(m)) for k, v := range m { @@ -74,3 +72,17 @@ func deepCast[V any](value any) (*V, error) { } return &result, nil } + +// popOrigin removes the origin from the map and returns it. +func popOrigin(m map[string]any, key string) (*Origin, error) { + if !IncludeOrigin { + return nil, nil + } + + origin, err := deepCast[Origin](m[key]) + if err != nil { + return nil, err + } + delete(m, key) + return origin, nil +} diff --git a/openapi3/tag.go b/openapi3/tag.go index 5e4086fe..841161ec 100644 --- a/openapi3/tag.go +++ b/openapi3/tag.go @@ -34,7 +34,7 @@ func (tags Tags) Validate(ctx context.Context, opts ...ValidationOption) error { // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#tag-object type Tag struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` diff --git a/openapi3/xml.go b/openapi3/xml.go index 7acd8188..e960249d 100644 --- a/openapi3/xml.go +++ b/openapi3/xml.go @@ -9,7 +9,7 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#xml-object type XML struct { Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` + Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` diff --git a/openapi3filter/issue991_test.go b/openapi3filter/issue991_test.go index a81b5b5f..10737ea9 100644 --- a/openapi3filter/issue991_test.go +++ b/openapi3filter/issue991_test.go @@ -9,8 +9,12 @@ import ( "github.com/stretchr/testify/require" ) -func TestValidateRequestDefault(t *testing.T) { - const spec = ` +func generateSpec(explode bool) string { + explodeStr := "false" + if explode { + explodeStr = "true" + } + return ` openapi: 3.0.0 info: title: 'Validator' @@ -29,6 +33,7 @@ components: in: query name: type required: false + explode: ` + explodeStr + ` description: Type parameter schema: type: array @@ -43,9 +48,9 @@ components: - B - C ` +} - router := setupTestRouter(t, spec) - +func TestValidateRequestDefault(t *testing.T) { type args struct { url string expected []string @@ -55,42 +60,57 @@ components: args args expectedModification bool expectedErr error + spec string }{ { - name: "Valid request without type parameters set", + name: "Valid request without type parameters set and explode is false", args: args{ url: "/category", - expected: []string{"A", "B", "C"}, + expected: []string{"A,B,C"}, }, expectedModification: false, expectedErr: nil, + spec: generateSpec(false), }, { - name: "Valid request with 1 type parameters set", + name: "Valid request with 1 type parameters set and explode is false", args: args{ url: "/category?type=A", expected: []string{"A"}, }, expectedModification: false, expectedErr: nil, + spec: generateSpec(false), }, { - name: "Valid request with 2 type parameters set", + name: "Valid request with 2 type parameters set and explode is false", args: args{ url: "/category?type=A&type=C", expected: []string{"A", "C"}, }, expectedModification: false, expectedErr: nil, + spec: generateSpec(false), }, { - name: "Valid request with 1 type parameters set out of enum", + name: "Valid request with 1 type parameters set out of enum and explode is false", args: args{ url: "/category?type=X", expected: nil, }, expectedModification: false, expectedErr: &RequestError{}, + spec: generateSpec(false), + }, + { + name: "Valid request without type parameters set and explode is true", + args: args{ + url: "/category", + expected: []string{"A", "B", "C"}, + }, + expectedModification: false, + expectedErr: nil, + spec: generateSpec(true), }, } for _, tc := range tests { @@ -99,6 +119,7 @@ components: req, err := http.NewRequest(http.MethodGet, tc.args.url, nil) require.NoError(t, err) + router := setupTestRouter(t, tc.spec) route, pathParams, err := router.FindRoute(req) require.NoError(t, err) diff --git a/openapi3filter/validate_request.go b/openapi3filter/validate_request.go index a28fbccd..672c6ea3 100644 --- a/openapi3filter/validate_request.go +++ b/openapi3filter/validate_request.go @@ -9,6 +9,7 @@ import ( "net/http" "net/url" "sort" + "strings" "github.com/getkin/kin-openapi/openapi3" ) @@ -111,15 +112,26 @@ func appendToQueryValues[T any](q url.Values, parameterName string, v []T) { } } +func joinValues(values []any, sep string) string { + strValues := make([]string, len(values)) + for i, v := range values { + strValues[i] = fmt.Sprintf("%v", v) + } + return strings.Join(strValues, sep) +} + // populateDefaultQueryParameters populates default values inside query parameters, while ensuring types are respected -func populateDefaultQueryParameters(q url.Values, parameterName string, value any) { +func populateDefaultQueryParameters(q url.Values, parameterName string, value any, explode bool) { switch t := value.(type) { case []any: - appendToQueryValues(q, parameterName, t) + if explode { + appendToQueryValues(q, parameterName, t) + } else { + q.Add(parameterName, joinValues(t, ",")) + } default: q.Add(parameterName, fmt.Sprintf("%v", value)) } - } // ValidateParameter validates a parameter's value by JSON schema. @@ -175,7 +187,8 @@ func ValidateParameter(ctx context.Context, input *RequestValidationInput, param // Next check `parameter.Required && !found` will catch this. case openapi3.ParameterInQuery: q := req.URL.Query() - populateDefaultQueryParameters(q, parameter.Name, value) + explode := parameter.Explode != nil && *parameter.Explode + populateDefaultQueryParameters(q, parameter.Name, value, explode) req.URL.RawQuery = q.Encode() case openapi3.ParameterInHeader: req.Header.Add(parameter.Name, fmt.Sprintf("%v", value)) diff --git a/openapi3filter/validate_response.go b/openapi3filter/validate_response.go index 77f127e7..a2dcc03a 100644 --- a/openapi3filter/validate_response.go +++ b/openapi3filter/validate_response.go @@ -94,7 +94,7 @@ func ValidateResponse(ctx context.Context, input *ResponseValidationInput) error } content := response.Content - if len(content) == 0 || options.ExcludeResponseBody { + if len(content) == 0 { // An operation does not contains a validation schema for responses with this status code. return nil } diff --git a/openapi3gen/openapi3gen.go b/openapi3gen/openapi3gen.go index 78e6901d..5d9b64ab 100644 --- a/openapi3gen/openapi3gen.go +++ b/openapi3gen/openapi3gen.go @@ -290,11 +290,10 @@ func (g *Generator) generateWithoutSaving(parents []*theTypeInfo, t reflect.Type case reflect.Slice: if t.Elem().Kind() == reflect.Uint8 { - if t == rawMessageType { - return &openapi3.SchemaRef{Value: schema}, nil + if t != rawMessageType { + schema.Type = &openapi3.Types{"string"} + schema.Format = "byte" } - schema.Type = &openapi3.Types{"string"} - schema.Format = "byte" } else { schema.Type = &openapi3.Types{"array"} items, err := g.generateSchemaRefFor(parents, t.Elem(), name, tag) @@ -430,6 +429,11 @@ func (g *Generator) generateWithoutSaving(parents []*theTypeInfo, t reflect.Type // For structs we add the schemas to the component schemas if len(parents) > 1 || g.opts.exportComponentSchemas.ExportTopLevelSchema { + // If struct is a time.Time instance, separate component shouldn't be generated + if t == timeType { + return openapi3.NewSchemaRef(t.Name(), schema), nil + } + typeName := g.generateTypeName(t) g.componentSchemaRefs[typeName] = struct{}{} diff --git a/openapi3gen/openapi3gen_test.go b/openapi3gen/openapi3gen_test.go index 4d9c1e20..e18d8619 100644 --- a/openapi3gen/openapi3gen_test.go +++ b/openapi3gen/openapi3gen_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/stretchr/testify/assert" "reflect" "strconv" "strings" @@ -407,7 +408,8 @@ func ExampleSchemaCustomizer() { InnerFieldWithTag int `mymintag:"-1" mymaxtag:"50"` NestedInnerBla } - Enum2Field string `json:"enum2" myenumtag:"c,d"` + Enum2Field string `json:"enum2" myenumtag:"c,d"` + JsonField json.RawMessage `json:"rawmsg" myjsontag:"raw"` } type Bla struct { @@ -435,6 +437,9 @@ func ExampleSchemaCustomizer() { schema.Enum = append(schema.Enum, s) } } + if tag.Get("myjsontag") != "" { + schema.Description = "description" + } return nil }) @@ -487,6 +492,9 @@ func ExampleSchemaCustomizer() { // "f" // ], // "type": "string" + // }, + // "rawmsg": { + // "description": "description" // } // }, // "type": "object" @@ -634,3 +642,28 @@ func ExampleSetSchemar() { // "type": "object" // } } + +func TestExportComponentSchemasForTimeProp(t *testing.T) { + type Some struct { + Name string + CreatedAt time.Time + } + + schemas := make(openapi3.Schemas) + g := openapi3gen.NewGenerator( + openapi3gen.UseAllExportedFields(), + openapi3gen.CreateComponentSchemas(openapi3gen.ExportComponentSchemasOptions{ + ExportComponentSchemas: true, + }), + ) + + ref, err := g.NewSchemaRefForValue(&Some{}, schemas) + require.NoError(t, err) + + schema, err := json.MarshalIndent(ref, "", " ") + require.NoError(t, err) + + assert.Condition(t, func() bool { + return !strings.Contains(string(schema), "#/components/schemas/Time") + }, "Expected no schema for time.Time property but got one: %s", schema) +}