From c3cf5f1e387636bde895a00aa3f13a7a63fae2b7 Mon Sep 17 00:00:00 2001 From: Dave Shanley Date: Thu, 15 Jun 2023 08:58:20 -0400 Subject: [PATCH] Added support for `unevaluatedProperties` as Schema and bool #118 Also ran `gofmt` across the entire project. Things need cleaning up. Signed-off-by: Dave Shanley --- datamodel/constants.go | 3 + datamodel/document_config_test.go | 16 +- datamodel/high/base/base.go | 1 - datamodel/high/base/contact.go | 8 +- datamodel/high/base/discriminator.go | 3 +- datamodel/high/base/dynamic_value.go | 1 - datamodel/high/base/example.go | 3 +- datamodel/high/base/external_doc.go | 5 +- datamodel/high/base/info_test.go | 2 - datamodel/high/base/licence_test.go | 66 +- datamodel/high/base/license.go | 5 +- datamodel/high/base/schema.go | 703 +++--- datamodel/high/base/schema_proxy_test.go | 67 +- datamodel/high/base/schema_test.go | 860 +++---- datamodel/high/base/security_requirement.go | 4 +- datamodel/high/base/xml.go | 54 +- datamodel/high/base/xml_test.go | 2 +- datamodel/high/node_builder.go | 1114 +++++----- datamodel/high/node_builder_test.go | 1190 +++++----- datamodel/high/shared.go | 6 +- datamodel/high/shared_test.go | 148 +- datamodel/high/v2/definitions.go | 2 +- datamodel/high/v2/examples.go | 2 +- datamodel/high/v2/header.go | 2 +- datamodel/high/v2/items.go | 2 +- datamodel/high/v2/operation.go | 2 +- datamodel/high/v2/parameter.go | 48 +- datamodel/high/v2/parameter_definitions.go | 2 +- datamodel/high/v2/path_item.go | 228 +- datamodel/high/v2/path_item_test.go | 30 +- datamodel/high/v2/response.go | 2 +- datamodel/high/v2/responses_definitions.go | 2 +- datamodel/high/v2/scopes.go | 2 +- datamodel/high/v2/security_definitions.go | 2 +- datamodel/high/v2/security_scheme.go | 2 +- datamodel/high/v3/callback.go | 2 +- datamodel/high/v3/callback_test.go | 90 +- datamodel/high/v3/components.go | 22 +- datamodel/high/v3/components_test.go | 80 +- datamodel/high/v3/encoding.go | 2 +- datamodel/high/v3/encoding_test.go | 52 +- datamodel/high/v3/header.go | 2 +- datamodel/high/v3/header_test.go | 38 +- datamodel/high/v3/link_test.go | 36 +- datamodel/high/v3/oauth_flow.go | 2 +- datamodel/high/v3/oauth_flow_test.go | 38 +- datamodel/high/v3/oauth_flows.go | 3 +- datamodel/high/v3/operation_test.go | 4 +- datamodel/high/v3/security_scheme.go | 2 +- datamodel/high/v3/security_scheme_test.go | 50 +- datamodel/high/v3/server_test.go | 38 +- datamodel/high/v3/server_variable_test.go | 30 +- datamodel/low/base/base.go | 1 - datamodel/low/base/contact.go | 6 +- datamodel/low/base/discriminator.go | 3 +- datamodel/low/base/example.go | 3 +- datamodel/low/base/external_doc.go | 5 +- datamodel/low/base/license.go | 5 +- datamodel/low/base/schema.go | 2065 ++++++++--------- datamodel/low/base/schema_test.go | 1310 +++++------ datamodel/low/base/security_requirement.go | 4 +- datamodel/low/base/tag.go | 4 +- datamodel/low/base/tag_test.go | 2 +- datamodel/low/base/xml.go | 5 +- datamodel/low/extraction_functions_test.go | 2 +- datamodel/low/model_builder.go | 774 +++---- datamodel/low/model_builder_test.go | 486 ++-- datamodel/low/reference_test.go | 7 +- datamodel/low/v2/definitions.go | 8 +- datamodel/low/v2/examples.go | 2 +- datamodel/low/v2/header.go | 2 +- datamodel/low/v2/header_test.go | 156 +- datamodel/low/v2/items.go | 2 +- datamodel/low/v2/operation.go | 2 +- datamodel/low/v2/parameter.go | 48 +- datamodel/low/v2/path_item.go | 2 +- datamodel/low/v2/response.go | 2 +- datamodel/low/v2/scopes.go | 2 +- datamodel/low/v2/security_scheme.go | 2 +- datamodel/low/v2/swagger.go | 404 ++-- datamodel/low/v2/swagger_test.go | 470 ++-- datamodel/low/v3/callback.go | 8 +- datamodel/low/v3/components.go | 2 +- datamodel/low/v3/encoding.go | 2 +- datamodel/low/v3/header.go | 2 +- datamodel/low/v3/link.go | 2 +- datamodel/low/v3/media_type.go | 2 +- datamodel/low/v3/oauth_flows.go | 4 +- datamodel/low/v3/operation.go | 2 +- datamodel/low/v3/parameter.go | 2 +- datamodel/low/v3/request_body.go | 2 +- datamodel/low/v3/security_scheme.go | 2 +- datamodel/low/v3/server.go | 2 +- datamodel/spec_info_test.go | 172 +- document_examples_test.go | 848 +++---- index/index_model_test.go | 30 +- index/index_utils.go | 150 +- index/utility_methods.go | 716 +++--- index/utility_methods_test.go | 58 +- resolver/resolver_test.go | 364 +-- utils/nodes.go | 82 +- utils/nodes_test.go | 48 +- utils/utils_test.go | 966 ++++---- what-changed/model/callback.go | 154 +- what-changed/model/change_types.go | 140 +- what-changed/model/contact.go | 92 +- what-changed/model/discriminator.go | 122 +- what-changed/model/encoding.go | 130 +- what-changed/model/examples.go | 104 +- what-changed/model/examples_test.go | 146 +- what-changed/model/extensions.go | 108 +- what-changed/model/external_docs.go | 96 +- what-changed/model/header.go | 378 ++-- what-changed/model/info.go | 18 +- what-changed/model/info_test.go | 362 +-- what-changed/model/items.go | 98 +- what-changed/model/items_test.go | 190 +- what-changed/model/license.go | 72 +- what-changed/model/link.go | 244 +- what-changed/model/link_test.go | 304 +-- what-changed/model/media_type.go | 216 +- what-changed/model/oauth_flows.go | 394 ++-- what-changed/model/oauth_flows_test.go | 324 +-- what-changed/model/parameter.go | 514 ++--- what-changed/model/paths.go | 314 +-- what-changed/model/request_body.go | 118 +- what-changed/model/request_body_test.go | 74 +- what-changed/model/response.go | 276 +-- what-changed/model/responses.go | 190 +- what-changed/model/schema.go | 2224 ++++++++++--------- what-changed/model/scopes.go | 94 +- what-changed/model/security_requirement.go | 208 +- what-changed/model/security_scheme.go | 242 +- what-changed/model/server.go | 102 +- what-changed/model/server_test.go | 148 +- what-changed/model/server_variable.go | 114 +- what-changed/model/tags.go | 208 +- what-changed/model/xml.go | 156 +- what-changed/model/xml_test.go | 142 +- what-changed/reports/summary.go | 124 +- what-changed/reports/summary_test.go | 50 +- what-changed/reports/types.go | 8 +- 142 files changed, 11751 insertions(+), 11577 deletions(-) diff --git a/datamodel/constants.go b/datamodel/constants.go index b1e51e3..77ae3ec 100644 --- a/datamodel/constants.go +++ b/datamodel/constants.go @@ -32,14 +32,17 @@ const ( ) // OpenAPI3SchemaData is an embedded version of the OpenAPI 3 Schema +// //go:embed schemas/oas3-schema.json var OpenAPI3SchemaData string // embedded OAS3 schema // OpenAPI31SchemaData is an embedded version of the OpenAPI 3.1 Schema +// //go:embed schemas/oas31-schema.json var OpenAPI31SchemaData string // embedded OAS31 schema // OpenAPI2SchemaData is an embedded version of the OpenAPI 2 (Swagger) Schema +// //go:embed schemas/swagger2-schema.json var OpenAPI2SchemaData string // embedded OAS3 schema diff --git a/datamodel/document_config_test.go b/datamodel/document_config_test.go index 89a7136..8d18f38 100644 --- a/datamodel/document_config_test.go +++ b/datamodel/document_config_test.go @@ -4,18 +4,18 @@ package datamodel import ( - "github.com/stretchr/testify/assert" - "testing" + "github.com/stretchr/testify/assert" + "testing" ) func TestNewClosedDocumentConfiguration(t *testing.T) { - cfg := NewClosedDocumentConfiguration() - assert.False(t, cfg.AllowRemoteReferences) - assert.False(t, cfg.AllowFileReferences) + cfg := NewClosedDocumentConfiguration() + assert.False(t, cfg.AllowRemoteReferences) + assert.False(t, cfg.AllowFileReferences) } func TestNewOpenDocumentConfiguration(t *testing.T) { - cfg := NewOpenDocumentConfiguration() - assert.True(t, cfg.AllowRemoteReferences) - assert.True(t, cfg.AllowFileReferences) + cfg := NewOpenDocumentConfiguration() + assert.True(t, cfg.AllowRemoteReferences) + assert.True(t, cfg.AllowFileReferences) } diff --git a/datamodel/high/base/base.go b/datamodel/high/base/base.go index f567bf4..064015d 100644 --- a/datamodel/high/base/base.go +++ b/datamodel/high/base/base.go @@ -9,4 +9,3 @@ // beats, particularly when polymorphism is used. By re-using the same superset Schema across versions, we can ensure // that all the latest features are collected, without damaging backwards compatibility. package base - diff --git a/datamodel/high/base/contact.go b/datamodel/high/base/contact.go index e8dc31e..a33ce90 100644 --- a/datamodel/high/base/contact.go +++ b/datamodel/high/base/contact.go @@ -10,8 +10,9 @@ import ( ) // Contact represents a high-level representation of the Contact definitions found at -// v2 - https://swagger.io/specification/v2/#contactObject -// v3 - https://spec.openapis.org/oas/v3.1.0#contact-object +// +// v2 - https://swagger.io/specification/v2/#contactObject +// v3 - https://spec.openapis.org/oas/v3.1.0#contact-object type Contact struct { Name string `json:"name,omitempty" yaml:"name,omitempty"` URL string `json:"url,omitempty" yaml:"url,omitempty"` @@ -47,6 +48,3 @@ func (c *Contact) MarshalYAML() (interface{}, error) { nb := low2.NewNodeBuilder(c, c.low) return nb.Render(), nil } - - - diff --git a/datamodel/high/base/discriminator.go b/datamodel/high/base/discriminator.go index 9ae81f1..f761803 100644 --- a/datamodel/high/base/discriminator.go +++ b/datamodel/high/base/discriminator.go @@ -16,7 +16,8 @@ import ( // which is used to inform the consumer of the document of an alternative schema based on the value associated with it. // // When using the discriminator, inline schemas will not be considered. -// v3 - https://spec.openapis.org/oas/v3.1.0#discriminator-object +// +// v3 - https://spec.openapis.org/oas/v3.1.0#discriminator-object type Discriminator struct { PropertyName string `json:"propertyName,omitempty" yaml:"propertyName,omitempty"` Mapping map[string]string `json:"mapping,omitempty" yaml:"mapping,omitempty"` diff --git a/datamodel/high/base/dynamic_value.go b/datamodel/high/base/dynamic_value.go index c949ad8..73a04fb 100644 --- a/datamodel/high/base/dynamic_value.go +++ b/datamodel/high/base/dynamic_value.go @@ -44,7 +44,6 @@ func (d *DynamicValue[A, B]) RenderInline() ([]byte, error) { return yaml.Marshal(d) } - // MarshalYAML will create a ready to render YAML representation of the DynamicValue object. func (d *DynamicValue[A, B]) MarshalYAML() (interface{}, error) { // this is a custom renderer, we can't use the NodeBuilder out of the gate. diff --git a/datamodel/high/base/example.go b/datamodel/high/base/example.go index 31d711b..7ade3b4 100644 --- a/datamodel/high/base/example.go +++ b/datamodel/high/base/example.go @@ -11,7 +11,8 @@ import ( ) // Example represents a high-level Example object as defined by OpenAPI 3+ -// v3 - https://spec.openapis.org/oas/v3.1.0#example-object +// +// v3 - https://spec.openapis.org/oas/v3.1.0#example-object type Example struct { Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` diff --git a/datamodel/high/base/external_doc.go b/datamodel/high/base/external_doc.go index 79dc117..98c7b9f 100644 --- a/datamodel/high/base/external_doc.go +++ b/datamodel/high/base/external_doc.go @@ -12,8 +12,9 @@ import ( // ExternalDoc represents a high-level External Documentation object as defined by OpenAPI 2 and 3 // // Allows referencing an external resource for extended documentation. -// v2 - https://swagger.io/specification/v2/#externalDocumentationObject -// v3 - https://spec.openapis.org/oas/v3.1.0#external-documentation-object +// +// v2 - https://swagger.io/specification/v2/#externalDocumentationObject +// v3 - https://spec.openapis.org/oas/v3.1.0#external-documentation-object type ExternalDoc struct { Description string `json:"description,omitempty" yaml:"description,omitempty"` URL string `json:"url,omitempty" yaml:"url,omitempty"` diff --git a/datamodel/high/base/info_test.go b/datamodel/high/base/info_test.go index 9a0bc57..87e95cd 100644 --- a/datamodel/high/base/info_test.go +++ b/datamodel/high/base/info_test.go @@ -200,5 +200,3 @@ x-cake: bytes, _ := highInfo.Render() assert.Len(t, bytes, 275) } - - diff --git a/datamodel/high/base/licence_test.go b/datamodel/high/base/licence_test.go index d830381..80dd70b 100644 --- a/datamodel/high/base/licence_test.go +++ b/datamodel/high/base/licence_test.go @@ -4,56 +4,56 @@ package base import ( - lowmodel "github.com/pb33f/libopenapi/datamodel/low" - lowbase "github.com/pb33f/libopenapi/datamodel/low/base" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "testing" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + lowbase "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "testing" ) func TestLicense_Render(t *testing.T) { - highL := &License{Name: "MIT", URL: "https://pb33f.io"} - dat, _ := highL.Render() + highL := &License{Name: "MIT", URL: "https://pb33f.io"} + dat, _ := highL.Render() - // unmarshal yaml into a *yaml.Node instance - var cNode yaml.Node - _ = yaml.Unmarshal(dat, &cNode) + // unmarshal yaml into a *yaml.Node instance + var cNode yaml.Node + _ = yaml.Unmarshal(dat, &cNode) - // build low - var lowLicense lowbase.License - _ = lowmodel.BuildModel(cNode.Content[0], &lowLicense) + // build low + var lowLicense lowbase.License + _ = lowmodel.BuildModel(cNode.Content[0], &lowLicense) - // build high - highLicense := NewLicense(&lowLicense) + // build high + highLicense := NewLicense(&lowLicense) - assert.Equal(t, "MIT", highLicense.Name) - assert.Equal(t, "https://pb33f.io", highLicense.URL) + assert.Equal(t, "MIT", highLicense.Name) + assert.Equal(t, "https://pb33f.io", highLicense.URL) } func TestLicense_RenderEqual(t *testing.T) { - yml := `name: MIT + yml := `name: MIT url: https://pb33f.io/not-real ` - // unmarshal yaml into a *yaml.Node instance - var cNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &cNode) + // unmarshal yaml into a *yaml.Node instance + var cNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &cNode) - // build low - var lowLicense lowbase.License - _ = lowmodel.BuildModel(cNode.Content[0], &lowLicense) - _ = lowLicense.Build(cNode.Content[0], nil) + // build low + var lowLicense lowbase.License + _ = lowmodel.BuildModel(cNode.Content[0], &lowLicense) + _ = lowLicense.Build(cNode.Content[0], nil) - // build high - highLicense := NewLicense(&lowLicense) + // build high + highLicense := NewLicense(&lowLicense) - assert.Equal(t, "MIT", highLicense.Name) - assert.Equal(t, "https://pb33f.io/not-real", highLicense.URL) + assert.Equal(t, "MIT", highLicense.Name) + assert.Equal(t, "https://pb33f.io/not-real", highLicense.URL) - // re-render and ensure everything is in the same order as before. - bytes, _ := highLicense.Render() - assert.Equal(t, yml, string(bytes)) + // re-render and ensure everything is in the same order as before. + bytes, _ := highLicense.Render() + assert.Equal(t, yml, string(bytes)) -} \ No newline at end of file +} diff --git a/datamodel/high/base/license.go b/datamodel/high/base/license.go index acc614a..b033df9 100644 --- a/datamodel/high/base/license.go +++ b/datamodel/high/base/license.go @@ -10,8 +10,9 @@ import ( ) // License is a high-level representation of a License object as defined by OpenAPI 2 and OpenAPI 3 -// v2 - https://swagger.io/specification/v2/#licenseObject -// v3 - https://spec.openapis.org/oas/v3.1.0#license-object +// +// v2 - https://swagger.io/specification/v2/#licenseObject +// v3 - https://spec.openapis.org/oas/v3.1.0#license-object type License struct { Name string `json:"name,omitempty" yaml:"name,omitempty"` URL string `json:"url,omitempty" yaml:"url,omitempty"` diff --git a/datamodel/high/base/schema.go b/datamodel/high/base/schema.go index df5e1f5..5b517a1 100644 --- a/datamodel/high/base/schema.go +++ b/datamodel/high/base/schema.go @@ -4,12 +4,12 @@ package base import ( - "gopkg.in/yaml.v3" - "sync" + "gopkg.in/yaml.v3" + "sync" - "github.com/pb33f/libopenapi/datamodel/high" - lowmodel "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/datamodel/high" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" ) // Schema represents a JSON Schema that support Swagger, OpenAPI 3 and OpenAPI 3.1 @@ -22,8 +22,8 @@ import ( // - v3 schema: https://swagger.io/specification/#schema-object // - v3.1 schema: https://spec.openapis.org/oas/v3.1.0#schema-object type Schema struct { - // 3.1 only, used to define a dialect for this schema, label is '$schema'. - SchemaTypeRef string `json:"$schema,omitempty" yaml:"$schema,omitempty"` + // 3.1 only, used to define a dialect for this schema, label is '$schema'. + SchemaTypeRef string `json:"$schema,omitempty" yaml:"$schema,omitempty"` // In versions 2 and 3.0, this ExclusiveMaximum can only be a boolean. // In version 3.1, ExclusiveMaximum is a number. @@ -33,76 +33,79 @@ type Schema struct { // In version 3.1, ExclusiveMinimum is a number. ExclusiveMinimum *DynamicValue[bool, float64] `json:"exclusiveMinimum,omitempty" yaml:"exclusiveMinimum,omitempty"` - // In versions 2 and 3.0, this Type is a single value, so array will only ever have one value - // in version 3.1, Type can be multiple values - Type []string `json:"type,omitempty" yaml:"type,omitempty"` + // In versions 2 and 3.0, this Type is a single value, so array will only ever have one value + // in version 3.1, Type can be multiple values + Type []string `json:"type,omitempty" yaml:"type,omitempty"` - // Schemas are resolved on demand using a SchemaProxy - AllOf []*SchemaProxy `json:"allOf,omitempty" yaml:"allOf,omitempty"` + // Schemas are resolved on demand using a SchemaProxy + AllOf []*SchemaProxy `json:"allOf,omitempty" yaml:"allOf,omitempty"` - // Polymorphic Schemas are only available in version 3+ - OneOf []*SchemaProxy `json:"oneOf,omitempty" yaml:"oneOf,omitempty"` - AnyOf []*SchemaProxy `json:"anyOf,omitempty" yaml:"anyOf,omitempty"` - Discriminator *Discriminator `json:"discriminator,omitempty" yaml:"discriminator,omitempty"` + // Polymorphic Schemas are only available in version 3+ + OneOf []*SchemaProxy `json:"oneOf,omitempty" yaml:"oneOf,omitempty"` + AnyOf []*SchemaProxy `json:"anyOf,omitempty" yaml:"anyOf,omitempty"` + Discriminator *Discriminator `json:"discriminator,omitempty" yaml:"discriminator,omitempty"` - // in 3.1 examples can be an array (which is recommended) - Examples []any `json:"examples,omitempty" yaml:"examples,omitempty"` + // in 3.1 examples can be an array (which is recommended) + Examples []any `json:"examples,omitempty" yaml:"examples,omitempty"` - // in 3.1 prefixItems provides tuple validation support. - PrefixItems []*SchemaProxy `json:"prefixItems,omitempty" yaml:"prefixItems,omitempty"` + // in 3.1 prefixItems provides tuple validation support. + PrefixItems []*SchemaProxy `json:"prefixItems,omitempty" yaml:"prefixItems,omitempty"` - // 3.1 Specific properties - Contains *SchemaProxy `json:"contains,omitempty" yaml:"contains,omitempty"` - MinContains *int64 `json:"minContains,omitempty" yaml:"minContains,omitempty"` - MaxContains *int64 `json:"maxContains,omitempty" yaml:"maxContains,omitempty"` - If *SchemaProxy `json:"if,omitempty" yaml:"if,omitempty"` - Else *SchemaProxy `json:"else,omitempty" yaml:"else,omitempty"` - Then *SchemaProxy `json:"then,omitempty" yaml:"then,omitempty"` - DependentSchemas map[string]*SchemaProxy `json:"dependentSchemas,omitempty" yaml:"dependentSchemas,omitempty"` - PatternProperties map[string]*SchemaProxy `json:"patternProperties,omitempty" yaml:"patternProperties,omitempty"` - PropertyNames *SchemaProxy `json:"propertyNames,omitempty" yaml:"propertyNames,omitempty"` - UnevaluatedItems *SchemaProxy `json:"unevaluatedItems,omitempty" yaml:"unevaluatedItems,omitempty"` - UnevaluatedProperties *SchemaProxy `json:"unevaluatedProperties,omitempty" yaml:"unevaluatedProperties,omitempty"` + // 3.1 Specific properties + Contains *SchemaProxy `json:"contains,omitempty" yaml:"contains,omitempty"` + MinContains *int64 `json:"minContains,omitempty" yaml:"minContains,omitempty"` + MaxContains *int64 `json:"maxContains,omitempty" yaml:"maxContains,omitempty"` + If *SchemaProxy `json:"if,omitempty" yaml:"if,omitempty"` + Else *SchemaProxy `json:"else,omitempty" yaml:"else,omitempty"` + Then *SchemaProxy `json:"then,omitempty" yaml:"then,omitempty"` + DependentSchemas map[string]*SchemaProxy `json:"dependentSchemas,omitempty" yaml:"dependentSchemas,omitempty"` + PatternProperties map[string]*SchemaProxy `json:"patternProperties,omitempty" yaml:"patternProperties,omitempty"` + PropertyNames *SchemaProxy `json:"propertyNames,omitempty" yaml:"propertyNames,omitempty"` + UnevaluatedItems *SchemaProxy `json:"unevaluatedItems,omitempty" yaml:"unevaluatedItems,omitempty"` - // in 3.1 Items can be a Schema or a boolean - Items *DynamicValue[*SchemaProxy, bool] `json:"items,omitempty" yaml:"items,omitempty"` + // in 3.1 UnevaluatedProperties can be a Schema or a boolean + // https://github.com/pb33f/libopenapi/issues/118 + UnevaluatedProperties *DynamicValue[*SchemaProxy, *bool] `json:"unevaluatedProperties,omitempty" yaml:"unevaluatedProperties,omitempty"` - // 3.1 only, part of the JSON Schema spec provides a way to identify a subschema - Anchor string `json:"$anchor,omitempty" yaml:"$anchor,omitempty"` + // in 3.1 Items can be a Schema or a boolean + Items *DynamicValue[*SchemaProxy, bool] `json:"items,omitempty" yaml:"items,omitempty"` - // Compatible with all versions - Not *SchemaProxy `json:"not,omitempty" yaml:"not,omitempty"` - Properties map[string]*SchemaProxy `json:"properties,omitempty" yaml:"properties,omitempty"` - Title string `json:"title,omitempty" yaml:"title,omitempty"` - MultipleOf *float64 `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"` - Maximum *float64 `json:"maximum,omitempty" yaml:"maximum,omitempty"` - Minimum *float64 `json:"minimum,omitempty" yaml:"minimum,omitempty"` - MaxLength *int64 `json:"maxLength,omitempty" yaml:"maxLength,omitempty"` - MinLength *int64 `json:"minLength,omitempty" yaml:"minLength,omitempty"` - Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"` - Format string `json:"format,omitempty" yaml:"format,omitempty"` - MaxItems *int64 `json:"maxItems,omitempty" yaml:"maxItems,omitempty"` - MinItems *int64 `json:"minItems,omitempty" yaml:"minItems,omitempty"` - UniqueItems *bool `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"` - MaxProperties *int64 `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"` - MinProperties *int64 `json:"minProperties,omitempty" yaml:"minProperties,omitempty"` - Required []string `json:"required,omitempty" yaml:"required,omitempty"` - Enum []any `json:"enum,omitempty" yaml:"enum,omitempty"` - AdditionalProperties any `json:"additionalProperties,omitempty" yaml:"additionalProperties,renderZero,omitempty"` - Description string `json:"description,omitempty" yaml:"description,omitempty"` - Default any `json:"default,omitempty" yaml:"default,renderZero,omitempty"` - Nullable *bool `json:"nullable,omitempty" yaml:"nullable,omitempty"` - ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30 - WriteOnly bool `json:"writeOnly,omitempty" yaml:"writeOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30 - XML *XML `json:"xml,omitempty" yaml:"xml,omitempty"` - ExternalDocs *ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` - Example any `json:"example,omitempty" yaml:"example,omitempty"` - Deprecated *bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` - Extensions map[string]any `json:"-" yaml:"-"` - low *base.Schema + // 3.1 only, part of the JSON Schema spec provides a way to identify a subschema + Anchor string `json:"$anchor,omitempty" yaml:"$anchor,omitempty"` - // Parent Proxy refers back to the low level SchemaProxy that is proxying this schema. - ParentProxy *SchemaProxy `json:"-" yaml:"-"` + // Compatible with all versions + Not *SchemaProxy `json:"not,omitempty" yaml:"not,omitempty"` + Properties map[string]*SchemaProxy `json:"properties,omitempty" yaml:"properties,omitempty"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + MultipleOf *float64 `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"` + Maximum *float64 `json:"maximum,omitempty" yaml:"maximum,omitempty"` + Minimum *float64 `json:"minimum,omitempty" yaml:"minimum,omitempty"` + MaxLength *int64 `json:"maxLength,omitempty" yaml:"maxLength,omitempty"` + MinLength *int64 `json:"minLength,omitempty" yaml:"minLength,omitempty"` + Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"` + Format string `json:"format,omitempty" yaml:"format,omitempty"` + MaxItems *int64 `json:"maxItems,omitempty" yaml:"maxItems,omitempty"` + MinItems *int64 `json:"minItems,omitempty" yaml:"minItems,omitempty"` + UniqueItems *bool `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"` + MaxProperties *int64 `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"` + MinProperties *int64 `json:"minProperties,omitempty" yaml:"minProperties,omitempty"` + Required []string `json:"required,omitempty" yaml:"required,omitempty"` + Enum []any `json:"enum,omitempty" yaml:"enum,omitempty"` + AdditionalProperties any `json:"additionalProperties,omitempty" yaml:"additionalProperties,renderZero,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Default any `json:"default,omitempty" yaml:"default,renderZero,omitempty"` + Nullable *bool `json:"nullable,omitempty" yaml:"nullable,omitempty"` + ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30 + WriteOnly bool `json:"writeOnly,omitempty" yaml:"writeOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30 + XML *XML `json:"xml,omitempty" yaml:"xml,omitempty"` + ExternalDocs *ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` + Example any `json:"example,omitempty" yaml:"example,omitempty"` + Deprecated *bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` + Extensions map[string]any `json:"-" yaml:"-"` + low *base.Schema + + // Parent Proxy refers back to the low level SchemaProxy that is proxying this schema. + ParentProxy *SchemaProxy `json:"-" yaml:"-"` } // NewSchema will create a new high-level schema from a low-level one. @@ -164,302 +167,318 @@ func NewSchema(schema *base.Schema) *Schema { s.MinProperties = &schema.MinProperties.Value } - if !schema.MaxContains.IsEmpty() { - s.MaxContains = &schema.MaxContains.Value - } - if !schema.MinContains.IsEmpty() { - s.MinContains = &schema.MinContains.Value - } - if !schema.UniqueItems.IsEmpty() { - s.UniqueItems = &schema.UniqueItems.Value - } - if !schema.Contains.IsEmpty() { - s.Contains = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ - ValueNode: schema.Contains.ValueNode, - Value: schema.Contains.Value, - }} - } - if !schema.If.IsEmpty() { - s.If = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ - ValueNode: schema.If.ValueNode, - Value: schema.If.Value, - }} - } - if !schema.Else.IsEmpty() { - s.Else = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ - ValueNode: schema.Else.ValueNode, - Value: schema.Else.Value, - }} - } - if !schema.Then.IsEmpty() { - s.Then = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ - ValueNode: schema.Then.ValueNode, - Value: schema.Then.Value, - }} - } - if !schema.PropertyNames.IsEmpty() { - s.PropertyNames = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ - ValueNode: schema.PropertyNames.ValueNode, - Value: schema.PropertyNames.Value, - }} - } - if !schema.UnevaluatedItems.IsEmpty() { - s.UnevaluatedItems = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ - ValueNode: schema.UnevaluatedItems.ValueNode, - Value: schema.UnevaluatedItems.Value, - }} - } - if !schema.UnevaluatedProperties.IsEmpty() { - s.UnevaluatedProperties = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ - ValueNode: schema.UnevaluatedProperties.ValueNode, - Value: schema.UnevaluatedProperties.Value, - }} - } + if !schema.MaxContains.IsEmpty() { + s.MaxContains = &schema.MaxContains.Value + } + if !schema.MinContains.IsEmpty() { + s.MinContains = &schema.MinContains.Value + } + if !schema.UniqueItems.IsEmpty() { + s.UniqueItems = &schema.UniqueItems.Value + } + if !schema.Contains.IsEmpty() { + s.Contains = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ + ValueNode: schema.Contains.ValueNode, + Value: schema.Contains.Value, + }} + } + if !schema.If.IsEmpty() { + s.If = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ + ValueNode: schema.If.ValueNode, + Value: schema.If.Value, + }} + } + if !schema.Else.IsEmpty() { + s.Else = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ + ValueNode: schema.Else.ValueNode, + Value: schema.Else.Value, + }} + } + if !schema.Then.IsEmpty() { + s.Then = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ + ValueNode: schema.Then.ValueNode, + Value: schema.Then.Value, + }} + } + if !schema.PropertyNames.IsEmpty() { + s.PropertyNames = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ + ValueNode: schema.PropertyNames.ValueNode, + Value: schema.PropertyNames.Value, + }} + } + if !schema.UnevaluatedItems.IsEmpty() { + s.UnevaluatedItems = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ + ValueNode: schema.UnevaluatedItems.ValueNode, + Value: schema.UnevaluatedItems.Value, + }} + } + // check if unevaluated properties is a schema + if !schema.UnevaluatedProperties.IsEmpty() && schema.UnevaluatedProperties.Value.IsA() { + s.UnevaluatedProperties = &DynamicValue[*SchemaProxy, *bool]{ + A: &SchemaProxy{ + schema: &lowmodel.NodeReference[*base.SchemaProxy]{ + ValueNode: schema.UnevaluatedProperties.ValueNode, + Value: schema.UnevaluatedProperties.Value.A, + }, + }, + } + } - s.Pattern = schema.Pattern.Value - s.Format = schema.Format.Value + // check if unevaluated properties is a bool + if !schema.UnevaluatedProperties.IsEmpty() && schema.UnevaluatedProperties.Value.IsB() { + s.UnevaluatedProperties = &DynamicValue[*SchemaProxy, *bool]{ + B: schema.UnevaluatedProperties.Value.B, + } + } - // 3.0 spec is a single value - if !schema.Type.IsEmpty() && schema.Type.Value.IsA() { - s.Type = []string{schema.Type.Value.A} - } - // 3.1 spec may have multiple values - if !schema.Type.IsEmpty() && schema.Type.Value.IsB() { - for i := range schema.Type.Value.B { - s.Type = append(s.Type, schema.Type.Value.B[i].Value) - } - } - if schema.AdditionalProperties.Value != nil { - if addPropSchema, ok := schema.AdditionalProperties.Value.(*base.SchemaProxy); ok { - s.AdditionalProperties = NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{ - KeyNode: schema.AdditionalProperties.KeyNode, - ValueNode: schema.AdditionalProperties.ValueNode, - Value: addPropSchema, - }) - } else { - // TODO: check for slice and map types and unpack correctly. + if !schema.UnevaluatedProperties.IsEmpty() { - s.AdditionalProperties = schema.AdditionalProperties.Value - } - } - s.Description = schema.Description.Value - s.Default = schema.Default.Value - if !schema.Nullable.IsEmpty() { - s.Nullable = &schema.Nullable.Value - } - if !schema.ReadOnly.IsEmpty() { - s.ReadOnly = schema.ReadOnly.Value - } - if !schema.WriteOnly.IsEmpty() { - s.WriteOnly = schema.WriteOnly.Value - } - if !schema.Deprecated.IsEmpty() { - s.Deprecated = &schema.Deprecated.Value - } - s.Example = schema.Example.Value - if len(schema.Examples.Value) > 0 { - examples := make([]any, len(schema.Examples.Value)) - for i := 0; i < len(schema.Examples.Value); i++ { - examples[i] = schema.Examples.Value[i].Value - } - s.Examples = examples - } - s.Extensions = high.ExtractExtensions(schema.Extensions) - if !schema.Discriminator.IsEmpty() { - s.Discriminator = NewDiscriminator(schema.Discriminator.Value) - } - if !schema.XML.IsEmpty() { - s.XML = NewXML(schema.XML.Value) - } - if !schema.ExternalDocs.IsEmpty() { - s.ExternalDocs = NewExternalDoc(schema.ExternalDocs.Value) - } - var req []string - for i := range schema.Required.Value { - req = append(req, schema.Required.Value[i].Value) - } - s.Required = req + } - var enum []any - if !schema.Anchor.IsEmpty() { - s.Anchor = schema.Anchor.Value - } + s.Pattern = schema.Pattern.Value + s.Format = schema.Format.Value - // TODO: check this behavior. - for i := range schema.Enum.Value { - enum = append(enum, schema.Enum.Value[i].Value) - } - s.Enum = enum + // 3.0 spec is a single value + if !schema.Type.IsEmpty() && schema.Type.Value.IsA() { + s.Type = []string{schema.Type.Value.A} + } + // 3.1 spec may have multiple values + if !schema.Type.IsEmpty() && schema.Type.Value.IsB() { + for i := range schema.Type.Value.B { + s.Type = append(s.Type, schema.Type.Value.B[i].Value) + } + } + if schema.AdditionalProperties.Value != nil { + if addPropSchema, ok := schema.AdditionalProperties.Value.(*base.SchemaProxy); ok { + s.AdditionalProperties = NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{ + KeyNode: schema.AdditionalProperties.KeyNode, + ValueNode: schema.AdditionalProperties.ValueNode, + Value: addPropSchema, + }) + } else { + // TODO: check for slice and map types and unpack correctly. - // async work. - // any polymorphic properties need to be handled in their own threads - // any properties each need to be processed in their own thread. - // we go as fast as we can. - polyCompletedChan := make(chan bool) - propsChan := make(chan bool) - errChan := make(chan error) + s.AdditionalProperties = schema.AdditionalProperties.Value + } + } + s.Description = schema.Description.Value + s.Default = schema.Default.Value + if !schema.Nullable.IsEmpty() { + s.Nullable = &schema.Nullable.Value + } + if !schema.ReadOnly.IsEmpty() { + s.ReadOnly = schema.ReadOnly.Value + } + if !schema.WriteOnly.IsEmpty() { + s.WriteOnly = schema.WriteOnly.Value + } + if !schema.Deprecated.IsEmpty() { + s.Deprecated = &schema.Deprecated.Value + } + s.Example = schema.Example.Value + if len(schema.Examples.Value) > 0 { + examples := make([]any, len(schema.Examples.Value)) + for i := 0; i < len(schema.Examples.Value); i++ { + examples[i] = schema.Examples.Value[i].Value + } + s.Examples = examples + } + s.Extensions = high.ExtractExtensions(schema.Extensions) + if !schema.Discriminator.IsEmpty() { + s.Discriminator = NewDiscriminator(schema.Discriminator.Value) + } + if !schema.XML.IsEmpty() { + s.XML = NewXML(schema.XML.Value) + } + if !schema.ExternalDocs.IsEmpty() { + s.ExternalDocs = NewExternalDoc(schema.ExternalDocs.Value) + } + var req []string + for i := range schema.Required.Value { + req = append(req, schema.Required.Value[i].Value) + } + s.Required = req - type buildResult struct { - idx int - s *SchemaProxy - } + var enum []any + if !schema.Anchor.IsEmpty() { + s.Anchor = schema.Anchor.Value + } - // for every item, build schema async - buildSchema := func(sch lowmodel.ValueReference[*base.SchemaProxy], idx int, bChan chan buildResult) { - p := &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ - ValueNode: sch.ValueNode, - Value: sch.Value, - Reference: sch.GetReference(), - }} + // TODO: check this behavior. + for i := range schema.Enum.Value { + enum = append(enum, schema.Enum.Value[i].Value) + } + s.Enum = enum - bChan <- buildResult{idx: idx, s: p} - } + // async work. + // any polymorphic properties need to be handled in their own threads + // any properties each need to be processed in their own thread. + // we go as fast as we can. + polyCompletedChan := make(chan bool) + propsChan := make(chan bool) + errChan := make(chan error) - // schema async - buildOutSchemas := func(schemas []lowmodel.ValueReference[*base.SchemaProxy], items *[]*SchemaProxy, - doneChan chan bool, e chan error, - ) { - bChan := make(chan buildResult) - totalSchemas := len(schemas) - for i := range schemas { - go buildSchema(schemas[i], i, bChan) - } - j := 0 - for j < totalSchemas { - select { - case r := <-bChan: - j++ - (*items)[r.idx] = r.s - } - } - doneChan <- true - } + type buildResult struct { + idx int + s *SchemaProxy + } - // props async - var plock sync.Mutex - buildProps := func(k lowmodel.KeyReference[string], v lowmodel.ValueReference[*base.SchemaProxy], c chan bool, - props map[string]*SchemaProxy, sw int, - ) { - plock.Lock() - props[k.Value] = &SchemaProxy{ - schema: &lowmodel.NodeReference[*base.SchemaProxy]{ - Value: v.Value, - KeyNode: k.KeyNode, - ValueNode: v.ValueNode, - }, - } - plock.Unlock() + // for every item, build schema async + buildSchema := func(sch lowmodel.ValueReference[*base.SchemaProxy], idx int, bChan chan buildResult) { + p := &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ + ValueNode: sch.ValueNode, + Value: sch.Value, + Reference: sch.GetReference(), + }} - switch sw { - case 0: - s.Properties = props - case 1: - s.DependentSchemas = props - case 2: - s.PatternProperties = props - } - c <- true - } + bChan <- buildResult{idx: idx, s: p} + } - props := make(map[string]*SchemaProxy) - for k, v := range schema.Properties.Value { - go buildProps(k, v, propsChan, props, 0) - } + // schema async + buildOutSchemas := func(schemas []lowmodel.ValueReference[*base.SchemaProxy], items *[]*SchemaProxy, + doneChan chan bool, e chan error, + ) { + bChan := make(chan buildResult) + totalSchemas := len(schemas) + for i := range schemas { + go buildSchema(schemas[i], i, bChan) + } + j := 0 + for j < totalSchemas { + select { + case r := <-bChan: + j++ + (*items)[r.idx] = r.s + } + } + doneChan <- true + } - dependents := make(map[string]*SchemaProxy) - for k, v := range schema.DependentSchemas.Value { - go buildProps(k, v, propsChan, dependents, 1) - } - patternProps := make(map[string]*SchemaProxy) - for k, v := range schema.PatternProperties.Value { - go buildProps(k, v, propsChan, patternProps, 2) - } + // props async + var plock sync.Mutex + buildProps := func(k lowmodel.KeyReference[string], v lowmodel.ValueReference[*base.SchemaProxy], c chan bool, + props map[string]*SchemaProxy, sw int, + ) { + plock.Lock() + props[k.Value] = &SchemaProxy{ + schema: &lowmodel.NodeReference[*base.SchemaProxy]{ + Value: v.Value, + KeyNode: k.KeyNode, + ValueNode: v.ValueNode, + }, + } + plock.Unlock() - var allOf []*SchemaProxy - var oneOf []*SchemaProxy - var anyOf []*SchemaProxy - var not *SchemaProxy - var items *DynamicValue[*SchemaProxy, bool] - var prefixItems []*SchemaProxy + switch sw { + case 0: + s.Properties = props + case 1: + s.DependentSchemas = props + case 2: + s.PatternProperties = props + } + c <- true + } - children := 0 - if !schema.AllOf.IsEmpty() { - children++ - allOf = make([]*SchemaProxy, len(schema.AllOf.Value)) - go buildOutSchemas(schema.AllOf.Value, &allOf, polyCompletedChan, errChan) - } - if !schema.AnyOf.IsEmpty() { - children++ - anyOf = make([]*SchemaProxy, len(schema.AnyOf.Value)) - go buildOutSchemas(schema.AnyOf.Value, &anyOf, polyCompletedChan, errChan) - } - if !schema.OneOf.IsEmpty() { - children++ - oneOf = make([]*SchemaProxy, len(schema.OneOf.Value)) - go buildOutSchemas(schema.OneOf.Value, &oneOf, polyCompletedChan, errChan) - } - if !schema.Not.IsEmpty() { - not = NewSchemaProxy(&schema.Not) - } - if !schema.Items.IsEmpty() { - if schema.Items.Value.IsA() { - items = &DynamicValue[*SchemaProxy, bool]{A: &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ - ValueNode: schema.Items.ValueNode, - Value: schema.Items.Value.A, - KeyNode: schema.Items.KeyNode, - }}} - } else { - items = &DynamicValue[*SchemaProxy, bool]{N: 1, B: schema.Items.Value.B} - } - } - if !schema.PrefixItems.IsEmpty() { - children++ - prefixItems = make([]*SchemaProxy, len(schema.PrefixItems.Value)) - go buildOutSchemas(schema.PrefixItems.Value, &prefixItems, polyCompletedChan, errChan) - } + props := make(map[string]*SchemaProxy) + for k, v := range schema.Properties.Value { + go buildProps(k, v, propsChan, props, 0) + } - completeChildren := 0 - completedProps := 0 - totalProps := len(schema.Properties.Value) + len(schema.DependentSchemas.Value) + len(schema.PatternProperties.Value) - if totalProps+children > 0 { - allDone: - for true { - select { - case <-polyCompletedChan: - completeChildren++ - if totalProps == completedProps && children == completeChildren { - break allDone - } - case <-propsChan: - completedProps++ - if totalProps == completedProps && children == completeChildren { - break allDone - } - } - } - } - s.OneOf = oneOf - s.AnyOf = anyOf - s.AllOf = allOf - s.Items = items - s.PrefixItems = prefixItems - s.Not = not - return s + dependents := make(map[string]*SchemaProxy) + for k, v := range schema.DependentSchemas.Value { + go buildProps(k, v, propsChan, dependents, 1) + } + patternProps := make(map[string]*SchemaProxy) + for k, v := range schema.PatternProperties.Value { + go buildProps(k, v, propsChan, patternProps, 2) + } + + var allOf []*SchemaProxy + var oneOf []*SchemaProxy + var anyOf []*SchemaProxy + var not *SchemaProxy + var items *DynamicValue[*SchemaProxy, bool] + var prefixItems []*SchemaProxy + + children := 0 + if !schema.AllOf.IsEmpty() { + children++ + allOf = make([]*SchemaProxy, len(schema.AllOf.Value)) + go buildOutSchemas(schema.AllOf.Value, &allOf, polyCompletedChan, errChan) + } + if !schema.AnyOf.IsEmpty() { + children++ + anyOf = make([]*SchemaProxy, len(schema.AnyOf.Value)) + go buildOutSchemas(schema.AnyOf.Value, &anyOf, polyCompletedChan, errChan) + } + if !schema.OneOf.IsEmpty() { + children++ + oneOf = make([]*SchemaProxy, len(schema.OneOf.Value)) + go buildOutSchemas(schema.OneOf.Value, &oneOf, polyCompletedChan, errChan) + } + if !schema.Not.IsEmpty() { + not = NewSchemaProxy(&schema.Not) + } + if !schema.Items.IsEmpty() { + if schema.Items.Value.IsA() { + items = &DynamicValue[*SchemaProxy, bool]{A: &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ + ValueNode: schema.Items.ValueNode, + Value: schema.Items.Value.A, + KeyNode: schema.Items.KeyNode, + }}} + } else { + items = &DynamicValue[*SchemaProxy, bool]{N: 1, B: schema.Items.Value.B} + } + } + if !schema.PrefixItems.IsEmpty() { + children++ + prefixItems = make([]*SchemaProxy, len(schema.PrefixItems.Value)) + go buildOutSchemas(schema.PrefixItems.Value, &prefixItems, polyCompletedChan, errChan) + } + + completeChildren := 0 + completedProps := 0 + totalProps := len(schema.Properties.Value) + len(schema.DependentSchemas.Value) + len(schema.PatternProperties.Value) + if totalProps+children > 0 { + allDone: + for true { + select { + case <-polyCompletedChan: + completeChildren++ + if totalProps == completedProps && children == completeChildren { + break allDone + } + case <-propsChan: + completedProps++ + if totalProps == completedProps && children == completeChildren { + break allDone + } + } + } + } + s.OneOf = oneOf + s.AnyOf = anyOf + s.AllOf = allOf + s.Items = items + s.PrefixItems = prefixItems + s.Not = not + return s } // GoLow will return the low-level instance of Schema that was used to create the high level one. func (s *Schema) GoLow() *base.Schema { - return s.low + return s.low } // GoLowUntyped will return the low-level Schema instance that was used to create the high-level one, with no type func (s *Schema) GoLowUntyped() any { - return s.low + return s.low } // Render will return a YAML representation of the Schema object as a byte slice. func (s *Schema) Render() ([]byte, error) { - return yaml.Marshal(s) + return yaml.Marshal(s) } // RenderInline will return a YAML representation of the Schema object as a byte slice. All of the @@ -467,18 +486,18 @@ func (s *Schema) Render() ([]byte, error) { // // Make sure you don't have any circular references! func (s *Schema) RenderInline() ([]byte, error) { - d, _ := s.MarshalYAMLInline() - return yaml.Marshal(d) + d, _ := s.MarshalYAMLInline() + return yaml.Marshal(d) } // MarshalYAML will create a ready to render YAML representation of the ExternalDoc object. func (s *Schema) MarshalYAML() (interface{}, error) { - nb := high.NewNodeBuilder(s, s.low) - return nb.Render(), nil + nb := high.NewNodeBuilder(s, s.low) + return nb.Render(), nil } func (s *Schema) MarshalYAMLInline() (interface{}, error) { - nb := high.NewNodeBuilder(s, s.low) - nb.Resolve = true - return nb.Render(), nil + nb := high.NewNodeBuilder(s, s.low) + nb.Resolve = true + return nb.Render(), nil } diff --git a/datamodel/high/base/schema_proxy_test.go b/datamodel/high/base/schema_proxy_test.go index 37b471b..4f95606 100644 --- a/datamodel/high/base/schema_proxy_test.go +++ b/datamodel/high/base/schema_proxy_test.go @@ -4,17 +4,17 @@ package base import ( - "github.com/pb33f/libopenapi/datamodel/low" - lowbase "github.com/pb33f/libopenapi/datamodel/low/base" - "github.com/pb33f/libopenapi/index" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "strings" - "testing" + "github.com/pb33f/libopenapi/datamodel/low" + lowbase "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/index" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "strings" + "testing" ) func TestSchemaProxy_MarshalYAML(t *testing.T) { - const ymlComponents = `components: + const ymlComponents = `components: schemas: rice: type: string @@ -27,42 +27,41 @@ func TestSchemaProxy_MarshalYAML(t *testing.T) { rice: $ref: '#/components/schemas/rice'` - idx := func() *index.SpecIndex { - var idxNode yaml.Node - err := yaml.Unmarshal([]byte(ymlComponents), &idxNode) - assert.NoError(t, err) - return index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig()) - }() + idx := func() *index.SpecIndex { + var idxNode yaml.Node + err := yaml.Unmarshal([]byte(ymlComponents), &idxNode) + assert.NoError(t, err) + return index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig()) + }() - const ref = "#/components/schemas/nice" - const ymlSchema = `$ref: '` + ref + `'` - var node yaml.Node - _ = yaml.Unmarshal([]byte(ymlSchema), &node) + const ref = "#/components/schemas/nice" + const ymlSchema = `$ref: '` + ref + `'` + var node yaml.Node + _ = yaml.Unmarshal([]byte(ymlSchema), &node) - lowProxy := new(lowbase.SchemaProxy) - err := lowProxy.Build(node.Content[0], idx) - assert.NoError(t, err) + lowProxy := new(lowbase.SchemaProxy) + err := lowProxy.Build(node.Content[0], idx) + assert.NoError(t, err) - lowRef := low.NodeReference[*lowbase.SchemaProxy]{ - Value: lowProxy, - } + lowRef := low.NodeReference[*lowbase.SchemaProxy]{ + Value: lowProxy, + } - sp := NewSchemaProxy(&lowRef) + sp := NewSchemaProxy(&lowRef) - rend, _ := sp.Render() - assert.Equal(t, "$ref: '#/components/schemas/nice'", strings.TrimSpace(string(rend))) + rend, _ := sp.Render() + assert.Equal(t, "$ref: '#/components/schemas/nice'", strings.TrimSpace(string(rend))) } func TestCreateSchemaProxy(t *testing.T) { - sp := CreateSchemaProxy(&Schema{Description: "iAmASchema"}) - assert.Equal(t, "iAmASchema", sp.rendered.Description) - assert.False(t, sp.IsReference()) + sp := CreateSchemaProxy(&Schema{Description: "iAmASchema"}) + assert.Equal(t, "iAmASchema", sp.rendered.Description) + assert.False(t, sp.IsReference()) } func TestCreateSchemaProxyRef(t *testing.T) { - sp := CreateSchemaProxyRef("#/components/schemas/MySchema") - assert.Equal(t, "#/components/schemas/MySchema", sp.GetReference()) - assert.True(t, sp.IsReference()) + sp := CreateSchemaProxyRef("#/components/schemas/MySchema") + assert.Equal(t, "#/components/schemas/MySchema", sp.GetReference()) + assert.True(t, sp.IsReference()) } - diff --git a/datamodel/high/base/schema_test.go b/datamodel/high/base/schema_test.go index 14958d4..a42c806 100644 --- a/datamodel/high/base/schema_test.go +++ b/datamodel/high/base/schema_test.go @@ -4,27 +4,27 @@ package base import ( - "fmt" - "strings" - "testing" + "fmt" + "strings" + "testing" - "github.com/pb33f/libopenapi/datamodel/low" - lowbase "github.com/pb33f/libopenapi/datamodel/low/base" - "github.com/pb33f/libopenapi/index" - "github.com/pb33f/libopenapi/utils" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" + "github.com/pb33f/libopenapi/datamodel/low" + lowbase "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/utils" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" ) func TestDynamicValue_IsA(t *testing.T) { - dv := &DynamicValue[int, bool]{N: 0, A: 23} - assert.True(t, dv.IsA()) - assert.False(t, dv.IsB()) + dv := &DynamicValue[int, bool]{N: 0, A: 23} + assert.True(t, dv.IsA()) + assert.False(t, dv.IsB()) } func TestNewSchemaProxy(t *testing.T) { - // check proxy - yml := `components: + // check proxy + yml := `components: schemas: rice: type: string @@ -37,85 +37,85 @@ func TestNewSchemaProxy(t *testing.T) { rice: $ref: '#/components/schemas/rice'` - var idxNode, compNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &idxNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&idxNode) + var idxNode, compNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &idxNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&idxNode) - yml = `properties: + yml = `properties: rice: $ref: '#/components/schemas/I-do-not-exist'` - _ = yaml.Unmarshal([]byte(yml), &compNode) + _ = yaml.Unmarshal([]byte(yml), &compNode) - sp := new(lowbase.SchemaProxy) - err := sp.Build(compNode.Content[0], idx) - assert.NoError(t, err) + sp := new(lowbase.SchemaProxy) + err := sp.Build(compNode.Content[0], idx) + assert.NoError(t, err) - lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ - Value: sp, - ValueNode: idxNode.Content[0], - } + lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ + Value: sp, + ValueNode: idxNode.Content[0], + } - sch1 := SchemaProxy{schema: &lowproxy} - assert.Nil(t, sch1.Schema()) - assert.Error(t, sch1.GetBuildError()) + sch1 := SchemaProxy{schema: &lowproxy} + assert.Nil(t, sch1.Schema()) + assert.Error(t, sch1.GetBuildError()) - rend, rendErr := sch1.Render() - assert.Nil(t, rend) - assert.Error(t, rendErr) + rend, rendErr := sch1.Render() + assert.Nil(t, rend) + assert.Error(t, rendErr) - g, o := sch1.BuildSchema() - assert.Nil(t, g) - assert.Error(t, o) + g, o := sch1.BuildSchema() + assert.Nil(t, g) + assert.Error(t, o) } func TestNewSchemaProxyRender(t *testing.T) { - // check proxy - yml := `components: + // check proxy + yml := `components: schemas: rice: type: string description: a rice` - var idxNode, compNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &idxNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig()) + var idxNode, compNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &idxNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig()) - yml = `properties: + yml = `properties: rice: $ref: '#/components/schemas/rice'` - _ = yaml.Unmarshal([]byte(yml), &compNode) + _ = yaml.Unmarshal([]byte(yml), &compNode) - sp := new(lowbase.SchemaProxy) - err := sp.Build(compNode.Content[0], idx) - assert.NoError(t, err) + sp := new(lowbase.SchemaProxy) + err := sp.Build(compNode.Content[0], idx) + assert.NoError(t, err) - lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ - Value: sp, - ValueNode: idxNode.Content[0], - } + lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ + Value: sp, + ValueNode: idxNode.Content[0], + } - sch1 := SchemaProxy{schema: &lowproxy} - assert.NotNil(t, sch1.Schema()) - assert.NoError(t, sch1.GetBuildError()) + sch1 := SchemaProxy{schema: &lowproxy} + assert.NotNil(t, sch1.Schema()) + assert.NoError(t, sch1.GetBuildError()) - g, o := sch1.BuildSchema() - assert.NotNil(t, g) - assert.NoError(t, o) + g, o := sch1.BuildSchema() + assert.NotNil(t, g) + assert.NoError(t, o) - rend, _ := sch1.Render() - desired := `properties: + rend, _ := sch1.Render() + desired := `properties: rice: $ref: '#/components/schemas/rice'` - assert.Equal(t, desired, strings.TrimSpace(string(rend))) + assert.Equal(t, desired, strings.TrimSpace(string(rend))) } func TestNewSchemaProxy_WithObject(t *testing.T) { - testSpec := `type: object + testSpec := `type: object description: something object if: type: string @@ -270,111 +270,111 @@ maxProperties: 30 minProperties: 1 $anchor: anchor` - var compNode yaml.Node - _ = yaml.Unmarshal([]byte(testSpec), &compNode) + var compNode yaml.Node + _ = yaml.Unmarshal([]byte(testSpec), &compNode) - sp := new(lowbase.SchemaProxy) - err := sp.Build(compNode.Content[0], nil) - assert.NoError(t, err) + sp := new(lowbase.SchemaProxy) + err := sp.Build(compNode.Content[0], nil) + assert.NoError(t, err) - lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ - Value: sp, - ValueNode: compNode.Content[0], - } + lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ + Value: sp, + ValueNode: compNode.Content[0], + } - schemaProxy := NewSchemaProxy(&lowproxy) - compiled := schemaProxy.Schema() + schemaProxy := NewSchemaProxy(&lowproxy) + compiled := schemaProxy.Schema() - assert.Equal(t, schemaProxy, compiled.ParentProxy) + assert.Equal(t, schemaProxy, compiled.ParentProxy) - assert.NotNil(t, compiled) - assert.Nil(t, schemaProxy.GetBuildError()) + assert.NotNil(t, compiled) + assert.Nil(t, schemaProxy.GetBuildError()) - // check 3.1 properties - assert.Equal(t, "int", compiled.Contains.Schema().Type[0]) - assert.Equal(t, int64(10), *compiled.MaxContains) - assert.Equal(t, int64(1), *compiled.MinContains) - assert.Equal(t, int64(10), *compiled.MaxLength) - assert.Equal(t, int64(1), *compiled.MinLength) - assert.Equal(t, int64(20), *compiled.MaxItems) - assert.Equal(t, int64(10), *compiled.MinItems) - assert.Equal(t, int64(30), *compiled.MaxProperties) - assert.Equal(t, int64(1), *compiled.MinProperties) - assert.Equal(t, "string", compiled.If.Schema().Type[0]) - assert.Equal(t, "integer", compiled.Else.Schema().Type[0]) - assert.Equal(t, "boolean", compiled.Then.Schema().Type[0]) - assert.Equal(t, "string", compiled.PatternProperties["patternOne"].Schema().Type[0]) - assert.Equal(t, "string", compiled.DependentSchemas["schemaOne"].Schema().Type[0]) - assert.Equal(t, "string", compiled.PropertyNames.Schema().Type[0]) - assert.Equal(t, "boolean", compiled.UnevaluatedItems.Schema().Type[0]) - assert.Equal(t, "integer", compiled.UnevaluatedProperties.Schema().Type[0]) - assert.True(t, compiled.ReadOnly) - assert.True(t, compiled.WriteOnly) - assert.True(t, *compiled.Deprecated) - assert.True(t, *compiled.Nullable) - assert.Equal(t, "anchor", compiled.Anchor) + // check 3.1 properties + assert.Equal(t, "int", compiled.Contains.Schema().Type[0]) + assert.Equal(t, int64(10), *compiled.MaxContains) + assert.Equal(t, int64(1), *compiled.MinContains) + assert.Equal(t, int64(10), *compiled.MaxLength) + assert.Equal(t, int64(1), *compiled.MinLength) + assert.Equal(t, int64(20), *compiled.MaxItems) + assert.Equal(t, int64(10), *compiled.MinItems) + assert.Equal(t, int64(30), *compiled.MaxProperties) + assert.Equal(t, int64(1), *compiled.MinProperties) + assert.Equal(t, "string", compiled.If.Schema().Type[0]) + assert.Equal(t, "integer", compiled.Else.Schema().Type[0]) + assert.Equal(t, "boolean", compiled.Then.Schema().Type[0]) + assert.Equal(t, "string", compiled.PatternProperties["patternOne"].Schema().Type[0]) + assert.Equal(t, "string", compiled.DependentSchemas["schemaOne"].Schema().Type[0]) + assert.Equal(t, "string", compiled.PropertyNames.Schema().Type[0]) + assert.Equal(t, "boolean", compiled.UnevaluatedItems.Schema().Type[0]) + assert.Equal(t, "integer", compiled.UnevaluatedProperties.A.Schema().Type[0]) + assert.True(t, compiled.ReadOnly) + assert.True(t, compiled.WriteOnly) + assert.True(t, *compiled.Deprecated) + assert.True(t, *compiled.Nullable) + assert.Equal(t, "anchor", compiled.Anchor) - wentLow := compiled.GoLow() - assert.Equal(t, 129, wentLow.AdditionalProperties.ValueNode.Line) - assert.NotNil(t, compiled.GoLowUntyped()) + wentLow := compiled.GoLow() + assert.Equal(t, 129, wentLow.AdditionalProperties.ValueNode.Line) + assert.NotNil(t, compiled.GoLowUntyped()) - // now render it out! - schemaBytes, _ := compiled.Render() - assert.Len(t, schemaBytes, 3494) + // now render it out! + schemaBytes, _ := compiled.Render() + assert.Len(t, schemaBytes, 3494) } func TestSchemaObjectWithAllOfSequenceOrder(t *testing.T) { - testSpec := test_get_allOf_schema_blob() + testSpec := test_get_allOf_schema_blob() - var compNode yaml.Node - _ = yaml.Unmarshal([]byte(testSpec), &compNode) + var compNode yaml.Node + _ = yaml.Unmarshal([]byte(testSpec), &compNode) - // test data is a map with one node - mapContent := compNode.Content[0].Content + // test data is a map with one node + mapContent := compNode.Content[0].Content - _, vn := utils.FindKeyNodeTop(lowbase.AllOfLabel, mapContent) - assert.True(t, utils.IsNodeArray(vn)) + _, vn := utils.FindKeyNodeTop(lowbase.AllOfLabel, mapContent) + assert.True(t, utils.IsNodeArray(vn)) - want := []string{} + want := []string{} - // Go over every element in AllOf and grab description - // Odd: object - // Event: description - for i := range vn.Content { - assert.True(t, utils.IsNodeMap(vn.Content[i])) - _, vn := utils.FindKeyNodeTop("description", vn.Content[i].Content) - assert.True(t, utils.IsNodeStringValue(vn)) - want = append(want, vn.Value) - } + // Go over every element in AllOf and grab description + // Odd: object + // Event: description + for i := range vn.Content { + assert.True(t, utils.IsNodeMap(vn.Content[i])) + _, vn := utils.FindKeyNodeTop("description", vn.Content[i].Content) + assert.True(t, utils.IsNodeStringValue(vn)) + want = append(want, vn.Value) + } - sp := new(lowbase.SchemaProxy) - err := sp.Build(compNode.Content[0], nil) - assert.NoError(t, err) + sp := new(lowbase.SchemaProxy) + err := sp.Build(compNode.Content[0], nil) + assert.NoError(t, err) - lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ - Value: sp, - ValueNode: compNode.Content[0], - } + lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ + Value: sp, + ValueNode: compNode.Content[0], + } - schemaProxy := NewSchemaProxy(&lowproxy) - compiled := schemaProxy.Schema() + schemaProxy := NewSchemaProxy(&lowproxy) + compiled := schemaProxy.Schema() - assert.Equal(t, schemaProxy, compiled.ParentProxy) + assert.Equal(t, schemaProxy, compiled.ParentProxy) - assert.NotNil(t, compiled) - assert.Nil(t, schemaProxy.GetBuildError()) + assert.NotNil(t, compiled) + assert.Nil(t, schemaProxy.GetBuildError()) - got := []string{} - for i := range compiled.AllOf { - v := compiled.AllOf[i] - got = append(got, v.Schema().Description) - } + got := []string{} + for i := range compiled.AllOf { + v := compiled.AllOf[i] + got = append(got, v.Schema().Description) + } - assert.Equal(t, want, got) + assert.Equal(t, want, got) } func TestNewSchemaProxy_WithObject_FinishPoly(t *testing.T) { - testSpec := `type: object + testSpec := `type: object description: something object discriminator: propertyName: athing @@ -481,37 +481,37 @@ externalDocs: enum: [fish, cake] required: [cake, fish]` - var compNode yaml.Node - _ = yaml.Unmarshal([]byte(testSpec), &compNode) + var compNode yaml.Node + _ = yaml.Unmarshal([]byte(testSpec), &compNode) - sp := new(lowbase.SchemaProxy) - err := sp.Build(compNode.Content[0], nil) - assert.NoError(t, err) + sp := new(lowbase.SchemaProxy) + err := sp.Build(compNode.Content[0], nil) + assert.NoError(t, err) - lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ - Value: sp, - ValueNode: compNode.Content[0], - } + lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ + Value: sp, + ValueNode: compNode.Content[0], + } - schemaProxy := NewSchemaProxy(&lowproxy) - compiled := schemaProxy.Schema() + schemaProxy := NewSchemaProxy(&lowproxy) + compiled := schemaProxy.Schema() - assert.NotNil(t, compiled) - assert.Nil(t, schemaProxy.GetBuildError()) + assert.NotNil(t, compiled) + assert.Nil(t, schemaProxy.GetBuildError()) assert.True(t, compiled.ExclusiveMaximum.A) assert.Equal(t, float64(123), compiled.Properties["somethingB"].Schema().ExclusiveMinimum.B) assert.Equal(t, float64(334), compiled.Properties["somethingB"].Schema().ExclusiveMaximum.B) assert.Len(t, compiled.Properties["somethingB"].Schema().Properties["somethingBProp"].Schema().Type, 2) - assert.Equal(t, "nice", compiled.AdditionalProperties.(*SchemaProxy).Schema().Description) + assert.Equal(t, "nice", compiled.AdditionalProperties.(*SchemaProxy).Schema().Description) - wentLow := compiled.GoLow() - assert.Equal(t, 97, wentLow.AdditionalProperties.ValueNode.Line) - assert.Equal(t, 102, wentLow.XML.ValueNode.Line) + wentLow := compiled.GoLow() + assert.Equal(t, 97, wentLow.AdditionalProperties.ValueNode.Line) + assert.Equal(t, 102, wentLow.XML.ValueNode.Line) - wentLower := compiled.XML.GoLow() - assert.Equal(t, 102, wentLower.Name.ValueNode.Line) + wentLower := compiled.XML.GoLow() + assert.Equal(t, 102, wentLower.Name.ValueNode.Line) } func TestSchemaProxy_GoLow(t *testing.T) { @@ -528,62 +528,62 @@ func TestSchemaProxy_GoLow(t *testing.T) { rice: $ref: '#/components/schemas/rice'` - idx := func() *index.SpecIndex { - var idxNode yaml.Node - err := yaml.Unmarshal([]byte(ymlComponents), &idxNode) - assert.NoError(t, err) - return index.NewSpecIndex(&idxNode) - }() + idx := func() *index.SpecIndex { + var idxNode yaml.Node + err := yaml.Unmarshal([]byte(ymlComponents), &idxNode) + assert.NoError(t, err) + return index.NewSpecIndex(&idxNode) + }() - const ref = "#/components/schemas/nice" - const ymlSchema = `$ref: '` + ref + `'` - var node yaml.Node - _ = yaml.Unmarshal([]byte(ymlSchema), &node) + const ref = "#/components/schemas/nice" + const ymlSchema = `$ref: '` + ref + `'` + var node yaml.Node + _ = yaml.Unmarshal([]byte(ymlSchema), &node) - lowProxy := new(lowbase.SchemaProxy) - err := lowProxy.Build(node.Content[0], idx) - assert.NoError(t, err) + lowProxy := new(lowbase.SchemaProxy) + err := lowProxy.Build(node.Content[0], idx) + assert.NoError(t, err) - lowRef := low.NodeReference[*lowbase.SchemaProxy]{ - Value: lowProxy, - } + lowRef := low.NodeReference[*lowbase.SchemaProxy]{ + Value: lowProxy, + } - sp := NewSchemaProxy(&lowRef) - assert.Equal(t, lowProxy, sp.GoLow()) - assert.Equal(t, ref, sp.GoLow().GetSchemaReference()) - assert.Equal(t, ref, sp.GoLow().GetReference()) + sp := NewSchemaProxy(&lowRef) + assert.Equal(t, lowProxy, sp.GoLow()) + assert.Equal(t, ref, sp.GoLow().GetSchemaReference()) + assert.Equal(t, ref, sp.GoLow().GetReference()) - spNil := NewSchemaProxy(nil) - assert.Nil(t, spNil.GoLow()) - assert.Nil(t, spNil.GoLowUntyped()) + spNil := NewSchemaProxy(nil) + assert.Nil(t, spNil.GoLow()) + assert.Nil(t, spNil.GoLowUntyped()) } func getHighSchema(t *testing.T, yml string) *Schema { - // unmarshal raw bytes - var node yaml.Node - assert.NoError(t, yaml.Unmarshal([]byte(yml), &node)) + // unmarshal raw bytes + var node yaml.Node + assert.NoError(t, yaml.Unmarshal([]byte(yml), &node)) - // build out the low-level model - var lowSchema lowbase.Schema - assert.NoError(t, low.BuildModel(node.Content[0], &lowSchema)) - assert.NoError(t, lowSchema.Build(node.Content[0], nil)) + // build out the low-level model + var lowSchema lowbase.Schema + assert.NoError(t, low.BuildModel(node.Content[0], &lowSchema)) + assert.NoError(t, lowSchema.Build(node.Content[0], nil)) - // build the high level model - return NewSchema(&lowSchema) + // build the high level model + return NewSchema(&lowSchema) } func TestSchemaNumberNoValidation(t *testing.T) { - yml := ` + yml := ` type: number ` - highSchema := getHighSchema(t, yml) + highSchema := getHighSchema(t, yml) - assert.Nil(t, highSchema.MultipleOf) - assert.Nil(t, highSchema.Minimum) - assert.Nil(t, highSchema.ExclusiveMinimum) - assert.Nil(t, highSchema.Maximum) - assert.Nil(t, highSchema.ExclusiveMaximum) + assert.Nil(t, highSchema.MultipleOf) + assert.Nil(t, highSchema.Minimum) + assert.Nil(t, highSchema.ExclusiveMinimum) + assert.Nil(t, highSchema.Maximum) + assert.Nil(t, highSchema.ExclusiveMaximum) } func TestSchemaNumberMultipleOfInt(t *testing.T) { @@ -591,7 +591,7 @@ func TestSchemaNumberMultipleOfInt(t *testing.T) { type: number multipleOf: 5 ` - highSchema := getHighSchema(t, yml) + highSchema := getHighSchema(t, yml) value := float64(5) assert.EqualValues(t, &value, highSchema.MultipleOf) @@ -609,11 +609,11 @@ multipleOf: 0.5 } func TestSchemaNumberMinimumInt(t *testing.T) { - yml := ` + yml := ` type: number minimum: 5 ` - highSchema := getHighSchema(t, yml) + highSchema := getHighSchema(t, yml) value := float64(5) assert.EqualValues(t, &value, highSchema.Minimum) @@ -627,92 +627,92 @@ minimum: 0.5 highSchema := getHighSchema(t, yml) value := 0.5 - assert.EqualValues(t, &value, highSchema.Minimum) + assert.EqualValues(t, &value, highSchema.Minimum) } func TestSchemaNumberMinimumZero(t *testing.T) { - yml := ` + yml := ` type: number minimum: 0 ` - highSchema := getHighSchema(t, yml) + highSchema := getHighSchema(t, yml) value := float64(0) assert.EqualValues(t, &value, highSchema.Minimum) } func TestSchemaNumberExclusiveMinimum(t *testing.T) { - yml := ` + yml := ` type: number exclusiveMinimum: 5 ` - highSchema := getHighSchema(t, yml) + highSchema := getHighSchema(t, yml) - value := int64(5) - assert.EqualValues(t, value, highSchema.ExclusiveMinimum.B) - assert.True(t, highSchema.ExclusiveMinimum.IsB()) + value := int64(5) + assert.EqualValues(t, value, highSchema.ExclusiveMinimum.B) + assert.True(t, highSchema.ExclusiveMinimum.IsB()) } func TestSchemaNumberMaximum(t *testing.T) { - yml := ` + yml := ` type: number maximum: 5 ` - highSchema := getHighSchema(t, yml) + highSchema := getHighSchema(t, yml) value := float64(5) assert.EqualValues(t, &value, highSchema.Maximum) } func TestSchemaNumberMaximumZero(t *testing.T) { - yml := ` + yml := ` type: number maximum: 0 ` - highSchema := getHighSchema(t, yml) + highSchema := getHighSchema(t, yml) value := float64(0) assert.EqualValues(t, &value, highSchema.Maximum) } func TestSchemaNumberExclusiveMaximum(t *testing.T) { - yml := ` + yml := ` type: number exclusiveMaximum: 5 ` - highSchema := getHighSchema(t, yml) + highSchema := getHighSchema(t, yml) - value := int64(5) - assert.EqualValues(t, value, highSchema.ExclusiveMaximum.B) - assert.True(t, highSchema.ExclusiveMaximum.IsB()) + value := int64(5) + assert.EqualValues(t, value, highSchema.ExclusiveMaximum.B) + assert.True(t, highSchema.ExclusiveMaximum.IsB()) } func TestSchema_Items_Boolean(t *testing.T) { - yml := ` + yml := ` type: number items: true ` - highSchema := getHighSchema(t, yml) + highSchema := getHighSchema(t, yml) - assert.True(t, highSchema.Items.B) + assert.True(t, highSchema.Items.B) } func TestSchemaExamples(t *testing.T) { - yml := ` + yml := ` type: number examples: - 5 - 10 ` - highSchema := getHighSchema(t, yml) + highSchema := getHighSchema(t, yml) - assert.Equal(t, []any{int64(5), int64(10)}, highSchema.Examples) + assert.Equal(t, []any{int64(5), int64(10)}, highSchema.Examples) } func ExampleNewSchema() { - // create an example schema object - // this can be either JSON or YAML. - yml := ` + // create an example schema object + // this can be either JSON or YAML. + yml := ` title: this is a schema type: object properties: @@ -721,27 +721,27 @@ properties: type: integer format: int64` - // unmarshal raw bytes - var node yaml.Node - _ = yaml.Unmarshal([]byte(yml), &node) + // unmarshal raw bytes + var node yaml.Node + _ = yaml.Unmarshal([]byte(yml), &node) - // build out the low-level model - var lowSchema lowbase.Schema - _ = low.BuildModel(node.Content[0], &lowSchema) - _ = lowSchema.Build(node.Content[0], nil) + // build out the low-level model + var lowSchema lowbase.Schema + _ = low.BuildModel(node.Content[0], &lowSchema) + _ = lowSchema.Build(node.Content[0], nil) - // build the high level model - highSchema := NewSchema(&lowSchema) + // build the high level model + highSchema := NewSchema(&lowSchema) - // print out the description of 'aProperty' - fmt.Print(highSchema.Properties["aProperty"].Schema().Description) - // Output: this is an integer property + // print out the description of 'aProperty' + fmt.Print(highSchema.Properties["aProperty"].Schema().Description) + // Output: this is an integer property } func ExampleNewSchemaProxy() { - // create an example schema object - // this can be either JSON or YAML. - yml := ` + // create an example schema object + // this can be either JSON or YAML. + yml := ` title: this is a schema type: object properties: @@ -750,27 +750,27 @@ properties: type: integer format: int64` - // unmarshal raw bytes - var node yaml.Node - _ = yaml.Unmarshal([]byte(yml), &node) + // unmarshal raw bytes + var node yaml.Node + _ = yaml.Unmarshal([]byte(yml), &node) - // build out the low-level model - var lowSchema lowbase.SchemaProxy - _ = low.BuildModel(node.Content[0], &lowSchema) - _ = lowSchema.Build(node.Content[0], nil) + // build out the low-level model + var lowSchema lowbase.SchemaProxy + _ = low.BuildModel(node.Content[0], &lowSchema) + _ = lowSchema.Build(node.Content[0], nil) - // build the high level schema proxy - highSchema := NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{ - Value: &lowSchema, - }) + // build the high level schema proxy + highSchema := NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{ + Value: &lowSchema, + }) - // print out the description of 'aProperty' - fmt.Print(highSchema.Schema().Properties["aProperty"].Schema().Description) - // Output: this is an integer property + // print out the description of 'aProperty' + fmt.Print(highSchema.Schema().Properties["aProperty"].Schema().Description) + // Output: this is an integer property } func test_get_allOf_schema_blob() string { - return `type: object + return `type: object description: allOf sequence check allOf: - type: object @@ -792,7 +792,7 @@ properties: } func TestNewSchemaProxy_RenderSchema(t *testing.T) { - testSpec := `type: object + testSpec := `type: object description: something object discriminator: propertyName: athing @@ -813,34 +813,34 @@ allOf: example: allOfBExp ` - var compNode yaml.Node - _ = yaml.Unmarshal([]byte(testSpec), &compNode) + var compNode yaml.Node + _ = yaml.Unmarshal([]byte(testSpec), &compNode) - sp := new(lowbase.SchemaProxy) - err := sp.Build(compNode.Content[0], nil) - assert.NoError(t, err) + sp := new(lowbase.SchemaProxy) + err := sp.Build(compNode.Content[0], nil) + assert.NoError(t, err) - lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ - Value: sp, - ValueNode: compNode.Content[0], - } + lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ + Value: sp, + ValueNode: compNode.Content[0], + } - schemaProxy := NewSchemaProxy(&lowproxy) - compiled := schemaProxy.Schema() + schemaProxy := NewSchemaProxy(&lowproxy) + compiled := schemaProxy.Schema() - assert.Equal(t, schemaProxy, compiled.ParentProxy) + assert.Equal(t, schemaProxy, compiled.ParentProxy) - assert.NotNil(t, compiled) - assert.Nil(t, schemaProxy.GetBuildError()) + assert.NotNil(t, compiled) + assert.Nil(t, schemaProxy.GetBuildError()) - // now render it out, it should be identical. - schemaBytes, _ := compiled.Render() - assert.Equal(t, testSpec, string(schemaBytes)) + // now render it out, it should be identical. + schemaBytes, _ := compiled.Render() + assert.Equal(t, testSpec, string(schemaBytes)) } func TestNewSchemaProxy_RenderSchemaWithMultipleObjectTypes(t *testing.T) { - testSpec := `type: object + testSpec := `type: object description: something object oneOf: - type: object @@ -877,33 +877,33 @@ items: example: itemsBExp ` - var compNode yaml.Node - _ = yaml.Unmarshal([]byte(testSpec), &compNode) + var compNode yaml.Node + _ = yaml.Unmarshal([]byte(testSpec), &compNode) - sp := new(lowbase.SchemaProxy) - err := sp.Build(compNode.Content[0], nil) - assert.NoError(t, err) + sp := new(lowbase.SchemaProxy) + err := sp.Build(compNode.Content[0], nil) + assert.NoError(t, err) - lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ - Value: sp, - ValueNode: compNode.Content[0], - } + lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ + Value: sp, + ValueNode: compNode.Content[0], + } - schemaProxy := NewSchemaProxy(&lowproxy) - compiled := schemaProxy.Schema() + schemaProxy := NewSchemaProxy(&lowproxy) + compiled := schemaProxy.Schema() - assert.Equal(t, schemaProxy, compiled.ParentProxy) + assert.Equal(t, schemaProxy, compiled.ParentProxy) - assert.NotNil(t, compiled) - assert.Nil(t, schemaProxy.GetBuildError()) + assert.NotNil(t, compiled) + assert.Nil(t, schemaProxy.GetBuildError()) - // now render it out, it should be identical. - schemaBytes, _ := compiled.Render() - assert.Equal(t, testSpec, string(schemaBytes)) + // now render it out, it should be identical. + schemaBytes, _ := compiled.Render() + assert.Equal(t, testSpec, string(schemaBytes)) } func TestNewSchemaProxy_RenderSchemaEnsurePropertyOrdering(t *testing.T) { - testSpec := `properties: + testSpec := `properties: somethingBee: type: number somethingThree: @@ -940,28 +940,28 @@ additionalProperties: true xml: name: XML Thing` - var compNode yaml.Node - _ = yaml.Unmarshal([]byte(testSpec), &compNode) + var compNode yaml.Node + _ = yaml.Unmarshal([]byte(testSpec), &compNode) - sp := new(lowbase.SchemaProxy) - err := sp.Build(compNode.Content[0], nil) - assert.NoError(t, err) + sp := new(lowbase.SchemaProxy) + err := sp.Build(compNode.Content[0], nil) + assert.NoError(t, err) - lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ - Value: sp, - ValueNode: compNode.Content[0], - } + lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ + Value: sp, + ValueNode: compNode.Content[0], + } - schemaProxy := NewSchemaProxy(&lowproxy) - compiled := schemaProxy.Schema() + schemaProxy := NewSchemaProxy(&lowproxy) + compiled := schemaProxy.Schema() - // now render it out, it should be identical. - schemaBytes, _ := compiled.Render() - assert.Equal(t, testSpec, strings.TrimSpace(string(schemaBytes))) + // now render it out, it should be identical. + schemaBytes, _ := compiled.Render() + assert.Equal(t, testSpec, strings.TrimSpace(string(schemaBytes))) } func TestNewSchemaProxy_RenderSchemaCheckDiscriminatorMappingOrder(t *testing.T) { - testSpec := `discriminator: + testSpec := `discriminator: mapping: log: cat pizza: party @@ -969,134 +969,134 @@ func TestNewSchemaProxy_RenderSchemaCheckDiscriminatorMappingOrder(t *testing.T) warm: soup cold: heart` - var compNode yaml.Node - _ = yaml.Unmarshal([]byte(testSpec), &compNode) + var compNode yaml.Node + _ = yaml.Unmarshal([]byte(testSpec), &compNode) - sp := new(lowbase.SchemaProxy) - err := sp.Build(compNode.Content[0], nil) - assert.NoError(t, err) + sp := new(lowbase.SchemaProxy) + err := sp.Build(compNode.Content[0], nil) + assert.NoError(t, err) - lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ - Value: sp, - ValueNode: compNode.Content[0], - } + lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ + Value: sp, + ValueNode: compNode.Content[0], + } - schemaProxy := NewSchemaProxy(&lowproxy) - compiled := schemaProxy.Schema() + schemaProxy := NewSchemaProxy(&lowproxy) + compiled := schemaProxy.Schema() - // now render it out, it should be identical. - schemaBytes, _ := compiled.Render() - assert.Equal(t, testSpec, strings.TrimSpace(string(schemaBytes))) + // now render it out, it should be identical. + schemaBytes, _ := compiled.Render() + assert.Equal(t, testSpec, strings.TrimSpace(string(schemaBytes))) } func TestNewSchemaProxy_RenderSchemaCheckAdditionalPropertiesSlice(t *testing.T) { - testSpec := `additionalProperties: + testSpec := `additionalProperties: - one - two - miss a few - ninety nine - hundred` - var compNode yaml.Node - _ = yaml.Unmarshal([]byte(testSpec), &compNode) + var compNode yaml.Node + _ = yaml.Unmarshal([]byte(testSpec), &compNode) - sp := new(lowbase.SchemaProxy) - err := sp.Build(compNode.Content[0], nil) - assert.NoError(t, err) + sp := new(lowbase.SchemaProxy) + err := sp.Build(compNode.Content[0], nil) + assert.NoError(t, err) - lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ - Value: sp, - ValueNode: compNode.Content[0], - } + lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ + Value: sp, + ValueNode: compNode.Content[0], + } - schemaProxy := NewSchemaProxy(&lowproxy) - compiled := schemaProxy.Schema() + schemaProxy := NewSchemaProxy(&lowproxy) + compiled := schemaProxy.Schema() - // now render it out, it should be identical. - schemaBytes, _ := compiled.Render() - assert.Len(t, schemaBytes, 91) + // now render it out, it should be identical. + schemaBytes, _ := compiled.Render() + assert.Len(t, schemaBytes, 91) } func TestNewSchemaProxy_RenderSchemaCheckAdditionalPropertiesSliceMap(t *testing.T) { - testSpec := `additionalProperties: + testSpec := `additionalProperties: - nice: cake - yummy: beer - hot: coffee` - var compNode yaml.Node - _ = yaml.Unmarshal([]byte(testSpec), &compNode) + var compNode yaml.Node + _ = yaml.Unmarshal([]byte(testSpec), &compNode) - sp := new(lowbase.SchemaProxy) - err := sp.Build(compNode.Content[0], nil) - assert.NoError(t, err) + sp := new(lowbase.SchemaProxy) + err := sp.Build(compNode.Content[0], nil) + assert.NoError(t, err) - lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ - Value: sp, - ValueNode: compNode.Content[0], - } + lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ + Value: sp, + ValueNode: compNode.Content[0], + } - schemaProxy := NewSchemaProxy(&lowproxy) - compiled := schemaProxy.Schema() + schemaProxy := NewSchemaProxy(&lowproxy) + compiled := schemaProxy.Schema() - // now render it out, it should be identical. - schemaBytes, _ := compiled.Render() - assert.Len(t, schemaBytes, 75) + // now render it out, it should be identical. + schemaBytes, _ := compiled.Render() + assert.Len(t, schemaBytes, 75) } func TestNewSchemaProxy_CheckDefaultBooleanFalse(t *testing.T) { - testSpec := `default: false` + testSpec := `default: false` - var compNode yaml.Node - _ = yaml.Unmarshal([]byte(testSpec), &compNode) + var compNode yaml.Node + _ = yaml.Unmarshal([]byte(testSpec), &compNode) - sp := new(lowbase.SchemaProxy) - err := sp.Build(compNode.Content[0], nil) - assert.NoError(t, err) + sp := new(lowbase.SchemaProxy) + err := sp.Build(compNode.Content[0], nil) + assert.NoError(t, err) - lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ - Value: sp, - ValueNode: compNode.Content[0], - } + lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ + Value: sp, + ValueNode: compNode.Content[0], + } - schemaProxy := NewSchemaProxy(&lowproxy) - compiled := schemaProxy.Schema() + schemaProxy := NewSchemaProxy(&lowproxy) + compiled := schemaProxy.Schema() - // now render it out, it should be identical. - schemaBytes, _ := compiled.Render() - assert.Equal(t, testSpec, strings.TrimSpace(string(schemaBytes))) + // now render it out, it should be identical. + schemaBytes, _ := compiled.Render() + assert.Equal(t, testSpec, strings.TrimSpace(string(schemaBytes))) } func TestNewSchemaProxy_RenderAdditionalPropertiesFalse(t *testing.T) { - testSpec := `additionalProperties: false` + testSpec := `additionalProperties: false` - var compNode yaml.Node - _ = yaml.Unmarshal([]byte(testSpec), &compNode) + var compNode yaml.Node + _ = yaml.Unmarshal([]byte(testSpec), &compNode) - sp := new(lowbase.SchemaProxy) - err := sp.Build(compNode.Content[0], nil) - assert.NoError(t, err) + sp := new(lowbase.SchemaProxy) + err := sp.Build(compNode.Content[0], nil) + assert.NoError(t, err) - lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ - Value: sp, - ValueNode: compNode.Content[0], - } + lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ + Value: sp, + ValueNode: compNode.Content[0], + } - schemaProxy := NewSchemaProxy(&lowproxy) - compiled := schemaProxy.Schema() + schemaProxy := NewSchemaProxy(&lowproxy) + compiled := schemaProxy.Schema() - // now render it out, it should be identical. - schemaBytes, _ := compiled.Render() - assert.Equal(t, testSpec, strings.TrimSpace(string(schemaBytes))) + // now render it out, it should be identical. + schemaBytes, _ := compiled.Render() + assert.Equal(t, testSpec, strings.TrimSpace(string(schemaBytes))) } func TestNewSchemaProxy_RenderMultiplePoly(t *testing.T) { - idxYaml := `openapi: 3.1.0 + idxYaml := `openapi: 3.1.0 components: schemas: balance_transaction: description: A balance transaction` - testSpec := `properties: + testSpec := `properties: bigBank: type: object properties: @@ -1109,32 +1109,32 @@ components: oneOf: - $ref: '#/components/schemas/balance_transaction'` - var compNode, idxNode yaml.Node - _ = yaml.Unmarshal([]byte(testSpec), &compNode) - _ = yaml.Unmarshal([]byte(idxYaml), &idxNode) + var compNode, idxNode yaml.Node + _ = yaml.Unmarshal([]byte(testSpec), &compNode) + _ = yaml.Unmarshal([]byte(idxYaml), &idxNode) - idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig()) + idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig()) - sp := new(lowbase.SchemaProxy) + sp := new(lowbase.SchemaProxy) - err := sp.Build(compNode.Content[0], idx) - assert.NoError(t, err) + err := sp.Build(compNode.Content[0], idx) + assert.NoError(t, err) - lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ - Value: sp, - ValueNode: idxNode.Content[0], - } + lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ + Value: sp, + ValueNode: idxNode.Content[0], + } - sch1 := SchemaProxy{schema: &lowproxy} - compiled := sch1.Schema() + sch1 := SchemaProxy{schema: &lowproxy} + compiled := sch1.Schema() - // now render it out, it should be identical. - schemaBytes, _ := compiled.Render() - assert.Equal(t, testSpec, strings.TrimSpace(string(schemaBytes))) + // now render it out, it should be identical. + schemaBytes, _ := compiled.Render() + assert.Equal(t, testSpec, strings.TrimSpace(string(schemaBytes))) } func TestNewSchemaProxy_RenderInline(t *testing.T) { - idxYaml := `openapi: 3.1.0 + idxYaml := `openapi: 3.1.0 components: schemas: balance_transaction: @@ -1149,7 +1149,7 @@ components: anyOf: - $ref: '#/components/schemas/balance_transaction'` - testSpec := `properties: + testSpec := `properties: bigBank: type: object properties: @@ -1161,26 +1161,48 @@ components: type: string - $ref: '#/components/schemas/balance_transaction'` - var compNode, idxNode yaml.Node - _ = yaml.Unmarshal([]byte(testSpec), &compNode) - _ = yaml.Unmarshal([]byte(idxYaml), &idxNode) + var compNode, idxNode yaml.Node + _ = yaml.Unmarshal([]byte(testSpec), &compNode) + _ = yaml.Unmarshal([]byte(idxYaml), &idxNode) - idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig()) + idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig()) - sp := new(lowbase.SchemaProxy) + sp := new(lowbase.SchemaProxy) - err := sp.Build(compNode.Content[0], idx) - assert.NoError(t, err) + err := sp.Build(compNode.Content[0], idx) + assert.NoError(t, err) - lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ - Value: sp, - ValueNode: idxNode.Content[0], - } + lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ + Value: sp, + ValueNode: idxNode.Content[0], + } - sch1 := SchemaProxy{schema: &lowproxy} - compiled := sch1.Schema() + sch1 := SchemaProxy{schema: &lowproxy} + compiled := sch1.Schema() - // now render it out, it should be identical. - schemaBytes, _ := compiled.RenderInline() - assert.Len(t, schemaBytes, 585) + // now render it out, it should be identical. + schemaBytes, _ := compiled.RenderInline() + assert.Len(t, schemaBytes, 585) +} + +func TestUnevaluatedPropertiesBoolean_True(t *testing.T) { + yml := ` +type: number +unevaluatedProperties: true +` + highSchema := getHighSchema(t, yml) + + value := true + assert.EqualValues(t, &value, highSchema.UnevaluatedProperties.B) +} + +func TestUnevaluatedPropertiesBoolean_False(t *testing.T) { + yml := ` +type: number +unevaluatedProperties: false +` + highSchema := getHighSchema(t, yml) + + value := false + assert.EqualValues(t, &value, highSchema.UnevaluatedProperties.B) } diff --git a/datamodel/high/base/security_requirement.go b/datamodel/high/base/security_requirement.go index b96cae2..b9c4783 100644 --- a/datamodel/high/base/security_requirement.go +++ b/datamodel/high/base/security_requirement.go @@ -17,7 +17,7 @@ import ( // security schemes declared in it which are all required (that is, there is a logical AND between the schemes). // // The name used for each property MUST correspond to a security scheme declared in the Security Definitions -// - https://swagger.io/specification/v2/#securityDefinitionsObject +// - https://swagger.io/specification/v2/#securityDefinitionsObject type SecurityRequirement struct { Requirements map[string][]string `json:"-" yaml:"-"` low *base.SecurityRequirement @@ -108,7 +108,7 @@ func (s *SecurityRequirement) MarshalYAML() (interface{}, error) { for t := range keys[k].val { reqs[t] = &req{val: keys[k].val[t], line: 9999 + t} if keys[k].lowVal != nil { - for _ = range keys[k].lowVal.Value[t].Value { + for range keys[k].lowVal.Value[t].Value { fh := keys[k].val[t] df := keys[k].lowVal.Value[t].Value if fh == df { diff --git a/datamodel/high/base/xml.go b/datamodel/high/base/xml.go index d6596d3..63d132c 100644 --- a/datamodel/high/base/xml.go +++ b/datamodel/high/base/xml.go @@ -4,9 +4,9 @@ package base import ( - "github.com/pb33f/libopenapi/datamodel/high" - low "github.com/pb33f/libopenapi/datamodel/low/base" - "gopkg.in/yaml.v3" + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/base" + "gopkg.in/yaml.v3" ) // XML represents a high-level representation of an XML object defined by all versions of OpenAPI and backed by @@ -16,49 +16,49 @@ import ( // // When using arrays, XML element names are not inferred (for singular/plural forms) and the name property SHOULD be // used to add that information. See examples for expected behavior. -// v2 - https://swagger.io/specification/v2/#xmlObject -// v3 - https://swagger.io/specification/#xml-object +// +// v2 - https://swagger.io/specification/v2/#xmlObject +// v3 - https://swagger.io/specification/#xml-object type XML struct { - Name string `json:"name,omitempty" yaml:"name,omitempty"` - Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` - Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"` - Attribute bool `json:"attribute,omitempty" yaml:"attribute,omitempty"` - Wrapped bool `json:"wrapped,omitempty" yaml:"wrapped,omitempty"` - Extensions map[string]any - low *low.XML + Name string `json:"name,omitempty" yaml:"name,omitempty"` + Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` + Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"` + Attribute bool `json:"attribute,omitempty" yaml:"attribute,omitempty"` + Wrapped bool `json:"wrapped,omitempty" yaml:"wrapped,omitempty"` + Extensions map[string]any + low *low.XML } // NewXML creates a new high-level XML instance from a low-level one. func NewXML(xml *low.XML) *XML { - x := new(XML) - x.low = xml - x.Name = xml.Name.Value - x.Namespace = xml.Namespace.Value - x.Prefix = xml.Prefix.Value - x.Attribute = xml.Attribute.Value - x.Wrapped = xml.Wrapped.Value - x.Extensions = high.ExtractExtensions(xml.Extensions) - return x + x := new(XML) + x.low = xml + x.Name = xml.Name.Value + x.Namespace = xml.Namespace.Value + x.Prefix = xml.Prefix.Value + x.Attribute = xml.Attribute.Value + x.Wrapped = xml.Wrapped.Value + x.Extensions = high.ExtractExtensions(xml.Extensions) + return x } // GoLow returns the low level XML reference used to create the high level one. func (x *XML) GoLow() *low.XML { - return x.low + return x.low } // GoLowUntyped will return the low-level XML instance that was used to create the high-level one, with no type func (x *XML) GoLowUntyped() any { - return x.low + return x.low } // Render will return a YAML representation of the XML object as a byte slice. func (x *XML) Render() ([]byte, error) { - return yaml.Marshal(x) + return yaml.Marshal(x) } // MarshalYAML will create a ready to render YAML representation of the XML object. func (x *XML) MarshalYAML() (interface{}, error) { - nb := high.NewNodeBuilder(x, x.low) - return nb.Render(), nil + nb := high.NewNodeBuilder(x, x.low) + return nb.Render(), nil } - diff --git a/datamodel/high/base/xml_test.go b/datamodel/high/base/xml_test.go index 9c2c7e7..c99b2c4 100644 --- a/datamodel/high/base/xml_test.go +++ b/datamodel/high/base/xml_test.go @@ -71,4 +71,4 @@ wrapped: true` highXMLBytes, _ = highXML.Render() assert.NotEqual(t, yml, strings.TrimSpace(string(highXMLBytes))) -} \ No newline at end of file +} diff --git a/datamodel/high/node_builder.go b/datamodel/high/node_builder.go index 19eb1b3..5aa1537 100644 --- a/datamodel/high/node_builder.go +++ b/datamodel/high/node_builder.go @@ -4,34 +4,34 @@ package high import ( - "fmt" - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/utils" - "gopkg.in/yaml.v3" - "reflect" - "sort" - "strconv" - "strings" - "unicode" + "fmt" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/utils" + "gopkg.in/yaml.v3" + "reflect" + "sort" + "strconv" + "strings" + "unicode" ) // NodeEntry represents a single node used by NodeBuilder. type NodeEntry struct { - Tag string - Key string - Value any - StringValue string - Line int - RenderZero bool + Tag string + Key string + Value any + StringValue string + Line int + RenderZero bool } // NodeBuilder is a structure used by libopenapi high-level objects, to render themselves back to YAML. // this allows high-level objects to be 'mutable' because all changes will be rendered out. type NodeBuilder struct { - Nodes []*NodeEntry - High any - Low any - Resolve bool // If set to true, all references will be rendered inline + Nodes []*NodeEntry + High any + Low any + Resolve bool // If set to true, all references will be rendered inline } const renderZero = "renderZero" @@ -41,615 +41,615 @@ const renderZero = "renderZero" // // Using reflection, a map of every field in the high level object is created, ready to be rendered. func NewNodeBuilder(high any, low any) *NodeBuilder { - // create a new node builder - nb := new(NodeBuilder) - nb.High = high - if low != nil { - nb.Low = low - } + // create a new node builder + nb := new(NodeBuilder) + nb.High = high + if low != nil { + nb.Low = low + } - // extract fields from the high level object and add them into our node builder. - // this will allow us to extract the line numbers from the low level object as well. - v := reflect.ValueOf(high).Elem() - num := v.NumField() - for i := 0; i < num; i++ { - nb.add(v.Type().Field(i).Name, i) - } - return nb + // extract fields from the high level object and add them into our node builder. + // this will allow us to extract the line numbers from the low level object as well. + v := reflect.ValueOf(high).Elem() + num := v.NumField() + for i := 0; i < num; i++ { + nb.add(v.Type().Field(i).Name, i) + } + return nb } func (n *NodeBuilder) add(key string, i int) { - // only operate on exported fields. - if unicode.IsLower(rune(key[0])) { - return - } + // only operate on exported fields. + if unicode.IsLower(rune(key[0])) { + return + } - // if the key is 'Extensions' then we need to extract the keys from the map - // and add them to the node builder. - if key == "Extensions" { - extensions := reflect.ValueOf(n.High).Elem().FieldByName(key) - for b, e := range extensions.MapKeys() { - v := extensions.MapIndex(e) + // if the key is 'Extensions' then we need to extract the keys from the map + // and add them to the node builder. + if key == "Extensions" { + extensions := reflect.ValueOf(n.High).Elem().FieldByName(key) + for b, e := range extensions.MapKeys() { + v := extensions.MapIndex(e) - extKey := e.String() - extValue := v.Interface() - nodeEntry := &NodeEntry{Tag: extKey, Key: extKey, Value: extValue, Line: 9999 + b} + extKey := e.String() + extValue := v.Interface() + nodeEntry := &NodeEntry{Tag: extKey, Key: extKey, Value: extValue, Line: 9999 + b} - if n.Low != nil && !reflect.ValueOf(n.Low).IsZero() { - fieldValue := reflect.ValueOf(n.Low).Elem().FieldByName("Extensions") - f := fieldValue.Interface() - value := reflect.ValueOf(f) - switch value.Kind() { - case reflect.Map: - if j, ok := n.Low.(low.HasExtensionsUntyped); ok { - originalExtensions := j.GetExtensions() - u := 0 - for k := range originalExtensions { - if k.Value == extKey { - if originalExtensions[k].ValueNode.Line != 0 { - nodeEntry.Line = originalExtensions[k].ValueNode.Line + u - } else { - nodeEntry.Line = 999999 + b + u - } - } - u++ - } - } - } - } - n.Nodes = append(n.Nodes, nodeEntry) - } - // done, extensions are handled separately. - return - } + if n.Low != nil && !reflect.ValueOf(n.Low).IsZero() { + fieldValue := reflect.ValueOf(n.Low).Elem().FieldByName("Extensions") + f := fieldValue.Interface() + value := reflect.ValueOf(f) + switch value.Kind() { + case reflect.Map: + if j, ok := n.Low.(low.HasExtensionsUntyped); ok { + originalExtensions := j.GetExtensions() + u := 0 + for k := range originalExtensions { + if k.Value == extKey { + if originalExtensions[k].ValueNode.Line != 0 { + nodeEntry.Line = originalExtensions[k].ValueNode.Line + u + } else { + nodeEntry.Line = 999999 + b + u + } + } + u++ + } + } + } + } + n.Nodes = append(n.Nodes, nodeEntry) + } + // done, extensions are handled separately. + return + } - // find the field with the tag supplied. - field, _ := reflect.TypeOf(n.High).Elem().FieldByName(key) - tag := string(field.Tag.Get("yaml")) - tagName := strings.Split(tag, ",")[0] + // find the field with the tag supplied. + field, _ := reflect.TypeOf(n.High).Elem().FieldByName(key) + tag := string(field.Tag.Get("yaml")) + tagName := strings.Split(tag, ",")[0] - if tag == "-" { - return - } + if tag == "-" { + return + } - renderZeroVal := strings.Split(tag, ",")[1] + renderZeroVal := strings.Split(tag, ",")[1] - // extract the value of the field - fieldValue := reflect.ValueOf(n.High).Elem().FieldByName(key) - f := fieldValue.Interface() - value := reflect.ValueOf(f) + // extract the value of the field + fieldValue := reflect.ValueOf(n.High).Elem().FieldByName(key) + f := fieldValue.Interface() + value := reflect.ValueOf(f) - if renderZeroVal != renderZero && (f == nil || value.IsZero()) { - return - } + if renderZeroVal != renderZero && (f == nil || value.IsZero()) { + return + } - // create a new node entry - nodeEntry := &NodeEntry{Tag: tagName, Key: key} - if renderZeroVal == renderZero { - nodeEntry.RenderZero = true - } + // create a new node entry + nodeEntry := &NodeEntry{Tag: tagName, Key: key} + if renderZeroVal == renderZero { + nodeEntry.RenderZero = true + } - switch value.Kind() { - case reflect.Float64, reflect.Float32: - nodeEntry.Value = value.Float() - x := float64(int(value.Float()*100)) / 100 // trim this down - nodeEntry.StringValue = strconv.FormatFloat(x, 'f', -1, 64) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - nodeEntry.Value = value.Int() - nodeEntry.StringValue = value.String() - case reflect.String: - nodeEntry.Value = value.String() - case reflect.Bool: - nodeEntry.Value = value.Bool() - case reflect.Slice: - if tagName == "type" { - if value.Len() == 1 { - nodeEntry.Value = value.Index(0).String() - } else { - nodeEntry.Value = f - } - } else { - if (renderZeroVal == renderZero) || (!value.IsNil() && !value.IsZero()) { - nodeEntry.Value = f - } - } - case reflect.Ptr: - if !value.IsNil() { - nodeEntry.Value = f - } - case reflect.Map: - if !value.IsNil() && value.Len() > 0 { - nodeEntry.Value = f - } - default: - nodeEntry.Value = f - } + switch value.Kind() { + case reflect.Float64, reflect.Float32: + nodeEntry.Value = value.Float() + x := float64(int(value.Float()*100)) / 100 // trim this down + nodeEntry.StringValue = strconv.FormatFloat(x, 'f', -1, 64) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + nodeEntry.Value = value.Int() + nodeEntry.StringValue = value.String() + case reflect.String: + nodeEntry.Value = value.String() + case reflect.Bool: + nodeEntry.Value = value.Bool() + case reflect.Slice: + if tagName == "type" { + if value.Len() == 1 { + nodeEntry.Value = value.Index(0).String() + } else { + nodeEntry.Value = f + } + } else { + if (renderZeroVal == renderZero) || (!value.IsNil() && !value.IsZero()) { + nodeEntry.Value = f + } + } + case reflect.Ptr: + if !value.IsNil() { + nodeEntry.Value = f + } + case reflect.Map: + if !value.IsNil() && value.Len() > 0 { + nodeEntry.Value = f + } + default: + nodeEntry.Value = f + } - // if there is no low level object, then we cannot extract line numbers, - // so skip and default to 0, which means a new entry to the spec. - // this will place new content and the top of the rendered object. - if n.Low != nil && !reflect.ValueOf(n.Low).IsZero() { - lowFieldValue := reflect.ValueOf(n.Low).Elem().FieldByName(key) - fLow := lowFieldValue.Interface() - value = reflect.ValueOf(fLow) - switch value.Kind() { + // if there is no low level object, then we cannot extract line numbers, + // so skip and default to 0, which means a new entry to the spec. + // this will place new content and the top of the rendered object. + if n.Low != nil && !reflect.ValueOf(n.Low).IsZero() { + lowFieldValue := reflect.ValueOf(n.Low).Elem().FieldByName(key) + fLow := lowFieldValue.Interface() + value = reflect.ValueOf(fLow) + switch value.Kind() { - case reflect.Slice: - l := value.Len() - lines := make([]int, l) - for g := 0; g < l; g++ { - qw := value.Index(g).Interface() - if we, wok := qw.(low.HasKeyNode); wok { - lines[g] = we.GetKeyNode().Line - } - } - sort.Ints(lines) - nodeEntry.Line = lines[0] // pick the lowest line number so this key is sorted in order. - break - case reflect.Map: + case reflect.Slice: + l := value.Len() + lines := make([]int, l) + for g := 0; g < l; g++ { + qw := value.Index(g).Interface() + if we, wok := qw.(low.HasKeyNode); wok { + lines[g] = we.GetKeyNode().Line + } + } + sort.Ints(lines) + nodeEntry.Line = lines[0] // pick the lowest line number so this key is sorted in order. + break + case reflect.Map: - l := value.Len() - lines := make([]int, l) - for q, ky := range value.MapKeys() { - if we, wok := ky.Interface().(low.HasKeyNode); wok { - lines[q] = we.GetKeyNode().Line - } - } - sort.Ints(lines) - nodeEntry.Line = lines[0] // pick the lowest line number, sort in order + l := value.Len() + lines := make([]int, l) + for q, ky := range value.MapKeys() { + if we, wok := ky.Interface().(low.HasKeyNode); wok { + lines[q] = we.GetKeyNode().Line + } + } + sort.Ints(lines) + nodeEntry.Line = lines[0] // pick the lowest line number, sort in order - case reflect.Struct: - y := value.Interface() - nodeEntry.Line = 9999 + i - if nb, ok := y.(low.HasValueNodeUntyped); ok { - if nb.IsReference() { - if jk, kj := y.(low.HasKeyNode); kj { - nodeEntry.Line = jk.GetKeyNode().Line - break - } - } - if nb.GetValueNode() != nil { - nodeEntry.Line = nb.GetValueNode().Line - } - } - default: - // everything else, weight it to the bottom of the rendered object. - // this is things that we have no way of knowing where they should be placed. - nodeEntry.Line = 9999 + i - } - } - if nodeEntry.Value != nil { - n.Nodes = append(n.Nodes, nodeEntry) - } + case reflect.Struct: + y := value.Interface() + nodeEntry.Line = 9999 + i + if nb, ok := y.(low.HasValueNodeUntyped); ok { + if nb.IsReference() { + if jk, kj := y.(low.HasKeyNode); kj { + nodeEntry.Line = jk.GetKeyNode().Line + break + } + } + if nb.GetValueNode() != nil { + nodeEntry.Line = nb.GetValueNode().Line + } + } + default: + // everything else, weight it to the bottom of the rendered object. + // this is things that we have no way of knowing where they should be placed. + nodeEntry.Line = 9999 + i + } + } + if nodeEntry.Value != nil { + n.Nodes = append(n.Nodes, nodeEntry) + } } func (n *NodeBuilder) renderReference() []*yaml.Node { - fg := n.Low.(low.IsReferenced) - nodes := make([]*yaml.Node, 2) - nodes[0] = utils.CreateStringNode("$ref") - nodes[1] = utils.CreateStringNode(fg.GetReference()) - return nodes + fg := n.Low.(low.IsReferenced) + nodes := make([]*yaml.Node, 2) + nodes[0] = utils.CreateStringNode("$ref") + nodes[1] = utils.CreateStringNode(fg.GetReference()) + return nodes } // Render will render the NodeBuilder back to a YAML node, iterating over every NodeEntry defined func (n *NodeBuilder) Render() *yaml.Node { - if len(n.Nodes) == 0 { - return utils.CreateEmptyMapNode() - } + if len(n.Nodes) == 0 { + return utils.CreateEmptyMapNode() + } - // order nodes by line number, retain original order - m := utils.CreateEmptyMapNode() - if fg, ok := n.Low.(low.IsReferenced); ok { - g := reflect.ValueOf(fg) - if !g.IsNil() { - if fg.IsReference() && !n.Resolve { - m.Content = append(m.Content, n.renderReference()...) - return m - } - } - } + // order nodes by line number, retain original order + m := utils.CreateEmptyMapNode() + if fg, ok := n.Low.(low.IsReferenced); ok { + g := reflect.ValueOf(fg) + if !g.IsNil() { + if fg.IsReference() && !n.Resolve { + m.Content = append(m.Content, n.renderReference()...) + return m + } + } + } - sort.Slice(n.Nodes, func(i, j int) bool { - if n.Nodes[i].Line != n.Nodes[j].Line { - return n.Nodes[i].Line < n.Nodes[j].Line - } - return false - }) + sort.Slice(n.Nodes, func(i, j int) bool { + if n.Nodes[i].Line != n.Nodes[j].Line { + return n.Nodes[i].Line < n.Nodes[j].Line + } + return false + }) - for i := range n.Nodes { - node := n.Nodes[i] - n.AddYAMLNode(m, node) - } - return m + for i := range n.Nodes { + node := n.Nodes[i] + n.AddYAMLNode(m, node) + } + return m } // AddYAMLNode will add a new *yaml.Node to the parent node, using the tag, key and value provided. // If the value is nil, then the node will not be added. This method is recursive, so it will dig down // into any non-scalar types. func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *NodeEntry) *yaml.Node { - if entry.Value == nil { - return parent - } + if entry.Value == nil { + return parent + } - // check the type - t := reflect.TypeOf(entry.Value) - var l *yaml.Node - if entry.Tag != "" { - l = utils.CreateStringNode(entry.Tag) - } + // check the type + t := reflect.TypeOf(entry.Value) + var l *yaml.Node + if entry.Tag != "" { + l = utils.CreateStringNode(entry.Tag) + } - value := entry.Value - line := entry.Line - key := entry.Key + value := entry.Value + line := entry.Line + key := entry.Key - var valueNode *yaml.Node - switch t.Kind() { + var valueNode *yaml.Node + switch t.Kind() { - case reflect.String: - val := value.(string) - valueNode = utils.CreateStringNode(val) - valueNode.Line = line - break + case reflect.String: + val := value.(string) + valueNode = utils.CreateStringNode(val) + valueNode.Line = line + break - case reflect.Bool: - val := value.(bool) - if !val { - valueNode = utils.CreateBoolNode("false") - } else { - valueNode = utils.CreateBoolNode("true") - } - valueNode.Line = line - break + case reflect.Bool: + val := value.(bool) + if !val { + valueNode = utils.CreateBoolNode("false") + } else { + valueNode = utils.CreateBoolNode("true") + } + valueNode.Line = line + break - case reflect.Int: - val := strconv.Itoa(value.(int)) - valueNode = utils.CreateIntNode(val) - valueNode.Line = line - break + case reflect.Int: + val := strconv.Itoa(value.(int)) + valueNode = utils.CreateIntNode(val) + valueNode.Line = line + break - case reflect.Int64: - val := strconv.FormatInt(value.(int64), 10) - valueNode = utils.CreateIntNode(val) - valueNode.Line = line - break + case reflect.Int64: + val := strconv.FormatInt(value.(int64), 10) + valueNode = utils.CreateIntNode(val) + valueNode.Line = line + break - case reflect.Float32: - val := strconv.FormatFloat(float64(value.(float32)), 'f', 2, 64) - valueNode = utils.CreateFloatNode(val) - valueNode.Line = line - break + case reflect.Float32: + val := strconv.FormatFloat(float64(value.(float32)), 'f', 2, 64) + valueNode = utils.CreateFloatNode(val) + valueNode.Line = line + break - case reflect.Float64: - precision := -1 - if entry.StringValue != "" && strings.Contains(entry.StringValue, ".") { - precision = len(strings.Split(fmt.Sprint(entry.StringValue), ".")[1]) - } - val := strconv.FormatFloat(value.(float64), 'f', precision, 64) - valueNode = utils.CreateFloatNode(val) - valueNode.Line = line - break + case reflect.Float64: + precision := -1 + if entry.StringValue != "" && strings.Contains(entry.StringValue, ".") { + precision = len(strings.Split(fmt.Sprint(entry.StringValue), ".")[1]) + } + val := strconv.FormatFloat(value.(float64), 'f', precision, 64) + valueNode = utils.CreateFloatNode(val) + valueNode.Line = line + break - case reflect.Map: + case reflect.Map: - // the keys will be rendered randomly, if we don't find out the original line - // number of the tag. + // the keys will be rendered randomly, if we don't find out the original line + // number of the tag. - var orderedCollection []*NodeEntry - m := reflect.ValueOf(value) - for g, k := range m.MapKeys() { - var x string - // extract key - yu := k.Interface() - if o, ok := yu.(low.HasKeyNode); ok { - x = o.GetKeyNode().Value - } else { - x = k.String() - } + var orderedCollection []*NodeEntry + m := reflect.ValueOf(value) + for g, k := range m.MapKeys() { + var x string + // extract key + yu := k.Interface() + if o, ok := yu.(low.HasKeyNode); ok { + x = o.GetKeyNode().Value + } else { + x = k.String() + } - // go low and pull out the line number. - lowProps := reflect.ValueOf(n.Low) - if n.Low != nil && !lowProps.IsZero() && !lowProps.IsNil() { - gu := lowProps.Elem() - gi := gu.FieldByName(key) - jl := reflect.ValueOf(gi) - if !jl.IsZero() && gi.Interface() != nil { - gh := gi.Interface() - // extract low level key line number - if pr, ok := gh.(low.HasValueUnTyped); ok { - fg := reflect.ValueOf(pr.GetValueUntyped()) - found := false - found, orderedCollection = n.extractLowMapKeys(fg, x, found, orderedCollection, m, k) - if found != true { - // this is something new, add it. - orderedCollection = append(orderedCollection, &NodeEntry{ - Tag: x, - Key: x, - Line: 9999 + g, - Value: m.MapIndex(k).Interface(), - }) - } - } else { - // this is a map, but it may be wrapped still. - bj := reflect.ValueOf(gh) - orderedCollection = n.extractLowMapKeysWrapped(bj, x, orderedCollection, g) - } - } else { - // this is a map, without any low level details available (probably an extension map). - orderedCollection = append(orderedCollection, &NodeEntry{ - Tag: x, - Key: x, - Line: 9999 + g, - Value: m.MapIndex(k).Interface(), - }) - } - } else { - // this is a map, without any low level details available (probably an extension map). - orderedCollection = append(orderedCollection, &NodeEntry{ - Tag: x, - Key: x, - Line: 9999 + g, - Value: m.MapIndex(k).Interface(), - }) - } - } + // go low and pull out the line number. + lowProps := reflect.ValueOf(n.Low) + if n.Low != nil && !lowProps.IsZero() && !lowProps.IsNil() { + gu := lowProps.Elem() + gi := gu.FieldByName(key) + jl := reflect.ValueOf(gi) + if !jl.IsZero() && gi.Interface() != nil { + gh := gi.Interface() + // extract low level key line number + if pr, ok := gh.(low.HasValueUnTyped); ok { + fg := reflect.ValueOf(pr.GetValueUntyped()) + found := false + found, orderedCollection = n.extractLowMapKeys(fg, x, found, orderedCollection, m, k) + if found != true { + // this is something new, add it. + orderedCollection = append(orderedCollection, &NodeEntry{ + Tag: x, + Key: x, + Line: 9999 + g, + Value: m.MapIndex(k).Interface(), + }) + } + } else { + // this is a map, but it may be wrapped still. + bj := reflect.ValueOf(gh) + orderedCollection = n.extractLowMapKeysWrapped(bj, x, orderedCollection, g) + } + } else { + // this is a map, without any low level details available (probably an extension map). + orderedCollection = append(orderedCollection, &NodeEntry{ + Tag: x, + Key: x, + Line: 9999 + g, + Value: m.MapIndex(k).Interface(), + }) + } + } else { + // this is a map, without any low level details available (probably an extension map). + orderedCollection = append(orderedCollection, &NodeEntry{ + Tag: x, + Key: x, + Line: 9999 + g, + Value: m.MapIndex(k).Interface(), + }) + } + } - // sort the slice by line number to ensure everything is rendered in order. - sort.Slice(orderedCollection, func(i, j int) bool { - return orderedCollection[i].Line < orderedCollection[j].Line - }) + // sort the slice by line number to ensure everything is rendered in order. + sort.Slice(orderedCollection, func(i, j int) bool { + return orderedCollection[i].Line < orderedCollection[j].Line + }) - // create an empty map. - p := utils.CreateEmptyMapNode() + // create an empty map. + p := utils.CreateEmptyMapNode() - // build out each map node in original order. - for _, cv := range orderedCollection { - n.AddYAMLNode(p, cv) - } - if len(p.Content) > 0 { - valueNode = p - } + // build out each map node in original order. + for _, cv := range orderedCollection { + n.AddYAMLNode(p, cv) + } + if len(p.Content) > 0 { + valueNode = p + } - case reflect.Slice: + case reflect.Slice: - var rawNode yaml.Node - m := reflect.ValueOf(value) - sl := utils.CreateEmptySequenceNode() - skip := false - for i := 0; i < m.Len(); i++ { - sqi := m.Index(i).Interface() - // check if this is a reference. - if glu, ok := sqi.(GoesLowUntyped); ok { - if glu != nil { - ut := glu.GoLowUntyped() - if !reflect.ValueOf(ut).IsNil() { - r := ut.(low.IsReferenced) - if ut != nil && r.GetReference() != "" && - ut.(low.IsReferenced).IsReference() { - if !n.Resolve { - refNode := utils.CreateRefNode(glu.GoLowUntyped().(low.IsReferenced).GetReference()) - sl.Content = append(sl.Content, refNode) - skip = true - } else { - skip = false - } - } else { - skip = false - } - } - } - } - if !skip { - if er, ko := sqi.(Renderable); ko { - var rend interface{} - if !n.Resolve { - rend, _ = er.(Renderable).MarshalYAML() - } else { - // try and render inline, if we can, otherwise treat as normal. - if _, ko := er.(RenderableInline); ko { - rend, _ = er.(RenderableInline).MarshalYAMLInline() - } else { - rend, _ = er.(Renderable).MarshalYAML() - } - } - // check if this is a pointer or not. - if _, ok := rend.(*yaml.Node); ok { - sl.Content = append(sl.Content, rend.(*yaml.Node)) - } - if _, ok := rend.(yaml.Node); ok { - k := rend.(yaml.Node) - sl.Content = append(sl.Content, &k) - } - } - } - } + var rawNode yaml.Node + m := reflect.ValueOf(value) + sl := utils.CreateEmptySequenceNode() + skip := false + for i := 0; i < m.Len(); i++ { + sqi := m.Index(i).Interface() + // check if this is a reference. + if glu, ok := sqi.(GoesLowUntyped); ok { + if glu != nil { + ut := glu.GoLowUntyped() + if !reflect.ValueOf(ut).IsNil() { + r := ut.(low.IsReferenced) + if ut != nil && r.GetReference() != "" && + ut.(low.IsReferenced).IsReference() { + if !n.Resolve { + refNode := utils.CreateRefNode(glu.GoLowUntyped().(low.IsReferenced).GetReference()) + sl.Content = append(sl.Content, refNode) + skip = true + } else { + skip = false + } + } else { + skip = false + } + } + } + } + if !skip { + if er, ko := sqi.(Renderable); ko { + var rend interface{} + if !n.Resolve { + rend, _ = er.(Renderable).MarshalYAML() + } else { + // try and render inline, if we can, otherwise treat as normal. + if _, ko := er.(RenderableInline); ko { + rend, _ = er.(RenderableInline).MarshalYAMLInline() + } else { + rend, _ = er.(Renderable).MarshalYAML() + } + } + // check if this is a pointer or not. + if _, ok := rend.(*yaml.Node); ok { + sl.Content = append(sl.Content, rend.(*yaml.Node)) + } + if _, ok := rend.(yaml.Node); ok { + k := rend.(yaml.Node) + sl.Content = append(sl.Content, &k) + } + } + } + } - if len(sl.Content) > 0 { - valueNode = sl - break - } - if skip { - break - } + if len(sl.Content) > 0 { + valueNode = sl + break + } + if skip { + break + } - err := rawNode.Encode(value) - if err != nil { - return parent - } else { - valueNode = &rawNode - } + err := rawNode.Encode(value) + if err != nil { + return parent + } else { + valueNode = &rawNode + } - case reflect.Struct: - if r, ok := value.(low.ValueReference[any]); ok { - valueNode = r.GetValueNode() - break - } - if r, ok := value.(low.ValueReference[string]); ok { - valueNode = r.GetValueNode() - break - } - if r, ok := value.(low.NodeReference[string]); ok { - valueNode = r.GetValueNode() - break - } - return parent + case reflect.Struct: + if r, ok := value.(low.ValueReference[any]); ok { + valueNode = r.GetValueNode() + break + } + if r, ok := value.(low.ValueReference[string]); ok { + valueNode = r.GetValueNode() + break + } + if r, ok := value.(low.NodeReference[string]); ok { + valueNode = r.GetValueNode() + break + } + return parent - case reflect.Ptr: - if r, ok := value.(Renderable); ok { - if gl, lg := value.(GoesLowUntyped); lg { - if gl.GoLowUntyped() != nil { - ut := reflect.ValueOf(gl.GoLowUntyped()) - if !ut.IsNil() { - if gl.GoLowUntyped().(low.IsReferenced).IsReference() { - if !n.Resolve { - // TODO: use renderReference here. - rvn := utils.CreateEmptyMapNode() - rvn.Content = append(rvn.Content, utils.CreateStringNode("$ref")) - rvn.Content = append(rvn.Content, utils.CreateStringNode(gl.GoLowUntyped().(low.IsReferenced).GetReference())) - valueNode = rvn - break - } - } - } - } - } - var rawRender interface{} - if !n.Resolve { - rawRender, _ = r.MarshalYAML() - } else { - // try an inline render if we can, otherwise there is no option but to default to the - // full render. - if _, ko := r.(RenderableInline); ko { - rawRender, _ = r.(RenderableInline).MarshalYAMLInline() - } else { - rawRender, _ = r.MarshalYAML() - } - } - if rawRender != nil { - if _, ko := rawRender.(*yaml.Node); ko { - valueNode = rawRender.(*yaml.Node) - } - if _, ko := rawRender.(yaml.Node); ko { - d := rawRender.(yaml.Node) - valueNode = &d - } - } - } else { + case reflect.Ptr: + if r, ok := value.(Renderable); ok { + if gl, lg := value.(GoesLowUntyped); lg { + if gl.GoLowUntyped() != nil { + ut := reflect.ValueOf(gl.GoLowUntyped()) + if !ut.IsNil() { + if gl.GoLowUntyped().(low.IsReferenced).IsReference() { + if !n.Resolve { + // TODO: use renderReference here. + rvn := utils.CreateEmptyMapNode() + rvn.Content = append(rvn.Content, utils.CreateStringNode("$ref")) + rvn.Content = append(rvn.Content, utils.CreateStringNode(gl.GoLowUntyped().(low.IsReferenced).GetReference())) + valueNode = rvn + break + } + } + } + } + } + var rawRender interface{} + if !n.Resolve { + rawRender, _ = r.MarshalYAML() + } else { + // try an inline render if we can, otherwise there is no option but to default to the + // full render. + if _, ko := r.(RenderableInline); ko { + rawRender, _ = r.(RenderableInline).MarshalYAMLInline() + } else { + rawRender, _ = r.MarshalYAML() + } + } + if rawRender != nil { + if _, ko := rawRender.(*yaml.Node); ko { + valueNode = rawRender.(*yaml.Node) + } + if _, ko := rawRender.(yaml.Node); ko { + d := rawRender.(yaml.Node) + valueNode = &d + } + } + } else { - encodeSkip := false - // check if the value is a bool, int or float - if b, bok := value.(*bool); bok { - encodeSkip = true - if *b { - valueNode = utils.CreateBoolNode("true") - valueNode.Line = line - } else { - if entry.RenderZero { - valueNode = utils.CreateBoolNode("false") - valueNode.Line = line - } - } - } - if b, bok := value.(*int64); bok { - encodeSkip = true - if *b > 0 { - valueNode = utils.CreateIntNode(strconv.Itoa(int(*b))) - valueNode.Line = line - } - } - if b, bok := value.(*float64); bok { - encodeSkip = true - if *b > 0 { - valueNode = utils.CreateFloatNode(strconv.FormatFloat(*b, 'f', -1, 64)) - valueNode.Line = line - } - } - if !encodeSkip { - var rawNode yaml.Node - err := rawNode.Encode(value) - if err != nil { - return parent - } else { - valueNode = &rawNode - valueNode.Line = line - } - } - } + encodeSkip := false + // check if the value is a bool, int or float + if b, bok := value.(*bool); bok { + encodeSkip = true + if *b { + valueNode = utils.CreateBoolNode("true") + valueNode.Line = line + } else { + if entry.RenderZero { + valueNode = utils.CreateBoolNode("false") + valueNode.Line = line + } + } + } + if b, bok := value.(*int64); bok { + encodeSkip = true + if *b > 0 { + valueNode = utils.CreateIntNode(strconv.Itoa(int(*b))) + valueNode.Line = line + } + } + if b, bok := value.(*float64); bok { + encodeSkip = true + if *b > 0 { + valueNode = utils.CreateFloatNode(strconv.FormatFloat(*b, 'f', -1, 64)) + valueNode.Line = line + } + } + if !encodeSkip { + var rawNode yaml.Node + err := rawNode.Encode(value) + if err != nil { + return parent + } else { + valueNode = &rawNode + valueNode.Line = line + } + } + } - } - if valueNode == nil { - return parent - } - if l != nil { - parent.Content = append(parent.Content, l, valueNode) - } else { - parent.Content = valueNode.Content - } - return parent + } + if valueNode == nil { + return parent + } + if l != nil { + parent.Content = append(parent.Content, l, valueNode) + } else { + parent.Content = valueNode.Content + } + return parent } func (n *NodeBuilder) extractLowMapKeysWrapped(iu reflect.Value, x string, orderedCollection []*NodeEntry, g int) []*NodeEntry { - for _, ky := range iu.MapKeys() { - ty := ky.Interface() - if ere, eok := ty.(low.HasKeyNode); eok { - er := ere.GetKeyNode().Value - if er == x { - orderedCollection = append(orderedCollection, &NodeEntry{ - Tag: x, - Key: x, - Line: ky.Interface().(low.HasKeyNode).GetKeyNode().Line, - Value: iu.MapIndex(ky).Interface(), - }) - } - } else { - orderedCollection = append(orderedCollection, &NodeEntry{ - Tag: x, - Key: x, - Line: 9999 + g, - Value: iu.MapIndex(ky).Interface(), - }) - } - } - return orderedCollection + for _, ky := range iu.MapKeys() { + ty := ky.Interface() + if ere, eok := ty.(low.HasKeyNode); eok { + er := ere.GetKeyNode().Value + if er == x { + orderedCollection = append(orderedCollection, &NodeEntry{ + Tag: x, + Key: x, + Line: ky.Interface().(low.HasKeyNode).GetKeyNode().Line, + Value: iu.MapIndex(ky).Interface(), + }) + } + } else { + orderedCollection = append(orderedCollection, &NodeEntry{ + Tag: x, + Key: x, + Line: 9999 + g, + Value: iu.MapIndex(ky).Interface(), + }) + } + } + return orderedCollection } func (n *NodeBuilder) extractLowMapKeys(fg reflect.Value, x string, found bool, orderedCollection []*NodeEntry, m reflect.Value, k reflect.Value) (bool, []*NodeEntry) { - for j, ky := range fg.MapKeys() { - hu := ky.Interface() - if we, wok := hu.(low.HasKeyNode); wok { - er := we.GetKeyNode().Value - if er == x { - found = true - orderedCollection = append(orderedCollection, &NodeEntry{ - Tag: x, - Key: x, - Line: we.GetKeyNode().Line, - Value: m.MapIndex(k).Interface(), - }) - } - } else { - uu := ky.Interface() - if uu == x { - // this is a map, without any low level details available - found = true - orderedCollection = append(orderedCollection, &NodeEntry{ - Tag: uu.(string), - Key: uu.(string), - Line: 9999 + j, - Value: m.MapIndex(k).Interface(), - }) - } - } - } - return found, orderedCollection + for j, ky := range fg.MapKeys() { + hu := ky.Interface() + if we, wok := hu.(low.HasKeyNode); wok { + er := we.GetKeyNode().Value + if er == x { + found = true + orderedCollection = append(orderedCollection, &NodeEntry{ + Tag: x, + Key: x, + Line: we.GetKeyNode().Line, + Value: m.MapIndex(k).Interface(), + }) + } + } else { + uu := ky.Interface() + if uu == x { + // this is a map, without any low level details available + found = true + orderedCollection = append(orderedCollection, &NodeEntry{ + Tag: uu.(string), + Key: uu.(string), + Line: 9999 + j, + Value: m.MapIndex(k).Interface(), + }) + } + } + } + return found, orderedCollection } // Renderable is an interface that can be implemented by types that provide a custom MarshaYAML method. type Renderable interface { - MarshalYAML() (interface{}, error) + MarshalYAML() (interface{}, error) } // RenderableInline is an interface that can be implemented by types that provide a custom MarshaYAML method. type RenderableInline interface { - MarshalYAMLInline() (interface{}, error) + MarshalYAMLInline() (interface{}, error) } diff --git a/datamodel/high/node_builder_test.go b/datamodel/high/node_builder_test.go index 5c2e1d0..80589ca 100644 --- a/datamodel/high/node_builder_test.go +++ b/datamodel/high/node_builder_test.go @@ -4,214 +4,214 @@ package high import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/utils" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "reflect" - "strings" - "testing" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/utils" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "reflect" + "strings" + "testing" ) type key struct { - Name string `yaml:"name,omitempty"` - ref bool - refStr string - ln int - nilval bool - v any - kn *yaml.Node - low.IsReferenced `yaml:"-"` + Name string `yaml:"name,omitempty"` + ref bool + refStr string + ln int + nilval bool + v any + kn *yaml.Node + low.IsReferenced `yaml:"-"` } func (k key) GetKeyNode() *yaml.Node { - if k.kn != nil { - return k.kn - } - kn := utils.CreateStringNode("meddy") - kn.Line = k.ln - return kn + if k.kn != nil { + return k.kn + } + kn := utils.CreateStringNode("meddy") + kn.Line = k.ln + return kn } func (k key) GetValueUntyped() any { - return k.v + return k.v } func (k key) GetValueNode() *yaml.Node { - return k.GetValueNodeUntyped() + return k.GetValueNodeUntyped() } func (k key) GetValueNodeUntyped() *yaml.Node { - if k.nilval { - return nil - } - kn := utils.CreateStringNode("maddy") - kn.Line = k.ln - return kn + if k.nilval { + return nil + } + kn := utils.CreateStringNode("maddy") + kn.Line = k.ln + return kn } func (k key) IsReference() bool { - return k.ref + return k.ref } func (k key) GetReference() string { - return k.refStr + return k.refStr } func (k key) SetReference(ref string) { - k.refStr = ref + k.refStr = ref } func (k key) GoLowUntyped() any { - return &k + return &k } func (k key) MarshalYAML() (interface{}, error) { - return utils.CreateStringNode("pizza"), nil + return utils.CreateStringNode("pizza"), nil } func (k key) MarshalYAMLInline() (interface{}, error) { - return utils.CreateStringNode("pizza-inline!"), nil + return utils.CreateStringNode("pizza-inline!"), nil } type plug struct { - Name []string `yaml:"name,omitempty"` + Name []string `yaml:"name,omitempty"` } type test1 struct { - Thrig map[string]*plug `yaml:"thrig,omitempty"` - Thing string `yaml:"thing,omitempty"` - Thong int `yaml:"thong,omitempty"` - Thrum int64 `yaml:"thrum,omitempty"` - Thang float32 `yaml:"thang,omitempty"` - Thung float64 `yaml:"thung,omitempty"` - Thyme bool `yaml:"thyme,omitempty"` - Thurm any `yaml:"thurm,omitempty"` - Thugg *bool `yaml:"thugg,renderZero"` - Thurr *int64 `yaml:"thurr,omitempty"` - Thral *float64 `yaml:"thral,omitempty"` - Tharg []string `yaml:"tharg,omitempty"` - Type []string `yaml:"type,omitempty"` - Throg []*key `yaml:"throg,omitempty"` - Thrat []interface{} `yaml:"thrat,omitempty"` - Thrag []map[string][]string `yaml:"thrag,omitempty"` - Thrug map[string]string `yaml:"thrug,omitempty"` - Thoom []map[string]string `yaml:"thoom,omitempty"` - Thomp map[key]string `yaml:"thomp,omitempty"` - Thump key `yaml:"thump,omitempty"` - Thane key `yaml:"thane,omitempty"` - Thunk key `yaml:"thunk,omitempty"` - Thrim *key `yaml:"thrim,omitempty"` - Thril map[string]*key `yaml:"thril,omitempty"` - Extensions map[string]any `yaml:"-"` - ignoreMe string `yaml:"-"` - IgnoreMe string `yaml:"-"` + Thrig map[string]*plug `yaml:"thrig,omitempty"` + Thing string `yaml:"thing,omitempty"` + Thong int `yaml:"thong,omitempty"` + Thrum int64 `yaml:"thrum,omitempty"` + Thang float32 `yaml:"thang,omitempty"` + Thung float64 `yaml:"thung,omitempty"` + Thyme bool `yaml:"thyme,omitempty"` + Thurm any `yaml:"thurm,omitempty"` + Thugg *bool `yaml:"thugg,renderZero"` + Thurr *int64 `yaml:"thurr,omitempty"` + Thral *float64 `yaml:"thral,omitempty"` + Tharg []string `yaml:"tharg,omitempty"` + Type []string `yaml:"type,omitempty"` + Throg []*key `yaml:"throg,omitempty"` + Thrat []interface{} `yaml:"thrat,omitempty"` + Thrag []map[string][]string `yaml:"thrag,omitempty"` + Thrug map[string]string `yaml:"thrug,omitempty"` + Thoom []map[string]string `yaml:"thoom,omitempty"` + Thomp map[key]string `yaml:"thomp,omitempty"` + Thump key `yaml:"thump,omitempty"` + Thane key `yaml:"thane,omitempty"` + Thunk key `yaml:"thunk,omitempty"` + Thrim *key `yaml:"thrim,omitempty"` + Thril map[string]*key `yaml:"thril,omitempty"` + Extensions map[string]any `yaml:"-"` + ignoreMe string `yaml:"-"` + IgnoreMe string `yaml:"-"` } func (te *test1) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] { - g := make(map[low.KeyReference[string]]low.ValueReference[any]) + g := make(map[low.KeyReference[string]]low.ValueReference[any]) - for i := range te.Extensions { + for i := range te.Extensions { - f := reflect.TypeOf(te.Extensions[i]) - switch f.Kind() { - case reflect.String: - vn := utils.CreateStringNode(te.Extensions[i].(string)) - vn.Line = 999999 // weighted to the bottom. - g[low.KeyReference[string]{ - Value: i, - KeyNode: vn, - }] = low.ValueReference[any]{ - ValueNode: vn, - Value: te.Extensions[i].(string), - } - case reflect.Map: - kn := utils.CreateStringNode(i) - var vn yaml.Node - _ = vn.Decode(te.Extensions[i]) + f := reflect.TypeOf(te.Extensions[i]) + switch f.Kind() { + case reflect.String: + vn := utils.CreateStringNode(te.Extensions[i].(string)) + vn.Line = 999999 // weighted to the bottom. + g[low.KeyReference[string]{ + Value: i, + KeyNode: vn, + }] = low.ValueReference[any]{ + ValueNode: vn, + Value: te.Extensions[i].(string), + } + case reflect.Map: + kn := utils.CreateStringNode(i) + var vn yaml.Node + _ = vn.Decode(te.Extensions[i]) - kn.Line = 999999 // weighted to the bottom. - g[low.KeyReference[string]{ - Value: i, - KeyNode: kn, - }] = low.ValueReference[any]{ - ValueNode: &vn, - Value: te.Extensions[i], - } - } + kn.Line = 999999 // weighted to the bottom. + g[low.KeyReference[string]{ + Value: i, + KeyNode: kn, + }] = low.ValueReference[any]{ + ValueNode: &vn, + Value: te.Extensions[i], + } + } - } - return g + } + return g } func (te *test1) MarshalYAML() (interface{}, error) { - nb := NewNodeBuilder(te, te) - return nb.Render(), nil + nb := NewNodeBuilder(te, te) + return nb.Render(), nil } func (te *test1) GetKeyNode() *yaml.Node { - kn := utils.CreateStringNode("meddy") - kn.Line = 20 - return kn + kn := utils.CreateStringNode("meddy") + kn.Line = 20 + return kn } func (te *test1) GoesLowUntyped() any { - return te + return te } func TestNewNodeBuilder(t *testing.T) { - b := true - c := int64(12345) - d := 1234.1234 + b := true + c := int64(12345) + d := 1234.1234 - t1 := test1{ - ignoreMe: "I should never be seen!", - Thing: "ding", - Thong: 1, - Thurm: nil, - Thrum: 1234567, - Thang: 2.2, - Thung: 3.33333, - Thyme: true, - Thugg: &b, - Thurr: &c, - Thral: &d, - Tharg: []string{"chicken", "nuggets"}, - Type: []string{"chicken"}, - Thoom: []map[string]string{ - { - "maddy": "champion", - }, - { - "ember": "naughty", - }, - }, - Thomp: map[key]string{ - {ln: 1}: "princess", - }, - Thane: key{ // this is going to be ignored, needs to be a ValueReference - ln: 2, - ref: true, - refStr: "ripples", - }, - Thrug: map[string]string{ - "chicken": "nuggets", - }, - Thump: key{Name: "I will be ignored", ln: 3}, - Thunk: key{ln: 4, nilval: true}, - Extensions: map[string]any{ - "x-pizza": "time", - }, - } + t1 := test1{ + ignoreMe: "I should never be seen!", + Thing: "ding", + Thong: 1, + Thurm: nil, + Thrum: 1234567, + Thang: 2.2, + Thung: 3.33333, + Thyme: true, + Thugg: &b, + Thurr: &c, + Thral: &d, + Tharg: []string{"chicken", "nuggets"}, + Type: []string{"chicken"}, + Thoom: []map[string]string{ + { + "maddy": "champion", + }, + { + "ember": "naughty", + }, + }, + Thomp: map[key]string{ + {ln: 1}: "princess", + }, + Thane: key{ // this is going to be ignored, needs to be a ValueReference + ln: 2, + ref: true, + refStr: "ripples", + }, + Thrug: map[string]string{ + "chicken": "nuggets", + }, + Thump: key{Name: "I will be ignored", ln: 3}, + Thunk: key{ln: 4, nilval: true}, + Extensions: map[string]any{ + "x-pizza": "time", + }, + } - nb := NewNodeBuilder(&t1, nil) - node := nb.Render() + nb := NewNodeBuilder(&t1, nil) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `thing: ding + desired := `thing: ding thong: 1 thrum: 1234567 thang: 2.2 @@ -233,755 +233,755 @@ thomp: meddy: princess x-pizza: time` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_Type(t *testing.T) { - t1 := test1{ - Type: []string{"chicken", "soup"}, - } + t1 := test1{ + Type: []string{"chicken", "soup"}, + } - nb := NewNodeBuilder(&t1, &t1) - node := nb.Render() + nb := NewNodeBuilder(&t1, &t1) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `type: + desired := `type: - chicken - soup` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_IsReferenced(t *testing.T) { - t1 := key{ - Name: "cotton", - ref: true, - refStr: "#/my/heart", - ln: 2, - } + t1 := key{ + Name: "cotton", + ref: true, + refStr: "#/my/heart", + ln: 2, + } - nb := NewNodeBuilder(&t1, &t1) - node := nb.Render() + nb := NewNodeBuilder(&t1, &t1) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `$ref: '#/my/heart'` + desired := `$ref: '#/my/heart'` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_Extensions(t *testing.T) { - t1 := test1{ - Thing: "ding", - Extensions: map[string]any{ - "x-pizza": "time", - "x-money": "time", - }, - Thong: 1, - } + t1 := test1{ + Thing: "ding", + Extensions: map[string]any{ + "x-pizza": "time", + "x-money": "time", + }, + Thong: 1, + } - nb := NewNodeBuilder(&t1, &t1) - node := nb.Render() + nb := NewNodeBuilder(&t1, &t1) + node := nb.Render() - data, _ := yaml.Marshal(node) - assert.Len(t, data, 49) + data, _ := yaml.Marshal(node) + assert.Len(t, data, 49) } func TestNewNodeBuilder_LowValueNode(t *testing.T) { - t1 := test1{ - Thing: "ding", - Extensions: map[string]any{ - "x-pizza": "time", - "x-money": "time", - }, - Thong: 1, - } + t1 := test1{ + Thing: "ding", + Extensions: map[string]any{ + "x-pizza": "time", + "x-money": "time", + }, + Thong: 1, + } - nb := NewNodeBuilder(&t1, &t1) - node := nb.Render() + nb := NewNodeBuilder(&t1, &t1) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - assert.Len(t, data, 49) + assert.Len(t, data, 49) } func TestNewNodeBuilder_NoValue(t *testing.T) { - t1 := test1{ - Thing: "", - } + t1 := test1{ + Thing: "", + } - nodeEnty := NodeEntry{} - nb := NewNodeBuilder(&t1, &t1) - node := nb.AddYAMLNode(nil, &nodeEnty) - assert.Nil(t, node) + nodeEnty := NodeEntry{} + nb := NewNodeBuilder(&t1, &t1) + node := nb.AddYAMLNode(nil, &nodeEnty) + assert.Nil(t, node) } func TestNewNodeBuilder_EmptyString(t *testing.T) { - t1 := new(test1) - nodeEnty := NodeEntry{} - nb := NewNodeBuilder(t1, t1) - node := nb.AddYAMLNode(nil, &nodeEnty) - assert.Nil(t, node) + t1 := new(test1) + nodeEnty := NodeEntry{} + nb := NewNodeBuilder(t1, t1) + node := nb.AddYAMLNode(nil, &nodeEnty) + assert.Nil(t, node) } func TestNewNodeBuilder_EmptyStringRenderZero(t *testing.T) { - t1 := new(test1) - nodeEnty := NodeEntry{RenderZero: true, Value: ""} - nb := NewNodeBuilder(t1, t1) - m := utils.CreateEmptyMapNode() - node := nb.AddYAMLNode(m, &nodeEnty) - assert.NotNil(t, node) + t1 := new(test1) + nodeEnty := NodeEntry{RenderZero: true, Value: ""} + nb := NewNodeBuilder(t1, t1) + m := utils.CreateEmptyMapNode() + node := nb.AddYAMLNode(m, &nodeEnty) + assert.NotNil(t, node) } func TestNewNodeBuilder_Bool(t *testing.T) { - t1 := new(test1) - nb := NewNodeBuilder(t1, t1) - nodeEnty := NodeEntry{} - node := nb.AddYAMLNode(nil, &nodeEnty) - assert.Nil(t, node) + t1 := new(test1) + nb := NewNodeBuilder(t1, t1) + nodeEnty := NodeEntry{} + node := nb.AddYAMLNode(nil, &nodeEnty) + assert.Nil(t, node) } func TestNewNodeBuilder_BoolRenderZero(t *testing.T) { - type yui struct { - Thrit bool `yaml:"thrit,renderZero"` - } - t1 := new(yui) - t1.Thrit = false - nb := NewNodeBuilder(t1, t1) - r := nb.Render() - assert.NotNil(t, r) + type yui struct { + Thrit bool `yaml:"thrit,renderZero"` + } + t1 := new(yui) + t1.Thrit = false + nb := NewNodeBuilder(t1, t1) + r := nb.Render() + assert.NotNil(t, r) } func TestNewNodeBuilder_Int(t *testing.T) { - t1 := new(test1) - nb := NewNodeBuilder(t1, t1) - p := utils.CreateEmptyMapNode() - nodeEnty := NodeEntry{Tag: "p", Value: 12, Key: "p"} - node := nb.AddYAMLNode(p, &nodeEnty) - assert.NotNil(t, node) - assert.Len(t, node.Content, 2) - assert.Equal(t, "12", node.Content[1].Value) + t1 := new(test1) + nb := NewNodeBuilder(t1, t1) + p := utils.CreateEmptyMapNode() + nodeEnty := NodeEntry{Tag: "p", Value: 12, Key: "p"} + node := nb.AddYAMLNode(p, &nodeEnty) + assert.NotNil(t, node) + assert.Len(t, node.Content, 2) + assert.Equal(t, "12", node.Content[1].Value) } func TestNewNodeBuilder_Int64(t *testing.T) { - t1 := new(test1) - nb := NewNodeBuilder(t1, t1) - p := utils.CreateEmptyMapNode() - nodeEnty := NodeEntry{Tag: "p", Value: int64(234556), Key: "p"} - node := nb.AddYAMLNode(p, &nodeEnty) - assert.NotNil(t, node) - assert.Len(t, node.Content, 2) - assert.Equal(t, "234556", node.Content[1].Value) + t1 := new(test1) + nb := NewNodeBuilder(t1, t1) + p := utils.CreateEmptyMapNode() + nodeEnty := NodeEntry{Tag: "p", Value: int64(234556), Key: "p"} + node := nb.AddYAMLNode(p, &nodeEnty) + assert.NotNil(t, node) + assert.Len(t, node.Content, 2) + assert.Equal(t, "234556", node.Content[1].Value) } func TestNewNodeBuilder_Float32(t *testing.T) { - t1 := new(test1) - nb := NewNodeBuilder(t1, t1) - p := utils.CreateEmptyMapNode() - nodeEnty := NodeEntry{Tag: "p", Value: float32(1234.23), Key: "p"} - node := nb.AddYAMLNode(p, &nodeEnty) - assert.NotNil(t, node) - assert.Len(t, node.Content, 2) - assert.Equal(t, "1234.23", node.Content[1].Value) + t1 := new(test1) + nb := NewNodeBuilder(t1, t1) + p := utils.CreateEmptyMapNode() + nodeEnty := NodeEntry{Tag: "p", Value: float32(1234.23), Key: "p"} + node := nb.AddYAMLNode(p, &nodeEnty) + assert.NotNil(t, node) + assert.Len(t, node.Content, 2) + assert.Equal(t, "1234.23", node.Content[1].Value) } func TestNewNodeBuilder_Float64(t *testing.T) { - t1 := new(test1) - nb := NewNodeBuilder(t1, t1) - p := utils.CreateEmptyMapNode() - nodeEnty := NodeEntry{Tag: "p", Value: 1234.232323, Key: "p", StringValue: "1234.232323"} - node := nb.AddYAMLNode(p, &nodeEnty) - assert.NotNil(t, node) - assert.Len(t, node.Content, 2) - assert.Equal(t, "1234.232323", node.Content[1].Value) + t1 := new(test1) + nb := NewNodeBuilder(t1, t1) + p := utils.CreateEmptyMapNode() + nodeEnty := NodeEntry{Tag: "p", Value: 1234.232323, Key: "p", StringValue: "1234.232323"} + node := nb.AddYAMLNode(p, &nodeEnty) + assert.NotNil(t, node) + assert.Len(t, node.Content, 2) + assert.Equal(t, "1234.232323", node.Content[1].Value) } func TestNewNodeBuilder_EmptyNode(t *testing.T) { - t1 := new(test1) - nb := NewNodeBuilder(t1, t1) - nb.Nodes = nil - m := nb.Render() - assert.Len(t, m.Content, 0) + t1 := new(test1) + nb := NewNodeBuilder(t1, t1) + nb.Nodes = nil + m := nb.Render() + assert.Len(t, m.Content, 0) } func TestNewNodeBuilder_MapKeyHasValue(t *testing.T) { - t1 := test1{ - Thrug: map[string]string{ - "dump": "trump", - }, - } + t1 := test1{ + Thrug: map[string]string{ + "dump": "trump", + }, + } - type test1low struct { - Thrug key `yaml:"thrug"` - Thugg *bool `yaml:"thugg"` - } + type test1low struct { + Thrug key `yaml:"thrug"` + Thugg *bool `yaml:"thugg"` + } - t2 := test1low{ - Thrug: key{ - v: map[string]string{ - "dump": "trump", - }, - ln: 2, - }, - } + t2 := test1low{ + Thrug: key{ + v: map[string]string{ + "dump": "trump", + }, + ln: 2, + }, + } - nb := NewNodeBuilder(&t1, &t2) - node := nb.Render() + nb := NewNodeBuilder(&t1, &t2) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `thrug: + desired := `thrug: dump: trump` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_MapKeyHasValueThatHasValue(t *testing.T) { - t1 := test1{ - Thomp: map[key]string{ - {v: "who"}: "princess", - }, - } + t1 := test1{ + Thomp: map[key]string{ + {v: "who"}: "princess", + }, + } - type test1low struct { - Thomp key `yaml:"thomp"` - Thugg *bool `yaml:"thugg"` - } + type test1low struct { + Thomp key `yaml:"thomp"` + Thugg *bool `yaml:"thugg"` + } - t2 := test1low{ - Thomp: key{ - v: map[key]string{ - { - v: key{ - v: "ice", - kn: utils.CreateStringNode("limes"), - }, - kn: utils.CreateStringNode("chimes"), - ln: 6}: "princess", - }, - ln: 2, - }, - } + t2 := test1low{ + Thomp: key{ + v: map[key]string{ + { + v: key{ + v: "ice", + kn: utils.CreateStringNode("limes"), + }, + kn: utils.CreateStringNode("chimes"), + ln: 6}: "princess", + }, + ln: 2, + }, + } - nb := NewNodeBuilder(&t1, &t2) - node := nb.Render() + nb := NewNodeBuilder(&t1, &t2) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `thomp: + desired := `thomp: meddy: princess` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_MapKeyHasValueThatHasValueMatch(t *testing.T) { - t1 := test1{ - Thomp: map[key]string{ - {v: "who"}: "princess", - }, - } + t1 := test1{ + Thomp: map[key]string{ + {v: "who"}: "princess", + }, + } - type test1low struct { - Thomp low.NodeReference[map[key]string] `yaml:"thomp"` - Thugg *bool `yaml:"thugg"` - } + type test1low struct { + Thomp low.NodeReference[map[key]string] `yaml:"thomp"` + Thugg *bool `yaml:"thugg"` + } - g := low.NodeReference[map[key]string]{ - Value: map[key]string{ - {v: "my", kn: utils.CreateStringNode("limes")}: "princess", - }, - } + g := low.NodeReference[map[key]string]{ + Value: map[key]string{ + {v: "my", kn: utils.CreateStringNode("limes")}: "princess", + }, + } - t2 := test1low{ - Thomp: g, - } + t2 := test1low{ + Thomp: g, + } - nb := NewNodeBuilder(&t1, &t2) - node := nb.Render() + nb := NewNodeBuilder(&t1, &t2) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `thomp: + desired := `thomp: meddy: princess` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_MapKeyHasValueThatHasValueMatchKeyNode(t *testing.T) { - t1 := test1{ - Thomp: map[key]string{ - {v: "who"}: "princess", - }, - } + t1 := test1{ + Thomp: map[key]string{ + {v: "who"}: "princess", + }, + } - type test1low struct { - Thomp low.NodeReference[map[key]string] `yaml:"thomp"` - Thugg *bool `yaml:"thugg"` - } + type test1low struct { + Thomp low.NodeReference[map[key]string] `yaml:"thomp"` + Thugg *bool `yaml:"thugg"` + } - g := low.NodeReference[map[key]string]{ - Value: map[key]string{ - {v: "my", kn: utils.CreateStringNode("limes")}: "princess", - }, - } + g := low.NodeReference[map[key]string]{ + Value: map[key]string{ + {v: "my", kn: utils.CreateStringNode("limes")}: "princess", + }, + } - t2 := test1low{ - Thomp: g, - } + t2 := test1low{ + Thomp: g, + } - nb := NewNodeBuilder(&t1, &t2) - node := nb.Render() + nb := NewNodeBuilder(&t1, &t2) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `thomp: + desired := `thomp: meddy: princess` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_MapKeyHasValueThatHasValueMatch_NoWrap(t *testing.T) { - t1 := test1{ - Thomp: map[key]string{ - {v: "who"}: "princess", - }, - } + t1 := test1{ + Thomp: map[key]string{ + {v: "who"}: "princess", + }, + } - type test1low struct { - Thomp map[key]string `yaml:"thomp"` - Thugg *bool `yaml:"thugg"` - } + type test1low struct { + Thomp map[key]string `yaml:"thomp"` + Thugg *bool `yaml:"thugg"` + } - t2 := test1low{ - Thomp: map[key]string{ - {v: "my", kn: utils.CreateStringNode("meddy")}: "princess", - }, - } + t2 := test1low{ + Thomp: map[key]string{ + {v: "my", kn: utils.CreateStringNode("meddy")}: "princess", + }, + } - nb := NewNodeBuilder(&t1, &t2) - node := nb.Render() + nb := NewNodeBuilder(&t1, &t2) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `thomp: + desired := `thomp: meddy: princess` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_MissingLabel(t *testing.T) { - t1 := new(test1) - nb := NewNodeBuilder(t1, t1) - p := utils.CreateEmptyMapNode() - nodeEnty := NodeEntry{Value: 1234.232323, Key: "p"} - node := nb.AddYAMLNode(p, &nodeEnty) - assert.NotNil(t, node) - assert.Len(t, node.Content, 0) + t1 := new(test1) + nb := NewNodeBuilder(t1, t1) + p := utils.CreateEmptyMapNode() + nodeEnty := NodeEntry{Value: 1234.232323, Key: "p"} + node := nb.AddYAMLNode(p, &nodeEnty) + assert.NotNil(t, node) + assert.Len(t, node.Content, 0) } func TestNewNodeBuilder_ExtensionMap(t *testing.T) { - t1 := test1{ - Thing: "ding", - Extensions: map[string]any{ - "x-pizza": map[string]string{ - "dump": "trump", - }, - "x-money": "time", - }, - Thong: 1, - } + t1 := test1{ + Thing: "ding", + Extensions: map[string]any{ + "x-pizza": map[string]string{ + "dump": "trump", + }, + "x-money": "time", + }, + Thong: 1, + } - nb := NewNodeBuilder(&t1, &t1) - node := nb.Render() + nb := NewNodeBuilder(&t1, &t1) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - assert.Len(t, data, 60) + assert.Len(t, data, 60) } func TestNewNodeBuilder_MapKeyHasValueThatHasValueMismatch(t *testing.T) { - t1 := test1{ - Extensions: map[string]any{ - "x-pizza": map[string]string{ - "dump": "trump", - }, - "x-cake": map[string]string{ - "maga": "nomore", - }, - }, - Thril: map[string]*key{ - "princess": {v: "who", Name: "beef", ln: 2}, - "heavy": {v: "who", Name: "industries", ln: 3}, - }, - } + t1 := test1{ + Extensions: map[string]any{ + "x-pizza": map[string]string{ + "dump": "trump", + }, + "x-cake": map[string]string{ + "maga": "nomore", + }, + }, + Thril: map[string]*key{ + "princess": {v: "who", Name: "beef", ln: 2}, + "heavy": {v: "who", Name: "industries", ln: 3}, + }, + } - nb := NewNodeBuilder(&t1, nil) - node := nb.Render() + nb := NewNodeBuilder(&t1, nil) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - assert.Len(t, data, 94) + assert.Len(t, data, 94) } func TestNewNodeBuilder_SliceRef(t *testing.T) { - c := key{ref: true, refStr: "#/red/robin/yummmmm", Name: "milky"} - ty := []*key{&c} - t1 := test1{ - Throg: ty, - } + c := key{ref: true, refStr: "#/red/robin/yummmmm", Name: "milky"} + ty := []*key{&c} + t1 := test1{ + Throg: ty, + } - nb := NewNodeBuilder(&t1, &t1) - node := nb.Render() + nb := NewNodeBuilder(&t1, &t1) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `throg: + desired := `throg: - $ref: '#/red/robin/yummmmm'` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_SliceRef_Inline(t *testing.T) { - c := key{ref: true, refStr: "#/red/robin/yummmmm", Name: "milky"} - ty := []*key{&c} - t1 := test1{ - Throg: ty, - } + c := key{ref: true, refStr: "#/red/robin/yummmmm", Name: "milky"} + ty := []*key{&c} + t1 := test1{ + Throg: ty, + } - nb := NewNodeBuilder(&t1, &t1) - nb.Resolve = true - node := nb.Render() + nb := NewNodeBuilder(&t1, &t1) + nb.Resolve = true + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `throg: + desired := `throg: - pizza-inline!` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } type testRender struct { } func (t testRender) MarshalYAML() (interface{}, error) { - return utils.CreateStringNode("testy!"), nil + return utils.CreateStringNode("testy!"), nil } type testRenderRawNode struct { } func (t testRenderRawNode) MarshalYAML() (interface{}, error) { - return yaml.Node{Kind: yaml.ScalarNode, Value: "zesty!"}, nil + return yaml.Node{Kind: yaml.ScalarNode, Value: "zesty!"}, nil } func TestNewNodeBuilder_SliceRef_Inline_NotCompatible(t *testing.T) { - ty := []interface{}{testRender{}} - t1 := test1{ - Thrat: ty, - } + ty := []interface{}{testRender{}} + t1 := test1{ + Thrat: ty, + } - nb := NewNodeBuilder(&t1, &t1) - nb.Resolve = true - node := nb.Render() + nb := NewNodeBuilder(&t1, &t1) + nb.Resolve = true + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `thrat: + desired := `thrat: - testy!` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_SliceRef_Inline_NotCompatible_NotPointer(t *testing.T) { - ty := []interface{}{testRenderRawNode{}} - t1 := test1{ - Thrat: ty, - } + ty := []interface{}{testRenderRawNode{}} + t1 := test1{ + Thrat: ty, + } - nb := NewNodeBuilder(&t1, &t1) - nb.Resolve = true - node := nb.Render() + nb := NewNodeBuilder(&t1, &t1) + nb.Resolve = true + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `thrat: + desired := `thrat: - zesty!` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_PointerRef_Inline_NotCompatible_RawNode(t *testing.T) { - ty := testRenderRawNode{} - t1 := test1{ - Thurm: &ty, - } + ty := testRenderRawNode{} + t1 := test1{ + Thurm: &ty, + } - nb := NewNodeBuilder(&t1, &t1) - nb.Resolve = true - node := nb.Render() + nb := NewNodeBuilder(&t1, &t1) + nb.Resolve = true + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `thurm: zesty!` + desired := `thurm: zesty!` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_PointerRef_Inline_NotCompatible(t *testing.T) { - ty := key{} - t1 := test1{ - Thurm: &ty, - } + ty := key{} + t1 := test1{ + Thurm: &ty, + } - nb := NewNodeBuilder(&t1, &t1) - nb.Resolve = true - node := nb.Render() + nb := NewNodeBuilder(&t1, &t1) + nb.Resolve = true + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `thurm: pizza-inline!` + desired := `thurm: pizza-inline!` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_SliceNoRef(t *testing.T) { - c := key{ref: false, Name: "milky"} - ty := []*key{&c} - t1 := test1{ - Throg: ty, - } + c := key{ref: false, Name: "milky"} + ty := []*key{&c} + t1 := test1{ + Throg: ty, + } - nb := NewNodeBuilder(&t1, &t1) - node := nb.Render() + nb := NewNodeBuilder(&t1, &t1) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `throg: + desired := `throg: - pizza` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_TestStructAny(t *testing.T) { - t1 := test1{ - Thurm: low.ValueReference[any]{ - ValueNode: utils.CreateStringNode("beer"), - }, - } + t1 := test1{ + Thurm: low.ValueReference[any]{ + ValueNode: utils.CreateStringNode("beer"), + }, + } - nb := NewNodeBuilder(&t1, &t1) - node := nb.Render() + nb := NewNodeBuilder(&t1, &t1) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `thurm: beer` + desired := `thurm: beer` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_TestStructString(t *testing.T) { - t1 := test1{ - Thurm: low.ValueReference[string]{ - ValueNode: utils.CreateStringNode("beer"), - }, - } + t1 := test1{ + Thurm: low.ValueReference[string]{ + ValueNode: utils.CreateStringNode("beer"), + }, + } - nb := NewNodeBuilder(&t1, &t1) - node := nb.Render() + nb := NewNodeBuilder(&t1, &t1) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `thurm: beer` + desired := `thurm: beer` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_TestStructPointer(t *testing.T) { - t1 := test1{ - Thrim: &key{ - ref: true, - refStr: "#/cash/money", - Name: "pizza", - }, - } + t1 := test1{ + Thrim: &key{ + ref: true, + refStr: "#/cash/money", + Name: "pizza", + }, + } - nb := NewNodeBuilder(&t1, &t1) - node := nb.Render() + nb := NewNodeBuilder(&t1, &t1) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `thrim: + desired := `thrim: $ref: '#/cash/money'` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_TestStructRef(t *testing.T) { - fkn := utils.CreateStringNode("pizzaBurgers") - fkn.Line = 22 + fkn := utils.CreateStringNode("pizzaBurgers") + fkn.Line = 22 - t1 := test1{ - Thurm: low.NodeReference[string]{ - Reference: "#/cash/money", - ReferenceNode: true, - KeyNode: fkn, - ValueNode: fkn, - }, - } + t1 := test1{ + Thurm: low.NodeReference[string]{ + Reference: "#/cash/money", + ReferenceNode: true, + KeyNode: fkn, + ValueNode: fkn, + }, + } - nb := NewNodeBuilder(&t1, &t1) - node := nb.Render() + nb := NewNodeBuilder(&t1, &t1) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `thurm: pizzaBurgers` + desired := `thurm: pizzaBurgers` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_TestStructDefaultEncode(t *testing.T) { - f := 1 - t1 := test1{ - Thurm: &f, - } + f := 1 + t1 := test1{ + Thurm: &f, + } - nb := NewNodeBuilder(&t1, &t1) - node := nb.Render() + nb := NewNodeBuilder(&t1, &t1) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `thurm: 1` + desired := `thurm: 1` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_TestSliceMapSliceStruct(t *testing.T) { - a := []map[string][]string{ - {"pizza": {"beer", "wine"}}, - } + a := []map[string][]string{ + {"pizza": {"beer", "wine"}}, + } - t1 := test1{ - Thrag: a, - } + t1 := test1{ + Thrag: a, + } - nb := NewNodeBuilder(&t1, &t1) - node := nb.Render() + nb := NewNodeBuilder(&t1, &t1) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `thrag: + desired := `thrag: - pizza: - beer - wine` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_TestRenderZero(t *testing.T) { - f := false - t1 := test1{ - Thugg: &f, - } + f := false + t1 := test1{ + Thugg: &f, + } - nb := NewNodeBuilder(&t1, &t1) - node := nb.Render() + nb := NewNodeBuilder(&t1, &t1) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `thugg: false` + desired := `thugg: false` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_TestRenderServerVariableSimulation(t *testing.T) { - t1 := test1{ - Thrig: map[string]*plug{ - "pork": {Name: []string{"gammon", "bacon"}}, - }, - } + t1 := test1{ + Thrig: map[string]*plug{ + "pork": {Name: []string{"gammon", "bacon"}}, + }, + } - nb := NewNodeBuilder(&t1, &t1) - node := nb.Render() + nb := NewNodeBuilder(&t1, &t1) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `thrig: + desired := `thrig: pork: name: - gammon - bacon` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_ShouldHaveNotDoneTestsLikeThisOhWell(t *testing.T) { - m := make(map[low.KeyReference[string]]low.ValueReference[*key]) + m := make(map[low.KeyReference[string]]low.ValueReference[*key]) - m[low.KeyReference[string]{ - KeyNode: utils.CreateStringNode("pizza"), - Value: "pizza", - }] = low.ValueReference[*key]{ - ValueNode: utils.CreateStringNode("beer"), - Value: &key{}, - } + m[low.KeyReference[string]{ + KeyNode: utils.CreateStringNode("pizza"), + Value: "pizza", + }] = low.ValueReference[*key]{ + ValueNode: utils.CreateStringNode("beer"), + Value: &key{}, + } - d := make(map[string]*key) - d["pizza"] = &key{} + d := make(map[string]*key) + d["pizza"] = &key{} - type t1low struct { - Thril low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*key]] - Thugg *bool `yaml:"thugg"` - } + type t1low struct { + Thril low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*key]] + Thugg *bool `yaml:"thugg"` + } - t1 := test1{ - Thril: d, - } + t1 := test1{ + Thril: d, + } - t2 := t1low{ - Thril: low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*key]]{ - Value: m, - ValueNode: utils.CreateStringNode("beer"), - }, - } + t2 := t1low{ + Thril: low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*key]]{ + Value: m, + ValueNode: utils.CreateStringNode("beer"), + }, + } - nb := NewNodeBuilder(&t1, &t2) - node := nb.Render() + nb := NewNodeBuilder(&t1, &t2) + node := nb.Render() - data, _ := yaml.Marshal(node) + data, _ := yaml.Marshal(node) - desired := `thril: + desired := `thril: pizza: pizza` - assert.Equal(t, desired, strings.TrimSpace(string(data))) + assert.Equal(t, desired, strings.TrimSpace(string(data))) } diff --git a/datamodel/high/shared.go b/datamodel/high/shared.go index 24bce08..ed9ffc2 100644 --- a/datamodel/high/shared.go +++ b/datamodel/high/shared.go @@ -58,8 +58,9 @@ func ExtractExtensions(extensions map[low.KeyReference[string]]low.ValueReferenc // `low` represents the HIGH type of the object that contains the extensions. // // to use: -// schema := schemaProxy.Schema() // any high-level object that has extensions -// extensions, err := UnpackExtensions[MyComplexType, low.Schema](schema) +// +// schema := schemaProxy.Schema() // any high-level object that has extensions +// extensions, err := UnpackExtensions[MyComplexType, low.Schema](schema) func UnpackExtensions[T any, R low.HasExtensions[T]](low GoesLow[R]) (map[string]*T, error) { m := make(map[string]*T) ext := low.GoLow().GetExtensions() @@ -75,4 +76,3 @@ func UnpackExtensions[T any, R low.HasExtensions[T]](low GoesLow[R]) (map[string } return m, nil } - diff --git a/datamodel/high/shared_test.go b/datamodel/high/shared_test.go index 2a6180a..cf03e54 100644 --- a/datamodel/high/shared_test.go +++ b/datamodel/high/shared_test.go @@ -4,127 +4,127 @@ package high import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "testing" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "testing" ) func TestExtractExtensions(t *testing.T) { - n := make(map[low.KeyReference[string]]low.ValueReference[any]) - n[low.KeyReference[string]{ - Value: "pb33f", - }] = low.ValueReference[any]{ - Value: "new cowboy in town", - } - ext := ExtractExtensions(n) - assert.Equal(t, "new cowboy in town", ext["pb33f"]) + n := make(map[low.KeyReference[string]]low.ValueReference[any]) + n[low.KeyReference[string]{ + Value: "pb33f", + }] = low.ValueReference[any]{ + Value: "new cowboy in town", + } + ext := ExtractExtensions(n) + assert.Equal(t, "new cowboy in town", ext["pb33f"]) } type textExtension struct { - Cowboy string - Power int + Cowboy string + Power int } type parent struct { - low *child + low *child } func (p *parent) GoLow() *child { - return p.low + return p.low } type child struct { - Extensions map[low.KeyReference[string]]low.ValueReference[any] + Extensions map[low.KeyReference[string]]low.ValueReference[any] } func (c *child) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] { - return c.Extensions + return c.Extensions } func TestUnpackExtensions(t *testing.T) { - var resultA, resultB yaml.Node + var resultA, resultB yaml.Node - ymlA := ` + ymlA := ` cowboy: buckaroo power: 100` - ymlB := ` + ymlB := ` cowboy: frogman power: 2` - err := yaml.Unmarshal([]byte(ymlA), &resultA) - assert.NoError(t, err) - err = yaml.Unmarshal([]byte(ymlB), &resultB) - assert.NoError(t, err) + err := yaml.Unmarshal([]byte(ymlA), &resultA) + assert.NoError(t, err) + err = yaml.Unmarshal([]byte(ymlB), &resultB) + assert.NoError(t, err) - n := make(map[low.KeyReference[string]]low.ValueReference[any]) - n[low.KeyReference[string]{ - Value: "x-rancher-a", - }] = low.ValueReference[any]{ - ValueNode: resultA.Content[0], - } + n := make(map[low.KeyReference[string]]low.ValueReference[any]) + n[low.KeyReference[string]{ + Value: "x-rancher-a", + }] = low.ValueReference[any]{ + ValueNode: resultA.Content[0], + } - n[low.KeyReference[string]{ - Value: "x-rancher-b", - }] = low.ValueReference[any]{ - ValueNode: resultB.Content[0], - } + n[low.KeyReference[string]{ + Value: "x-rancher-b", + }] = low.ValueReference[any]{ + ValueNode: resultB.Content[0], + } - c := new(child) - c.Extensions = n + c := new(child) + c.Extensions = n - p := new(parent) - p.low = c + p := new(parent) + p.low = c - res, err := UnpackExtensions[textExtension, *child](p) - assert.NoError(t, err) - assert.NotEmpty(t, res) - assert.Equal(t, "buckaroo", res["x-rancher-a"].Cowboy) - assert.Equal(t, 100, res["x-rancher-a"].Power) - assert.Equal(t, "frogman", res["x-rancher-b"].Cowboy) - assert.Equal(t, 2, res["x-rancher-b"].Power) + res, err := UnpackExtensions[textExtension, *child](p) + assert.NoError(t, err) + assert.NotEmpty(t, res) + assert.Equal(t, "buckaroo", res["x-rancher-a"].Cowboy) + assert.Equal(t, 100, res["x-rancher-a"].Power) + assert.Equal(t, "frogman", res["x-rancher-b"].Cowboy) + assert.Equal(t, 2, res["x-rancher-b"].Power) } func TestUnpackExtensions_Fail(t *testing.T) { - var resultA, resultB yaml.Node + var resultA, resultB yaml.Node - ymlA := ` + ymlA := ` cowboy: buckaroo power: 100` - // this is incorrect types, unpacking will fail. - ymlB := ` + // this is incorrect types, unpacking will fail. + ymlB := ` cowboy: 0 power: hello` - err := yaml.Unmarshal([]byte(ymlA), &resultA) - assert.NoError(t, err) - err = yaml.Unmarshal([]byte(ymlB), &resultB) - assert.NoError(t, err) + err := yaml.Unmarshal([]byte(ymlA), &resultA) + assert.NoError(t, err) + err = yaml.Unmarshal([]byte(ymlB), &resultB) + assert.NoError(t, err) - n := make(map[low.KeyReference[string]]low.ValueReference[any]) - n[low.KeyReference[string]{ - Value: "x-rancher-a", - }] = low.ValueReference[any]{ - ValueNode: resultA.Content[0], - } + n := make(map[low.KeyReference[string]]low.ValueReference[any]) + n[low.KeyReference[string]{ + Value: "x-rancher-a", + }] = low.ValueReference[any]{ + ValueNode: resultA.Content[0], + } - n[low.KeyReference[string]{ - Value: "x-rancher-b", - }] = low.ValueReference[any]{ - ValueNode: resultB.Content[0], - } + n[low.KeyReference[string]{ + Value: "x-rancher-b", + }] = low.ValueReference[any]{ + ValueNode: resultB.Content[0], + } - c := new(child) - c.Extensions = n + c := new(child) + c.Extensions = n - p := new(parent) - p.low = c + p := new(parent) + p.low = c - res, er := UnpackExtensions[textExtension, *child](p) - assert.Error(t, er) - assert.Empty(t, res) -} \ No newline at end of file + res, er := UnpackExtensions[textExtension, *child](p) + assert.Error(t, er) + assert.Empty(t, res) +} diff --git a/datamodel/high/v2/definitions.go b/datamodel/high/v2/definitions.go index ae1e247..4021711 100644 --- a/datamodel/high/v2/definitions.go +++ b/datamodel/high/v2/definitions.go @@ -14,7 +14,7 @@ import ( // // An object to hold data types that can be consumed and produced by operations. These data types can be primitives, // arrays or models. -// - https://swagger.io/specification/v2/#definitionsObject +// - https://swagger.io/specification/v2/#definitionsObject type Definitions struct { Definitions map[string]*highbase.SchemaProxy low *low.Definitions diff --git a/datamodel/high/v2/examples.go b/datamodel/high/v2/examples.go index d863a16..f80c8e5 100644 --- a/datamodel/high/v2/examples.go +++ b/datamodel/high/v2/examples.go @@ -7,7 +7,7 @@ import low "github.com/pb33f/libopenapi/datamodel/low/v2" // Example represents a high-level Swagger / OpenAPI 2 Example object, backed by a low level one. // Allows sharing examples for operation responses -// - https://swagger.io/specification/v2/#exampleObject +// - https://swagger.io/specification/v2/#exampleObject type Example struct { Values map[string]any low *low.Examples diff --git a/datamodel/high/v2/header.go b/datamodel/high/v2/header.go index 157f834..5a197f5 100644 --- a/datamodel/high/v2/header.go +++ b/datamodel/high/v2/header.go @@ -10,7 +10,7 @@ import ( // Header Represents a high-level Swagger / OpenAPI 2 Header object, backed by a low-level one. // A Header is essentially identical to a Parameter, except it does not contain 'name' or 'in' properties. -// - https://swagger.io/specification/v2/#headerObject +// - https://swagger.io/specification/v2/#headerObject type Header struct { Type string Format string diff --git a/datamodel/high/v2/items.go b/datamodel/high/v2/items.go index cb07b53..4749ef7 100644 --- a/datamodel/high/v2/items.go +++ b/datamodel/high/v2/items.go @@ -10,7 +10,7 @@ import ( // Items is a high-level representation of a Swagger / OpenAPI 2 Items object, backed by a low level one. // Items is a limited subset of JSON-Schema's items object. It is used by parameter definitions that are not // located in "body" -// - https://swagger.io/specification/v2/#itemsObject +// - https://swagger.io/specification/v2/#itemsObject type Items struct { Type string Format string diff --git a/datamodel/high/v2/operation.go b/datamodel/high/v2/operation.go index 8dbccb7..710f586 100644 --- a/datamodel/high/v2/operation.go +++ b/datamodel/high/v2/operation.go @@ -11,7 +11,7 @@ import ( // Operation represents a high-level Swagger / OpenAPI 2 Operation object, backed by a low-level one. // It describes a single API operation on a path. -// - https://swagger.io/specification/v2/#operationObject +// - https://swagger.io/specification/v2/#operationObject type Operation struct { Tags []string Summary string diff --git a/datamodel/high/v2/parameter.go b/datamodel/high/v2/parameter.go index e233274..3012a47 100644 --- a/datamodel/high/v2/parameter.go +++ b/datamodel/high/v2/parameter.go @@ -16,29 +16,39 @@ import ( // There are five possible parameter types. // // Path -// Used together with Path Templating, where the parameter value is actually part of the operation's URL. -// This does not include the host or base path of the API. For example, in /items/{itemId}, the path parameter is itemId. +// +// Used together with Path Templating, where the parameter value is actually part of the operation's URL. +// This does not include the host or base path of the API. For example, in /items/{itemId}, the path parameter is itemId. +// // Query -// Parameters that are appended to the URL. For example, in /items?id=###, the query parameter is id. +// +// Parameters that are appended to the URL. For example, in /items?id=###, the query parameter is id. +// // Header -// Custom headers that are expected as part of the request. +// +// Custom headers that are expected as part of the request. +// // Body -// The payload that's appended to the HTTP request. Since there can only be one payload, there can only be one body parameter. -// The name of the body parameter has no effect on the parameter itself and is used for documentation purposes only. -// Since Form parameters are also in the payload, body and form parameters cannot exist together for the same operation. +// +// The payload that's appended to the HTTP request. Since there can only be one payload, there can only be one body parameter. +// The name of the body parameter has no effect on the parameter itself and is used for documentation purposes only. +// Since Form parameters are also in the payload, body and form parameters cannot exist together for the same operation. +// // Form -// Used to describe the payload of an HTTP request when either application/x-www-form-urlencoded, multipart/form-data -// or both are used as the content type of the request (in Swagger's definition, the consumes property of an operation). -// This is the only parameter type that can be used to send files, thus supporting the file type. Since form parameters -// are sent in the payload, they cannot be declared together with a body parameter for the same operation. Form -// parameters have a different format based on the content-type used (for further details, -// consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4): -// application/x-www-form-urlencoded - Similar to the format of Query parameters but as a payload. For example, -// foo=1&bar=swagger - both foo and bar are form parameters. This is normally used for simple parameters that are -// being transferred. -// multipart/form-data - each parameter takes a section in the payload with an internal header. For example, for -// the header Content-Disposition: form-data; name="submit-name" the name of the parameter is -// submit-name. This type of form parameters is more commonly used for file transfers +// +// Used to describe the payload of an HTTP request when either application/x-www-form-urlencoded, multipart/form-data +// or both are used as the content type of the request (in Swagger's definition, the consumes property of an operation). +// This is the only parameter type that can be used to send files, thus supporting the file type. Since form parameters +// are sent in the payload, they cannot be declared together with a body parameter for the same operation. Form +// parameters have a different format based on the content-type used (for further details, +// consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4): +// application/x-www-form-urlencoded - Similar to the format of Query parameters but as a payload. For example, +// foo=1&bar=swagger - both foo and bar are form parameters. This is normally used for simple parameters that are +// being transferred. +// multipart/form-data - each parameter takes a section in the payload with an internal header. For example, for +// the header Content-Disposition: form-data; name="submit-name" the name of the parameter is +// submit-name. This type of form parameters is more commonly used for file transfers +// // https://swagger.io/specification/v2/#parameterObject type Parameter struct { Name string diff --git a/datamodel/high/v2/parameter_definitions.go b/datamodel/high/v2/parameter_definitions.go index 0544edc..0d741f1 100644 --- a/datamodel/high/v2/parameter_definitions.go +++ b/datamodel/high/v2/parameter_definitions.go @@ -10,7 +10,7 @@ import low "github.com/pb33f/libopenapi/datamodel/low/v2" // // ParameterDefinitions holds parameters to be reused across operations. Parameter definitions can be // referenced to the ones defined here. It does not define global operation parameters -// - https://swagger.io/specification/v2/#parametersDefinitionsObject +// - https://swagger.io/specification/v2/#parametersDefinitionsObject type ParameterDefinitions struct { Definitions map[string]*Parameter low *low.ParameterDefinitions diff --git a/datamodel/high/v2/path_item.go b/datamodel/high/v2/path_item.go index eda52dc..1cb1bb1 100644 --- a/datamodel/high/v2/path_item.go +++ b/datamodel/high/v2/path_item.go @@ -4,8 +4,8 @@ package v2 import ( - "github.com/pb33f/libopenapi/datamodel/high" - low "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/v2" ) // PathItem represents a high-level Swagger / OpenAPI 2 PathItem object backed by a low-level one. @@ -13,128 +13,128 @@ import ( // Describes the operations available on a single path. A Path Item may be empty, due to ACL constraints. // The path itself is still exposed to the tooling, but will not know which operations and parameters // are available. -// - https://swagger.io/specification/v2/#pathItemObject +// - https://swagger.io/specification/v2/#pathItemObject type PathItem struct { - Ref string - Get *Operation - Put *Operation - Post *Operation - Delete *Operation - Options *Operation - Head *Operation - Patch *Operation - Parameters []*Parameter - Extensions map[string]any - low *low.PathItem + Ref string + Get *Operation + Put *Operation + Post *Operation + Delete *Operation + Options *Operation + Head *Operation + Patch *Operation + Parameters []*Parameter + Extensions map[string]any + low *low.PathItem } // NewPathItem will create a new high-level PathItem from a low-level one. All paths are built out asynchronously. func NewPathItem(pathItem *low.PathItem) *PathItem { - p := new(PathItem) - p.low = pathItem - p.Extensions = high.ExtractExtensions(pathItem.Extensions) - if !pathItem.Parameters.IsEmpty() { - var params []*Parameter - for k := range pathItem.Parameters.Value { - params = append(params, NewParameter(pathItem.Parameters.Value[k].Value)) - } - p.Parameters = params - } - var buildOperation = func(method string, op *low.Operation, resChan chan<- asyncResult[*Operation]) { - resChan <- asyncResult[*Operation]{ - key: method, - result: NewOperation(op), - } - } - totalOperations := 0 - resChan := make(chan asyncResult[*Operation]) - if !pathItem.Get.IsEmpty() { - totalOperations++ - go buildOperation(low.GetLabel, pathItem.Get.Value, resChan) - } - if !pathItem.Put.IsEmpty() { - totalOperations++ - go buildOperation(low.PutLabel, pathItem.Put.Value, resChan) - } - if !pathItem.Post.IsEmpty() { - totalOperations++ - go buildOperation(low.PostLabel, pathItem.Post.Value, resChan) - } - if !pathItem.Patch.IsEmpty() { - totalOperations++ - go buildOperation(low.PatchLabel, pathItem.Patch.Value, resChan) - } - if !pathItem.Delete.IsEmpty() { - totalOperations++ - go buildOperation(low.DeleteLabel, pathItem.Delete.Value, resChan) - } - if !pathItem.Head.IsEmpty() { - totalOperations++ - go buildOperation(low.HeadLabel, pathItem.Head.Value, resChan) - } - if !pathItem.Options.IsEmpty() { - totalOperations++ - go buildOperation(low.OptionsLabel, pathItem.Options.Value, resChan) - } - completedOperations := 0 - for completedOperations < totalOperations { - select { - case r := <-resChan: - switch r.key { - case low.GetLabel: - completedOperations++ - p.Get = r.result - case low.PutLabel: - completedOperations++ - p.Put = r.result - case low.PostLabel: - completedOperations++ - p.Post = r.result - case low.PatchLabel: - completedOperations++ - p.Patch = r.result - case low.DeleteLabel: - completedOperations++ - p.Delete = r.result - case low.HeadLabel: - completedOperations++ - p.Head = r.result - case low.OptionsLabel: - completedOperations++ - p.Options = r.result - } - } - } - return p + p := new(PathItem) + p.low = pathItem + p.Extensions = high.ExtractExtensions(pathItem.Extensions) + if !pathItem.Parameters.IsEmpty() { + var params []*Parameter + for k := range pathItem.Parameters.Value { + params = append(params, NewParameter(pathItem.Parameters.Value[k].Value)) + } + p.Parameters = params + } + var buildOperation = func(method string, op *low.Operation, resChan chan<- asyncResult[*Operation]) { + resChan <- asyncResult[*Operation]{ + key: method, + result: NewOperation(op), + } + } + totalOperations := 0 + resChan := make(chan asyncResult[*Operation]) + if !pathItem.Get.IsEmpty() { + totalOperations++ + go buildOperation(low.GetLabel, pathItem.Get.Value, resChan) + } + if !pathItem.Put.IsEmpty() { + totalOperations++ + go buildOperation(low.PutLabel, pathItem.Put.Value, resChan) + } + if !pathItem.Post.IsEmpty() { + totalOperations++ + go buildOperation(low.PostLabel, pathItem.Post.Value, resChan) + } + if !pathItem.Patch.IsEmpty() { + totalOperations++ + go buildOperation(low.PatchLabel, pathItem.Patch.Value, resChan) + } + if !pathItem.Delete.IsEmpty() { + totalOperations++ + go buildOperation(low.DeleteLabel, pathItem.Delete.Value, resChan) + } + if !pathItem.Head.IsEmpty() { + totalOperations++ + go buildOperation(low.HeadLabel, pathItem.Head.Value, resChan) + } + if !pathItem.Options.IsEmpty() { + totalOperations++ + go buildOperation(low.OptionsLabel, pathItem.Options.Value, resChan) + } + completedOperations := 0 + for completedOperations < totalOperations { + select { + case r := <-resChan: + switch r.key { + case low.GetLabel: + completedOperations++ + p.Get = r.result + case low.PutLabel: + completedOperations++ + p.Put = r.result + case low.PostLabel: + completedOperations++ + p.Post = r.result + case low.PatchLabel: + completedOperations++ + p.Patch = r.result + case low.DeleteLabel: + completedOperations++ + p.Delete = r.result + case low.HeadLabel: + completedOperations++ + p.Head = r.result + case low.OptionsLabel: + completedOperations++ + p.Options = r.result + } + } + } + return p } // GoLow returns the low-level PathItem used to create the high-level one. func (p *PathItem) GoLow() *low.PathItem { - return p.low + return p.low } func (p *PathItem) GetOperations() map[string]*Operation { - o := make(map[string]*Operation) - if p.Get != nil { - o[low.GetLabel] = p.Get - } - if p.Put != nil { - o[low.PutLabel] = p.Put - } - if p.Post != nil { - o[low.PostLabel] = p.Post - } - if p.Delete != nil { - o[low.DeleteLabel] = p.Delete - } - if p.Options != nil { - o[low.OptionsLabel] = p.Options - } - if p.Head != nil { - o[low.HeadLabel] = p.Head - } - if p.Patch != nil { - o[low.PatchLabel] = p.Patch - } - return o + o := make(map[string]*Operation) + if p.Get != nil { + o[low.GetLabel] = p.Get + } + if p.Put != nil { + o[low.PutLabel] = p.Put + } + if p.Post != nil { + o[low.PostLabel] = p.Post + } + if p.Delete != nil { + o[low.DeleteLabel] = p.Delete + } + if p.Options != nil { + o[low.OptionsLabel] = p.Options + } + if p.Head != nil { + o[low.HeadLabel] = p.Head + } + if p.Patch != nil { + o[low.PatchLabel] = p.Patch + } + return o } diff --git a/datamodel/high/v2/path_item_test.go b/datamodel/high/v2/path_item_test.go index 3b3a50c..3579c61 100644 --- a/datamodel/high/v2/path_item_test.go +++ b/datamodel/high/v2/path_item_test.go @@ -4,17 +4,17 @@ package v2 import ( - "github.com/pb33f/libopenapi/datamodel/low" - v2 "github.com/pb33f/libopenapi/datamodel/low/v2" - "github.com/pb33f/libopenapi/index" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "testing" + "github.com/pb33f/libopenapi/datamodel/low" + v2 "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/index" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "testing" ) func TestPathItem_GetOperations(t *testing.T) { - yml := `get: + yml := `get: description: get put: description: put @@ -30,15 +30,15 @@ options: description: options ` - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) - idx := index.NewSpecIndex(&idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) + idx := index.NewSpecIndex(&idxNode) - var n v2.PathItem - _ = low.BuildModel(&idxNode, &n) - _ = n.Build(idxNode.Content[0], idx) + var n v2.PathItem + _ = low.BuildModel(&idxNode, &n) + _ = n.Build(idxNode.Content[0], idx) - r := NewPathItem(&n) + r := NewPathItem(&n) - assert.Len(t, r.GetOperations(), 7) + assert.Len(t, r.GetOperations(), 7) } diff --git a/datamodel/high/v2/response.go b/datamodel/high/v2/response.go index 0c3a8e9..4e460ce 100644 --- a/datamodel/high/v2/response.go +++ b/datamodel/high/v2/response.go @@ -11,7 +11,7 @@ import ( // Response is a representation of a high-level Swagger / OpenAPI 2 Response object, backed by a low-level one. // Response describes a single response from an API Operation -// - https://swagger.io/specification/v2/#responseObject +// - https://swagger.io/specification/v2/#responseObject type Response struct { Description string Schema *base.SchemaProxy diff --git a/datamodel/high/v2/responses_definitions.go b/datamodel/high/v2/responses_definitions.go index 8972698..94fb819 100644 --- a/datamodel/high/v2/responses_definitions.go +++ b/datamodel/high/v2/responses_definitions.go @@ -10,7 +10,7 @@ import low "github.com/pb33f/libopenapi/datamodel/low/v2" // // ResponsesDefinitions is an object to hold responses to be reused across operations. Response definitions can be // referenced to the ones defined here. It does not define global operation responses -// - https://swagger.io/specification/v2/#responsesDefinitionsObject +// - https://swagger.io/specification/v2/#responsesDefinitionsObject type ResponsesDefinitions struct { Definitions map[string]*Response low *low.ResponsesDefinitions diff --git a/datamodel/high/v2/scopes.go b/datamodel/high/v2/scopes.go index 9b27d47..cd82c7e 100644 --- a/datamodel/high/v2/scopes.go +++ b/datamodel/high/v2/scopes.go @@ -10,7 +10,7 @@ import ( // Scopes is a high-level representation of a Swagger / OpenAPI 2 OAuth2 Scopes object, that is backed by a low-level one. // // Scopes lists the available scopes for an OAuth2 security scheme. -// - https://swagger.io/specification/v2/#scopesObject +// - https://swagger.io/specification/v2/#scopesObject type Scopes struct { Values map[string]string low *low.Scopes diff --git a/datamodel/high/v2/security_definitions.go b/datamodel/high/v2/security_definitions.go index ee2dd49..06ccb61 100644 --- a/datamodel/high/v2/security_definitions.go +++ b/datamodel/high/v2/security_definitions.go @@ -10,7 +10,7 @@ import low "github.com/pb33f/libopenapi/datamodel/low/v2" // // A declaration of the security schemes available to be used in the specification. This does not enforce the security // schemes on the operations and only serves to provide the relevant details for each scheme -// - https://swagger.io/specification/v2/#securityDefinitionsObject +// - https://swagger.io/specification/v2/#securityDefinitionsObject type SecurityDefinitions struct { Definitions map[string]*SecurityScheme low *low.SecurityDefinitions diff --git a/datamodel/high/v2/security_scheme.go b/datamodel/high/v2/security_scheme.go index 4c3c751..774dfed 100644 --- a/datamodel/high/v2/security_scheme.go +++ b/datamodel/high/v2/security_scheme.go @@ -14,7 +14,7 @@ import ( // SecurityScheme allows the definition of a security scheme that can be used by the operations. Supported schemes are // basic authentication, an API key (either as a header or as a query parameter) and OAuth2's common flows // (implicit, password, application and access code) -// - https://swagger.io/specification/v2/#securityDefinitionsObject +// - https://swagger.io/specification/v2/#securityDefinitionsObject type SecurityScheme struct { Type string Description string diff --git a/datamodel/high/v3/callback.go b/datamodel/high/v3/callback.go index 79363f0..81b0fc7 100644 --- a/datamodel/high/v3/callback.go +++ b/datamodel/high/v3/callback.go @@ -17,7 +17,7 @@ import ( // PathItem Object that describes a set of requests that may be initiated by the API provider and the expected // responses. The key value used to identify the path item object is an expression, evaluated at runtime, // that identifies a URL to use for the callback operation. -// - https://spec.openapis.org/oas/v3.1.0#callback-object +// - https://spec.openapis.org/oas/v3.1.0#callback-object type Callback struct { Expression map[string]*PathItem `json:"-" yaml:"-"` Extensions map[string]any `json:"-" yaml:"-"` diff --git a/datamodel/high/v3/callback_test.go b/datamodel/high/v3/callback_test.go index 4b7d4db..9037169 100644 --- a/datamodel/high/v3/callback_test.go +++ b/datamodel/high/v3/callback_test.go @@ -4,49 +4,49 @@ package v3 import ( - "github.com/pb33f/libopenapi/datamodel/low" - v3 "github.com/pb33f/libopenapi/datamodel/low/v3" - "github.com/pb33f/libopenapi/index" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "strings" - "testing" + "github.com/pb33f/libopenapi/datamodel/low" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/index" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "strings" + "testing" ) func TestCallback_MarshalYAML(t *testing.T) { - cb := &Callback{ - Expression: map[string]*PathItem{ - "https://pb33f.io": { - Get: &Operation{ - OperationId: "oneTwoThree", - }, - }, - "https://pb33f.io/libopenapi": { - Get: &Operation{ - OperationId: "openaypeeeye", - }, - }, - }, - Extensions: map[string]any{ - "x-burgers": "why not?", - }, - } + cb := &Callback{ + Expression: map[string]*PathItem{ + "https://pb33f.io": { + Get: &Operation{ + OperationId: "oneTwoThree", + }, + }, + "https://pb33f.io/libopenapi": { + Get: &Operation{ + OperationId: "openaypeeeye", + }, + }, + }, + Extensions: map[string]any{ + "x-burgers": "why not?", + }, + } - rend, _ := cb.Render() + rend, _ := cb.Render() - // there is no way to determine order in brand new maps, so we have to check length. - assert.Len(t, rend, 152) + // there is no way to determine order in brand new maps, so we have to check length. + assert.Len(t, rend, 152) - // mutate - cb.Expression["https://pb33f.io"].Get.OperationId = "blim-blam" - cb.Extensions = map[string]interface{}{"x-burgers": "yes please!"} + // mutate + cb.Expression["https://pb33f.io"].Get.OperationId = "blim-blam" + cb.Extensions = map[string]interface{}{"x-burgers": "yes please!"} - rend, _ = cb.Render() - // there is no way to determine order in brand new maps, so we have to check length. - assert.Len(t, rend, 153) + rend, _ = cb.Render() + // there is no way to determine order in brand new maps, so we have to check length. + assert.Len(t, rend, 153) - k := `x-break-everything: please + k := `x-break-everything: please '{$request.query.queryUrl}': post: description: Callback payload @@ -58,19 +58,19 @@ func TestCallback_MarshalYAML(t *testing.T) { schema: type: string` - var idxNode yaml.Node - err := yaml.Unmarshal([]byte(k), &idxNode) - assert.NoError(t, err) - idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig()) + var idxNode yaml.Node + err := yaml.Unmarshal([]byte(k), &idxNode) + assert.NoError(t, err) + idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig()) - var n v3.Callback - _ = low.BuildModel(idxNode.Content[0], &n) - _ = n.Build(idxNode.Content[0], idx) + var n v3.Callback + _ = low.BuildModel(idxNode.Content[0], &n) + _ = n.Build(idxNode.Content[0], idx) - r := NewCallback(&n) + r := NewCallback(&n) - assert.Equal(t, "please", r.Extensions["x-break-everything"]) + assert.Equal(t, "please", r.Extensions["x-break-everything"]) - rend, _ = r.Render() - assert.Equal(t, k, strings.TrimSpace(string(rend))) + rend, _ = r.Render() + assert.Equal(t, k, strings.TrimSpace(string(rend))) } diff --git a/datamodel/high/v3/components.go b/datamodel/high/v3/components.go index dbeec10..afdc52a 100644 --- a/datamodel/high/v3/components.go +++ b/datamodel/high/v3/components.go @@ -28,18 +28,18 @@ const ( // // Holds a set of reusable objects for different aspects of the OAS. All objects defined within the components object // will have no effect on the API unless they are explicitly referenced from properties outside the components object. -// - https://spec.openapis.org/oas/v3.1.0#components-object +// - https://spec.openapis.org/oas/v3.1.0#components-object type Components struct { - Schemas map[string]*highbase.SchemaProxy `json:"schemas,omitempty" yaml:"schemas,omitempty"` - Responses map[string]*Response `json:"responses,omitempty" yaml:"responses,omitempty"` - Parameters map[string]*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"` - Examples map[string]*highbase.Example `json:"examples,omitempty" yaml:"examples,omitempty"` - RequestBodies map[string]*RequestBody `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"` - Headers map[string]*Header `json:"headers,omitempty" yaml:"headers,omitempty"` - SecuritySchemes map[string]*SecurityScheme `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"` - Links map[string]*Link `json:"links,omitempty" yaml:"links,omitempty"` - Callbacks map[string]*Callback `json:"callbacks,omitempty" yaml:"callbacks,omitempty"` - Extensions map[string]any `json:"-" yaml:"-"` + Schemas map[string]*highbase.SchemaProxy `json:"schemas,omitempty" yaml:"schemas,omitempty"` + Responses map[string]*Response `json:"responses,omitempty" yaml:"responses,omitempty"` + Parameters map[string]*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"` + Examples map[string]*highbase.Example `json:"examples,omitempty" yaml:"examples,omitempty"` + RequestBodies map[string]*RequestBody `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"` + Headers map[string]*Header `json:"headers,omitempty" yaml:"headers,omitempty"` + SecuritySchemes map[string]*SecurityScheme `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"` + Links map[string]*Link `json:"links,omitempty" yaml:"links,omitempty"` + Callbacks map[string]*Callback `json:"callbacks,omitempty" yaml:"callbacks,omitempty"` + Extensions map[string]any `json:"-" yaml:"-"` low *low.Components } diff --git a/datamodel/high/v3/components_test.go b/datamodel/high/v3/components_test.go index 20b9703..5be13d4 100644 --- a/datamodel/high/v3/components_test.go +++ b/datamodel/high/v3/components_test.go @@ -4,53 +4,53 @@ package v3 import ( - "github.com/pb33f/libopenapi/datamodel/low" - v3 "github.com/pb33f/libopenapi/datamodel/low/v3" - "github.com/pb33f/libopenapi/index" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "strings" - "testing" + "github.com/pb33f/libopenapi/datamodel/low" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/index" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "strings" + "testing" ) func TestComponents_MarshalYAML(t *testing.T) { - comp := &Components{ - Responses: map[string]*Response{ - "200": { - Description: "OK", - }, - }, - Parameters: map[string]*Parameter{ - "id": { - Name: "id", - In: "path", - }, - }, - RequestBodies: map[string]*RequestBody{ - "body": { - Content: map[string]*MediaType{ - "application/json": { - Example: "why?", - }, - }, - }, - }, - } + comp := &Components{ + Responses: map[string]*Response{ + "200": { + Description: "OK", + }, + }, + Parameters: map[string]*Parameter{ + "id": { + Name: "id", + In: "path", + }, + }, + RequestBodies: map[string]*RequestBody{ + "body": { + Content: map[string]*MediaType{ + "application/json": { + Example: "why?", + }, + }, + }, + }, + } - dat, _ := comp.Render() + dat, _ := comp.Render() - var idxNode yaml.Node - _ = yaml.Unmarshal(dat, &idxNode) - idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig()) + var idxNode yaml.Node + _ = yaml.Unmarshal(dat, &idxNode) + idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig()) - var n v3.Components - _ = low.BuildModel(idxNode.Content[0], &n) - _ = n.Build(idxNode.Content[0], idx) + var n v3.Components + _ = low.BuildModel(idxNode.Content[0], &n) + _ = n.Build(idxNode.Content[0], idx) - r := NewComponents(&n) + r := NewComponents(&n) - desired := `responses: + desired := `responses: "200": description: OK parameters: @@ -63,6 +63,6 @@ requestBodies: application/json: example: why?` - dat, _ = r.Render() - assert.Equal(t, desired, strings.TrimSpace(string(dat))) + dat, _ = r.Render() + assert.Equal(t, desired, strings.TrimSpace(string(dat))) } diff --git a/datamodel/high/v3/encoding.go b/datamodel/high/v3/encoding.go index 51aaf80..e4c348b 100644 --- a/datamodel/high/v3/encoding.go +++ b/datamodel/high/v3/encoding.go @@ -11,7 +11,7 @@ import ( ) // Encoding represents an OpenAPI 3+ Encoding object -// - https://spec.openapis.org/oas/v3.1.0#encoding-object +// - https://spec.openapis.org/oas/v3.1.0#encoding-object type Encoding struct { ContentType string `json:"contentType,omitempty" yaml:"contentType,omitempty"` Headers map[string]*Header `json:"headers,omitempty" yaml:"headers,omitempty"` diff --git a/datamodel/high/v3/encoding_test.go b/datamodel/high/v3/encoding_test.go index 27a77cd..56c2698 100644 --- a/datamodel/high/v3/encoding_test.go +++ b/datamodel/high/v3/encoding_test.go @@ -4,64 +4,64 @@ package v3 import ( - "github.com/stretchr/testify/assert" - "strings" - "testing" + "github.com/stretchr/testify/assert" + "strings" + "testing" ) func TestEncoding_MarshalYAML(t *testing.T) { - explode := true - encoding := &Encoding{ - ContentType: "application/json", - Headers: map[string]*Header{"x-pizza-time": {Description: "oh yes please"}}, - Style: "simple", - Explode: &explode, - } + explode := true + encoding := &Encoding{ + ContentType: "application/json", + Headers: map[string]*Header{"x-pizza-time": {Description: "oh yes please"}}, + Style: "simple", + Explode: &explode, + } - rend, _ := encoding.Render() + rend, _ := encoding.Render() - desired := `contentType: application/json + desired := `contentType: application/json headers: x-pizza-time: description: oh yes please style: simple explode: true` - assert.Equal(t, desired, strings.TrimSpace(string(rend))) + assert.Equal(t, desired, strings.TrimSpace(string(rend))) - explode = false - encoding.Explode = &explode - rend, _ = encoding.Render() + explode = false + encoding.Explode = &explode + rend, _ = encoding.Render() - desired = `contentType: application/json + desired = `contentType: application/json headers: x-pizza-time: description: oh yes please style: simple` - assert.Equal(t, desired, strings.TrimSpace(string(rend))) + assert.Equal(t, desired, strings.TrimSpace(string(rend))) - encoding.Explode = nil - rend, _ = encoding.Render() + encoding.Explode = nil + rend, _ = encoding.Render() - desired = `contentType: application/json + desired = `contentType: application/json headers: x-pizza-time: description: oh yes please style: simple` - assert.Equal(t, desired, strings.TrimSpace(string(rend))) + assert.Equal(t, desired, strings.TrimSpace(string(rend))) - encoding.Explode = &explode - rend, _ = encoding.Render() + encoding.Explode = &explode + rend, _ = encoding.Render() - desired = `contentType: application/json + desired = `contentType: application/json headers: x-pizza-time: description: oh yes please style: simple` - assert.Equal(t, desired, strings.TrimSpace(string(rend))) + assert.Equal(t, desired, strings.TrimSpace(string(rend))) } diff --git a/datamodel/high/v3/header.go b/datamodel/high/v3/header.go index af4f8fc..9dc0bf9 100644 --- a/datamodel/high/v3/header.go +++ b/datamodel/high/v3/header.go @@ -13,7 +13,7 @@ import ( ) // Header represents a high-level OpenAPI 3+ Header object that is backed by a low-level one. -// - https://spec.openapis.org/oas/v3.1.0#header-object +// - https://spec.openapis.org/oas/v3.1.0#header-object type Header struct { Description string `json:"description,omitempty" yaml:"description,omitempty"` Required bool `json:"required,omitempty" yaml:"required,omitempty"` diff --git a/datamodel/high/v3/header_test.go b/datamodel/high/v3/header_test.go index 0dbc03b..5cb4c6a 100644 --- a/datamodel/high/v3/header_test.go +++ b/datamodel/high/v3/header_test.go @@ -4,30 +4,30 @@ package v3 import ( - "github.com/pb33f/libopenapi/datamodel/high/base" - "github.com/stretchr/testify/assert" - "strings" - "testing" + "github.com/pb33f/libopenapi/datamodel/high/base" + "github.com/stretchr/testify/assert" + "strings" + "testing" ) func TestHeader_MarshalYAML(t *testing.T) { - header := &Header{ - Description: "A header", - Required: true, - Deprecated: true, - AllowEmptyValue: true, - Style: "simple", - Explode: true, - AllowReserved: true, - Example: "example", - Examples: map[string]*base.Example{"example": {Value: "example"}}, - Extensions: map[string]interface{}{"x-burgers": "why not?"}, - } + header := &Header{ + Description: "A header", + Required: true, + Deprecated: true, + AllowEmptyValue: true, + Style: "simple", + Explode: true, + AllowReserved: true, + Example: "example", + Examples: map[string]*base.Example{"example": {Value: "example"}}, + Extensions: map[string]interface{}{"x-burgers": "why not?"}, + } - rend, _ := header.Render() + rend, _ := header.Render() - desired := `description: A header + desired := `description: A header required: true deprecated: true allowEmptyValue: true @@ -40,6 +40,6 @@ examples: value: example x-burgers: why not?` - assert.Equal(t, desired, strings.TrimSpace(string(rend))) + assert.Equal(t, desired, strings.TrimSpace(string(rend))) } diff --git a/datamodel/high/v3/link_test.go b/datamodel/high/v3/link_test.go index 06df681..111db3e 100644 --- a/datamodel/high/v3/link_test.go +++ b/datamodel/high/v3/link_test.go @@ -4,27 +4,27 @@ package v3 import ( - "github.com/stretchr/testify/assert" - "strings" - "testing" + "github.com/stretchr/testify/assert" + "strings" + "testing" ) func TestLink_MarshalYAML(t *testing.T) { - link := Link{ - OperationRef: "somewhere", - OperationId: "somewhereOutThere", - Parameters: map[string]string{ - "over": "theRainbow", - }, - RequestBody: "hello?", - Description: "are you there?", - Server: &Server{ - URL: "https://pb33f.io", - }, - } + link := Link{ + OperationRef: "somewhere", + OperationId: "somewhereOutThere", + Parameters: map[string]string{ + "over": "theRainbow", + }, + RequestBody: "hello?", + Description: "are you there?", + Server: &Server{ + URL: "https://pb33f.io", + }, + } - dat, _ := link.Render() - desired := `operationRef: somewhere + dat, _ := link.Render() + desired := `operationRef: somewhere operationId: somewhereOutThere parameters: over: theRainbow @@ -33,5 +33,5 @@ description: are you there? server: url: https://pb33f.io` - assert.Equal(t, desired, strings.TrimSpace(string(dat))) + assert.Equal(t, desired, strings.TrimSpace(string(dat))) } diff --git a/datamodel/high/v3/oauth_flow.go b/datamodel/high/v3/oauth_flow.go index e0174cd..5aa3d59 100644 --- a/datamodel/high/v3/oauth_flow.go +++ b/datamodel/high/v3/oauth_flow.go @@ -10,7 +10,7 @@ import ( ) // OAuthFlow represents a high-level OpenAPI 3+ OAuthFlow object that is backed by a low-level one. -// - https://spec.openapis.org/oas/v3.1.0#oauth-flow-object +// - https://spec.openapis.org/oas/v3.1.0#oauth-flow-object type OAuthFlow struct { AuthorizationUrl string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"` TokenUrl string `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"` diff --git a/datamodel/high/v3/oauth_flow_test.go b/datamodel/high/v3/oauth_flow_test.go index 2972e4f..f81a00f 100644 --- a/datamodel/high/v3/oauth_flow_test.go +++ b/datamodel/high/v3/oauth_flow_test.go @@ -4,42 +4,42 @@ package v3 import ( - "github.com/stretchr/testify/assert" - "strings" - "testing" + "github.com/stretchr/testify/assert" + "strings" + "testing" ) func TestOAuthFlow_MarshalYAML(t *testing.T) { - oflow := &OAuthFlow{ - AuthorizationUrl: "https://pb33f.io", - TokenUrl: "https://pb33f.io/token", - RefreshUrl: "https://pb33f.io/refresh", - Scopes: map[string]string{"chicken": "nuggets", "beefy": "soup"}, - } + oflow := &OAuthFlow{ + AuthorizationUrl: "https://pb33f.io", + TokenUrl: "https://pb33f.io/token", + RefreshUrl: "https://pb33f.io/refresh", + Scopes: map[string]string{"chicken": "nuggets", "beefy": "soup"}, + } - rend, _ := oflow.Render() + rend, _ := oflow.Render() - desired := `authorizationUrl: https://pb33f.io + desired := `authorizationUrl: https://pb33f.io tokenUrl: https://pb33f.io/token refreshUrl: https://pb33f.io/refresh scopes: chicken: nuggets beefy: soup` - // we can't check for equality, as the scopes map will be randomly ordered when created from scratch. - assert.Len(t, desired, 149) + // we can't check for equality, as the scopes map will be randomly ordered when created from scratch. + assert.Len(t, desired, 149) - // mutate - oflow.Scopes = nil - oflow.Extensions = map[string]interface{}{"x-burgers": "why not?"} + // mutate + oflow.Scopes = nil + oflow.Extensions = map[string]interface{}{"x-burgers": "why not?"} - desired = `authorizationUrl: https://pb33f.io + desired = `authorizationUrl: https://pb33f.io tokenUrl: https://pb33f.io/token refreshUrl: https://pb33f.io/refresh x-burgers: why not?` - rend, _ = oflow.Render() - assert.Equal(t, desired, strings.TrimSpace(string(rend))) + rend, _ = oflow.Render() + assert.Equal(t, desired, strings.TrimSpace(string(rend))) } diff --git a/datamodel/high/v3/oauth_flows.go b/datamodel/high/v3/oauth_flows.go index 16b6819..f86ac35 100644 --- a/datamodel/high/v3/oauth_flows.go +++ b/datamodel/high/v3/oauth_flows.go @@ -10,7 +10,7 @@ import ( ) // OAuthFlows represents a high-level OpenAPI 3+ OAuthFlows object that is backed by a low-level one. -// - https://spec.openapis.org/oas/v3.1.0#oauth-flows-object +// - https://spec.openapis.org/oas/v3.1.0#oauth-flows-object type OAuthFlows struct { Implicit *OAuthFlow `json:"implicit,omitempty" yaml:"implicit,omitempty"` Password *OAuthFlow `json:"password,omitempty" yaml:"password,omitempty"` @@ -63,4 +63,3 @@ func (o *OAuthFlows) MarshalYAML() (interface{}, error) { nb := high.NewNodeBuilder(o, o.low) return nb.Render(), nil } - diff --git a/datamodel/high/v3/operation_test.go b/datamodel/high/v3/operation_test.go index 1873564..68f80ac 100644 --- a/datamodel/high/v3/operation_test.go +++ b/datamodel/high/v3/operation_test.go @@ -65,7 +65,7 @@ func TestOperation_MarshalYAML(t *testing.T) { }, OperationId: "slice", Parameters: []*Parameter{ - &Parameter{ + { Name: "mice", }, }, @@ -103,7 +103,7 @@ func TestOperation_MarshalYAMLInline(t *testing.T) { }, OperationId: "slice", Parameters: []*Parameter{ - &Parameter{ + { Name: "mice", }, }, diff --git a/datamodel/high/v3/security_scheme.go b/datamodel/high/v3/security_scheme.go index 3b0597f..60fbfd6 100644 --- a/datamodel/high/v3/security_scheme.go +++ b/datamodel/high/v3/security_scheme.go @@ -18,7 +18,7 @@ import ( // authorization code) as defined in RFC6749 (https://www.rfc-editor.org/rfc/rfc6749), and OpenID Connect Discovery. // Please note that as of 2020, the implicit flow is about to be deprecated by OAuth 2.0 Security Best Current Practice. // Recommended for most use case is Authorization Code Grant flow with PKCE. -// - https://spec.openapis.org/oas/v3.1.0#security-scheme-object +// - https://spec.openapis.org/oas/v3.1.0#security-scheme-object type SecurityScheme struct { Type string `json:"type,omitempty" yaml:"type,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` diff --git a/datamodel/high/v3/security_scheme_test.go b/datamodel/high/v3/security_scheme_test.go index cd963a1..531ab08 100644 --- a/datamodel/high/v3/security_scheme_test.go +++ b/datamodel/high/v3/security_scheme_test.go @@ -4,44 +4,44 @@ package v3 import ( - "github.com/pb33f/libopenapi/datamodel/low" - v3 "github.com/pb33f/libopenapi/datamodel/low/v3" - "github.com/pb33f/libopenapi/index" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "strings" - "testing" + "github.com/pb33f/libopenapi/datamodel/low" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/index" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "strings" + "testing" ) func TestSecurityScheme_MarshalYAML(t *testing.T) { - ss := &SecurityScheme{ - Type: "apiKey", - Description: "this is a description", - Name: "superSecret", - In: "header", - Scheme: "https", - } + ss := &SecurityScheme{ + Type: "apiKey", + Description: "this is a description", + Name: "superSecret", + In: "header", + Scheme: "https", + } - dat, _ := ss.Render() + dat, _ := ss.Render() - var idxNode yaml.Node - _ = yaml.Unmarshal(dat, &idxNode) - idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig()) + var idxNode yaml.Node + _ = yaml.Unmarshal(dat, &idxNode) + idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig()) - var n v3.SecurityScheme - _ = low.BuildModel(idxNode.Content[0], &n) - _ = n.Build(idxNode.Content[0], idx) + var n v3.SecurityScheme + _ = low.BuildModel(idxNode.Content[0], &n) + _ = n.Build(idxNode.Content[0], idx) - r := NewSecurityScheme(&n) + r := NewSecurityScheme(&n) - dat, _ = r.Render() + dat, _ = r.Render() - desired := `type: apiKey + desired := `type: apiKey description: this is a description name: superSecret in: header scheme: https` - assert.Equal(t, desired, strings.TrimSpace(string(dat))) + assert.Equal(t, desired, strings.TrimSpace(string(dat))) } diff --git a/datamodel/high/v3/server_test.go b/datamodel/high/v3/server_test.go index a595a43..bda480c 100644 --- a/datamodel/high/v3/server_test.go +++ b/datamodel/high/v3/server_test.go @@ -4,32 +4,32 @@ package v3 import ( - "github.com/stretchr/testify/assert" - "strings" - "testing" + "github.com/stretchr/testify/assert" + "strings" + "testing" ) func TestServer_MarshalYAML(t *testing.T) { - server := &Server{ - URL: "https://pb33f.io", - Description: "the b33f", - } + server := &Server{ + URL: "https://pb33f.io", + Description: "the b33f", + } - desired := `url: https://pb33f.io + desired := `url: https://pb33f.io description: the b33f` - rend, _ := server.Render() - assert.Equal(t, desired, strings.TrimSpace(string(rend))) + rend, _ := server.Render() + assert.Equal(t, desired, strings.TrimSpace(string(rend))) - // mutate - server.Variables = map[string]*ServerVariable{ - "rainbow": { - Enum: []string{"one", "two", "three"}, - }, - } + // mutate + server.Variables = map[string]*ServerVariable{ + "rainbow": { + Enum: []string{"one", "two", "three"}, + }, + } - desired = `url: https://pb33f.io + desired = `url: https://pb33f.io description: the b33f variables: rainbow: @@ -38,6 +38,6 @@ variables: - two - three` - rend, _ = server.Render() - assert.Equal(t, desired, strings.TrimSpace(string(rend))) + rend, _ = server.Render() + assert.Equal(t, desired, strings.TrimSpace(string(rend))) } diff --git a/datamodel/high/v3/server_variable_test.go b/datamodel/high/v3/server_variable_test.go index 150eb44..54919b0 100644 --- a/datamodel/high/v3/server_variable_test.go +++ b/datamodel/high/v3/server_variable_test.go @@ -4,40 +4,40 @@ package v3 import ( - "github.com/stretchr/testify/assert" - "strings" - "testing" + "github.com/stretchr/testify/assert" + "strings" + "testing" ) func TestServerVariable_MarshalYAML(t *testing.T) { - svar := &ServerVariable{ - Enum: []string{"one", "two", "three"}, - Description: "money day", - } + svar := &ServerVariable{ + Enum: []string{"one", "two", "three"}, + Description: "money day", + } - desired := `enum: + desired := `enum: - one - two - three description: money day` - svarRend, _ := svar.Render() + svarRend, _ := svar.Render() - assert.Equal(t, desired, strings.TrimSpace(string(svarRend))) + assert.Equal(t, desired, strings.TrimSpace(string(svarRend))) - // mutate + // mutate - svar.Default = "is moments away" + svar.Default = "is moments away" - desired = `enum: + desired = `enum: - one - two - three default: is moments away description: money day` - svarRend, _ = svar.Render() + svarRend, _ = svar.Render() - assert.Equal(t, desired, strings.TrimSpace(string(svarRend))) + assert.Equal(t, desired, strings.TrimSpace(string(svarRend))) } diff --git a/datamodel/low/base/base.go b/datamodel/low/base/base.go index 1fa6287..0bfacf2 100644 --- a/datamodel/low/base/base.go +++ b/datamodel/low/base/base.go @@ -9,4 +9,3 @@ // beats, particularly when polymorphism is used. By re-using the same superset Schema across versions, we can ensure // that all the latest features are collected, without damaging backwards compatibility. package base - diff --git a/datamodel/low/base/contact.go b/datamodel/low/base/contact.go index f232678..f3fe32b 100644 --- a/datamodel/low/base/contact.go +++ b/datamodel/low/base/contact.go @@ -12,8 +12,9 @@ import ( ) // Contact represents a low-level representation of the Contact definitions found at -// v2 - https://swagger.io/specification/v2/#contactObject -// v3 - https://spec.openapis.org/oas/v3.1.0#contact-object +// +// v2 - https://swagger.io/specification/v2/#contactObject +// v3 - https://spec.openapis.org/oas/v3.1.0#contact-object type Contact struct { Name low.NodeReference[string] URL low.NodeReference[string] @@ -42,4 +43,3 @@ func (c *Contact) Hash() [32]byte { } return sha256.Sum256([]byte(strings.Join(f, "|"))) } - diff --git a/datamodel/low/base/discriminator.go b/datamodel/low/base/discriminator.go index 09c428c..6f94a3f 100644 --- a/datamodel/low/base/discriminator.go +++ b/datamodel/low/base/discriminator.go @@ -17,7 +17,8 @@ import ( // which is used to inform the consumer of the document of an alternative schema based on the value associated with it. // // When using the discriminator, inline schemas will not be considered. -// v3 - https://spec.openapis.org/oas/v3.1.0#discriminator-object +// +// v3 - https://spec.openapis.org/oas/v3.1.0#discriminator-object type Discriminator struct { PropertyName low.NodeReference[string] Mapping low.NodeReference[map[low.KeyReference[string]]low.ValueReference[string]] diff --git a/datamodel/low/base/example.go b/datamodel/low/base/example.go index 73571fa..341d8e9 100644 --- a/datamodel/low/base/example.go +++ b/datamodel/low/base/example.go @@ -16,7 +16,8 @@ import ( ) // Example represents a low-level Example object as defined by OpenAPI 3+ -// v3 - https://spec.openapis.org/oas/v3.1.0#example-object +// +// v3 - https://spec.openapis.org/oas/v3.1.0#example-object type Example struct { Summary low.NodeReference[string] Description low.NodeReference[string] diff --git a/datamodel/low/base/external_doc.go b/datamodel/low/base/external_doc.go index 1acfeeb..0b617af 100644 --- a/datamodel/low/base/external_doc.go +++ b/datamodel/low/base/external_doc.go @@ -16,8 +16,9 @@ import ( // ExternalDoc represents a low-level External Documentation object as defined by OpenAPI 2 and 3 // // Allows referencing an external resource for extended documentation. -// v2 - https://swagger.io/specification/v2/#externalDocumentationObject -// v3 - https://spec.openapis.org/oas/v3.1.0#external-documentation-object +// +// v2 - https://swagger.io/specification/v2/#externalDocumentationObject +// v3 - https://spec.openapis.org/oas/v3.1.0#external-documentation-object type ExternalDoc struct { Description low.NodeReference[string] URL low.NodeReference[string] diff --git a/datamodel/low/base/license.go b/datamodel/low/base/license.go index 96b54c8..1553940 100644 --- a/datamodel/low/base/license.go +++ b/datamodel/low/base/license.go @@ -12,8 +12,9 @@ import ( ) // License is a low-level representation of a License object as defined by OpenAPI 2 and OpenAPI 3 -// v2 - https://swagger.io/specification/v2/#licenseObject -// v3 - https://spec.openapis.org/oas/v3.1.0#license-object +// +// v2 - https://swagger.io/specification/v2/#licenseObject +// v3 - https://spec.openapis.org/oas/v3.1.0#license-object type License struct { Name low.NodeReference[string] URL low.NodeReference[string] diff --git a/datamodel/low/base/schema.go b/datamodel/low/base/schema.go index 1a76457..ea55d9d 100644 --- a/datamodel/low/base/schema.go +++ b/datamodel/low/base/schema.go @@ -1,17 +1,17 @@ package base import ( - "crypto/sha256" - "fmt" - "reflect" - "sort" - "strconv" - "strings" + "crypto/sha256" + "fmt" + "reflect" + "sort" + "strconv" + "strings" - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/index" - "github.com/pb33f/libopenapi/utils" - "gopkg.in/yaml.v3" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/utils" + "gopkg.in/yaml.v3" ) // SchemaDynamicValue is used to hold multiple possible values for a schema property. There are two values, a left @@ -23,19 +23,30 @@ import ( // The N value is a bit to make it each to know which value (A or B) is used, this prevents having to // if/else on the value to determine which one is set. type SchemaDynamicValue[A any, B any] struct { - N int // 0 == A, 1 == B - A A - B B + N int // 0 == A, 1 == B + A A + B B } // IsA will return true if the 'A' or left value is set. (OpenAPI 3) -func (s SchemaDynamicValue[A, B]) IsA() bool { - return s.N == 0 +func (s *SchemaDynamicValue[A, B]) IsA() bool { + return s.N == 0 } // IsB will return true if the 'B' or right value is set (OpenAPI 3.1) -func (s SchemaDynamicValue[A, B]) IsB() bool { - return s.N == 1 +func (s *SchemaDynamicValue[A, B]) IsB() bool { + return s.N == 1 +} + +// Hash will generate a stable hash of the SchemaDynamicValue +func (s *SchemaDynamicValue[A, B]) Hash() [32]byte { + var hash string + if s.IsA() { + hash = low.GenerateHashString(s.A) + } else { + hash = low.GenerateHashString(s.B) + } + return sha256.Sum256([]byte(hash)) } // Schema represents a JSON Schema that support Swagger, OpenAPI 3 and OpenAPI 3.1 @@ -48,8 +59,8 @@ func (s SchemaDynamicValue[A, B]) IsB() bool { // - v3 schema: https://swagger.io/specification/#schema-object // - v3.1 schema: https://spec.openapis.org/oas/v3.1.0#schema-object type Schema struct { - // Reference to the '$schema' dialect setting (3.1 only) - SchemaTypeRef low.NodeReference[string] + // Reference to the '$schema' dialect setting (3.1 only) + SchemaTypeRef low.NodeReference[string] // In versions 2 and 3.0, this ExclusiveMaximum can only be a boolean. ExclusiveMaximum low.NodeReference[*SchemaDynamicValue[bool, float64]] @@ -57,40 +68,40 @@ type Schema struct { // In versions 2 and 3.0, this ExclusiveMinimum can only be a boolean. ExclusiveMinimum low.NodeReference[*SchemaDynamicValue[bool, float64]] - // In versions 2 and 3.0, this Type is a single value, so array will only ever have one value - // in version 3.1, Type can be multiple values - Type low.NodeReference[SchemaDynamicValue[string, []low.ValueReference[string]]] + // In versions 2 and 3.0, this Type is a single value, so array will only ever have one value + // in version 3.1, Type can be multiple values + Type low.NodeReference[SchemaDynamicValue[string, []low.ValueReference[string]]] - // Schemas are resolved on demand using a SchemaProxy - AllOf low.NodeReference[[]low.ValueReference[*SchemaProxy]] + // Schemas are resolved on demand using a SchemaProxy + AllOf low.NodeReference[[]low.ValueReference[*SchemaProxy]] - // Polymorphic Schemas are only available in version 3+ - OneOf low.NodeReference[[]low.ValueReference[*SchemaProxy]] - AnyOf low.NodeReference[[]low.ValueReference[*SchemaProxy]] - Discriminator low.NodeReference[*Discriminator] + // Polymorphic Schemas are only available in version 3+ + OneOf low.NodeReference[[]low.ValueReference[*SchemaProxy]] + AnyOf low.NodeReference[[]low.ValueReference[*SchemaProxy]] + Discriminator low.NodeReference[*Discriminator] - // in 3.1 examples can be an array (which is recommended) - Examples low.NodeReference[[]low.ValueReference[any]] - // in 3.1 PrefixItems provides tuple validation using prefixItems. - PrefixItems low.NodeReference[[]low.ValueReference[*SchemaProxy]] - // in 3.1 Contains is used by arrays and points to a Schema. - Contains low.NodeReference[*SchemaProxy] - MinContains low.NodeReference[int64] - MaxContains low.NodeReference[int64] + // in 3.1 examples can be an array (which is recommended) + Examples low.NodeReference[[]low.ValueReference[any]] + // in 3.1 PrefixItems provides tuple validation using prefixItems. + PrefixItems low.NodeReference[[]low.ValueReference[*SchemaProxy]] + // in 3.1 Contains is used by arrays and points to a Schema. + Contains low.NodeReference[*SchemaProxy] + MinContains low.NodeReference[int64] + MaxContains low.NodeReference[int64] - // items can be a schema in 2.0, 3.0 and 3.1 or a bool in 3.1 - Items low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]] + // items can be a schema in 2.0, 3.0 and 3.1 or a bool in 3.1 + Items low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]] - // 3.1 only - If low.NodeReference[*SchemaProxy] - Else low.NodeReference[*SchemaProxy] - Then low.NodeReference[*SchemaProxy] - DependentSchemas low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]] - PatternProperties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]] - PropertyNames low.NodeReference[*SchemaProxy] - UnevaluatedItems low.NodeReference[*SchemaProxy] - UnevaluatedProperties low.NodeReference[*SchemaProxy] - Anchor low.NodeReference[string] + // 3.1 only + If low.NodeReference[*SchemaProxy] + Else low.NodeReference[*SchemaProxy] + Then low.NodeReference[*SchemaProxy] + DependentSchemas low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]] + PatternProperties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]] + PropertyNames low.NodeReference[*SchemaProxy] + UnevaluatedItems low.NodeReference[*SchemaProxy] + UnevaluatedProperties low.NodeReference[*SchemaDynamicValue[*SchemaProxy, *bool]] + Anchor low.NodeReference[string] // Compatible with all versions Title low.NodeReference[string] @@ -124,375 +135,375 @@ type Schema struct { Deprecated low.NodeReference[bool] Extensions map[low.KeyReference[string]]low.ValueReference[any] - // Parent Proxy refers back to the low level SchemaProxy that is proxying this schema. - ParentProxy *SchemaProxy - *low.Reference + // Parent Proxy refers back to the low level SchemaProxy that is proxying this schema. + ParentProxy *SchemaProxy + *low.Reference } // Hash will calculate a SHA256 hash from the values of the schema, This allows equality checking against // Schemas defined inside an OpenAPI document. The only way to know if a schema has changed, is to hash it. func (s *Schema) Hash() [32]byte { - // calculate a hash from every property in the schema. - var d []string - if !s.SchemaTypeRef.IsEmpty() { - d = append(d, fmt.Sprint(s.SchemaTypeRef.Value)) - } - if !s.Title.IsEmpty() { - d = append(d, fmt.Sprint(s.Title.Value)) - } - if !s.MultipleOf.IsEmpty() { - d = append(d, fmt.Sprint(s.MultipleOf.Value)) - } - if !s.Maximum.IsEmpty() { - d = append(d, fmt.Sprint(s.Maximum.Value)) - } - if !s.Minimum.IsEmpty() { - d = append(d, fmt.Sprint(s.Minimum.Value)) - } - if !s.MaxLength.IsEmpty() { - d = append(d, fmt.Sprint(s.MaxLength.Value)) - } - if !s.MinLength.IsEmpty() { - d = append(d, fmt.Sprint(s.MinLength.Value)) - } - if !s.Pattern.IsEmpty() { - d = append(d, fmt.Sprint(s.Pattern.Value)) - } - if !s.Format.IsEmpty() { - d = append(d, fmt.Sprint(s.Format.Value)) - } - if !s.MaxItems.IsEmpty() { - d = append(d, fmt.Sprint(s.MaxItems.Value)) - } - if !s.MinItems.IsEmpty() { - d = append(d, fmt.Sprint(s.MinItems.Value)) - } - if !s.UniqueItems.IsEmpty() { - d = append(d, fmt.Sprint(s.UniqueItems.Value)) - } - if !s.MaxProperties.IsEmpty() { - d = append(d, fmt.Sprint(s.MaxProperties.Value)) - } - if !s.MinProperties.IsEmpty() { - d = append(d, fmt.Sprint(s.MinProperties.Value)) - } - if !s.AdditionalProperties.IsEmpty() { + // calculate a hash from every property in the schema. + var d []string + if !s.SchemaTypeRef.IsEmpty() { + d = append(d, fmt.Sprint(s.SchemaTypeRef.Value)) + } + if !s.Title.IsEmpty() { + d = append(d, fmt.Sprint(s.Title.Value)) + } + if !s.MultipleOf.IsEmpty() { + d = append(d, fmt.Sprint(s.MultipleOf.Value)) + } + if !s.Maximum.IsEmpty() { + d = append(d, fmt.Sprint(s.Maximum.Value)) + } + if !s.Minimum.IsEmpty() { + d = append(d, fmt.Sprint(s.Minimum.Value)) + } + if !s.MaxLength.IsEmpty() { + d = append(d, fmt.Sprint(s.MaxLength.Value)) + } + if !s.MinLength.IsEmpty() { + d = append(d, fmt.Sprint(s.MinLength.Value)) + } + if !s.Pattern.IsEmpty() { + d = append(d, fmt.Sprint(s.Pattern.Value)) + } + if !s.Format.IsEmpty() { + d = append(d, fmt.Sprint(s.Format.Value)) + } + if !s.MaxItems.IsEmpty() { + d = append(d, fmt.Sprint(s.MaxItems.Value)) + } + if !s.MinItems.IsEmpty() { + d = append(d, fmt.Sprint(s.MinItems.Value)) + } + if !s.UniqueItems.IsEmpty() { + d = append(d, fmt.Sprint(s.UniqueItems.Value)) + } + if !s.MaxProperties.IsEmpty() { + d = append(d, fmt.Sprint(s.MaxProperties.Value)) + } + if !s.MinProperties.IsEmpty() { + d = append(d, fmt.Sprint(s.MinProperties.Value)) + } + if !s.AdditionalProperties.IsEmpty() { - // check type of properties, if we have a low level map, we need to hash the values in a repeatable - // order. - to := reflect.TypeOf(s.AdditionalProperties.Value) - vo := reflect.ValueOf(s.AdditionalProperties.Value) - var values []string - switch to.Kind() { - case reflect.Slice: - for i := 0; i < vo.Len(); i++ { - vn := vo.Index(i).Interface() + // check type of properties, if we have a low level map, we need to hash the values in a repeatable + // order. + to := reflect.TypeOf(s.AdditionalProperties.Value) + vo := reflect.ValueOf(s.AdditionalProperties.Value) + var values []string + switch to.Kind() { + case reflect.Slice: + for i := 0; i < vo.Len(); i++ { + vn := vo.Index(i).Interface() - if jh, ok := vn.(low.HasValueUnTyped); ok { - vn = jh.GetValueUntyped() - fg := reflect.TypeOf(vn) - gf := reflect.ValueOf(vn) + if jh, ok := vn.(low.HasValueUnTyped); ok { + vn = jh.GetValueUntyped() + fg := reflect.TypeOf(vn) + gf := reflect.ValueOf(vn) - if fg.Kind() == reflect.Map { - for _, ky := range gf.MapKeys() { - hu := ky.Interface() - values = append(values, fmt.Sprintf("%s:%s", hu, low.GenerateHashString(gf.MapIndex(ky).Interface()))) - } - continue - } - values = append(values, fmt.Sprintf("%d:%s", i, low.GenerateHashString(vn))) - } - } - sort.Strings(values) - d = append(d, strings.Join(values, "||")) + if fg.Kind() == reflect.Map { + for _, ky := range gf.MapKeys() { + hu := ky.Interface() + values = append(values, fmt.Sprintf("%s:%s", hu, low.GenerateHashString(gf.MapIndex(ky).Interface()))) + } + continue + } + values = append(values, fmt.Sprintf("%d:%s", i, low.GenerateHashString(vn))) + } + } + sort.Strings(values) + d = append(d, strings.Join(values, "||")) - case reflect.Map: - for _, k := range vo.MapKeys() { - var x string - var l int - var v any - // extract key - if o, ok := k.Interface().(low.HasKeyNode); ok { - x = o.GetKeyNode().Value - l = o.GetKeyNode().Line - v = vo.MapIndex(k).Interface().(low.HasValueNodeUntyped).GetValueNode().Value - } - values = append(values, fmt.Sprintf("%d:%s:%s", l, x, low.GenerateHashString(v))) - } - sort.Strings(values) - d = append(d, strings.Join(values, "||")) - default: - d = append(d, low.GenerateHashString(s.AdditionalProperties.Value)) - } - } - if !s.Description.IsEmpty() { - d = append(d, fmt.Sprint(s.Description.Value)) - } - if !s.ContentEncoding.IsEmpty() { - d = append(d, fmt.Sprint(s.ContentEncoding.Value)) - } - if !s.ContentMediaType.IsEmpty() { - d = append(d, fmt.Sprint(s.ContentMediaType.Value)) - } - if !s.Default.IsEmpty() { - d = append(d, low.GenerateHashString(s.Default.Value)) - } - if !s.Nullable.IsEmpty() { - d = append(d, fmt.Sprint(s.Nullable.Value)) - } - if !s.ReadOnly.IsEmpty() { - d = append(d, fmt.Sprint(s.ReadOnly.Value)) - } - if !s.WriteOnly.IsEmpty() { - d = append(d, fmt.Sprint(s.WriteOnly.Value)) - } - if !s.Deprecated.IsEmpty() { - d = append(d, fmt.Sprint(s.Deprecated.Value)) - } - if !s.ExclusiveMaximum.IsEmpty() && s.ExclusiveMaximum.Value.IsA() { - d = append(d, fmt.Sprint(s.ExclusiveMaximum.Value.A)) - } - if !s.ExclusiveMaximum.IsEmpty() && s.ExclusiveMaximum.Value.IsB() { - d = append(d, fmt.Sprint(s.ExclusiveMaximum.Value.B)) - } - if !s.ExclusiveMinimum.IsEmpty() && s.ExclusiveMinimum.Value.IsA() { - d = append(d, fmt.Sprint(s.ExclusiveMinimum.Value.A)) - } - if !s.ExclusiveMinimum.IsEmpty() && s.ExclusiveMinimum.Value.IsB() { - d = append(d, fmt.Sprint(s.ExclusiveMinimum.Value.B)) - } - if !s.Type.IsEmpty() && s.Type.Value.IsA() { - d = append(d, fmt.Sprint(s.Type.Value.A)) - } - if !s.Type.IsEmpty() && s.Type.Value.IsB() { - j := make([]string, len(s.Type.Value.B)) - for h := range s.Type.Value.B { - j[h] = s.Type.Value.B[h].Value - } - sort.Strings(j) - d = append(d, strings.Join(j, "|")) - } + case reflect.Map: + for _, k := range vo.MapKeys() { + var x string + var l int + var v any + // extract key + if o, ok := k.Interface().(low.HasKeyNode); ok { + x = o.GetKeyNode().Value + l = o.GetKeyNode().Line + v = vo.MapIndex(k).Interface().(low.HasValueNodeUntyped).GetValueNode().Value + } + values = append(values, fmt.Sprintf("%d:%s:%s", l, x, low.GenerateHashString(v))) + } + sort.Strings(values) + d = append(d, strings.Join(values, "||")) + default: + d = append(d, low.GenerateHashString(s.AdditionalProperties.Value)) + } + } + if !s.Description.IsEmpty() { + d = append(d, fmt.Sprint(s.Description.Value)) + } + if !s.ContentEncoding.IsEmpty() { + d = append(d, fmt.Sprint(s.ContentEncoding.Value)) + } + if !s.ContentMediaType.IsEmpty() { + d = append(d, fmt.Sprint(s.ContentMediaType.Value)) + } + if !s.Default.IsEmpty() { + d = append(d, low.GenerateHashString(s.Default.Value)) + } + if !s.Nullable.IsEmpty() { + d = append(d, fmt.Sprint(s.Nullable.Value)) + } + if !s.ReadOnly.IsEmpty() { + d = append(d, fmt.Sprint(s.ReadOnly.Value)) + } + if !s.WriteOnly.IsEmpty() { + d = append(d, fmt.Sprint(s.WriteOnly.Value)) + } + if !s.Deprecated.IsEmpty() { + d = append(d, fmt.Sprint(s.Deprecated.Value)) + } + if !s.ExclusiveMaximum.IsEmpty() && s.ExclusiveMaximum.Value.IsA() { + d = append(d, fmt.Sprint(s.ExclusiveMaximum.Value.A)) + } + if !s.ExclusiveMaximum.IsEmpty() && s.ExclusiveMaximum.Value.IsB() { + d = append(d, fmt.Sprint(s.ExclusiveMaximum.Value.B)) + } + if !s.ExclusiveMinimum.IsEmpty() && s.ExclusiveMinimum.Value.IsA() { + d = append(d, fmt.Sprint(s.ExclusiveMinimum.Value.A)) + } + if !s.ExclusiveMinimum.IsEmpty() && s.ExclusiveMinimum.Value.IsB() { + d = append(d, fmt.Sprint(s.ExclusiveMinimum.Value.B)) + } + if !s.Type.IsEmpty() && s.Type.Value.IsA() { + d = append(d, fmt.Sprint(s.Type.Value.A)) + } + if !s.Type.IsEmpty() && s.Type.Value.IsB() { + j := make([]string, len(s.Type.Value.B)) + for h := range s.Type.Value.B { + j[h] = s.Type.Value.B[h].Value + } + sort.Strings(j) + d = append(d, strings.Join(j, "|")) + } - keys := make([]string, len(s.Required.Value)) - for i := range s.Required.Value { - keys[i] = s.Required.Value[i].Value - } - sort.Strings(keys) - d = append(d, keys...) + keys := make([]string, len(s.Required.Value)) + for i := range s.Required.Value { + keys[i] = s.Required.Value[i].Value + } + sort.Strings(keys) + d = append(d, keys...) - keys = make([]string, len(s.Enum.Value)) - for i := range s.Enum.Value { - keys[i] = fmt.Sprint(s.Enum.Value[i].Value) - } - sort.Strings(keys) - d = append(d, keys...) + keys = make([]string, len(s.Enum.Value)) + for i := range s.Enum.Value { + keys[i] = fmt.Sprint(s.Enum.Value[i].Value) + } + sort.Strings(keys) + d = append(d, keys...) - for i := range s.Enum.Value { - d = append(d, fmt.Sprint(s.Enum.Value[i].Value)) - } - propKeys := make([]string, len(s.Properties.Value)) - z := 0 - for i := range s.Properties.Value { - propKeys[z] = i.Value - z++ - } - sort.Strings(propKeys) - for k := range propKeys { - d = append(d, low.GenerateHashString(s.FindProperty(propKeys[k]).Value)) - } - if s.XML.Value != nil { - d = append(d, low.GenerateHashString(s.XML.Value)) - } - if s.ExternalDocs.Value != nil { - d = append(d, low.GenerateHashString(s.ExternalDocs.Value)) - } - if s.Discriminator.Value != nil { - d = append(d, low.GenerateHashString(s.Discriminator.Value)) - } + for i := range s.Enum.Value { + d = append(d, fmt.Sprint(s.Enum.Value[i].Value)) + } + propKeys := make([]string, len(s.Properties.Value)) + z := 0 + for i := range s.Properties.Value { + propKeys[z] = i.Value + z++ + } + sort.Strings(propKeys) + for k := range propKeys { + d = append(d, low.GenerateHashString(s.FindProperty(propKeys[k]).Value)) + } + if s.XML.Value != nil { + d = append(d, low.GenerateHashString(s.XML.Value)) + } + if s.ExternalDocs.Value != nil { + d = append(d, low.GenerateHashString(s.ExternalDocs.Value)) + } + if s.Discriminator.Value != nil { + d = append(d, low.GenerateHashString(s.Discriminator.Value)) + } - // hash polymorphic data - if len(s.OneOf.Value) > 0 { - oneOfKeys := make([]string, len(s.OneOf.Value)) - oneOfEntities := make(map[string]*SchemaProxy) - z = 0 - for i := range s.OneOf.Value { - g := s.OneOf.Value[i].Value - r := low.GenerateHashString(g) - oneOfEntities[r] = g - oneOfKeys[z] = r - z++ + // hash polymorphic data + if len(s.OneOf.Value) > 0 { + oneOfKeys := make([]string, len(s.OneOf.Value)) + oneOfEntities := make(map[string]*SchemaProxy) + z = 0 + for i := range s.OneOf.Value { + g := s.OneOf.Value[i].Value + r := low.GenerateHashString(g) + oneOfEntities[r] = g + oneOfKeys[z] = r + z++ - } - sort.Strings(oneOfKeys) - for k := range oneOfKeys { - d = append(d, low.GenerateHashString(oneOfEntities[oneOfKeys[k]])) - } - } + } + sort.Strings(oneOfKeys) + for k := range oneOfKeys { + d = append(d, low.GenerateHashString(oneOfEntities[oneOfKeys[k]])) + } + } - if len(s.AllOf.Value) > 0 { - allOfKeys := make([]string, len(s.AllOf.Value)) - allOfEntities := make(map[string]*SchemaProxy) - z = 0 - for i := range s.AllOf.Value { - g := s.AllOf.Value[i].Value - r := low.GenerateHashString(g) - allOfEntities[r] = g - allOfKeys[z] = r - z++ + if len(s.AllOf.Value) > 0 { + allOfKeys := make([]string, len(s.AllOf.Value)) + allOfEntities := make(map[string]*SchemaProxy) + z = 0 + for i := range s.AllOf.Value { + g := s.AllOf.Value[i].Value + r := low.GenerateHashString(g) + allOfEntities[r] = g + allOfKeys[z] = r + z++ - } - sort.Strings(allOfKeys) - for k := range allOfKeys { - d = append(d, low.GenerateHashString(allOfEntities[allOfKeys[k]])) - } - } + } + sort.Strings(allOfKeys) + for k := range allOfKeys { + d = append(d, low.GenerateHashString(allOfEntities[allOfKeys[k]])) + } + } - if len(s.AnyOf.Value) > 0 { - anyOfKeys := make([]string, len(s.AnyOf.Value)) - anyOfEntities := make(map[string]*SchemaProxy) - z = 0 - for i := range s.AnyOf.Value { - g := s.AnyOf.Value[i].Value - r := low.GenerateHashString(g) - anyOfEntities[r] = g - anyOfKeys[z] = r - z++ + if len(s.AnyOf.Value) > 0 { + anyOfKeys := make([]string, len(s.AnyOf.Value)) + anyOfEntities := make(map[string]*SchemaProxy) + z = 0 + for i := range s.AnyOf.Value { + g := s.AnyOf.Value[i].Value + r := low.GenerateHashString(g) + anyOfEntities[r] = g + anyOfKeys[z] = r + z++ - } - sort.Strings(anyOfKeys) - for k := range anyOfKeys { - d = append(d, low.GenerateHashString(anyOfEntities[anyOfKeys[k]])) - } - } + } + sort.Strings(anyOfKeys) + for k := range anyOfKeys { + d = append(d, low.GenerateHashString(anyOfEntities[anyOfKeys[k]])) + } + } - if !s.Not.IsEmpty() { - d = append(d, low.GenerateHashString(s.Not.Value)) - } + if !s.Not.IsEmpty() { + d = append(d, low.GenerateHashString(s.Not.Value)) + } - // check if items is a schema or a bool. - if !s.Items.IsEmpty() && s.Items.Value.IsA() { - d = append(d, low.GenerateHashString(s.Items.Value.A)) - } - if !s.Items.IsEmpty() && s.Items.Value.IsB() { - d = append(d, fmt.Sprint(s.Items.Value.B)) - } - // 3.1 only props - if !s.If.IsEmpty() { - d = append(d, low.GenerateHashString(s.If.Value)) - } - if !s.Else.IsEmpty() { - d = append(d, low.GenerateHashString(s.Else.Value)) - } - if !s.Then.IsEmpty() { - d = append(d, low.GenerateHashString(s.Then.Value)) - } - if !s.PropertyNames.IsEmpty() { - d = append(d, low.GenerateHashString(s.PropertyNames.Value)) - } - if !s.UnevaluatedProperties.IsEmpty() { - d = append(d, low.GenerateHashString(s.UnevaluatedProperties.Value)) - } - if !s.UnevaluatedItems.IsEmpty() { - d = append(d, low.GenerateHashString(s.UnevaluatedItems.Value)) - } - if !s.Anchor.IsEmpty() { - d = append(d, fmt.Sprint(s.Anchor.Value)) - } + // check if items is a schema or a bool. + if !s.Items.IsEmpty() && s.Items.Value.IsA() { + d = append(d, low.GenerateHashString(s.Items.Value.A)) + } + if !s.Items.IsEmpty() && s.Items.Value.IsB() { + d = append(d, fmt.Sprint(s.Items.Value.B)) + } + // 3.1 only props + if !s.If.IsEmpty() { + d = append(d, low.GenerateHashString(s.If.Value)) + } + if !s.Else.IsEmpty() { + d = append(d, low.GenerateHashString(s.Else.Value)) + } + if !s.Then.IsEmpty() { + d = append(d, low.GenerateHashString(s.Then.Value)) + } + if !s.PropertyNames.IsEmpty() { + d = append(d, low.GenerateHashString(s.PropertyNames.Value)) + } + if !s.UnevaluatedProperties.IsEmpty() { + d = append(d, low.GenerateHashString(s.UnevaluatedProperties.Value)) + } + if !s.UnevaluatedItems.IsEmpty() { + d = append(d, low.GenerateHashString(s.UnevaluatedItems.Value)) + } + if !s.Anchor.IsEmpty() { + d = append(d, fmt.Sprint(s.Anchor.Value)) + } - depSchemasKeys := make([]string, len(s.DependentSchemas.Value)) - z = 0 - for i := range s.DependentSchemas.Value { - depSchemasKeys[z] = i.Value - z++ - } - sort.Strings(depSchemasKeys) - for k := range depSchemasKeys { - d = append(d, low.GenerateHashString(s.FindDependentSchema(depSchemasKeys[k]).Value)) - } + depSchemasKeys := make([]string, len(s.DependentSchemas.Value)) + z = 0 + for i := range s.DependentSchemas.Value { + depSchemasKeys[z] = i.Value + z++ + } + sort.Strings(depSchemasKeys) + for k := range depSchemasKeys { + d = append(d, low.GenerateHashString(s.FindDependentSchema(depSchemasKeys[k]).Value)) + } - patternPropsKeys := make([]string, len(s.PatternProperties.Value)) - z = 0 - for i := range s.PatternProperties.Value { - patternPropsKeys[z] = i.Value - z++ - } - sort.Strings(patternPropsKeys) - for k := range patternPropsKeys { - d = append(d, low.GenerateHashString(s.FindPatternProperty(patternPropsKeys[k]).Value)) - } + patternPropsKeys := make([]string, len(s.PatternProperties.Value)) + z = 0 + for i := range s.PatternProperties.Value { + patternPropsKeys[z] = i.Value + z++ + } + sort.Strings(patternPropsKeys) + for k := range patternPropsKeys { + d = append(d, low.GenerateHashString(s.FindPatternProperty(patternPropsKeys[k]).Value)) + } - if len(s.PrefixItems.Value) > 0 { - itemsKeys := make([]string, len(s.PrefixItems.Value)) - itemsEntities := make(map[string]*SchemaProxy) - z = 0 - for i := range s.PrefixItems.Value { - g := s.PrefixItems.Value[i].Value - r := low.GenerateHashString(g) - itemsEntities[r] = g - itemsKeys[z] = r - z++ - } - sort.Strings(itemsKeys) - for k := range itemsKeys { - d = append(d, low.GenerateHashString(itemsEntities[itemsKeys[k]])) - } - } + if len(s.PrefixItems.Value) > 0 { + itemsKeys := make([]string, len(s.PrefixItems.Value)) + itemsEntities := make(map[string]*SchemaProxy) + z = 0 + for i := range s.PrefixItems.Value { + g := s.PrefixItems.Value[i].Value + r := low.GenerateHashString(g) + itemsEntities[r] = g + itemsKeys[z] = r + z++ + } + sort.Strings(itemsKeys) + for k := range itemsKeys { + d = append(d, low.GenerateHashString(itemsEntities[itemsKeys[k]])) + } + } - // add extensions to hash - keys = make([]string, len(s.Extensions)) - z = 0 - for k := range s.Extensions { - keys[z] = fmt.Sprintf("%s-%x", k.Value, sha256.Sum256([]byte(fmt.Sprint(s.Extensions[k].Value)))) - z++ - } - sort.Strings(keys) - d = append(d, keys...) - if s.Example.Value != nil { - d = append(d, low.GenerateHashString(s.Example.Value)) - } + // add extensions to hash + keys = make([]string, len(s.Extensions)) + z = 0 + for k := range s.Extensions { + keys[z] = fmt.Sprintf("%s-%x", k.Value, sha256.Sum256([]byte(fmt.Sprint(s.Extensions[k].Value)))) + z++ + } + sort.Strings(keys) + d = append(d, keys...) + if s.Example.Value != nil { + d = append(d, low.GenerateHashString(s.Example.Value)) + } - // contains - if !s.Contains.IsEmpty() { - d = append(d, low.GenerateHashString(s.Contains.Value)) - } - if !s.MinContains.IsEmpty() { - d = append(d, fmt.Sprint(s.MinContains.Value)) - } - if !s.MaxContains.IsEmpty() { - d = append(d, fmt.Sprint(s.MaxContains.Value)) - } - if !s.Examples.IsEmpty() { - var xph []string - for w := range s.Examples.Value { - xph = append(xph, low.GenerateHashString(s.Examples.Value[w].Value)) - } - sort.Strings(xph) - d = append(d, strings.Join(xph, "|")) - } - return sha256.Sum256([]byte(strings.Join(d, "|"))) + // contains + if !s.Contains.IsEmpty() { + d = append(d, low.GenerateHashString(s.Contains.Value)) + } + if !s.MinContains.IsEmpty() { + d = append(d, fmt.Sprint(s.MinContains.Value)) + } + if !s.MaxContains.IsEmpty() { + d = append(d, fmt.Sprint(s.MaxContains.Value)) + } + if !s.Examples.IsEmpty() { + var xph []string + for w := range s.Examples.Value { + xph = append(xph, low.GenerateHashString(s.Examples.Value[w].Value)) + } + sort.Strings(xph) + d = append(d, strings.Join(xph, "|")) + } + return sha256.Sum256([]byte(strings.Join(d, "|"))) } // FindProperty will return a ValueReference pointer containing a SchemaProxy pointer // from a property key name. if found func (s *Schema) FindProperty(name string) *low.ValueReference[*SchemaProxy] { - return low.FindItemInMap[*SchemaProxy](name, s.Properties.Value) + return low.FindItemInMap[*SchemaProxy](name, s.Properties.Value) } // FindDependentSchema will return a ValueReference pointer containing a SchemaProxy pointer // from a dependent schema key name. if found (3.1+ only) func (s *Schema) FindDependentSchema(name string) *low.ValueReference[*SchemaProxy] { - return low.FindItemInMap[*SchemaProxy](name, s.DependentSchemas.Value) + return low.FindItemInMap[*SchemaProxy](name, s.DependentSchemas.Value) } // FindPatternProperty will return a ValueReference pointer containing a SchemaProxy pointer // from a pattern property key name. if found (3.1+ only) func (s *Schema) FindPatternProperty(name string) *low.ValueReference[*SchemaProxy] { - return low.FindItemInMap[*SchemaProxy](name, s.PatternProperties.Value) + return low.FindItemInMap[*SchemaProxy](name, s.PatternProperties.Value) } // GetExtensions returns all extensions for Schema func (s *Schema) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] { - return s.Extensions + return s.Extensions } // Build will perform a number of operations. @@ -520,55 +531,55 @@ func (s *Schema) GetExtensions() map[low.KeyReference[string]]low.ValueReference // - UnevaluatedProperties // - Anchor func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error { - s.Reference = new(low.Reference) - if h, _, _ := utils.IsNodeRefValue(root); h { - ref, err := low.LocateRefNode(root, idx) - if ref != nil { - root = ref - if err != nil { - if !idx.AllowCircularReferenceResolving() { - return fmt.Errorf("build schema failed: %s", err.Error()) - } - } - } else { - return fmt.Errorf("build schema failed: reference cannot be found: '%s', line %d, col %d", - root.Content[1].Value, root.Content[1].Line, root.Content[1].Column) - } - } + s.Reference = new(low.Reference) + if h, _, _ := utils.IsNodeRefValue(root); h { + ref, err := low.LocateRefNode(root, idx) + if ref != nil { + root = ref + if err != nil { + if !idx.AllowCircularReferenceResolving() { + return fmt.Errorf("build schema failed: %s", err.Error()) + } + } + } else { + return fmt.Errorf("build schema failed: reference cannot be found: '%s', line %d, col %d", + root.Content[1].Value, root.Content[1].Line, root.Content[1].Column) + } + } - // Build model using possibly dereferenced root - if err := low.BuildModel(root, s); err != nil { - return err - } + // Build model using possibly dereferenced root + if err := low.BuildModel(root, s); err != nil { + return err + } - s.extractExtensions(root) + s.extractExtensions(root) - // determine schema type, singular (3.0) or multiple (3.1), use a variable value - _, typeLabel, typeValue := utils.FindKeyNodeFullTop(TypeLabel, root.Content) - if typeValue != nil { - if utils.IsNodeStringValue(typeValue) { - s.Type = low.NodeReference[SchemaDynamicValue[string, []low.ValueReference[string]]]{ - KeyNode: typeLabel, - ValueNode: typeValue, - Value: SchemaDynamicValue[string, []low.ValueReference[string]]{N: 0, A: typeValue.Value}, - } - } - if utils.IsNodeArray(typeValue) { + // determine schema type, singular (3.0) or multiple (3.1), use a variable value + _, typeLabel, typeValue := utils.FindKeyNodeFullTop(TypeLabel, root.Content) + if typeValue != nil { + if utils.IsNodeStringValue(typeValue) { + s.Type = low.NodeReference[SchemaDynamicValue[string, []low.ValueReference[string]]]{ + KeyNode: typeLabel, + ValueNode: typeValue, + Value: SchemaDynamicValue[string, []low.ValueReference[string]]{N: 0, A: typeValue.Value}, + } + } + if utils.IsNodeArray(typeValue) { - var refs []low.ValueReference[string] - for r := range typeValue.Content { - refs = append(refs, low.ValueReference[string]{ - Value: typeValue.Content[r].Value, - ValueNode: typeValue.Content[r], - }) - } - s.Type = low.NodeReference[SchemaDynamicValue[string, []low.ValueReference[string]]]{ - KeyNode: typeLabel, - ValueNode: typeValue, - Value: SchemaDynamicValue[string, []low.ValueReference[string]]{N: 1, B: refs}, - } - } - } + var refs []low.ValueReference[string] + for r := range typeValue.Content { + refs = append(refs, low.ValueReference[string]{ + Value: typeValue.Content[r].Value, + ValueNode: typeValue.Content[r], + }) + } + s.Type = low.NodeReference[SchemaDynamicValue[string, []low.ValueReference[string]]]{ + KeyNode: typeLabel, + ValueNode: typeValue, + Value: SchemaDynamicValue[string, []low.ValueReference[string]]{N: 1, B: refs}, + } + } + } // determine exclusive minimum type, bool (3.0) or int (3.1) _, exMinLabel, exMinValue := utils.FindKeyNodeFullTop(ExclusiveMinimumLabel, root.Content) @@ -612,664 +623,686 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error { } } - // handle schema reference type if set. (3.1) - _, schemaRefLabel, schemaRefNode := utils.FindKeyNodeFullTop(SchemaTypeLabel, root.Content) - if schemaRefNode != nil { - s.SchemaTypeRef = low.NodeReference[string]{ - Value: schemaRefNode.Value, KeyNode: schemaRefLabel, ValueNode: schemaRefLabel, - } - } + // handle schema reference type if set. (3.1) + _, schemaRefLabel, schemaRefNode := utils.FindKeyNodeFullTop(SchemaTypeLabel, root.Content) + if schemaRefNode != nil { + s.SchemaTypeRef = low.NodeReference[string]{ + Value: schemaRefNode.Value, KeyNode: schemaRefLabel, ValueNode: schemaRefLabel, + } + } - // handle anchor if set. (3.1) - _, anchorLabel, anchorNode := utils.FindKeyNodeFullTop(AnchorLabel, root.Content) - if anchorNode != nil { - s.Anchor = low.NodeReference[string]{ - Value: anchorNode.Value, KeyNode: anchorLabel, ValueNode: anchorLabel, - } - } + // handle anchor if set. (3.1) + _, anchorLabel, anchorNode := utils.FindKeyNodeFullTop(AnchorLabel, root.Content) + if anchorNode != nil { + s.Anchor = low.NodeReference[string]{ + Value: anchorNode.Value, KeyNode: anchorLabel, ValueNode: anchorLabel, + } + } - // handle example if set. (3.0) - _, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content) - if expNode != nil { - s.Example = low.NodeReference[any]{Value: ExtractExampleValue(expNode), KeyNode: expLabel, ValueNode: expNode} - } + // handle example if set. (3.0) + _, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content) + if expNode != nil { + s.Example = low.NodeReference[any]{Value: ExtractExampleValue(expNode), KeyNode: expLabel, ValueNode: expNode} + } - // handle examples if set.(3.1) - _, expArrLabel, expArrNode := utils.FindKeyNodeFullTop(ExamplesLabel, root.Content) - if expArrNode != nil { - if utils.IsNodeArray(expArrNode) { - var examples []low.ValueReference[any] - for i := range expArrNode.Content { - examples = append(examples, low.ValueReference[any]{Value: ExtractExampleValue(expArrNode.Content[i]), ValueNode: expArrNode.Content[i]}) - } - s.Examples = low.NodeReference[[]low.ValueReference[any]]{ - Value: examples, - ValueNode: expArrNode, - KeyNode: expArrLabel, - } - } - } + // handle examples if set.(3.1) + _, expArrLabel, expArrNode := utils.FindKeyNodeFullTop(ExamplesLabel, root.Content) + if expArrNode != nil { + if utils.IsNodeArray(expArrNode) { + var examples []low.ValueReference[any] + for i := range expArrNode.Content { + examples = append(examples, low.ValueReference[any]{Value: ExtractExampleValue(expArrNode.Content[i]), ValueNode: expArrNode.Content[i]}) + } + s.Examples = low.NodeReference[[]low.ValueReference[any]]{ + Value: examples, + ValueNode: expArrNode, + KeyNode: expArrLabel, + } + } + } - _, addPLabel, addPNode := utils.FindKeyNodeFullTop(AdditionalPropertiesLabel, root.Content) - if addPNode != nil { - if utils.IsNodeMap(addPNode) || utils.IsNodeArray(addPNode) { - // check if this is a reference, or an inline schema. - isRef, _, _ := utils.IsNodeRefValue(addPNode) - var sp *SchemaProxy - // now check if this object has a 'type' if so, it's a schema, if not... it's a random - // object, and we should treat it as a raw map. - if _, v := utils.FindKeyNodeTop(TypeLabel, addPNode.Content); v != nil { - sp = &SchemaProxy{ - kn: addPLabel, - vn: addPNode, - idx: idx, - } - } - if isRef { - _, vn := utils.FindKeyNodeTop("$ref", addPNode.Content) - sp = &SchemaProxy{ - kn: addPLabel, - vn: addPNode, - idx: idx, - isReference: true, - referenceLookup: vn.Value, - } - } + _, addPLabel, addPNode := utils.FindKeyNodeFullTop(AdditionalPropertiesLabel, root.Content) + if addPNode != nil { + if utils.IsNodeMap(addPNode) || utils.IsNodeArray(addPNode) { + // check if this is a reference, or an inline schema. + isRef, _, _ := utils.IsNodeRefValue(addPNode) + var sp *SchemaProxy + // now check if this object has a 'type' if so, it's a schema, if not... it's a random + // object, and we should treat it as a raw map. + if _, v := utils.FindKeyNodeTop(TypeLabel, addPNode.Content); v != nil { + sp = &SchemaProxy{ + kn: addPLabel, + vn: addPNode, + idx: idx, + } + } + if isRef { + _, vn := utils.FindKeyNodeTop("$ref", addPNode.Content) + sp = &SchemaProxy{ + kn: addPLabel, + vn: addPNode, + idx: idx, + isReference: true, + referenceLookup: vn.Value, + } + } - // if this is a reference, or a schema, we're done. - if sp != nil { - s.AdditionalProperties = low.NodeReference[any]{Value: sp, KeyNode: addPLabel, ValueNode: addPNode} - } else { + // if this is a reference, or a schema, we're done. + if sp != nil { + s.AdditionalProperties = low.NodeReference[any]{Value: sp, KeyNode: addPLabel, ValueNode: addPNode} + } else { - // if this is a map, collect all the keys and values. - if utils.IsNodeMap(addPNode) { + // if this is a map, collect all the keys and values. + if utils.IsNodeMap(addPNode) { - addProps := make(map[low.KeyReference[string]]low.ValueReference[any]) - var label string - for g := range addPNode.Content { - if g%2 == 0 { - label = addPNode.Content[g].Value - continue - } else { - addProps[low.KeyReference[string]{Value: label, KeyNode: addPNode.Content[g-1]}] = - low.ValueReference[any]{Value: addPNode.Content[g].Value, ValueNode: addPNode.Content[g]} - } - } - s.AdditionalProperties = low.NodeReference[any]{Value: addProps, KeyNode: addPLabel, ValueNode: addPNode} - } + addProps := make(map[low.KeyReference[string]]low.ValueReference[any]) + var label string + for g := range addPNode.Content { + if g%2 == 0 { + label = addPNode.Content[g].Value + continue + } else { + addProps[low.KeyReference[string]{Value: label, KeyNode: addPNode.Content[g-1]}] = + low.ValueReference[any]{Value: addPNode.Content[g].Value, ValueNode: addPNode.Content[g]} + } + } + s.AdditionalProperties = low.NodeReference[any]{Value: addProps, KeyNode: addPLabel, ValueNode: addPNode} + } - // if the node is an array, extract everything into a trackable structure - if utils.IsNodeArray(addPNode) { - var addProps []low.ValueReference[any] + // if the node is an array, extract everything into a trackable structure + if utils.IsNodeArray(addPNode) { + var addProps []low.ValueReference[any] - // if this is an array or maps, encode the map items correctly. - for i := range addPNode.Content { - if utils.IsNodeMap(addPNode.Content[i]) { - var prop map[string]any - addPNode.Content[i].Decode(&prop) - addProps = append(addProps, - low.ValueReference[any]{Value: prop, ValueNode: addPNode.Content[i]}) - } else { - addProps = append(addProps, - low.ValueReference[any]{Value: addPNode.Content[i].Value, ValueNode: addPNode.Content[i]}) - } - } + // if this is an array or maps, encode the map items correctly. + for i := range addPNode.Content { + if utils.IsNodeMap(addPNode.Content[i]) { + var prop map[string]any + _ = addPNode.Content[i].Decode(&prop) + addProps = append(addProps, + low.ValueReference[any]{Value: prop, ValueNode: addPNode.Content[i]}) + } else { + addProps = append(addProps, + low.ValueReference[any]{Value: addPNode.Content[i].Value, ValueNode: addPNode.Content[i]}) + } + } - s.AdditionalProperties = - low.NodeReference[any]{Value: addProps, KeyNode: addPLabel, ValueNode: addPNode} - } - } - } - if utils.IsNodeBoolValue(addPNode) { - b, _ := strconv.ParseBool(addPNode.Value) - s.AdditionalProperties = low.NodeReference[any]{Value: b, KeyNode: addPLabel, ValueNode: addPNode} - } - } + s.AdditionalProperties = + low.NodeReference[any]{Value: addProps, KeyNode: addPLabel, ValueNode: addPNode} + } + } + } + if utils.IsNodeBoolValue(addPNode) { + b, _ := strconv.ParseBool(addPNode.Value) + s.AdditionalProperties = low.NodeReference[any]{Value: b, KeyNode: addPLabel, ValueNode: addPNode} + } + } - // handle discriminator if set. - _, discLabel, discNode := utils.FindKeyNodeFullTop(DiscriminatorLabel, root.Content) - if discNode != nil { - var discriminator Discriminator - _ = low.BuildModel(discNode, &discriminator) - s.Discriminator = low.NodeReference[*Discriminator]{Value: &discriminator, KeyNode: discLabel, ValueNode: discNode} - } + // handle discriminator if set. + _, discLabel, discNode := utils.FindKeyNodeFullTop(DiscriminatorLabel, root.Content) + if discNode != nil { + var discriminator Discriminator + _ = low.BuildModel(discNode, &discriminator) + s.Discriminator = low.NodeReference[*Discriminator]{Value: &discriminator, KeyNode: discLabel, ValueNode: discNode} + } - // handle externalDocs if set. - _, extDocLabel, extDocNode := utils.FindKeyNodeFullTop(ExternalDocsLabel, root.Content) - if extDocNode != nil { - var exDoc ExternalDoc - _ = low.BuildModel(extDocNode, &exDoc) - _ = exDoc.Build(extDocNode, idx) // throws no errors, can't check for one. - s.ExternalDocs = low.NodeReference[*ExternalDoc]{Value: &exDoc, KeyNode: extDocLabel, ValueNode: extDocNode} - } + // handle externalDocs if set. + _, extDocLabel, extDocNode := utils.FindKeyNodeFullTop(ExternalDocsLabel, root.Content) + if extDocNode != nil { + var exDoc ExternalDoc + _ = low.BuildModel(extDocNode, &exDoc) + _ = exDoc.Build(extDocNode, idx) // throws no errors, can't check for one. + s.ExternalDocs = low.NodeReference[*ExternalDoc]{Value: &exDoc, KeyNode: extDocLabel, ValueNode: extDocNode} + } - // handle xml if set. - _, xmlLabel, xmlNode := utils.FindKeyNodeFullTop(XMLLabel, root.Content) - if xmlNode != nil { - var xml XML - _ = low.BuildModel(xmlNode, &xml) - // extract extensions if set. - _ = xml.Build(xmlNode, idx) // returns no errors, can't check for one. - s.XML = low.NodeReference[*XML]{Value: &xml, KeyNode: xmlLabel, ValueNode: xmlNode} - } + // handle xml if set. + _, xmlLabel, xmlNode := utils.FindKeyNodeFullTop(XMLLabel, root.Content) + if xmlNode != nil { + var xml XML + _ = low.BuildModel(xmlNode, &xml) + // extract extensions if set. + _ = xml.Build(xmlNode, idx) // returns no errors, can't check for one. + s.XML = low.NodeReference[*XML]{Value: &xml, KeyNode: xmlLabel, ValueNode: xmlNode} + } - // handle properties - props, err := buildPropertyMap(root, idx, PropertiesLabel) - if err != nil { - return err - } - if props != nil { - s.Properties = *props - } + // handle properties + props, err := buildPropertyMap(root, idx, PropertiesLabel) + if err != nil { + return err + } + if props != nil { + s.Properties = *props + } - // handle dependent schemas - props, err = buildPropertyMap(root, idx, DependentSchemasLabel) - if err != nil { - return err - } - if props != nil { - s.DependentSchemas = *props - } + // handle dependent schemas + props, err = buildPropertyMap(root, idx, DependentSchemasLabel) + if err != nil { + return err + } + if props != nil { + s.DependentSchemas = *props + } - // handle pattern properties - props, err = buildPropertyMap(root, idx, PatternPropertiesLabel) - if err != nil { - return err - } - if props != nil { - s.PatternProperties = *props - } + // handle pattern properties + props, err = buildPropertyMap(root, idx, PatternPropertiesLabel) + if err != nil { + return err + } + if props != nil { + s.PatternProperties = *props + } - // check items type for schema or bool (3.1 only) - itemsIsBool := false - itemsBoolValue := false - _, itemsLabel, itemsValue := utils.FindKeyNodeFullTop(ItemsLabel, root.Content) - if itemsValue != nil { - if utils.IsNodeBoolValue(itemsValue) { - itemsIsBool = true - itemsBoolValue, _ = strconv.ParseBool(itemsValue.Value) - } - } - if itemsIsBool { - s.Items = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]{ - Value: &SchemaDynamicValue[*SchemaProxy, bool]{ - B: itemsBoolValue, - N: 1, - }, - KeyNode: itemsLabel, - ValueNode: itemsValue, - } - } + // check items type for schema or bool (3.1 only) + itemsIsBool := false + itemsBoolValue := false + _, itemsLabel, itemsValue := utils.FindKeyNodeFullTop(ItemsLabel, root.Content) + if itemsValue != nil { + if utils.IsNodeBoolValue(itemsValue) { + itemsIsBool = true + itemsBoolValue, _ = strconv.ParseBool(itemsValue.Value) + } + } + if itemsIsBool { + s.Items = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]{ + Value: &SchemaDynamicValue[*SchemaProxy, bool]{ + B: itemsBoolValue, + N: 1, + }, + KeyNode: itemsLabel, + ValueNode: itemsValue, + } + } - var allOf, anyOf, oneOf, prefixItems []low.ValueReference[*SchemaProxy] - var items, not, contains, sif, selse, sthen, propertyNames, unevalItems, unevalProperties low.ValueReference[*SchemaProxy] + // check unevaluatedProperties type for schema or bool (3.1 only) + unevalIsBool := false + unevalBoolValue := true + _, unevalLabel, unevalValue := utils.FindKeyNodeFullTop(UnevaluatedPropertiesLabel, root.Content) + if unevalValue != nil { + if utils.IsNodeBoolValue(unevalValue) { + unevalIsBool = true + unevalBoolValue, _ = strconv.ParseBool(unevalValue.Value) + } + } + if unevalIsBool { + s.UnevaluatedProperties = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, *bool]]{ + Value: &SchemaDynamicValue[*SchemaProxy, *bool]{ + B: &unevalBoolValue, + N: 1, + }, + KeyNode: unevalLabel, + ValueNode: unevalValue, + } + } - _, allOfLabel, allOfValue := utils.FindKeyNodeFullTop(AllOfLabel, root.Content) - _, anyOfLabel, anyOfValue := utils.FindKeyNodeFullTop(AnyOfLabel, root.Content) - _, oneOfLabel, oneOfValue := utils.FindKeyNodeFullTop(OneOfLabel, root.Content) - _, notLabel, notValue := utils.FindKeyNodeFullTop(NotLabel, root.Content) - _, prefixItemsLabel, prefixItemsValue := utils.FindKeyNodeFullTop(PrefixItemsLabel, root.Content) - _, containsLabel, containsValue := utils.FindKeyNodeFullTop(ContainsLabel, root.Content) - _, sifLabel, sifValue := utils.FindKeyNodeFullTop(IfLabel, root.Content) - _, selseLabel, selseValue := utils.FindKeyNodeFullTop(ElseLabel, root.Content) - _, sthenLabel, sthenValue := utils.FindKeyNodeFullTop(ThenLabel, root.Content) - _, propNamesLabel, propNamesValue := utils.FindKeyNodeFullTop(PropertyNamesLabel, root.Content) + var allOf, anyOf, oneOf, prefixItems []low.ValueReference[*SchemaProxy] + var items, not, contains, sif, selse, sthen, propertyNames, unevalItems, unevalProperties low.ValueReference[*SchemaProxy] - _, unevalItemsLabel, unevalItemsValue := utils.FindKeyNodeFullTop(UnevaluatedItemsLabel, root.Content) - _, unevalPropsLabel, unevalPropsValue := utils.FindKeyNodeFullTop(UnevaluatedPropertiesLabel, root.Content) + _, allOfLabel, allOfValue := utils.FindKeyNodeFullTop(AllOfLabel, root.Content) + _, anyOfLabel, anyOfValue := utils.FindKeyNodeFullTop(AnyOfLabel, root.Content) + _, oneOfLabel, oneOfValue := utils.FindKeyNodeFullTop(OneOfLabel, root.Content) + _, notLabel, notValue := utils.FindKeyNodeFullTop(NotLabel, root.Content) + _, prefixItemsLabel, prefixItemsValue := utils.FindKeyNodeFullTop(PrefixItemsLabel, root.Content) + _, containsLabel, containsValue := utils.FindKeyNodeFullTop(ContainsLabel, root.Content) + _, sifLabel, sifValue := utils.FindKeyNodeFullTop(IfLabel, root.Content) + _, selseLabel, selseValue := utils.FindKeyNodeFullTop(ElseLabel, root.Content) + _, sthenLabel, sthenValue := utils.FindKeyNodeFullTop(ThenLabel, root.Content) + _, propNamesLabel, propNamesValue := utils.FindKeyNodeFullTop(PropertyNamesLabel, root.Content) + _, unevalItemsLabel, unevalItemsValue := utils.FindKeyNodeFullTop(UnevaluatedItemsLabel, root.Content) + _, unevalPropsLabel, unevalPropsValue := utils.FindKeyNodeFullTop(UnevaluatedPropertiesLabel, root.Content) - errorChan := make(chan error) - allOfChan := make(chan schemaProxyBuildResult) - anyOfChan := make(chan schemaProxyBuildResult) - oneOfChan := make(chan schemaProxyBuildResult) - itemsChan := make(chan schemaProxyBuildResult) - prefixItemsChan := make(chan schemaProxyBuildResult) - notChan := make(chan schemaProxyBuildResult) - containsChan := make(chan schemaProxyBuildResult) - ifChan := make(chan schemaProxyBuildResult) - elseChan := make(chan schemaProxyBuildResult) - thenChan := make(chan schemaProxyBuildResult) - propNamesChan := make(chan schemaProxyBuildResult) - unevalItemsChan := make(chan schemaProxyBuildResult) - unevalPropsChan := make(chan schemaProxyBuildResult) + errorChan := make(chan error) + allOfChan := make(chan schemaProxyBuildResult) + anyOfChan := make(chan schemaProxyBuildResult) + oneOfChan := make(chan schemaProxyBuildResult) + itemsChan := make(chan schemaProxyBuildResult) + prefixItemsChan := make(chan schemaProxyBuildResult) + notChan := make(chan schemaProxyBuildResult) + containsChan := make(chan schemaProxyBuildResult) + ifChan := make(chan schemaProxyBuildResult) + elseChan := make(chan schemaProxyBuildResult) + thenChan := make(chan schemaProxyBuildResult) + propNamesChan := make(chan schemaProxyBuildResult) + unevalItemsChan := make(chan schemaProxyBuildResult) + unevalPropsChan := make(chan schemaProxyBuildResult) - totalBuilds := countSubSchemaItems(allOfValue) + - countSubSchemaItems(anyOfValue) + - countSubSchemaItems(oneOfValue) + - countSubSchemaItems(prefixItemsValue) + totalBuilds := countSubSchemaItems(allOfValue) + + countSubSchemaItems(anyOfValue) + + countSubSchemaItems(oneOfValue) + + countSubSchemaItems(prefixItemsValue) - if allOfValue != nil { - go buildSchema(allOfChan, allOfLabel, allOfValue, errorChan, idx) - } - if anyOfValue != nil { - go buildSchema(anyOfChan, anyOfLabel, anyOfValue, errorChan, idx) - } - if oneOfValue != nil { - go buildSchema(oneOfChan, oneOfLabel, oneOfValue, errorChan, idx) - } - if prefixItemsValue != nil { - go buildSchema(prefixItemsChan, prefixItemsLabel, prefixItemsValue, errorChan, idx) - } - if notValue != nil { - totalBuilds++ - go buildSchema(notChan, notLabel, notValue, errorChan, idx) - } - if containsValue != nil { - totalBuilds++ - go buildSchema(containsChan, containsLabel, containsValue, errorChan, idx) - } - if !itemsIsBool && itemsValue != nil { - totalBuilds++ - go buildSchema(itemsChan, itemsLabel, itemsValue, errorChan, idx) - } - if sifValue != nil { - totalBuilds++ - go buildSchema(ifChan, sifLabel, sifValue, errorChan, idx) - } - if selseValue != nil { - totalBuilds++ - go buildSchema(elseChan, selseLabel, selseValue, errorChan, idx) - } - if sthenValue != nil { - totalBuilds++ - go buildSchema(thenChan, sthenLabel, sthenValue, errorChan, idx) - } - if propNamesValue != nil { - totalBuilds++ - go buildSchema(propNamesChan, propNamesLabel, propNamesValue, errorChan, idx) - } - if unevalItemsValue != nil { - totalBuilds++ - go buildSchema(unevalItemsChan, unevalItemsLabel, unevalItemsValue, errorChan, idx) - } - if unevalPropsValue != nil { - totalBuilds++ - go buildSchema(unevalPropsChan, unevalPropsLabel, unevalPropsValue, errorChan, idx) - } + if allOfValue != nil { + go buildSchema(allOfChan, allOfLabel, allOfValue, errorChan, idx) + } + if anyOfValue != nil { + go buildSchema(anyOfChan, anyOfLabel, anyOfValue, errorChan, idx) + } + if oneOfValue != nil { + go buildSchema(oneOfChan, oneOfLabel, oneOfValue, errorChan, idx) + } + if prefixItemsValue != nil { + go buildSchema(prefixItemsChan, prefixItemsLabel, prefixItemsValue, errorChan, idx) + } + if notValue != nil { + totalBuilds++ + go buildSchema(notChan, notLabel, notValue, errorChan, idx) + } + if containsValue != nil { + totalBuilds++ + go buildSchema(containsChan, containsLabel, containsValue, errorChan, idx) + } + if !itemsIsBool && itemsValue != nil { + totalBuilds++ + go buildSchema(itemsChan, itemsLabel, itemsValue, errorChan, idx) + } + if sifValue != nil { + totalBuilds++ + go buildSchema(ifChan, sifLabel, sifValue, errorChan, idx) + } + if selseValue != nil { + totalBuilds++ + go buildSchema(elseChan, selseLabel, selseValue, errorChan, idx) + } + if sthenValue != nil { + totalBuilds++ + go buildSchema(thenChan, sthenLabel, sthenValue, errorChan, idx) + } + if propNamesValue != nil { + totalBuilds++ + go buildSchema(propNamesChan, propNamesLabel, propNamesValue, errorChan, idx) + } + if unevalItemsValue != nil { + totalBuilds++ + go buildSchema(unevalItemsChan, unevalItemsLabel, unevalItemsValue, errorChan, idx) + } + if !unevalIsBool && unevalPropsValue != nil { + totalBuilds++ + go buildSchema(unevalPropsChan, unevalPropsLabel, unevalPropsValue, errorChan, idx) + } - completeCount := 0 - for completeCount < totalBuilds { - select { - case e := <-errorChan: - return e - case r := <-allOfChan: - completeCount++ - allOf = append(allOf, r.v) - case r := <-anyOfChan: - completeCount++ - anyOf = append(anyOf, r.v) - case r := <-oneOfChan: - completeCount++ - oneOf = append(oneOf, r.v) - case r := <-itemsChan: - completeCount++ - items = r.v - case r := <-prefixItemsChan: - completeCount++ - prefixItems = append(prefixItems, r.v) - case r := <-notChan: - completeCount++ - not = r.v - case r := <-containsChan: - completeCount++ - contains = r.v - case r := <-ifChan: - completeCount++ - sif = r.v - case r := <-elseChan: - completeCount++ - selse = r.v - case r := <-thenChan: - completeCount++ - sthen = r.v - case r := <-propNamesChan: - completeCount++ - propertyNames = r.v - case r := <-unevalItemsChan: - completeCount++ - unevalItems = r.v - case r := <-unevalPropsChan: - completeCount++ - unevalProperties = r.v - } - } + completeCount := 0 + for completeCount < totalBuilds { + select { + case e := <-errorChan: + return e + case r := <-allOfChan: + completeCount++ + allOf = append(allOf, r.v) + case r := <-anyOfChan: + completeCount++ + anyOf = append(anyOf, r.v) + case r := <-oneOfChan: + completeCount++ + oneOf = append(oneOf, r.v) + case r := <-itemsChan: + completeCount++ + items = r.v + case r := <-prefixItemsChan: + completeCount++ + prefixItems = append(prefixItems, r.v) + case r := <-notChan: + completeCount++ + not = r.v + case r := <-containsChan: + completeCount++ + contains = r.v + case r := <-ifChan: + completeCount++ + sif = r.v + case r := <-elseChan: + completeCount++ + selse = r.v + case r := <-thenChan: + completeCount++ + sthen = r.v + case r := <-propNamesChan: + completeCount++ + propertyNames = r.v + case r := <-unevalItemsChan: + completeCount++ + unevalItems = r.v + case r := <-unevalPropsChan: + completeCount++ + unevalProperties = r.v + } + } - if len(anyOf) > 0 { - s.AnyOf = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ - Value: anyOf, - KeyNode: anyOfLabel, - ValueNode: anyOfValue, - } - } - if len(oneOf) > 0 { - s.OneOf = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ - Value: oneOf, - KeyNode: oneOfLabel, - ValueNode: oneOfValue, - } - } - if len(allOf) > 0 { - s.AllOf = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ - Value: allOf, - KeyNode: allOfLabel, - ValueNode: allOfValue, - } - } - if !not.IsEmpty() { - s.Not = low.NodeReference[*SchemaProxy]{ - Value: not.Value, - KeyNode: notLabel, - ValueNode: notValue, - } - } - if !itemsIsBool && !items.IsEmpty() { - s.Items = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]{ - Value: &SchemaDynamicValue[*SchemaProxy, bool]{ - A: items.Value, - }, - KeyNode: itemsLabel, - ValueNode: itemsValue, - } - } - if len(prefixItems) > 0 { - s.PrefixItems = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ - Value: prefixItems, - KeyNode: prefixItemsLabel, - ValueNode: prefixItemsValue, - } - } - if !contains.IsEmpty() { - s.Contains = low.NodeReference[*SchemaProxy]{ - Value: contains.Value, - KeyNode: containsLabel, - ValueNode: containsValue, - } - } - if !sif.IsEmpty() { - s.If = low.NodeReference[*SchemaProxy]{ - Value: sif.Value, - KeyNode: sifLabel, - ValueNode: sifValue, - } - } - if !selse.IsEmpty() { - s.Else = low.NodeReference[*SchemaProxy]{ - Value: selse.Value, - KeyNode: selseLabel, - ValueNode: selseValue, - } - } - if !sthen.IsEmpty() { - s.Then = low.NodeReference[*SchemaProxy]{ - Value: sthen.Value, - KeyNode: sthenLabel, - ValueNode: sthenValue, - } - } - if !propertyNames.IsEmpty() { - s.PropertyNames = low.NodeReference[*SchemaProxy]{ - Value: propertyNames.Value, - KeyNode: propNamesLabel, - ValueNode: propNamesValue, - } - } - if !unevalItems.IsEmpty() { - s.UnevaluatedItems = low.NodeReference[*SchemaProxy]{ - Value: unevalItems.Value, - KeyNode: unevalItemsLabel, - ValueNode: unevalItemsValue, - } - } - if !unevalProperties.IsEmpty() { - s.UnevaluatedProperties = low.NodeReference[*SchemaProxy]{ - Value: unevalProperties.Value, - KeyNode: unevalPropsLabel, - ValueNode: unevalPropsValue, - } - } - return nil + if len(anyOf) > 0 { + s.AnyOf = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ + Value: anyOf, + KeyNode: anyOfLabel, + ValueNode: anyOfValue, + } + } + if len(oneOf) > 0 { + s.OneOf = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ + Value: oneOf, + KeyNode: oneOfLabel, + ValueNode: oneOfValue, + } + } + if len(allOf) > 0 { + s.AllOf = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ + Value: allOf, + KeyNode: allOfLabel, + ValueNode: allOfValue, + } + } + if !not.IsEmpty() { + s.Not = low.NodeReference[*SchemaProxy]{ + Value: not.Value, + KeyNode: notLabel, + ValueNode: notValue, + } + } + if !itemsIsBool && !items.IsEmpty() { + s.Items = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]{ + Value: &SchemaDynamicValue[*SchemaProxy, bool]{ + A: items.Value, + }, + KeyNode: itemsLabel, + ValueNode: itemsValue, + } + } + if len(prefixItems) > 0 { + s.PrefixItems = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ + Value: prefixItems, + KeyNode: prefixItemsLabel, + ValueNode: prefixItemsValue, + } + } + if !contains.IsEmpty() { + s.Contains = low.NodeReference[*SchemaProxy]{ + Value: contains.Value, + KeyNode: containsLabel, + ValueNode: containsValue, + } + } + if !sif.IsEmpty() { + s.If = low.NodeReference[*SchemaProxy]{ + Value: sif.Value, + KeyNode: sifLabel, + ValueNode: sifValue, + } + } + if !selse.IsEmpty() { + s.Else = low.NodeReference[*SchemaProxy]{ + Value: selse.Value, + KeyNode: selseLabel, + ValueNode: selseValue, + } + } + if !sthen.IsEmpty() { + s.Then = low.NodeReference[*SchemaProxy]{ + Value: sthen.Value, + KeyNode: sthenLabel, + ValueNode: sthenValue, + } + } + if !propertyNames.IsEmpty() { + s.PropertyNames = low.NodeReference[*SchemaProxy]{ + Value: propertyNames.Value, + KeyNode: propNamesLabel, + ValueNode: propNamesValue, + } + } + if !unevalItems.IsEmpty() { + s.UnevaluatedItems = low.NodeReference[*SchemaProxy]{ + Value: unevalItems.Value, + KeyNode: unevalItemsLabel, + ValueNode: unevalItemsValue, + } + } + if !unevalIsBool && !unevalProperties.IsEmpty() { + s.UnevaluatedProperties = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, *bool]]{ + Value: &SchemaDynamicValue[*SchemaProxy, *bool]{ + A: unevalProperties.Value, + }, + KeyNode: unevalPropsLabel, + ValueNode: unevalPropsValue, + } + } + return nil } func buildPropertyMap(root *yaml.Node, idx *index.SpecIndex, label string) (*low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]], error) { - // for property, build in a new thread! - bChan := make(chan schemaProxyBuildResult) + // for property, build in a new thread! + bChan := make(chan schemaProxyBuildResult) - buildProperty := func(label *yaml.Node, value *yaml.Node, c chan schemaProxyBuildResult, isRef bool, - refString string, - ) { - c <- schemaProxyBuildResult{ - k: low.KeyReference[string]{ - KeyNode: label, - Value: label.Value, - }, - v: low.ValueReference[*SchemaProxy]{ - Value: &SchemaProxy{kn: label, vn: value, idx: idx, isReference: isRef, referenceLookup: refString}, - ValueNode: value, - }, - } - } + buildProperty := func(label *yaml.Node, value *yaml.Node, c chan schemaProxyBuildResult, isRef bool, + refString string, + ) { + c <- schemaProxyBuildResult{ + k: low.KeyReference[string]{ + KeyNode: label, + Value: label.Value, + }, + v: low.ValueReference[*SchemaProxy]{ + Value: &SchemaProxy{kn: label, vn: value, idx: idx, isReference: isRef, referenceLookup: refString}, + ValueNode: value, + }, + } + } - _, propLabel, propsNode := utils.FindKeyNodeFullTop(label, root.Content) - if propsNode != nil { - propertyMap := make(map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]) - var currentProp *yaml.Node - totalProps := 0 - for i, prop := range propsNode.Content { - if i%2 == 0 { - currentProp = prop - continue - } + _, propLabel, propsNode := utils.FindKeyNodeFullTop(label, root.Content) + if propsNode != nil { + propertyMap := make(map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]) + var currentProp *yaml.Node + totalProps := 0 + for i, prop := range propsNode.Content { + if i%2 == 0 { + currentProp = prop + continue + } - // check our prop isn't reference - isRef := false - refString := "" - if h, _, l := utils.IsNodeRefValue(prop); h { - ref, _ := low.LocateRefNode(prop, idx) - if ref != nil { - isRef = true - prop = ref - refString = l - } else { - return nil, fmt.Errorf("schema properties build failed: cannot find reference %s, line %d, col %d", - prop.Content[1].Value, prop.Content[1].Line, prop.Content[1].Column) - } - } - totalProps++ - go buildProperty(currentProp, prop, bChan, isRef, refString) - } - completedProps := 0 - for completedProps < totalProps { - select { - case res := <-bChan: - completedProps++ - propertyMap[res.k] = res.v - } - } - return &low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]]{ - Value: propertyMap, - KeyNode: propLabel, - ValueNode: propsNode, - }, nil - } - return nil, nil + // check our prop isn't reference + isRef := false + refString := "" + if h, _, l := utils.IsNodeRefValue(prop); h { + ref, _ := low.LocateRefNode(prop, idx) + if ref != nil { + isRef = true + prop = ref + refString = l + } else { + return nil, fmt.Errorf("schema properties build failed: cannot find reference %s, line %d, col %d", + prop.Content[1].Value, prop.Content[1].Line, prop.Content[1].Column) + } + } + totalProps++ + go buildProperty(currentProp, prop, bChan, isRef, refString) + } + completedProps := 0 + for completedProps < totalProps { + select { + case res := <-bChan: + completedProps++ + propertyMap[res.k] = res.v + } + } + return &low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]]{ + Value: propertyMap, + KeyNode: propLabel, + ValueNode: propsNode, + }, nil + } + return nil, nil } // count the number of sub-schemas in a node. func countSubSchemaItems(node *yaml.Node) int { - if utils.IsNodeMap(node) { - return 1 - } - if utils.IsNodeArray(node) { - return len(node.Content) - } - return 0 + if utils.IsNodeMap(node) { + return 1 + } + if utils.IsNodeArray(node) { + return len(node.Content) + } + return 0 } // schema build result container used for async building. type schemaProxyBuildResult struct { - k low.KeyReference[string] - v low.ValueReference[*SchemaProxy] + k low.KeyReference[string] + v low.ValueReference[*SchemaProxy] } // extract extensions from schema func (s *Schema) extractExtensions(root *yaml.Node) { - s.Extensions = low.ExtractExtensions(root) + s.Extensions = low.ExtractExtensions(root) } // build out a child schema for parent schema. func buildSchema(schemas chan schemaProxyBuildResult, labelNode, valueNode *yaml.Node, errors chan error, idx *index.SpecIndex) { - if valueNode != nil { - type buildResult struct { - res *low.ValueReference[*SchemaProxy] - idx int - } + if valueNode != nil { + type buildResult struct { + res *low.ValueReference[*SchemaProxy] + idx int + } - syncChan := make(chan buildResult) + syncChan := make(chan buildResult) - // build out a SchemaProxy for every sub-schema. - build := func(kn *yaml.Node, vn *yaml.Node, schemaIdx int, c chan buildResult, - isRef bool, refLocation string, - ) { - // a proxy design works best here. polymorphism, pretty much guarantees that a sub-schema can - // take on circular references through polymorphism. Like the resolver, if we try and follow these - // journey's through hyperspace, we will end up creating endless amounts of threads, spinning off - // chasing down circles, that in turn spin up endless threads. - // In order to combat this, we need a schema proxy that will only resolve the schema when asked, and then - // it will only do it one level at a time. - sp := new(SchemaProxy) - sp.kn = kn - sp.vn = vn - sp.idx = idx - if isRef { - sp.referenceLookup = refLocation - sp.isReference = true - } - res := &low.ValueReference[*SchemaProxy]{ - Value: sp, - ValueNode: vn, - } - c <- buildResult{ - res: res, - idx: schemaIdx, - } - } + // build out a SchemaProxy for every sub-schema. + build := func(kn *yaml.Node, vn *yaml.Node, schemaIdx int, c chan buildResult, + isRef bool, refLocation string, + ) { + // a proxy design works best here. polymorphism, pretty much guarantees that a sub-schema can + // take on circular references through polymorphism. Like the resolver, if we try and follow these + // journey's through hyperspace, we will end up creating endless amounts of threads, spinning off + // chasing down circles, that in turn spin up endless threads. + // In order to combat this, we need a schema proxy that will only resolve the schema when asked, and then + // it will only do it one level at a time. + sp := new(SchemaProxy) + sp.kn = kn + sp.vn = vn + sp.idx = idx + if isRef { + sp.referenceLookup = refLocation + sp.isReference = true + } + res := &low.ValueReference[*SchemaProxy]{ + Value: sp, + ValueNode: vn, + } + c <- buildResult{ + res: res, + idx: schemaIdx, + } + } - isRef := false - refLocation := "" - if utils.IsNodeMap(valueNode) { - h := false - if h, _, refLocation = utils.IsNodeRefValue(valueNode); h { - isRef = true - ref, _ := low.LocateRefNode(valueNode, idx) - if ref != nil { - valueNode = ref - } else { - errors <- fmt.Errorf("build schema failed: reference cannot be found: %s, line %d, col %d", - valueNode.Content[1].Value, valueNode.Content[1].Line, valueNode.Content[1].Column) - } - } + isRef := false + refLocation := "" + if utils.IsNodeMap(valueNode) { + h := false + if h, _, refLocation = utils.IsNodeRefValue(valueNode); h { + isRef = true + ref, _ := low.LocateRefNode(valueNode, idx) + if ref != nil { + valueNode = ref + } else { + errors <- fmt.Errorf("build schema failed: reference cannot be found: %s, line %d, col %d", + valueNode.Content[1].Value, valueNode.Content[1].Line, valueNode.Content[1].Column) + } + } - // this only runs once, however to keep things consistent, it makes sense to use the same async method - // that arrays will use. - go build(labelNode, valueNode, -1, syncChan, isRef, refLocation) - select { - case r := <-syncChan: - schemas <- schemaProxyBuildResult{ - k: low.KeyReference[string]{ - KeyNode: labelNode, - Value: labelNode.Value, - }, - v: *r.res, - } - } - } - if utils.IsNodeArray(valueNode) { - refBuilds := 0 - results := make([]*low.ValueReference[*SchemaProxy], len(valueNode.Content)) + // this only runs once, however to keep things consistent, it makes sense to use the same async method + // that arrays will use. + go build(labelNode, valueNode, -1, syncChan, isRef, refLocation) + select { + case r := <-syncChan: + schemas <- schemaProxyBuildResult{ + k: low.KeyReference[string]{ + KeyNode: labelNode, + Value: labelNode.Value, + }, + v: *r.res, + } + } + } + if utils.IsNodeArray(valueNode) { + refBuilds := 0 + results := make([]*low.ValueReference[*SchemaProxy], len(valueNode.Content)) - for i, vn := range valueNode.Content { - isRef = false - h := false - if h, _, refLocation = utils.IsNodeRefValue(vn); h { - isRef = true - ref, _ := low.LocateRefNode(vn, idx) - if ref != nil { - vn = ref - } else { - err := fmt.Errorf("build schema failed: reference cannot be found: %s, line %d, col %d", - vn.Content[1].Value, vn.Content[1].Line, vn.Content[1].Column) - errors <- err - return - } - } - refBuilds++ - go build(vn, vn, i, syncChan, isRef, refLocation) - } + for i, vn := range valueNode.Content { + isRef = false + h := false + if h, _, refLocation = utils.IsNodeRefValue(vn); h { + isRef = true + ref, _ := low.LocateRefNode(vn, idx) + if ref != nil { + vn = ref + } else { + err := fmt.Errorf("build schema failed: reference cannot be found: %s, line %d, col %d", + vn.Content[1].Value, vn.Content[1].Line, vn.Content[1].Column) + errors <- err + return + } + } + refBuilds++ + go build(vn, vn, i, syncChan, isRef, refLocation) + } - completedBuilds := 0 - for completedBuilds < refBuilds { - select { - case res := <-syncChan: - completedBuilds++ - results[res.idx] = res.res - } - } + completedBuilds := 0 + for completedBuilds < refBuilds { + select { + case res := <-syncChan: + completedBuilds++ + results[res.idx] = res.res + } + } - for _, r := range results { - schemas <- schemaProxyBuildResult{ - k: low.KeyReference[string]{ - KeyNode: labelNode, - Value: labelNode.Value, - }, - v: *r, - } - } - } - } + for _, r := range results { + schemas <- schemaProxyBuildResult{ + k: low.KeyReference[string]{ + KeyNode: labelNode, + Value: labelNode.Value, + }, + v: *r, + } + } + } + } } // ExtractSchema will return a pointer to a NodeReference that contains a *SchemaProxy if successful. The function // will specifically look for a key node named 'schema' and extract the value mapped to that key. If the operation // fails then no NodeReference is returned and an error is returned instead. func ExtractSchema(root *yaml.Node, idx *index.SpecIndex) (*low.NodeReference[*SchemaProxy], error) { - var schLabel, schNode *yaml.Node - errStr := "schema build failed: reference '%s' cannot be found at line %d, col %d" + var schLabel, schNode *yaml.Node + errStr := "schema build failed: reference '%s' cannot be found at line %d, col %d" - isRef := false - refLocation := "" - if rf, rl, _ := utils.IsNodeRefValue(root); rf { - // locate reference in index. - isRef = true - ref, _ := low.LocateRefNode(root, idx) - if ref != nil { - schNode = ref - schLabel = rl - } else { - return nil, fmt.Errorf(errStr, - root.Content[1].Value, root.Content[1].Line, root.Content[1].Column) - } - } else { - _, schLabel, schNode = utils.FindKeyNodeFull(SchemaLabel, root.Content) - if schNode != nil { - h := false - if h, _, refLocation = utils.IsNodeRefValue(schNode); h { - isRef = true - ref, _ := low.LocateRefNode(schNode, idx) - if ref != nil { - schNode = ref - } else { - return nil, fmt.Errorf(errStr, - schNode.Content[1].Value, schNode.Content[1].Line, schNode.Content[1].Column) - } - } - } - } + isRef := false + refLocation := "" + if rf, rl, _ := utils.IsNodeRefValue(root); rf { + // locate reference in index. + isRef = true + ref, _ := low.LocateRefNode(root, idx) + if ref != nil { + schNode = ref + schLabel = rl + } else { + return nil, fmt.Errorf(errStr, + root.Content[1].Value, root.Content[1].Line, root.Content[1].Column) + } + } else { + _, schLabel, schNode = utils.FindKeyNodeFull(SchemaLabel, root.Content) + if schNode != nil { + h := false + if h, _, refLocation = utils.IsNodeRefValue(schNode); h { + isRef = true + ref, _ := low.LocateRefNode(schNode, idx) + if ref != nil { + schNode = ref + } else { + return nil, fmt.Errorf(errStr, + schNode.Content[1].Value, schNode.Content[1].Line, schNode.Content[1].Column) + } + } + } + } - if schNode != nil { - // check if schema has already been built. - schema := &SchemaProxy{kn: schLabel, vn: schNode, idx: idx, isReference: isRef, referenceLookup: refLocation} - return &low.NodeReference[*SchemaProxy]{Value: schema, KeyNode: schLabel, ValueNode: schNode, ReferenceNode: isRef, - Reference: refLocation}, nil - } - return nil, nil + if schNode != nil { + // check if schema has already been built. + schema := &SchemaProxy{kn: schLabel, vn: schNode, idx: idx, isReference: isRef, referenceLookup: refLocation} + return &low.NodeReference[*SchemaProxy]{Value: schema, KeyNode: schLabel, ValueNode: schNode, ReferenceNode: isRef, + Reference: refLocation}, nil + } + return nil, nil } diff --git a/datamodel/low/base/schema_test.go b/datamodel/low/base/schema_test.go index 34a9942..e679cb2 100644 --- a/datamodel/low/base/schema_test.go +++ b/datamodel/low/base/schema_test.go @@ -1,18 +1,18 @@ package base import ( - "testing" + "testing" - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/index" - "github.com/pb33f/libopenapi/resolver" - "github.com/pb33f/libopenapi/utils" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/resolver" + "github.com/pb33f/libopenapi/utils" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" ) func test_get_schema_blob() string { - return `type: object + return `type: object description: something object if: type: string @@ -156,245 +156,245 @@ $anchor: anchor` } func Test_Schema(t *testing.T) { - testSpec := test_get_schema_blob() + testSpec := test_get_schema_blob() - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(testSpec), &rootNode) - assert.NoError(t, mErr) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(testSpec), &rootNode) + assert.NoError(t, mErr) - sch := Schema{} - mbErr := low.BuildModel(rootNode.Content[0], &sch) - assert.NoError(t, mbErr) + sch := Schema{} + mbErr := low.BuildModel(rootNode.Content[0], &sch) + assert.NoError(t, mbErr) - schErr := sch.Build(rootNode.Content[0], nil) - assert.NoError(t, schErr) - assert.Equal(t, "something object", sch.Description.Value) - assert.True(t, sch.AdditionalProperties.Value.(bool)) + schErr := sch.Build(rootNode.Content[0], nil) + assert.NoError(t, schErr) + assert.Equal(t, "something object", sch.Description.Value) + assert.True(t, sch.AdditionalProperties.Value.(bool)) - assert.Len(t, sch.Properties.Value, 2) - v := sch.FindProperty("somethingB") + assert.Len(t, sch.Properties.Value, 2) + v := sch.FindProperty("somethingB") - assert.Equal(t, "https://pb33f.io", v.Value.Schema().ExternalDocs.Value.URL.Value) - assert.Equal(t, "the best docs", v.Value.Schema().ExternalDocs.Value.Description.Value) + assert.Equal(t, "https://pb33f.io", v.Value.Schema().ExternalDocs.Value.URL.Value) + assert.Equal(t, "the best docs", v.Value.Schema().ExternalDocs.Value.Description.Value) - assert.True(t, v.Value.Schema().ExclusiveMinimum.Value.A) - assert.True(t, v.Value.Schema().ExclusiveMaximum.Value.A) + assert.True(t, v.Value.Schema().ExclusiveMinimum.Value.A) + assert.True(t, v.Value.Schema().ExclusiveMaximum.Value.A) - j := v.Value.Schema().FindProperty("somethingBProp").Value.Schema() - k := v.Value.Schema().FindProperty("somethingBProp").Value + j := v.Value.Schema().FindProperty("somethingBProp").Value.Schema() + k := v.Value.Schema().FindProperty("somethingBProp").Value - assert.Equal(t, k, j.ParentProxy) + assert.Equal(t, k, j.ParentProxy) - assert.NotNil(t, j) - assert.NotNil(t, j.XML.Value) - assert.Equal(t, "an xml thing", j.XML.Value.Name.Value) - assert.Equal(t, "an xml namespace", j.XML.Value.Namespace.Value) - assert.Equal(t, "a prefix", j.XML.Value.Prefix.Value) - assert.Equal(t, true, j.XML.Value.Attribute.Value) - assert.Len(t, j.XML.Value.Extensions, 1) - assert.Len(t, j.XML.Value.GetExtensions(), 1) + assert.NotNil(t, j) + assert.NotNil(t, j.XML.Value) + assert.Equal(t, "an xml thing", j.XML.Value.Name.Value) + assert.Equal(t, "an xml namespace", j.XML.Value.Namespace.Value) + assert.Equal(t, "a prefix", j.XML.Value.Prefix.Value) + assert.Equal(t, true, j.XML.Value.Attribute.Value) + assert.Len(t, j.XML.Value.Extensions, 1) + assert.Len(t, j.XML.Value.GetExtensions(), 1) - assert.NotNil(t, v.Value.Schema().AdditionalProperties.Value) + assert.NotNil(t, v.Value.Schema().AdditionalProperties.Value) - var addProps map[string]interface{} - v.Value.Schema().AdditionalProperties.ValueNode.Decode(&addProps) - assert.Equal(t, "yes", addProps["why"]) - assert.Equal(t, true, addProps["thatIs"]) + var addProps map[string]interface{} + v.Value.Schema().AdditionalProperties.ValueNode.Decode(&addProps) + assert.Equal(t, "yes", addProps["why"]) + assert.Equal(t, true, addProps["thatIs"]) - // check polymorphic values allOf - f := sch.AllOf.Value[0].Value.Schema() - assert.Equal(t, "an allof thing", f.Description.Value) - assert.Len(t, f.Properties.Value, 2) + // check polymorphic values allOf + f := sch.AllOf.Value[0].Value.Schema() + assert.Equal(t, "an allof thing", f.Description.Value) + assert.Len(t, f.Properties.Value, 2) - v = f.FindProperty("allOfA") - assert.NotNil(t, v) + v = f.FindProperty("allOfA") + assert.NotNil(t, v) - io := v.Value.Schema() + io := v.Value.Schema() - assert.Equal(t, "allOfA description", io.Description.Value) - assert.Equal(t, "allOfAExp", io.Example.Value) + assert.Equal(t, "allOfA description", io.Description.Value) + assert.Equal(t, "allOfAExp", io.Example.Value) - qw := f.FindProperty("allOfB").Value.Schema() - assert.NotNil(t, v) - assert.Equal(t, "allOfB description", qw.Description.Value) - assert.Equal(t, "allOfBExp", qw.Example.Value) + qw := f.FindProperty("allOfB").Value.Schema() + assert.NotNil(t, v) + assert.Equal(t, "allOfB description", qw.Description.Value) + assert.Equal(t, "allOfBExp", qw.Example.Value) - // check polymorphic values anyOf - assert.Equal(t, "an anyOf thing", sch.AnyOf.Value[0].Value.Schema().Description.Value) - assert.Len(t, sch.AnyOf.Value[0].Value.Schema().Properties.Value, 2) + // check polymorphic values anyOf + assert.Equal(t, "an anyOf thing", sch.AnyOf.Value[0].Value.Schema().Description.Value) + assert.Len(t, sch.AnyOf.Value[0].Value.Schema().Properties.Value, 2) - v = sch.AnyOf.Value[0].Value.Schema().FindProperty("anyOfA") - assert.NotNil(t, v) - assert.Equal(t, "anyOfA description", v.Value.Schema().Description.Value) - assert.Equal(t, "anyOfAExp", v.Value.Schema().Example.Value) + v = sch.AnyOf.Value[0].Value.Schema().FindProperty("anyOfA") + assert.NotNil(t, v) + assert.Equal(t, "anyOfA description", v.Value.Schema().Description.Value) + assert.Equal(t, "anyOfAExp", v.Value.Schema().Example.Value) - v = sch.AnyOf.Value[0].Value.Schema().FindProperty("anyOfB") - assert.NotNil(t, v) - assert.Equal(t, "anyOfB description", v.Value.Schema().Description.Value) - assert.Equal(t, "anyOfBExp", v.Value.Schema().Example.Value) + v = sch.AnyOf.Value[0].Value.Schema().FindProperty("anyOfB") + assert.NotNil(t, v) + assert.Equal(t, "anyOfB description", v.Value.Schema().Description.Value) + assert.Equal(t, "anyOfBExp", v.Value.Schema().Example.Value) - // check polymorphic values oneOf - assert.Equal(t, "a oneof thing", sch.OneOf.Value[0].Value.Schema().Description.Value) - assert.Len(t, sch.OneOf.Value[0].Value.Schema().Properties.Value, 2) + // check polymorphic values oneOf + assert.Equal(t, "a oneof thing", sch.OneOf.Value[0].Value.Schema().Description.Value) + assert.Len(t, sch.OneOf.Value[0].Value.Schema().Properties.Value, 2) - v = sch.OneOf.Value[0].Value.Schema().FindProperty("oneOfA") - assert.NotNil(t, v) - assert.Equal(t, "oneOfA description", v.Value.Schema().Description.Value) - assert.Equal(t, "oneOfAExp", v.Value.Schema().Example.Value) + v = sch.OneOf.Value[0].Value.Schema().FindProperty("oneOfA") + assert.NotNil(t, v) + assert.Equal(t, "oneOfA description", v.Value.Schema().Description.Value) + assert.Equal(t, "oneOfAExp", v.Value.Schema().Example.Value) - v = sch.OneOf.Value[0].Value.Schema().FindProperty("oneOfB") - assert.NotNil(t, v) - assert.Equal(t, "oneOfB description", v.Value.Schema().Description.Value) - assert.Equal(t, "oneOfBExp", v.Value.Schema().Example.Value) + v = sch.OneOf.Value[0].Value.Schema().FindProperty("oneOfB") + assert.NotNil(t, v) + assert.Equal(t, "oneOfB description", v.Value.Schema().Description.Value) + assert.Equal(t, "oneOfBExp", v.Value.Schema().Example.Value) - // check values NOT - assert.Equal(t, "a not thing", sch.Not.Value.Schema().Description.Value) - assert.Len(t, sch.Not.Value.Schema().Properties.Value, 2) + // check values NOT + assert.Equal(t, "a not thing", sch.Not.Value.Schema().Description.Value) + assert.Len(t, sch.Not.Value.Schema().Properties.Value, 2) - v = sch.Not.Value.Schema().FindProperty("notA") - assert.NotNil(t, v) - assert.Equal(t, "notA description", v.Value.Schema().Description.Value) - assert.Equal(t, "notAExp", v.Value.Schema().Example.Value) + v = sch.Not.Value.Schema().FindProperty("notA") + assert.NotNil(t, v) + assert.Equal(t, "notA description", v.Value.Schema().Description.Value) + assert.Equal(t, "notAExp", v.Value.Schema().Example.Value) - v = sch.Not.Value.Schema().FindProperty("notB") - assert.NotNil(t, v) - assert.Equal(t, "notB description", v.Value.Schema().Description.Value) - assert.Equal(t, "notBExp", v.Value.Schema().Example.Value) + v = sch.Not.Value.Schema().FindProperty("notB") + assert.NotNil(t, v) + assert.Equal(t, "notB description", v.Value.Schema().Description.Value) + assert.Equal(t, "notBExp", v.Value.Schema().Example.Value) - // check values Items - assert.Equal(t, "an items thing", sch.Items.Value.A.Schema().Description.Value) - assert.Len(t, sch.Items.Value.A.Schema().Properties.Value, 2) + // check values Items + assert.Equal(t, "an items thing", sch.Items.Value.A.Schema().Description.Value) + assert.Len(t, sch.Items.Value.A.Schema().Properties.Value, 2) - v = sch.Items.Value.A.Schema().FindProperty("itemsA") - assert.NotNil(t, v) - assert.Equal(t, "itemsA description", v.Value.Schema().Description.Value) - assert.Equal(t, "itemsAExp", v.Value.Schema().Example.Value) + v = sch.Items.Value.A.Schema().FindProperty("itemsA") + assert.NotNil(t, v) + assert.Equal(t, "itemsA description", v.Value.Schema().Description.Value) + assert.Equal(t, "itemsAExp", v.Value.Schema().Example.Value) - v = sch.Items.Value.A.Schema().FindProperty("itemsB") - assert.NotNil(t, v) - assert.Equal(t, "itemsB description", v.Value.Schema().Description.Value) - assert.Equal(t, "itemsBExp", v.Value.Schema().Example.Value) + v = sch.Items.Value.A.Schema().FindProperty("itemsB") + assert.NotNil(t, v) + assert.Equal(t, "itemsB description", v.Value.Schema().Description.Value) + assert.Equal(t, "itemsBExp", v.Value.Schema().Example.Value) - // check values PrefixItems - assert.Equal(t, "an items thing", sch.PrefixItems.Value[0].Value.Schema().Description.Value) - assert.Len(t, sch.PrefixItems.Value[0].Value.Schema().Properties.Value, 2) + // check values PrefixItems + assert.Equal(t, "an items thing", sch.PrefixItems.Value[0].Value.Schema().Description.Value) + assert.Len(t, sch.PrefixItems.Value[0].Value.Schema().Properties.Value, 2) - v = sch.PrefixItems.Value[0].Value.Schema().FindProperty("itemsA") - assert.NotNil(t, v) - assert.Equal(t, "itemsA description", v.Value.Schema().Description.Value) - assert.Equal(t, "itemsAExp", v.Value.Schema().Example.Value) + v = sch.PrefixItems.Value[0].Value.Schema().FindProperty("itemsA") + assert.NotNil(t, v) + assert.Equal(t, "itemsA description", v.Value.Schema().Description.Value) + assert.Equal(t, "itemsAExp", v.Value.Schema().Example.Value) - v = sch.PrefixItems.Value[0].Value.Schema().FindProperty("itemsB") - assert.NotNil(t, v) - assert.Equal(t, "itemsB description", v.Value.Schema().Description.Value) - assert.Equal(t, "itemsBExp", v.Value.Schema().Example.Value) + v = sch.PrefixItems.Value[0].Value.Schema().FindProperty("itemsB") + assert.NotNil(t, v) + assert.Equal(t, "itemsB description", v.Value.Schema().Description.Value) + assert.Equal(t, "itemsBExp", v.Value.Schema().Example.Value) - // check discriminator - assert.NotNil(t, sch.Discriminator.Value) - assert.Equal(t, "athing", sch.Discriminator.Value.PropertyName.Value) - assert.Len(t, sch.Discriminator.Value.Mapping.Value, 2) - mv := sch.Discriminator.Value.FindMappingValue("log") - assert.Equal(t, "cat", mv.Value) - mv = sch.Discriminator.Value.FindMappingValue("pizza") - assert.Equal(t, "party", mv.Value) + // check discriminator + assert.NotNil(t, sch.Discriminator.Value) + assert.Equal(t, "athing", sch.Discriminator.Value.PropertyName.Value) + assert.Len(t, sch.Discriminator.Value.Mapping.Value, 2) + mv := sch.Discriminator.Value.FindMappingValue("log") + assert.Equal(t, "cat", mv.Value) + mv = sch.Discriminator.Value.FindMappingValue("pizza") + assert.Equal(t, "party", mv.Value) - // check 3.1 properties. - assert.Equal(t, "int", sch.Contains.Value.Schema().Type.Value.A) - assert.Equal(t, int64(1), sch.MinContains.Value) - assert.Equal(t, int64(10), sch.MaxContains.Value) - assert.Equal(t, "string", sch.If.Value.Schema().Type.Value.A) - assert.Equal(t, "integer", sch.Else.Value.Schema().Type.Value.A) - assert.Equal(t, "boolean", sch.Then.Value.Schema().Type.Value.A) - assert.Equal(t, "string", sch.FindDependentSchema("schemaOne").Value.Schema().Type.Value.A) - assert.Equal(t, "string", sch.FindPatternProperty("patternOne").Value.Schema().Type.Value.A) - assert.Equal(t, "string", sch.PropertyNames.Value.Schema().Type.Value.A) - assert.Equal(t, "boolean", sch.UnevaluatedItems.Value.Schema().Type.Value.A) - assert.Equal(t, "integer", sch.UnevaluatedProperties.Value.Schema().Type.Value.A) - assert.Equal(t, "anchor", sch.Anchor.Value) + // check 3.1 properties. + assert.Equal(t, "int", sch.Contains.Value.Schema().Type.Value.A) + assert.Equal(t, int64(1), sch.MinContains.Value) + assert.Equal(t, int64(10), sch.MaxContains.Value) + assert.Equal(t, "string", sch.If.Value.Schema().Type.Value.A) + assert.Equal(t, "integer", sch.Else.Value.Schema().Type.Value.A) + assert.Equal(t, "boolean", sch.Then.Value.Schema().Type.Value.A) + assert.Equal(t, "string", sch.FindDependentSchema("schemaOne").Value.Schema().Type.Value.A) + assert.Equal(t, "string", sch.FindPatternProperty("patternOne").Value.Schema().Type.Value.A) + assert.Equal(t, "string", sch.PropertyNames.Value.Schema().Type.Value.A) + assert.Equal(t, "boolean", sch.UnevaluatedItems.Value.Schema().Type.Value.A) + assert.Equal(t, "integer", sch.UnevaluatedProperties.Value.A.Schema().Type.Value.A) + assert.Equal(t, "anchor", sch.Anchor.Value) } func TestSchemaAllOfSequenceOrder(t *testing.T) { - testSpec := test_get_allOf_schema_blob() + testSpec := test_get_allOf_schema_blob() - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(testSpec), &rootNode) - assert.NoError(t, mErr) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(testSpec), &rootNode) + assert.NoError(t, mErr) - // test data is a map with one node - mapContent := rootNode.Content[0].Content + // test data is a map with one node + mapContent := rootNode.Content[0].Content - _, vn := utils.FindKeyNodeTop(AllOfLabel, mapContent) - assert.True(t, utils.IsNodeArray(vn)) + _, vn := utils.FindKeyNodeTop(AllOfLabel, mapContent) + assert.True(t, utils.IsNodeArray(vn)) - want := []string{} + want := []string{} - // Go over every element in AllOf and grab description - // Odd: object - // Event: description - for i := range vn.Content { - assert.True(t, utils.IsNodeMap(vn.Content[i])) - _, vn := utils.FindKeyNodeTop("description", vn.Content[i].Content) - assert.True(t, utils.IsNodeStringValue(vn)) - want = append(want, vn.Value) - } + // Go over every element in AllOf and grab description + // Odd: object + // Event: description + for i := range vn.Content { + assert.True(t, utils.IsNodeMap(vn.Content[i])) + _, vn := utils.FindKeyNodeTop("description", vn.Content[i].Content) + assert.True(t, utils.IsNodeStringValue(vn)) + want = append(want, vn.Value) + } - sch := Schema{} - mbErr := low.BuildModel(rootNode.Content[0], &sch) - assert.NoError(t, mbErr) + sch := Schema{} + mbErr := low.BuildModel(rootNode.Content[0], &sch) + assert.NoError(t, mbErr) - schErr := sch.Build(rootNode.Content[0], nil) - assert.NoError(t, schErr) - assert.Equal(t, "allOf sequence check", sch.Description.Value) + schErr := sch.Build(rootNode.Content[0], nil) + assert.NoError(t, schErr) + assert.Equal(t, "allOf sequence check", sch.Description.Value) - got := []string{} - for i := range sch.AllOf.Value { - v := sch.AllOf.Value[i] - got = append(got, v.Value.Schema().Description.Value) - } + got := []string{} + for i := range sch.AllOf.Value { + v := sch.AllOf.Value[i] + got = append(got, v.Value.Schema().Description.Value) + } - assert.Equal(t, want, got) + assert.Equal(t, want, got) } func TestSchema_Hash(t *testing.T) { - // create two versions - testSpec := test_get_schema_blob() - var sc1n yaml.Node - _ = yaml.Unmarshal([]byte(testSpec), &sc1n) - sch1 := Schema{} - _ = low.BuildModel(&sc1n, &sch1) - _ = sch1.Build(sc1n.Content[0], nil) + // create two versions + testSpec := test_get_schema_blob() + var sc1n yaml.Node + _ = yaml.Unmarshal([]byte(testSpec), &sc1n) + sch1 := Schema{} + _ = low.BuildModel(&sc1n, &sch1) + _ = sch1.Build(sc1n.Content[0], nil) - var sc2n yaml.Node - _ = yaml.Unmarshal([]byte(testSpec), &sc2n) - sch2 := Schema{} - _ = low.BuildModel(&sc2n, &sch2) - _ = sch2.Build(sc2n.Content[0], nil) + var sc2n yaml.Node + _ = yaml.Unmarshal([]byte(testSpec), &sc2n) + sch2 := Schema{} + _ = low.BuildModel(&sc2n, &sch2) + _ = sch2.Build(sc2n.Content[0], nil) - assert.Equal(t, sch1.Hash(), sch2.Hash()) + assert.Equal(t, sch1.Hash(), sch2.Hash()) } func BenchmarkSchema_Hash(b *testing.B) { - // create two versions - testSpec := test_get_schema_blob() - var sc1n yaml.Node - _ = yaml.Unmarshal([]byte(testSpec), &sc1n) - sch1 := Schema{} - _ = low.BuildModel(&sc1n, &sch1) - _ = sch1.Build(sc1n.Content[0], nil) + // create two versions + testSpec := test_get_schema_blob() + var sc1n yaml.Node + _ = yaml.Unmarshal([]byte(testSpec), &sc1n) + sch1 := Schema{} + _ = low.BuildModel(&sc1n, &sch1) + _ = sch1.Build(sc1n.Content[0], nil) - var sc2n yaml.Node - _ = yaml.Unmarshal([]byte(testSpec), &sc2n) - sch2 := Schema{} - _ = low.BuildModel(&sc2n, &sch2) - _ = sch2.Build(sc2n.Content[0], nil) + var sc2n yaml.Node + _ = yaml.Unmarshal([]byte(testSpec), &sc2n) + sch2 := Schema{} + _ = low.BuildModel(&sc2n, &sch2) + _ = sch2.Build(sc2n.Content[0], nil) - for i := 0; i < b.N; i++ { - assert.Equal(b, sch1.Hash(), sch2.Hash()) - } + for i := 0; i < b.N; i++ { + assert.Equal(b, sch1.Hash(), sch2.Hash()) + } } func Test_Schema_31(t *testing.T) { - testSpec := `$schema: https://something + testSpec := `$schema: https://something type: - object - null @@ -407,13 +407,13 @@ items: true examples: - testing` - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(testSpec), &rootNode) - assert.NoError(t, mErr) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(testSpec), &rootNode) + assert.NoError(t, mErr) - sch := Schema{} - mbErr := low.BuildModel(rootNode.Content[0], &sch) - assert.NoError(t, mbErr) + sch := Schema{} + mbErr := low.BuildModel(rootNode.Content[0], &sch) + assert.NoError(t, mbErr) schErr := sch.Build(rootNode.Content[0], nil) assert.NoError(t, schErr) @@ -435,108 +435,108 @@ examples: } func TestSchema_Build_PropsLookup(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: description: this is something type: string` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `type: object + yml = `type: object properties: aValue: $ref: '#/components/schemas/Something'` - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - var n Schema - err := n.Build(idxNode.Content[0], idx) - assert.NoError(t, err) - assert.Equal(t, "this is something", n.FindProperty("aValue").Value.Schema().Description.Value) + var n Schema + err := n.Build(idxNode.Content[0], idx) + assert.NoError(t, err) + assert.Equal(t, "this is something", n.FindProperty("aValue").Value.Schema().Description.Value) } func TestSchema_Build_PropsLookup_Fail(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: description: this is something type: string` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `type: object + yml = `type: object properties: aValue: $ref: '#/bork'` - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - var n Schema - err := n.Build(idxNode.Content[0], idx) - assert.Error(t, err) + var n Schema + err := n.Build(idxNode.Content[0], idx) + assert.Error(t, err) } func TestSchema_Build_DependentSchemas_Fail(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: description: this is something type: string` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `type: object + yml = `type: object dependentSchemas: aValue: $ref: '#/bork'` - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - var n Schema - err := n.Build(idxNode.Content[0], idx) - assert.Error(t, err) + var n Schema + err := n.Build(idxNode.Content[0], idx) + assert.Error(t, err) } func TestSchema_Build_PatternProperties_Fail(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: description: this is something type: string` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `type: object + yml = `type: object patternProperties: aValue: $ref: '#/bork'` - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - var n Schema - err := n.Build(idxNode.Content[0], idx) - assert.Error(t, err) + var n Schema + err := n.Build(idxNode.Content[0], idx) + assert.Error(t, err) } func Test_Schema_Polymorphism_Array_Ref(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: type: object @@ -547,12 +547,12 @@ func Test_Schema_Polymorphism_Array_Ref(t *testing.T) { description: a property example: anything` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `type: object + yml = `type: object allOf: - $ref: '#/components/schemas/Something' oneOf: @@ -564,26 +564,26 @@ not: items: - $ref: '#/components/schemas/Something'` - var sch Schema - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var sch Schema + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - err := low.BuildModel(&idxNode, &sch) - assert.NoError(t, err) + err := low.BuildModel(&idxNode, &sch) + assert.NoError(t, err) - schErr := sch.Build(idxNode.Content[0], idx) - assert.NoError(t, schErr) + schErr := sch.Build(idxNode.Content[0], idx) + assert.NoError(t, schErr) - desc := "poly thing" - assert.Equal(t, desc, sch.OneOf.Value[0].Value.Schema().Description.Value) - assert.Equal(t, desc, sch.AnyOf.Value[0].Value.Schema().Description.Value) - assert.Equal(t, desc, sch.AllOf.Value[0].Value.Schema().Description.Value) - assert.Equal(t, desc, sch.Not.Value.Schema().Description.Value) - assert.Equal(t, desc, sch.Items.Value.A.Schema().Description.Value) + desc := "poly thing" + assert.Equal(t, desc, sch.OneOf.Value[0].Value.Schema().Description.Value) + assert.Equal(t, desc, sch.AnyOf.Value[0].Value.Schema().Description.Value) + assert.Equal(t, desc, sch.AllOf.Value[0].Value.Schema().Description.Value) + assert.Equal(t, desc, sch.Not.Value.Schema().Description.Value) + assert.Equal(t, desc, sch.Items.Value.A.Schema().Description.Value) } func Test_Schema_Polymorphism_Array_Ref_Fail(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: type: object @@ -594,12 +594,12 @@ func Test_Schema_Polymorphism_Array_Ref_Fail(t *testing.T) { description: a property example: anything` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `type: object + yml = `type: object allOf: - $ref: '#/components/schemas/Missing' oneOf: @@ -611,19 +611,19 @@ not: items: - $ref: '#/components/schemas/Something'` - var sch Schema - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var sch Schema + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - err := low.BuildModel(&idxNode, &sch) - assert.NoError(t, err) + err := low.BuildModel(&idxNode, &sch) + assert.NoError(t, err) - schErr := sch.Build(idxNode.Content[0], idx) - assert.Error(t, schErr) + schErr := sch.Build(idxNode.Content[0], idx) + assert.Error(t, schErr) } func Test_Schema_Polymorphism_Map_Ref(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: type: object @@ -634,12 +634,12 @@ func Test_Schema_Polymorphism_Map_Ref(t *testing.T) { description: a property example: anything` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `type: object + yml = `type: object allOf: $ref: '#/components/schemas/Something' oneOf: @@ -651,26 +651,26 @@ not: items: $ref: '#/components/schemas/Something'` - var sch Schema - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var sch Schema + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - err := low.BuildModel(&idxNode, &sch) - assert.NoError(t, err) + err := low.BuildModel(&idxNode, &sch) + assert.NoError(t, err) - schErr := sch.Build(idxNode.Content[0], idx) - assert.NoError(t, schErr) + schErr := sch.Build(idxNode.Content[0], idx) + assert.NoError(t, schErr) - desc := "poly thing" - assert.Equal(t, desc, sch.OneOf.Value[0].Value.Schema().Description.Value) - assert.Equal(t, desc, sch.AnyOf.Value[0].Value.Schema().Description.Value) - assert.Equal(t, desc, sch.AllOf.Value[0].Value.Schema().Description.Value) - assert.Equal(t, desc, sch.Not.Value.Schema().Description.Value) - assert.Equal(t, desc, sch.Items.Value.A.Schema().Description.Value) + desc := "poly thing" + assert.Equal(t, desc, sch.OneOf.Value[0].Value.Schema().Description.Value) + assert.Equal(t, desc, sch.AnyOf.Value[0].Value.Schema().Description.Value) + assert.Equal(t, desc, sch.AllOf.Value[0].Value.Schema().Description.Value) + assert.Equal(t, desc, sch.Not.Value.Schema().Description.Value) + assert.Equal(t, desc, sch.Items.Value.A.Schema().Description.Value) } func Test_Schema_Polymorphism_Map_Ref_Fail(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: type: object @@ -681,12 +681,12 @@ func Test_Schema_Polymorphism_Map_Ref_Fail(t *testing.T) { description: a property example: anything` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `type: object + yml = `type: object allOf: $ref: '#/components/schemas/Missing' oneOf: @@ -698,342 +698,342 @@ not: items: $ref: '#/components/schemas/Something'` - var sch Schema - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var sch Schema + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - err := low.BuildModel(&idxNode, &sch) - assert.NoError(t, err) + err := low.BuildModel(&idxNode, &sch) + assert.NoError(t, err) - schErr := sch.Build(idxNode.Content[0], idx) - assert.Error(t, schErr) + schErr := sch.Build(idxNode.Content[0], idx) + assert.Error(t, schErr) } func Test_Schema_Polymorphism_BorkParent(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: $ref: #borko` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `type: object + yml = `type: object allOf: $ref: '#/components/schemas/Something'` - var sch Schema - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var sch Schema + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - err := low.BuildModel(&idxNode, &sch) - assert.NoError(t, err) + err := low.BuildModel(&idxNode, &sch) + assert.NoError(t, err) - schErr := sch.Build(idxNode.Content[0], idx) - assert.Error(t, schErr) + schErr := sch.Build(idxNode.Content[0], idx) + assert.Error(t, schErr) } func Test_Schema_Polymorphism_BorkChild(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: $ref: #borko` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `type: object + yml = `type: object allOf: $ref: #borko` - var sch Schema - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var sch Schema + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - err := low.BuildModel(&idxNode, &sch) - assert.NoError(t, err) + err := low.BuildModel(&idxNode, &sch) + assert.NoError(t, err) - schErr := sch.Build(idxNode.Content[0], idx) - assert.Error(t, schErr) + schErr := sch.Build(idxNode.Content[0], idx) + assert.Error(t, schErr) } func Test_Schema_Polymorphism_BorkChild_Array(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: $ref: #borko` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `type: object + yml = `type: object allOf: - type: object allOf: - $ref: #bork'` - var sch Schema - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var sch Schema + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - err := low.BuildModel(&idxNode, &sch) - assert.NoError(t, err) + err := low.BuildModel(&idxNode, &sch) + assert.NoError(t, err) - schErr := sch.Build(idxNode.Content[0], idx) - assert.NoError(t, schErr) - assert.Nil(t, sch.AllOf.Value[0].Value.Schema()) // child can't be resolved, so this will be nil. - assert.Error(t, sch.AllOf.Value[0].Value.GetBuildError()) + schErr := sch.Build(idxNode.Content[0], idx) + assert.NoError(t, schErr) + assert.Nil(t, sch.AllOf.Value[0].Value.Schema()) // child can't be resolved, so this will be nil. + assert.Error(t, sch.AllOf.Value[0].Value.GetBuildError()) } func Test_Schema_Polymorphism_RefMadness(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: $ref: '#/components/schemas/Else' Else: description: madness` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `type: object + yml = `type: object allOf: $ref: '#/components/schemas/Something'` - var sch Schema - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var sch Schema + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - err := low.BuildModel(&idxNode, &sch) - assert.NoError(t, err) + err := low.BuildModel(&idxNode, &sch) + assert.NoError(t, err) - schErr := sch.Build(idxNode.Content[0], idx) - assert.NoError(t, schErr) + schErr := sch.Build(idxNode.Content[0], idx) + assert.NoError(t, schErr) - desc := "madness" - assert.Equal(t, desc, sch.AllOf.Value[0].Value.Schema().Description.Value) + desc := "madness" + assert.Equal(t, desc, sch.AllOf.Value[0].Value.Schema().Description.Value) } func Test_Schema_Polymorphism_RefMadnessBork(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: $ref: '#/components/schemas/Else' Else: $ref: #borko` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `type: object + yml = `type: object allOf: $ref: '#/components/schemas/Something'` - var sch Schema - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var sch Schema + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - err := low.BuildModel(&idxNode, &sch) - assert.NoError(t, err) + err := low.BuildModel(&idxNode, &sch) + assert.NoError(t, err) - err = sch.Build(idxNode.Content[0], idx) - assert.Error(t, err) + err = sch.Build(idxNode.Content[0], idx) + assert.Error(t, err) } func Test_Schema_Polymorphism_RefMadnessIllegal(t *testing.T) { - // this does not work, but it won't error out. + // this does not work, but it won't error out. - yml := `components: + yml := `components: schemas: Something: $ref: '#/components/schemas/Else' Else: description: hey!` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `$ref: '#/components/schemas/Something'` + yml = `$ref: '#/components/schemas/Something'` - var sch Schema - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var sch Schema + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - err := low.BuildModel(&idxNode, &sch) - assert.NoError(t, err) + err := low.BuildModel(&idxNode, &sch) + assert.NoError(t, err) - schErr := sch.Build(idxNode.Content[0], idx) - assert.NoError(t, schErr) + schErr := sch.Build(idxNode.Content[0], idx) + assert.NoError(t, schErr) } func Test_Schema_RefMadnessIllegal_Circular(t *testing.T) { - // this does not work, but it won't error out. + // this does not work, but it won't error out. - yml := `components: + yml := `components: schemas: Something: $ref: '#/components/schemas/Else' Else: $ref: '#/components/schemas/Something'` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `$ref: '#/components/schemas/Something'` + yml = `$ref: '#/components/schemas/Something'` - var sch Schema - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var sch Schema + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - resolve := resolver.NewResolver(idx) - errs := resolve.CheckForCircularReferences() - assert.Len(t, errs, 1) + resolve := resolver.NewResolver(idx) + errs := resolve.CheckForCircularReferences() + assert.Len(t, errs, 1) - err := low.BuildModel(&idxNode, &sch) - assert.NoError(t, err) + err := low.BuildModel(&idxNode, &sch) + assert.NoError(t, err) - schErr := sch.Build(idxNode.Content[0], idx) - assert.Error(t, schErr) + schErr := sch.Build(idxNode.Content[0], idx) + assert.Error(t, schErr) } func Test_Schema_RefMadnessIllegal_Nonexist(t *testing.T) { - // this does not work, but it won't error out. + // this does not work, but it won't error out. - yml := `components: + yml := `components: schemas: Something: $ref: '#/components/schemas/Else' Else: $ref: '#/components/schemas/Something'` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `$ref: #BORKLE` + yml = `$ref: #BORKLE` - var sch Schema - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var sch Schema + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - resolve := resolver.NewResolver(idx) - errs := resolve.CheckForCircularReferences() - assert.Len(t, errs, 1) + resolve := resolver.NewResolver(idx) + errs := resolve.CheckForCircularReferences() + assert.Len(t, errs, 1) - err := low.BuildModel(&idxNode, &sch) - assert.NoError(t, err) + err := low.BuildModel(&idxNode, &sch) + assert.NoError(t, err) - schErr := sch.Build(idxNode.Content[0], idx) - assert.Error(t, schErr) + schErr := sch.Build(idxNode.Content[0], idx) + assert.Error(t, schErr) } func TestExtractSchema(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: description: this is something type: string` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `schema: + yml = `schema: type: object properties: aValue: $ref: '#/components/schemas/Something'` - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - res, err := ExtractSchema(idxNode.Content[0], idx) - assert.NoError(t, err) - assert.NotNil(t, res.Value) - aValue := res.Value.Schema().FindProperty("aValue") - assert.Equal(t, "this is something", aValue.Value.Schema().Description.Value) + res, err := ExtractSchema(idxNode.Content[0], idx) + assert.NoError(t, err) + assert.NotNil(t, res.Value) + aValue := res.Value.Schema().FindProperty("aValue") + assert.Equal(t, "this is something", aValue.Value.Schema().Description.Value) } func TestExtractSchema_DefaultPrimitive(t *testing.T) { - yml := ` + yml := ` schema: type: object default: 5` - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - res, err := ExtractSchema(idxNode.Content[0], nil) - assert.NoError(t, err) - assert.NotNil(t, res.Value) - sch := res.Value.Schema() - assert.Equal(t, 5, sch.Default.Value) + res, err := ExtractSchema(idxNode.Content[0], nil) + assert.NoError(t, err) + assert.NotNil(t, res.Value) + sch := res.Value.Schema() + assert.Equal(t, 5, sch.Default.Value) } func TestExtractSchema_Ref(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: description: this is something type: string` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `schema: + yml = `schema: $ref: '#/components/schemas/Something'` - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - res, err := ExtractSchema(idxNode.Content[0], idx) - assert.NoError(t, err) - assert.NotNil(t, res.Value) - assert.Equal(t, "this is something", res.Value.Schema().Description.Value) + res, err := ExtractSchema(idxNode.Content[0], idx) + assert.NoError(t, err) + assert.NotNil(t, res.Value) + assert.Equal(t, "this is something", res.Value.Schema().Description.Value) } func TestExtractSchema_Ref_Fail(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: description: this is something type: string` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `schema: + yml = `schema: $ref: '#/components/schemas/Missing'` - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - _, err := ExtractSchema(idxNode.Content[0], idx) - assert.Error(t, err) + _, err := ExtractSchema(idxNode.Content[0], idx) + assert.Error(t, err) } func TestExtractSchema_CheckChildPropCircular(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: properties: @@ -1049,163 +1049,163 @@ func TestExtractSchema_CheckChildPropCircular(t *testing.T) { - something ` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `$ref: '#/components/schemas/Something'` + yml = `$ref: '#/components/schemas/Something'` - resolve := resolver.NewResolver(idx) - errs := resolve.CheckForCircularReferences() - assert.Len(t, errs, 1) + resolve := resolver.NewResolver(idx) + errs := resolve.CheckForCircularReferences() + assert.Len(t, errs, 1) - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - res, err := ExtractSchema(idxNode.Content[0], idx) - assert.NoError(t, err) - assert.NotNil(t, res.Value) + res, err := ExtractSchema(idxNode.Content[0], idx) + assert.NoError(t, err) + assert.NotNil(t, res.Value) - props := res.Value.Schema().FindProperty("nothing") - assert.NotNil(t, props) + props := res.Value.Schema().FindProperty("nothing") + assert.NotNil(t, props) } func TestExtractSchema_RefRoot(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: description: this is something type: string` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `$ref: '#/components/schemas/Something'` + yml = `$ref: '#/components/schemas/Something'` - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - res, err := ExtractSchema(idxNode.Content[0], idx) - assert.NoError(t, err) - assert.NotNil(t, res.Value) - assert.Equal(t, "this is something", res.Value.Schema().Description.Value) + res, err := ExtractSchema(idxNode.Content[0], idx) + assert.NoError(t, err) + assert.NotNil(t, res.Value) + assert.Equal(t, "this is something", res.Value.Schema().Description.Value) } func TestExtractSchema_RefRoot_Fail(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: description: this is something type: string` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `$ref: '#/components/schemas/Missing'` + yml = `$ref: '#/components/schemas/Missing'` - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - _, err := ExtractSchema(idxNode.Content[0], idx) - assert.Error(t, err) + _, err := ExtractSchema(idxNode.Content[0], idx) + assert.Error(t, err) } func TestExtractSchema_RefRoot_Child_Fail(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: $ref: #bork` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `$ref: '#/components/schemas/Something'` + yml = `$ref: '#/components/schemas/Something'` - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - _, err := ExtractSchema(idxNode.Content[0], idx) - assert.Error(t, err) + _, err := ExtractSchema(idxNode.Content[0], idx) + assert.Error(t, err) } func TestExtractSchema_AdditionalPropertiesAsSchema(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: additionalProperties: type: string` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `$ref: '#/components/schemas/Something'` + yml = `$ref: '#/components/schemas/Something'` - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - res, err := ExtractSchema(idxNode.Content[0], idx) + res, err := ExtractSchema(idxNode.Content[0], idx) - assert.NotNil(t, res.Value.Schema().AdditionalProperties.Value.(*SchemaProxy).Schema()) - assert.Nil(t, err) + assert.NotNil(t, res.Value.Schema().AdditionalProperties.Value.(*SchemaProxy).Schema()) + assert.Nil(t, err) } func TestExtractSchema_AdditionalPropertiesAsSchemaSlice(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: additionalProperties: - nice: rice` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `$ref: '#/components/schemas/Something'` + yml = `$ref: '#/components/schemas/Something'` - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - res, err := ExtractSchema(idxNode.Content[0], idx) + res, err := ExtractSchema(idxNode.Content[0], idx) - assert.NotNil(t, res.Value.Schema().AdditionalProperties.Value.([]low.ValueReference[interface{}])) - assert.Nil(t, err) + assert.NotNil(t, res.Value.Schema().AdditionalProperties.Value.([]low.ValueReference[interface{}])) + assert.Nil(t, err) } func TestExtractSchema_DoNothing(t *testing.T) { - yml := `components: + yml := `components: schemas: Something: $ref: #bork` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `please: do nothing.` + yml = `please: do nothing.` - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - res, err := ExtractSchema(idxNode.Content[0], idx) - assert.Nil(t, res) - assert.Nil(t, err) + res, err := ExtractSchema(idxNode.Content[0], idx) + assert.Nil(t, res) + assert.Nil(t, err) } func TestExtractSchema_AdditionalProperties_Ref(t *testing.T) { - yml := `components: + yml := `components: schemas: Nothing: type: int @@ -1214,26 +1214,26 @@ func TestExtractSchema_AdditionalProperties_Ref(t *testing.T) { cake: $ref: '#/components/schemas/Nothing'` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `schema: + yml = `schema: type: int additionalProperties: $ref: '#/components/schemas/Something'` - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - res, err := ExtractSchema(idxNode.Content[0], idx) - assert.NotNil(t, res.Value.Schema().AdditionalProperties.Value.(*SchemaProxy).Schema()) - assert.Nil(t, err) + res, err := ExtractSchema(idxNode.Content[0], idx) + assert.NotNil(t, res.Value.Schema().AdditionalProperties.Value.(*SchemaProxy).Schema()) + assert.Nil(t, err) } func TestExtractSchema_OneOfRef(t *testing.T) { - yml := `components: + yml := `components: schemas: Error: type: object @@ -1330,25 +1330,25 @@ func TestExtractSchema_OneOfRef(t *testing.T) { oneOf: - $ref: '#/components/schemas/Drink'` - var iNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &iNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&iNode) + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&iNode) - yml = `schema: + yml = `schema: $ref: '#/components/schemas/SomePayload'` - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) - res, err := ExtractSchema(idxNode.Content[0], idx) - assert.NoError(t, err) - assert.Equal(t, "a frosty cold beverage can be coke or sprite", - res.Value.Schema().OneOf.Value[0].Value.Schema().Description.Value) + res, err := ExtractSchema(idxNode.Content[0], idx) + assert.NoError(t, err) + assert.Equal(t, "a frosty cold beverage can be coke or sprite", + res.Value.Schema().OneOf.Value[0].Value.Schema().Description.Value) } func TestSchema_Hash_Equal(t *testing.T) { - left := `schema: + left := `schema: $schema: https://athing.com multipleOf: 1 maximum: 10 @@ -1389,7 +1389,7 @@ func TestSchema_Hash_Equal(t *testing.T) { title: a proxy property type: string` - right := `schema: + right := `schema: $schema: https://athing.com multipleOf: 1 maximum: 10 @@ -1430,74 +1430,74 @@ func TestSchema_Hash_Equal(t *testing.T) { title: a proxy property type: string` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - lDoc, _ := ExtractSchema(lNode.Content[0], nil) - rDoc, _ := ExtractSchema(rNode.Content[0], nil) + lDoc, _ := ExtractSchema(lNode.Content[0], nil) + rDoc, _ := ExtractSchema(rNode.Content[0], nil) - assert.NotNil(t, lDoc) - assert.NotNil(t, rDoc) + assert.NotNil(t, lDoc) + assert.NotNil(t, rDoc) - lHash := lDoc.Value.Schema().Hash() - rHash := rDoc.Value.Schema().Hash() + lHash := lDoc.Value.Schema().Hash() + rHash := rDoc.Value.Schema().Hash() - assert.Equal(t, lHash, rHash) + assert.Equal(t, lHash, rHash) } func TestSchema_Hash_AdditionalPropsSlice(t *testing.T) { - left := `schema: + left := `schema: additionalProperties: - type: string` - right := `schema: + right := `schema: additionalProperties: - type: string` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - lDoc, _ := ExtractSchema(lNode.Content[0], nil) - rDoc, _ := ExtractSchema(rNode.Content[0], nil) + lDoc, _ := ExtractSchema(lNode.Content[0], nil) + rDoc, _ := ExtractSchema(rNode.Content[0], nil) - assert.NotNil(t, lDoc) - assert.NotNil(t, rDoc) + assert.NotNil(t, lDoc) + assert.NotNil(t, rDoc) - lHash := lDoc.Value.Schema().Hash() - rHash := rDoc.Value.Schema().Hash() + lHash := lDoc.Value.Schema().Hash() + rHash := rDoc.Value.Schema().Hash() - assert.Equal(t, lHash, rHash) + assert.Equal(t, lHash, rHash) } func TestSchema_Hash_AdditionalPropsSliceNoMap(t *testing.T) { - left := `schema: + left := `schema: additionalProperties: - hello` - right := `schema: + right := `schema: additionalProperties: - hello` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - lDoc, _ := ExtractSchema(lNode.Content[0], nil) - rDoc, _ := ExtractSchema(rNode.Content[0], nil) + lDoc, _ := ExtractSchema(lNode.Content[0], nil) + rDoc, _ := ExtractSchema(rNode.Content[0], nil) - assert.NotNil(t, lDoc) - assert.NotNil(t, rDoc) + assert.NotNil(t, lDoc) + assert.NotNil(t, rDoc) - lHash := lDoc.Value.Schema().Hash() - rHash := rDoc.Value.Schema().Hash() + lHash := lDoc.Value.Schema().Hash() + rHash := rDoc.Value.Schema().Hash() - assert.Equal(t, lHash, rHash) + assert.Equal(t, lHash, rHash) } func TestSchema_Hash_NotEqual(t *testing.T) { - left := `schema: + left := `schema: title: an OK message - but different items: true minContains: 3 @@ -1507,7 +1507,7 @@ func TestSchema_Hash_NotEqual(t *testing.T) { title: a proxy property type: string` - right := `schema: + right := `schema: title: an OK message items: false minContains: 2 @@ -1517,18 +1517,18 @@ func TestSchema_Hash_NotEqual(t *testing.T) { title: a proxy property type: string` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - lDoc, _ := ExtractSchema(lNode.Content[0], nil) - rDoc, _ := ExtractSchema(rNode.Content[0], nil) + lDoc, _ := ExtractSchema(lNode.Content[0], nil) + rDoc, _ := ExtractSchema(rNode.Content[0], nil) - assert.False(t, low.AreEqual(lDoc.Value.Schema(), rDoc.Value.Schema())) + assert.False(t, low.AreEqual(lDoc.Value.Schema(), rDoc.Value.Schema())) } func TestSchema_Hash_EqualJumbled(t *testing.T) { - left := `schema: + left := `schema: title: an OK message description: a nice thing. properties: @@ -1541,7 +1541,7 @@ func TestSchema_Hash_EqualJumbled(t *testing.T) { title: a proxy property type: string` - right := `schema: + right := `schema: description: a nice thing. properties: propA: @@ -1554,17 +1554,17 @@ func TestSchema_Hash_EqualJumbled(t *testing.T) { type: int title: an OK message` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - lDoc, _ := ExtractSchema(lNode.Content[0], nil) - rDoc, _ := ExtractSchema(rNode.Content[0], nil) - assert.True(t, low.AreEqual(lDoc.Value.Schema(), rDoc.Value.Schema())) + lDoc, _ := ExtractSchema(lNode.Content[0], nil) + rDoc, _ := ExtractSchema(rNode.Content[0], nil) + assert.True(t, low.AreEqual(lDoc.Value.Schema(), rDoc.Value.Schema())) } func test_get_allOf_schema_blob() string { - return `type: object + return `type: object description: allOf sequence check allOf: - type: object @@ -1575,3 +1575,71 @@ allOf: - description: allOf sequence check 4 ` } + +func TestSchema_UnevaluatedPropertiesAsBool_DefinedAsTrue(t *testing.T) { + yml := `components: + schemas: + Something: + unevaluatedProperties: true` + + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndexWithConfig(&iNode, index.CreateOpenAPIIndexConfig()) + + yml = `$ref: '#/components/schemas/Something'` + + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) + + res, _ := ExtractSchema(idxNode.Content[0], idx) + + assert.True(t, res.Value.Schema().UnevaluatedProperties.Value.IsB()) + assert.True(t, *res.Value.Schema().UnevaluatedProperties.Value.B) + +} + +func TestSchema_UnevaluatedPropertiesAsBool_DefinedAsFalse(t *testing.T) { + yml := `components: + schemas: + Something: + unevaluatedProperties: false` + + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndexWithConfig(&iNode, index.CreateOpenAPIIndexConfig()) + + yml = `$ref: '#/components/schemas/Something'` + + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) + + res, _ := ExtractSchema(idxNode.Content[0], idx) + + assert.True(t, res.Value.Schema().UnevaluatedProperties.Value.IsB()) + assert.False(t, *res.Value.Schema().UnevaluatedProperties.Value.B) + +} + +func TestSchema_UnevaluatedPropertiesAsBool_Undefined(t *testing.T) { + yml := `components: + schemas: + Something: + description: I have not defined unevaluatedProperties` + + var iNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &iNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndexWithConfig(&iNode, index.CreateOpenAPIIndexConfig()) + + yml = `$ref: '#/components/schemas/Something'` + + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) + + res, _ := ExtractSchema(idxNode.Content[0], idx) + + assert.Nil(t, res.Value.Schema().UnevaluatedProperties.Value) + +} diff --git a/datamodel/low/base/security_requirement.go b/datamodel/low/base/security_requirement.go index ac57e6e..16a4d22 100644 --- a/datamodel/low/base/security_requirement.go +++ b/datamodel/low/base/security_requirement.go @@ -19,8 +19,8 @@ import ( // security schemes declared in it which are all required (that is, there is a logical AND between the schemes). // // The name used for each property MUST correspond to a security scheme declared in the Security Definitions -// - https://swagger.io/specification/v2/#securityDefinitionsObject -// - https://swagger.io/specification/#security-requirement-object +// - https://swagger.io/specification/v2/#securityDefinitionsObject +// - https://swagger.io/specification/#security-requirement-object type SecurityRequirement struct { Requirements low.ValueReference[map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]] *low.Reference diff --git a/datamodel/low/base/tag.go b/datamodel/low/base/tag.go index dc99cb9..4dd1032 100644 --- a/datamodel/low/base/tag.go +++ b/datamodel/low/base/tag.go @@ -17,8 +17,8 @@ import ( // // Adds metadata to a single tag that is used by the Operation Object. It is not mandatory to have a Tag Object per // tag defined in the Operation Object instances. -// - v2: https://swagger.io/specification/v2/#tagObject -// - v3: https://swagger.io/specification/#tag-object +// - v2: https://swagger.io/specification/v2/#tagObject +// - v3: https://swagger.io/specification/#tag-object type Tag struct { Name low.NodeReference[string] Description low.NodeReference[string] diff --git a/datamodel/low/base/tag_test.go b/datamodel/low/base/tag_test.go index 3169719..2fd97b8 100644 --- a/datamodel/low/base/tag_test.go +++ b/datamodel/low/base/tag_test.go @@ -84,4 +84,4 @@ x-b33f: princess` assert.Equal(t, lDoc.Hash(), rDoc.Hash()) -} \ No newline at end of file +} diff --git a/datamodel/low/base/xml.go b/datamodel/low/base/xml.go index 607f709..94344c8 100644 --- a/datamodel/low/base/xml.go +++ b/datamodel/low/base/xml.go @@ -16,8 +16,9 @@ import ( // // When using arrays, XML element names are not inferred (for singular/plural forms) and the name property SHOULD be // used to add that information. See examples for expected behavior. -// v2 - https://swagger.io/specification/v2/#xmlObject -// v3 - https://swagger.io/specification/#xml-object +// +// v2 - https://swagger.io/specification/v2/#xmlObject +// v3 - https://swagger.io/specification/#xml-object type XML struct { Name low.NodeReference[string] Namespace low.NodeReference[string] diff --git a/datamodel/low/extraction_functions_test.go b/datamodel/low/extraction_functions_test.go index 6e465f6..a58c187 100644 --- a/datamodel/low/extraction_functions_test.go +++ b/datamodel/low/extraction_functions_test.go @@ -1024,7 +1024,7 @@ one: assert.NoError(t, err) assert.Len(t, things, 1) - for k, _ := range things { + for k := range things { assert.Equal(t, "one", k.Value) } } diff --git a/datamodel/low/model_builder.go b/datamodel/low/model_builder.go index 14b46e3..d8098e3 100644 --- a/datamodel/low/model_builder.go +++ b/datamodel/low/model_builder.go @@ -4,14 +4,14 @@ package low import ( - "fmt" - "reflect" - "strconv" - "strings" - "sync" + "fmt" + "reflect" + "strconv" + "strings" + "sync" - "github.com/pb33f/libopenapi/utils" - "gopkg.in/yaml.v3" + "github.com/pb33f/libopenapi/utils" + "gopkg.in/yaml.v3" ) // BuildModel accepts a yaml.Node pointer and a model, which can be any struct. Using reflection, the model is @@ -20,198 +20,198 @@ import ( // // BuildModel is non-recursive and will only build out a single layer of the node tree. func BuildModel(node *yaml.Node, model interface{}) error { - if node == nil { - return nil - } + if node == nil { + return nil + } - if reflect.ValueOf(model).Type().Kind() != reflect.Pointer { - return fmt.Errorf("cannot build model on non-pointer: %v", reflect.ValueOf(model).Type().Kind()) - } - v := reflect.ValueOf(model).Elem() - num := v.NumField() - for i := 0; i < num; i++ { + if reflect.ValueOf(model).Type().Kind() != reflect.Pointer { + return fmt.Errorf("cannot build model on non-pointer: %v", reflect.ValueOf(model).Type().Kind()) + } + v := reflect.ValueOf(model).Elem() + num := v.NumField() + for i := 0; i < num; i++ { - fName := v.Type().Field(i).Name + fName := v.Type().Field(i).Name - if fName == "Extensions" { - continue // internal construct - } + if fName == "Extensions" { + continue // internal construct + } - if fName == "PathItems" { - continue // internal construct - } + if fName == "PathItems" { + continue // internal construct + } - kn, vn := utils.FindKeyNodeTop(strings.ToLower(fName), node.Content) - if vn == nil { - // no point in going on. - continue - } + kn, vn := utils.FindKeyNodeTop(strings.ToLower(fName), node.Content) + if vn == nil { + // no point in going on. + continue + } - field := v.FieldByName(fName) - kind := field.Kind() - switch kind { - case reflect.Struct, reflect.Slice, reflect.Map, reflect.Pointer: - err := SetField(&field, vn, kn) - if err != nil { - return err - } - default: - return fmt.Errorf("unable to parse unsupported type: %v", kind) - } + field := v.FieldByName(fName) + kind := field.Kind() + switch kind { + case reflect.Struct, reflect.Slice, reflect.Map, reflect.Pointer: + err := SetField(&field, vn, kn) + if err != nil { + return err + } + default: + return fmt.Errorf("unable to parse unsupported type: %v", kind) + } - } + } - return nil + return nil } // SetField accepts a field reflection value, a yaml.Node valueNode and a yaml.Node keyNode. Using reflection, the // function will attempt to set the value of the field based on the key and value nodes. This method is only useful // for low-level models, it has no value to high-level ones. func SetField(field *reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) error { - if valueNode == nil { - return nil - } + if valueNode == nil { + return nil + } - switch field.Type() { + switch field.Type() { - case reflect.TypeOf(map[string]NodeReference[any]{}): - if utils.IsNodeMap(valueNode) { - if field.CanSet() { - items := make(map[string]NodeReference[any]) - var currentLabel string - for i, sliceItem := range valueNode.Content { - if i%2 == 0 { - currentLabel = sliceItem.Value - continue - } - var decoded map[string]interface{} - // I cannot think of a way to make this error out by this point. - _ = sliceItem.Decode(&decoded) - items[currentLabel] = NodeReference[any]{ - Value: decoded, - ValueNode: sliceItem, - KeyNode: valueNode, - } - } - field.Set(reflect.ValueOf(items)) - } - } + case reflect.TypeOf(map[string]NodeReference[any]{}): + if utils.IsNodeMap(valueNode) { + if field.CanSet() { + items := make(map[string]NodeReference[any]) + var currentLabel string + for i, sliceItem := range valueNode.Content { + if i%2 == 0 { + currentLabel = sliceItem.Value + continue + } + var decoded map[string]interface{} + // I cannot think of a way to make this error out by this point. + _ = sliceItem.Decode(&decoded) + items[currentLabel] = NodeReference[any]{ + Value: decoded, + ValueNode: sliceItem, + KeyNode: valueNode, + } + } + field.Set(reflect.ValueOf(items)) + } + } - case reflect.TypeOf(map[string]NodeReference[string]{}): + case reflect.TypeOf(map[string]NodeReference[string]{}): - if utils.IsNodeMap(valueNode) { - if field.CanSet() { - items := make(map[string]NodeReference[string]) - var currentLabel string - for i, sliceItem := range valueNode.Content { - if i%2 == 0 { - currentLabel = sliceItem.Value - continue - } - items[currentLabel] = NodeReference[string]{ - Value: fmt.Sprintf("%v", sliceItem.Value), - ValueNode: sliceItem, - KeyNode: valueNode, - } - } - field.Set(reflect.ValueOf(items)) - } - } + if utils.IsNodeMap(valueNode) { + if field.CanSet() { + items := make(map[string]NodeReference[string]) + var currentLabel string + for i, sliceItem := range valueNode.Content { + if i%2 == 0 { + currentLabel = sliceItem.Value + continue + } + items[currentLabel] = NodeReference[string]{ + Value: fmt.Sprintf("%v", sliceItem.Value), + ValueNode: sliceItem, + KeyNode: valueNode, + } + } + field.Set(reflect.ValueOf(items)) + } + } - case reflect.TypeOf(NodeReference[any]{}): + case reflect.TypeOf(NodeReference[any]{}): - var decoded interface{} - _ = valueNode.Decode(&decoded) - if field.CanSet() { - or := NodeReference[any]{Value: decoded, ValueNode: valueNode, KeyNode: keyNode} - field.Set(reflect.ValueOf(or)) - } + var decoded interface{} + _ = valueNode.Decode(&decoded) + if field.CanSet() { + or := NodeReference[any]{Value: decoded, ValueNode: valueNode, KeyNode: keyNode} + field.Set(reflect.ValueOf(or)) + } - case reflect.TypeOf([]NodeReference[any]{}): + case reflect.TypeOf([]NodeReference[any]{}): - if utils.IsNodeArray(valueNode) { - if field.CanSet() { - var items []NodeReference[any] - for _, sliceItem := range valueNode.Content { - var decoded map[string]interface{} - err := sliceItem.Decode(&decoded) - if err != nil { - return err - } - items = append(items, NodeReference[any]{ - Value: decoded, - ValueNode: sliceItem, - KeyNode: valueNode, - }) - } - field.Set(reflect.ValueOf(items)) - } - } + if utils.IsNodeArray(valueNode) { + if field.CanSet() { + var items []NodeReference[any] + for _, sliceItem := range valueNode.Content { + var decoded map[string]interface{} + err := sliceItem.Decode(&decoded) + if err != nil { + return err + } + items = append(items, NodeReference[any]{ + Value: decoded, + ValueNode: sliceItem, + KeyNode: valueNode, + }) + } + field.Set(reflect.ValueOf(items)) + } + } - case reflect.TypeOf(NodeReference[string]{}): + case reflect.TypeOf(NodeReference[string]{}): - if field.CanSet() { - nr := NodeReference[string]{ - Value: fmt.Sprintf("%v", valueNode.Value), - ValueNode: valueNode, - KeyNode: keyNode, - } - field.Set(reflect.ValueOf(nr)) - } + if field.CanSet() { + nr := NodeReference[string]{ + Value: fmt.Sprintf("%v", valueNode.Value), + ValueNode: valueNode, + KeyNode: keyNode, + } + field.Set(reflect.ValueOf(nr)) + } - case reflect.TypeOf(ValueReference[string]{}): + case reflect.TypeOf(ValueReference[string]{}): - if field.CanSet() { - nr := ValueReference[string]{ - Value: fmt.Sprintf("%v", valueNode.Value), - ValueNode: valueNode, - } - field.Set(reflect.ValueOf(nr)) - } + if field.CanSet() { + nr := ValueReference[string]{ + Value: fmt.Sprintf("%v", valueNode.Value), + ValueNode: valueNode, + } + field.Set(reflect.ValueOf(nr)) + } - case reflect.TypeOf(NodeReference[bool]{}): + case reflect.TypeOf(NodeReference[bool]{}): - if utils.IsNodeBoolValue(valueNode) { - if field.CanSet() { - bv, _ := strconv.ParseBool(valueNode.Value) - nr := NodeReference[bool]{ - Value: bv, - ValueNode: valueNode, - KeyNode: keyNode, - } - field.Set(reflect.ValueOf(nr)) - } - } + if utils.IsNodeBoolValue(valueNode) { + if field.CanSet() { + bv, _ := strconv.ParseBool(valueNode.Value) + nr := NodeReference[bool]{ + Value: bv, + ValueNode: valueNode, + KeyNode: keyNode, + } + field.Set(reflect.ValueOf(nr)) + } + } - case reflect.TypeOf(NodeReference[int]{}): + case reflect.TypeOf(NodeReference[int]{}): - if utils.IsNodeIntValue(valueNode) { - if field.CanSet() { - fv, _ := strconv.Atoi(valueNode.Value) - nr := NodeReference[int]{ - Value: fv, - ValueNode: valueNode, - KeyNode: keyNode, - } - field.Set(reflect.ValueOf(nr)) - } - } + if utils.IsNodeIntValue(valueNode) { + if field.CanSet() { + fv, _ := strconv.Atoi(valueNode.Value) + nr := NodeReference[int]{ + Value: fv, + ValueNode: valueNode, + KeyNode: keyNode, + } + field.Set(reflect.ValueOf(nr)) + } + } - case reflect.TypeOf(NodeReference[int64]{}): + case reflect.TypeOf(NodeReference[int64]{}): - if utils.IsNodeIntValue(valueNode) || utils.IsNodeFloatValue(valueNode) { - if field.CanSet() { - fv, _ := strconv.ParseInt(valueNode.Value, 10, 64) - nr := NodeReference[int64]{ - Value: fv, - ValueNode: valueNode, - KeyNode: keyNode, - } - field.Set(reflect.ValueOf(nr)) - } - } + if utils.IsNodeIntValue(valueNode) || utils.IsNodeFloatValue(valueNode) { + if field.CanSet() { + fv, _ := strconv.ParseInt(valueNode.Value, 10, 64) + nr := NodeReference[int64]{ + Value: fv, + ValueNode: valueNode, + KeyNode: keyNode, + } + field.Set(reflect.ValueOf(nr)) + } + } - case reflect.TypeOf(NodeReference[float32]{}): + case reflect.TypeOf(NodeReference[float32]{}): if utils.IsNodeNumberValue(valueNode) { if field.CanSet() { @@ -225,7 +225,7 @@ func SetField(field *reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) er } } - case reflect.TypeOf(NodeReference[float64]{}): + case reflect.TypeOf(NodeReference[float64]{}): if utils.IsNodeNumberValue(valueNode) { if field.CanSet() { @@ -239,249 +239,249 @@ func SetField(field *reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) er } } - case reflect.TypeOf([]NodeReference[string]{}): + case reflect.TypeOf([]NodeReference[string]{}): - if utils.IsNodeArray(valueNode) { - if field.CanSet() { - var items []NodeReference[string] - for _, sliceItem := range valueNode.Content { - items = append(items, NodeReference[string]{ - Value: sliceItem.Value, - ValueNode: sliceItem, - KeyNode: valueNode, - }) - } - field.Set(reflect.ValueOf(items)) - } - } + if utils.IsNodeArray(valueNode) { + if field.CanSet() { + var items []NodeReference[string] + for _, sliceItem := range valueNode.Content { + items = append(items, NodeReference[string]{ + Value: sliceItem.Value, + ValueNode: sliceItem, + KeyNode: valueNode, + }) + } + field.Set(reflect.ValueOf(items)) + } + } - case reflect.TypeOf([]NodeReference[float32]{}): + case reflect.TypeOf([]NodeReference[float32]{}): - if utils.IsNodeArray(valueNode) { - if field.CanSet() { - var items []NodeReference[float32] - for _, sliceItem := range valueNode.Content { - fv, _ := strconv.ParseFloat(sliceItem.Value, 32) - items = append(items, NodeReference[float32]{ - Value: float32(fv), - ValueNode: sliceItem, - KeyNode: valueNode, - }) - } - field.Set(reflect.ValueOf(items)) - } - } + if utils.IsNodeArray(valueNode) { + if field.CanSet() { + var items []NodeReference[float32] + for _, sliceItem := range valueNode.Content { + fv, _ := strconv.ParseFloat(sliceItem.Value, 32) + items = append(items, NodeReference[float32]{ + Value: float32(fv), + ValueNode: sliceItem, + KeyNode: valueNode, + }) + } + field.Set(reflect.ValueOf(items)) + } + } - case reflect.TypeOf([]NodeReference[float64]{}): + case reflect.TypeOf([]NodeReference[float64]{}): - if utils.IsNodeArray(valueNode) { - if field.CanSet() { - var items []NodeReference[float64] - for _, sliceItem := range valueNode.Content { - fv, _ := strconv.ParseFloat(sliceItem.Value, 64) - items = append(items, NodeReference[float64]{Value: fv, ValueNode: sliceItem}) - } - field.Set(reflect.ValueOf(items)) - } - } + if utils.IsNodeArray(valueNode) { + if field.CanSet() { + var items []NodeReference[float64] + for _, sliceItem := range valueNode.Content { + fv, _ := strconv.ParseFloat(sliceItem.Value, 64) + items = append(items, NodeReference[float64]{Value: fv, ValueNode: sliceItem}) + } + field.Set(reflect.ValueOf(items)) + } + } - case reflect.TypeOf([]NodeReference[int]{}): + case reflect.TypeOf([]NodeReference[int]{}): - if utils.IsNodeArray(valueNode) { - if field.CanSet() { - var items []NodeReference[int] - for _, sliceItem := range valueNode.Content { - iv, _ := strconv.Atoi(sliceItem.Value) - items = append(items, NodeReference[int]{ - Value: iv, - ValueNode: sliceItem, - KeyNode: valueNode, - }) - } - field.Set(reflect.ValueOf(items)) - } - } + if utils.IsNodeArray(valueNode) { + if field.CanSet() { + var items []NodeReference[int] + for _, sliceItem := range valueNode.Content { + iv, _ := strconv.Atoi(sliceItem.Value) + items = append(items, NodeReference[int]{ + Value: iv, + ValueNode: sliceItem, + KeyNode: valueNode, + }) + } + field.Set(reflect.ValueOf(items)) + } + } - case reflect.TypeOf([]NodeReference[int64]{}): + case reflect.TypeOf([]NodeReference[int64]{}): - if utils.IsNodeArray(valueNode) { - if field.CanSet() { - var items []NodeReference[int64] - for _, sliceItem := range valueNode.Content { - iv, _ := strconv.ParseInt(sliceItem.Value, 10, 64) - items = append(items, NodeReference[int64]{ - Value: iv, - ValueNode: sliceItem, - KeyNode: valueNode, - }) - } - field.Set(reflect.ValueOf(items)) - } - } + if utils.IsNodeArray(valueNode) { + if field.CanSet() { + var items []NodeReference[int64] + for _, sliceItem := range valueNode.Content { + iv, _ := strconv.ParseInt(sliceItem.Value, 10, 64) + items = append(items, NodeReference[int64]{ + Value: iv, + ValueNode: sliceItem, + KeyNode: valueNode, + }) + } + field.Set(reflect.ValueOf(items)) + } + } - case reflect.TypeOf([]NodeReference[bool]{}): + case reflect.TypeOf([]NodeReference[bool]{}): - if utils.IsNodeArray(valueNode) { - if field.CanSet() { - var items []NodeReference[bool] - for _, sliceItem := range valueNode.Content { - bv, _ := strconv.ParseBool(sliceItem.Value) - items = append(items, NodeReference[bool]{ - Value: bv, - ValueNode: sliceItem, - KeyNode: valueNode, - }) - } - field.Set(reflect.ValueOf(items)) - } - } + if utils.IsNodeArray(valueNode) { + if field.CanSet() { + var items []NodeReference[bool] + for _, sliceItem := range valueNode.Content { + bv, _ := strconv.ParseBool(sliceItem.Value) + items = append(items, NodeReference[bool]{ + Value: bv, + ValueNode: sliceItem, + KeyNode: valueNode, + }) + } + field.Set(reflect.ValueOf(items)) + } + } - // helper for unpacking string maps. - case reflect.TypeOf(map[KeyReference[string]]ValueReference[string]{}): + // helper for unpacking string maps. + case reflect.TypeOf(map[KeyReference[string]]ValueReference[string]{}): - if utils.IsNodeMap(valueNode) { - if field.CanSet() { - items := make(map[KeyReference[string]]ValueReference[string]) - var cf *yaml.Node - for i, sliceItem := range valueNode.Content { - if i%2 == 0 { - cf = sliceItem - continue - } - items[KeyReference[string]{ - Value: cf.Value, - KeyNode: cf, - }] = ValueReference[string]{ - Value: sliceItem.Value, - ValueNode: sliceItem, - } - } - field.Set(reflect.ValueOf(items)) - } - } + if utils.IsNodeMap(valueNode) { + if field.CanSet() { + items := make(map[KeyReference[string]]ValueReference[string]) + var cf *yaml.Node + for i, sliceItem := range valueNode.Content { + if i%2 == 0 { + cf = sliceItem + continue + } + items[KeyReference[string]{ + Value: cf.Value, + KeyNode: cf, + }] = ValueReference[string]{ + Value: sliceItem.Value, + ValueNode: sliceItem, + } + } + field.Set(reflect.ValueOf(items)) + } + } - case reflect.TypeOf(KeyReference[map[KeyReference[string]]ValueReference[string]]{}): + case reflect.TypeOf(KeyReference[map[KeyReference[string]]ValueReference[string]]{}): - if utils.IsNodeMap(valueNode) { - if field.CanSet() { - items := make(map[KeyReference[string]]ValueReference[string]) - var cf *yaml.Node - for i, sliceItem := range valueNode.Content { - if i%2 == 0 { - cf = sliceItem - continue - } - items[KeyReference[string]{ - Value: cf.Value, - KeyNode: cf, - }] = ValueReference[string]{ - Value: sliceItem.Value, - ValueNode: sliceItem, - } - } - ref := KeyReference[map[KeyReference[string]]ValueReference[string]]{ - Value: items, - KeyNode: keyNode, - } - field.Set(reflect.ValueOf(ref)) - } - } - case reflect.TypeOf(NodeReference[map[KeyReference[string]]ValueReference[string]]{}): - if utils.IsNodeMap(valueNode) { - if field.CanSet() { - items := make(map[KeyReference[string]]ValueReference[string]) - var cf *yaml.Node - for i, sliceItem := range valueNode.Content { - if i%2 == 0 { - cf = sliceItem - continue - } - items[KeyReference[string]{ - Value: cf.Value, - KeyNode: cf, - }] = ValueReference[string]{ - Value: sliceItem.Value, - ValueNode: sliceItem, - } - } - ref := NodeReference[map[KeyReference[string]]ValueReference[string]]{ - Value: items, - KeyNode: keyNode, - ValueNode: valueNode, - } - field.Set(reflect.ValueOf(ref)) - } - } - case reflect.TypeOf(NodeReference[[]ValueReference[string]]{}): + if utils.IsNodeMap(valueNode) { + if field.CanSet() { + items := make(map[KeyReference[string]]ValueReference[string]) + var cf *yaml.Node + for i, sliceItem := range valueNode.Content { + if i%2 == 0 { + cf = sliceItem + continue + } + items[KeyReference[string]{ + Value: cf.Value, + KeyNode: cf, + }] = ValueReference[string]{ + Value: sliceItem.Value, + ValueNode: sliceItem, + } + } + ref := KeyReference[map[KeyReference[string]]ValueReference[string]]{ + Value: items, + KeyNode: keyNode, + } + field.Set(reflect.ValueOf(ref)) + } + } + case reflect.TypeOf(NodeReference[map[KeyReference[string]]ValueReference[string]]{}): + if utils.IsNodeMap(valueNode) { + if field.CanSet() { + items := make(map[KeyReference[string]]ValueReference[string]) + var cf *yaml.Node + for i, sliceItem := range valueNode.Content { + if i%2 == 0 { + cf = sliceItem + continue + } + items[KeyReference[string]{ + Value: cf.Value, + KeyNode: cf, + }] = ValueReference[string]{ + Value: sliceItem.Value, + ValueNode: sliceItem, + } + } + ref := NodeReference[map[KeyReference[string]]ValueReference[string]]{ + Value: items, + KeyNode: keyNode, + ValueNode: valueNode, + } + field.Set(reflect.ValueOf(ref)) + } + } + case reflect.TypeOf(NodeReference[[]ValueReference[string]]{}): - if utils.IsNodeArray(valueNode) { - if field.CanSet() { - var items []ValueReference[string] - for _, sliceItem := range valueNode.Content { - items = append(items, ValueReference[string]{ - Value: sliceItem.Value, - ValueNode: sliceItem, - }) - } - n := NodeReference[[]ValueReference[string]]{ - Value: items, - KeyNode: keyNode, - ValueNode: valueNode, - } - field.Set(reflect.ValueOf(n)) - } - } + if utils.IsNodeArray(valueNode) { + if field.CanSet() { + var items []ValueReference[string] + for _, sliceItem := range valueNode.Content { + items = append(items, ValueReference[string]{ + Value: sliceItem.Value, + ValueNode: sliceItem, + }) + } + n := NodeReference[[]ValueReference[string]]{ + Value: items, + KeyNode: keyNode, + ValueNode: valueNode, + } + field.Set(reflect.ValueOf(n)) + } + } - case reflect.TypeOf(NodeReference[[]ValueReference[any]]{}): + case reflect.TypeOf(NodeReference[[]ValueReference[any]]{}): - if utils.IsNodeArray(valueNode) { - if field.CanSet() { - var items []ValueReference[any] - for _, sliceItem := range valueNode.Content { + if utils.IsNodeArray(valueNode) { + if field.CanSet() { + var items []ValueReference[any] + for _, sliceItem := range valueNode.Content { - var val any - if utils.IsNodeIntValue(sliceItem) || utils.IsNodeFloatValue(sliceItem) { - if utils.IsNodeIntValue(sliceItem) { - val, _ = strconv.ParseInt(sliceItem.Value, 10, 64) - } else { - val, _ = strconv.ParseFloat(sliceItem.Value, 64) - } - } - if utils.IsNodeBoolValue(sliceItem) { - val, _ = strconv.ParseBool(sliceItem.Value) - } - if utils.IsNodeStringValue(sliceItem) { - val = sliceItem.Value - } + var val any + if utils.IsNodeIntValue(sliceItem) || utils.IsNodeFloatValue(sliceItem) { + if utils.IsNodeIntValue(sliceItem) { + val, _ = strconv.ParseInt(sliceItem.Value, 10, 64) + } else { + val, _ = strconv.ParseFloat(sliceItem.Value, 64) + } + } + if utils.IsNodeBoolValue(sliceItem) { + val, _ = strconv.ParseBool(sliceItem.Value) + } + if utils.IsNodeStringValue(sliceItem) { + val = sliceItem.Value + } - items = append(items, ValueReference[any]{ - Value: val, - ValueNode: sliceItem, - }) - } - n := NodeReference[[]ValueReference[any]]{ - Value: items, - KeyNode: keyNode, - ValueNode: valueNode, - } - field.Set(reflect.ValueOf(n)) - } - } + items = append(items, ValueReference[any]{ + Value: val, + ValueNode: sliceItem, + }) + } + n := NodeReference[[]ValueReference[any]]{ + Value: items, + KeyNode: keyNode, + ValueNode: valueNode, + } + field.Set(reflect.ValueOf(n)) + } + } - default: - // we want to ignore everything else, each model handles its own complex types. - break - } - return nil + default: + // we want to ignore everything else, each model handles its own complex types. + break + } + return nil } // BuildModelAsync is a convenience function for calling BuildModel from a goroutine, requires a sync.WaitGroup func BuildModelAsync(n *yaml.Node, model interface{}, lwg *sync.WaitGroup, errors *[]error) { - if n != nil { - err := BuildModel(n, model) - if err != nil { - *errors = append(*errors, err) - } - } - lwg.Done() + if n != nil { + err := BuildModel(n, model) + if err != nil { + *errors = append(*errors, err) + } + } + lwg.Done() } diff --git a/datamodel/low/model_builder_test.go b/datamodel/low/model_builder_test.go index 5a8eb77..2dbeac9 100644 --- a/datamodel/low/model_builder_test.go +++ b/datamodel/low/model_builder_test.go @@ -1,53 +1,53 @@ package low import ( - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "sync" - "testing" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "sync" + "testing" ) type hotdog struct { - Name NodeReference[string] - ValueName ValueReference[string] - Fat NodeReference[int] - Ketchup NodeReference[float32] - Mustard NodeReference[float64] - Grilled NodeReference[bool] - MaxTemp NodeReference[int] - MaxTempHigh NodeReference[int64] - MaxTempAlt []NodeReference[int] - Drinks []NodeReference[string] - Sides []NodeReference[float32] - BigSides []NodeReference[float64] - Temps []NodeReference[int] - HighTemps []NodeReference[int64] - Buns []NodeReference[bool] - UnknownElements NodeReference[any] - LotsOfUnknowns []NodeReference[any] - Where map[string]NodeReference[any] - There map[string]NodeReference[string] - AllTheThings NodeReference[map[KeyReference[string]]ValueReference[string]] + Name NodeReference[string] + ValueName ValueReference[string] + Fat NodeReference[int] + Ketchup NodeReference[float32] + Mustard NodeReference[float64] + Grilled NodeReference[bool] + MaxTemp NodeReference[int] + MaxTempHigh NodeReference[int64] + MaxTempAlt []NodeReference[int] + Drinks []NodeReference[string] + Sides []NodeReference[float32] + BigSides []NodeReference[float64] + Temps []NodeReference[int] + HighTemps []NodeReference[int64] + Buns []NodeReference[bool] + UnknownElements NodeReference[any] + LotsOfUnknowns []NodeReference[any] + Where map[string]NodeReference[any] + There map[string]NodeReference[string] + AllTheThings NodeReference[map[KeyReference[string]]ValueReference[string]] } func TestBuildModel_Mismatch(t *testing.T) { - yml := `crisps: are tasty` + yml := `crisps: are tasty` - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - hd := hotdog{} - cErr := BuildModel(&rootNode, &hd) - assert.NoError(t, cErr) - assert.Empty(t, hd.Name) + hd := hotdog{} + cErr := BuildModel(&rootNode, &hd) + assert.NoError(t, cErr) + assert.Empty(t, hd.Name) } func TestBuildModel(t *testing.T) { - yml := `name: yummy + yml := `name: yummy valueName: yammy beef: true fat: 200 @@ -105,347 +105,347 @@ allTheThings: beer: isGood cake: isNice` - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - hd := hotdog{} - cErr := BuildModel(rootNode.Content[0], &hd) - assert.Equal(t, 200, hd.Fat.Value) - assert.Equal(t, 4, hd.Fat.ValueNode.Line) - assert.Equal(t, true, hd.Grilled.Value) - assert.Equal(t, "yummy", hd.Name.Value) - assert.Equal(t, "yammy", hd.ValueName.Value) - assert.Equal(t, float32(200.45), hd.Ketchup.Value) - assert.Len(t, hd.Drinks, 3) - assert.Len(t, hd.Sides, 4) - assert.Len(t, hd.BigSides, 4) - assert.Len(t, hd.Temps, 2) - assert.Len(t, hd.HighTemps, 2) - assert.Equal(t, int64(11732849090192923), hd.HighTemps[1].Value) - assert.Len(t, hd.MaxTempAlt, 5) - assert.Equal(t, int64(7392837462032342), hd.MaxTempHigh.Value) - assert.Equal(t, 2, hd.Temps[1].Value) - assert.Equal(t, 27, hd.Temps[1].ValueNode.Line) - assert.Len(t, hd.UnknownElements.Value, 2) - assert.Len(t, hd.LotsOfUnknowns, 3) - assert.Len(t, hd.Where, 2) - assert.Len(t, hd.There, 2) - assert.Equal(t, "bear", hd.There["care"].Value) - assert.Equal(t, 324938249028.98234892374892374923874823974, hd.Mustard.Value) + hd := hotdog{} + cErr := BuildModel(rootNode.Content[0], &hd) + assert.Equal(t, 200, hd.Fat.Value) + assert.Equal(t, 4, hd.Fat.ValueNode.Line) + assert.Equal(t, true, hd.Grilled.Value) + assert.Equal(t, "yummy", hd.Name.Value) + assert.Equal(t, "yammy", hd.ValueName.Value) + assert.Equal(t, float32(200.45), hd.Ketchup.Value) + assert.Len(t, hd.Drinks, 3) + assert.Len(t, hd.Sides, 4) + assert.Len(t, hd.BigSides, 4) + assert.Len(t, hd.Temps, 2) + assert.Len(t, hd.HighTemps, 2) + assert.Equal(t, int64(11732849090192923), hd.HighTemps[1].Value) + assert.Len(t, hd.MaxTempAlt, 5) + assert.Equal(t, int64(7392837462032342), hd.MaxTempHigh.Value) + assert.Equal(t, 2, hd.Temps[1].Value) + assert.Equal(t, 27, hd.Temps[1].ValueNode.Line) + assert.Len(t, hd.UnknownElements.Value, 2) + assert.Len(t, hd.LotsOfUnknowns, 3) + assert.Len(t, hd.Where, 2) + assert.Len(t, hd.There, 2) + assert.Equal(t, "bear", hd.There["care"].Value) + assert.Equal(t, 324938249028.98234892374892374923874823974, hd.Mustard.Value) - allTheThings := hd.AllTheThings.Value - for i := range allTheThings { - if i.Value == "beer" { - assert.Equal(t, "isGood", allTheThings[i].Value) - } - if i.Value == "cake" { - assert.Equal(t, "isNice", allTheThings[i].Value) - } - } - assert.NoError(t, cErr) + allTheThings := hd.AllTheThings.Value + for i := range allTheThings { + if i.Value == "beer" { + assert.Equal(t, "isGood", allTheThings[i].Value) + } + if i.Value == "cake" { + assert.Equal(t, "isNice", allTheThings[i].Value) + } + } + assert.NoError(t, cErr) } func TestBuildModel_UseCopyNotRef(t *testing.T) { - yml := `cake: -99999` + yml := `cake: -99999` - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - hd := hotdog{} - cErr := BuildModel(&rootNode, hd) - assert.Error(t, cErr) - assert.Empty(t, hd.Name) + hd := hotdog{} + cErr := BuildModel(&rootNode, hd) + assert.Error(t, cErr) + assert.Empty(t, hd.Name) } func TestBuildModel_UseUnsupportedPrimitive(t *testing.T) { - type notSupported struct { - cake string - } - ns := notSupported{} - yml := `cake: party` + type notSupported struct { + cake string + } + ns := notSupported{} + yml := `cake: party` - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - cErr := BuildModel(rootNode.Content[0], &ns) - assert.Error(t, cErr) - assert.Empty(t, ns.cake) + cErr := BuildModel(rootNode.Content[0], &ns) + assert.Error(t, cErr) + assert.Empty(t, ns.cake) } func TestBuildModel_UsingInternalConstructs(t *testing.T) { - type internal struct { - Extensions NodeReference[string] - PathItems NodeReference[string] - Thing NodeReference[string] - } + type internal struct { + Extensions NodeReference[string] + PathItems NodeReference[string] + Thing NodeReference[string] + } - yml := `extensions: one + yml := `extensions: one pathItems: two thing: yeah` - ins := new(internal) - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + ins := new(internal) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - // try a null build - try := BuildModel(nil, ins) - assert.NoError(t, try) + // try a null build + try := BuildModel(nil, ins) + assert.NoError(t, try) - cErr := BuildModel(rootNode.Content[0], ins) - assert.NoError(t, cErr) - assert.Empty(t, ins.PathItems.Value) - assert.Empty(t, ins.Extensions.Value) - assert.Equal(t, "yeah", ins.Thing.Value) + cErr := BuildModel(rootNode.Content[0], ins) + assert.NoError(t, cErr) + assert.Empty(t, ins.PathItems.Value) + assert.Empty(t, ins.Extensions.Value) + assert.Equal(t, "yeah", ins.Thing.Value) } func TestSetField_NodeRefAny_Error(t *testing.T) { - type internal struct { - Thing []NodeReference[any] - } + type internal struct { + Thing []NodeReference[any] + } - yml := `thing: + yml := `thing: - 999 - false` - ins := new(internal) - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + ins := new(internal) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - try := BuildModel(rootNode.Content[0], ins) - assert.Error(t, try) + try := BuildModel(rootNode.Content[0], ins) + assert.Error(t, try) } func TestSetField_MapHelperWrapped(t *testing.T) { - type internal struct { - Thing KeyReference[map[KeyReference[string]]ValueReference[string]] - } + type internal struct { + Thing KeyReference[map[KeyReference[string]]ValueReference[string]] + } - yml := `thing: + yml := `thing: what: not chip: chop lip: lop` - ins := new(internal) - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + ins := new(internal) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - try := BuildModel(rootNode.Content[0], ins) - assert.NoError(t, try) - assert.Len(t, ins.Thing.Value, 3) + try := BuildModel(rootNode.Content[0], ins) + assert.NoError(t, try) + assert.Len(t, ins.Thing.Value, 3) } func TestSetField_MapHelper(t *testing.T) { - type internal struct { - Thing map[KeyReference[string]]ValueReference[string] - } + type internal struct { + Thing map[KeyReference[string]]ValueReference[string] + } - yml := `thing: + yml := `thing: what: not chip: chop lip: lop` - ins := new(internal) - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + ins := new(internal) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - try := BuildModel(rootNode.Content[0], ins) - assert.NoError(t, try) - assert.Len(t, ins.Thing, 3) + try := BuildModel(rootNode.Content[0], ins) + assert.NoError(t, try) + assert.Len(t, ins.Thing, 3) } func TestSetField_ArrayHelper(t *testing.T) { - type internal struct { - Thing NodeReference[[]ValueReference[string]] - } + type internal struct { + Thing NodeReference[[]ValueReference[string]] + } - yml := `thing: + yml := `thing: - nice - rice - slice` - ins := new(internal) - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + ins := new(internal) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - try := BuildModel(rootNode.Content[0], ins) - assert.NoError(t, try) - assert.Len(t, ins.Thing.Value, 3) + try := BuildModel(rootNode.Content[0], ins) + assert.NoError(t, try) + assert.Len(t, ins.Thing.Value, 3) } func TestSetField_Enum_Helper(t *testing.T) { - type internal struct { - Thing NodeReference[[]ValueReference[any]] - } + type internal struct { + Thing NodeReference[[]ValueReference[any]] + } - yml := `thing: + yml := `thing: - nice - rice - slice` - ins := new(internal) - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + ins := new(internal) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - try := BuildModel(rootNode.Content[0], ins) - assert.NoError(t, try) - assert.Len(t, ins.Thing.Value, 3) + try := BuildModel(rootNode.Content[0], ins) + assert.NoError(t, try) + assert.Len(t, ins.Thing.Value, 3) } func TestSetField_Default_Helper(t *testing.T) { - type cake struct { - thing int - } + type cake struct { + thing int + } - // this should be ignored, no custom objects in here my friend. - type internal struct { - Thing cake - } + // this should be ignored, no custom objects in here my friend. + type internal struct { + Thing cake + } - yml := `thing: + yml := `thing: type: cake` - ins := new(internal) - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + ins := new(internal) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - try := BuildModel(rootNode.Content[0], ins) - assert.NoError(t, try) - assert.Equal(t, 0, ins.Thing.thing) + try := BuildModel(rootNode.Content[0], ins) + assert.NoError(t, try) + assert.Equal(t, 0, ins.Thing.thing) } func TestHandleSlicesOfInts(t *testing.T) { - type internal struct { - Thing NodeReference[[]ValueReference[any]] - } + type internal struct { + Thing NodeReference[[]ValueReference[any]] + } - yml := `thing: + yml := `thing: - 5 - 1.234` - ins := new(internal) - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + ins := new(internal) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - try := BuildModel(rootNode.Content[0], ins) - assert.NoError(t, try) - assert.Equal(t, int64(5), ins.Thing.Value[0].Value) - assert.Equal(t, 1.234, ins.Thing.Value[1].Value) + try := BuildModel(rootNode.Content[0], ins) + assert.NoError(t, try) + assert.Equal(t, int64(5), ins.Thing.Value[0].Value) + assert.Equal(t, 1.234, ins.Thing.Value[1].Value) } func TestHandleSlicesOfBools(t *testing.T) { - type internal struct { - Thing NodeReference[[]ValueReference[any]] - } + type internal struct { + Thing NodeReference[[]ValueReference[any]] + } - yml := `thing: + yml := `thing: - true - false` - ins := new(internal) - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + ins := new(internal) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - try := BuildModel(rootNode.Content[0], ins) - assert.NoError(t, try) - assert.Equal(t, true, ins.Thing.Value[0].Value) - assert.Equal(t, false, ins.Thing.Value[1].Value) + try := BuildModel(rootNode.Content[0], ins) + assert.NoError(t, try) + assert.Equal(t, true, ins.Thing.Value[0].Value) + assert.Equal(t, false, ins.Thing.Value[1].Value) } func TestSetField_Ignore(t *testing.T) { - type Complex struct { - name string - } - type internal struct { - Thing *Complex - } + type Complex struct { + name string + } + type internal struct { + Thing *Complex + } - yml := `thing: + yml := `thing: - nice - rice - slice` - ins := new(internal) - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + ins := new(internal) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - try := BuildModel(&rootNode, ins) - assert.NoError(t, try) - assert.Nil(t, ins.Thing) + try := BuildModel(&rootNode, ins) + assert.NoError(t, try) + assert.Nil(t, ins.Thing) } func TestBuildModelAsync(t *testing.T) { - type internal struct { - Thing KeyReference[map[KeyReference[string]]ValueReference[string]] - } + type internal struct { + Thing KeyReference[map[KeyReference[string]]ValueReference[string]] + } - yml := `thing: + yml := `thing: what: not chip: chop lip: lop` - ins := new(internal) - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + ins := new(internal) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - var wg sync.WaitGroup - var errors []error - wg.Add(1) - BuildModelAsync(rootNode.Content[0], ins, &wg, &errors) - wg.Wait() - assert.Len(t, ins.Thing.Value, 3) + var wg sync.WaitGroup + var errors []error + wg.Add(1) + BuildModelAsync(rootNode.Content[0], ins, &wg, &errors) + wg.Wait() + assert.Len(t, ins.Thing.Value, 3) } func TestBuildModelAsync_Error(t *testing.T) { - type internal struct { - Thing []NodeReference[any] - } + type internal struct { + Thing []NodeReference[any] + } - yml := `thing: + yml := `thing: - 999 - false` - ins := new(internal) - var rootNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &rootNode) - assert.NoError(t, mErr) + ins := new(internal) + var rootNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &rootNode) + assert.NoError(t, mErr) - var wg sync.WaitGroup - var errors []error - wg.Add(1) - BuildModelAsync(rootNode.Content[0], ins, &wg, &errors) - wg.Wait() - assert.Len(t, errors, 1) - assert.Len(t, ins.Thing, 0) + var wg sync.WaitGroup + var errors []error + wg.Add(1) + BuildModelAsync(rootNode.Content[0], ins, &wg, &errors) + wg.Wait() + assert.Len(t, errors, 1) + assert.Len(t, ins.Thing, 0) } diff --git a/datamodel/low/reference_test.go b/datamodel/low/reference_test.go index 11d433f..2a33f2e 100644 --- a/datamodel/low/reference_test.go +++ b/datamodel/low/reference_test.go @@ -51,7 +51,7 @@ func TestNodeReference_Mutate(t *testing.T) { func TestNodeReference_RefNode(t *testing.T) { nr := new(NodeReference[string]) nr.KeyNode = &yaml.Node{ - Content: []*yaml.Node{&yaml.Node{ + Content: []*yaml.Node{{ Value: "$ref", }}, } @@ -749,8 +749,3 @@ func TestKeyReference_GetKeyNode(t *testing.T) { assert.Equal(t, 3, nr.GetKeyNode().Line) assert.Equal(t, "pizza", nr.GetKeyNode().Value) } - - - - - diff --git a/datamodel/low/v2/definitions.go b/datamodel/low/v2/definitions.go index 226a9c4..a707b00 100644 --- a/datamodel/low/v2/definitions.go +++ b/datamodel/low/v2/definitions.go @@ -17,7 +17,7 @@ import ( // // ParameterDefinitions holds parameters to be reused across operations. Parameter definitions can be // referenced to the ones defined here. It does not define global operation parameters -// - https://swagger.io/specification/v2/#parametersDefinitionsObject +// - https://swagger.io/specification/v2/#parametersDefinitionsObject type ParameterDefinitions struct { Definitions map[low.KeyReference[string]]low.ValueReference[*Parameter] } @@ -26,7 +26,7 @@ type ParameterDefinitions struct { // // ResponsesDefinitions is an object to hold responses to be reused across operations. Response definitions can be // referenced to the ones defined here. It does not define global operation responses -// - https://swagger.io/specification/v2/#responsesDefinitionsObject +// - https://swagger.io/specification/v2/#responsesDefinitionsObject type ResponsesDefinitions struct { Definitions map[low.KeyReference[string]]low.ValueReference[*Response] } @@ -35,7 +35,7 @@ type ResponsesDefinitions struct { // // A declaration of the security schemes available to be used in the specification. This does not enforce the security // schemes on the operations and only serves to provide the relevant details for each scheme -// - https://swagger.io/specification/v2/#securityDefinitionsObject +// - https://swagger.io/specification/v2/#securityDefinitionsObject type SecurityDefinitions struct { Definitions map[low.KeyReference[string]]low.ValueReference[*SecurityScheme] } @@ -44,7 +44,7 @@ type SecurityDefinitions struct { // // An object to hold data types that can be consumed and produced by operations. These data types can be primitives, // arrays or models. -// - https://swagger.io/specification/v2/#definitionsObject +// - https://swagger.io/specification/v2/#definitionsObject type Definitions struct { Schemas map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy] } diff --git a/datamodel/low/v2/examples.go b/datamodel/low/v2/examples.go index 03a63e6..57345c8 100644 --- a/datamodel/low/v2/examples.go +++ b/datamodel/low/v2/examples.go @@ -15,7 +15,7 @@ import ( // Examples represents a low-level Swagger / OpenAPI 2 Example object. // Allows sharing examples for operation responses -// - https://swagger.io/specification/v2/#exampleObject +// - https://swagger.io/specification/v2/#exampleObject type Examples struct { Values map[low.KeyReference[string]]low.ValueReference[any] } diff --git a/datamodel/low/v2/header.go b/datamodel/low/v2/header.go index e58c4f8..f0385ff 100644 --- a/datamodel/low/v2/header.go +++ b/datamodel/low/v2/header.go @@ -17,7 +17,7 @@ import ( // Header Represents a low-level Swagger / OpenAPI 2 Header object. // // A Header is essentially identical to a Parameter, except it does not contain 'name' or 'in' properties. -// - https://swagger.io/specification/v2/#headerObject +// - https://swagger.io/specification/v2/#headerObject type Header struct { Type low.NodeReference[string] Format low.NodeReference[string] diff --git a/datamodel/low/v2/header_test.go b/datamodel/low/v2/header_test.go index 0ee8e59..3f11fe1 100644 --- a/datamodel/low/v2/header_test.go +++ b/datamodel/low/v2/header_test.go @@ -4,90 +4,90 @@ package v2 import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/index" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "testing" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "testing" ) func TestHeader_Build(t *testing.T) { - yml := `items: + yml := `items: $ref: break` - var idxNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &idxNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&idxNode) + var idxNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &idxNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&idxNode) - var n Header - err := low.BuildModel(idxNode.Content[0], &n) - assert.NoError(t, err) + var n Header + err := low.BuildModel(idxNode.Content[0], &n) + assert.NoError(t, err) - err = n.Build(idxNode.Content[0], idx) - assert.Error(t, err) + err = n.Build(idxNode.Content[0], idx) + assert.Error(t, err) } func TestHeader_DefaultAsSlice(t *testing.T) { - yml := `x-ext: thing + yml := `x-ext: thing default: - why - so many - variations` - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) - idx := index.NewSpecIndex(&idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) + idx := index.NewSpecIndex(&idxNode) - var n Header - _ = low.BuildModel(idxNode.Content[0], &n) - _ = n.Build(idxNode.Content[0], idx) + var n Header + _ = low.BuildModel(idxNode.Content[0], &n) + _ = n.Build(idxNode.Content[0], idx) - assert.NotNil(t, n.Default.Value) - assert.Len(t, n.Default.Value, 3) - assert.Len(t, n.GetExtensions(), 1) + assert.NotNil(t, n.Default.Value) + assert.Len(t, n.Default.Value, 3) + assert.Len(t, n.GetExtensions(), 1) } func TestHeader_DefaultAsObject(t *testing.T) { - yml := `default: + yml := `default: lets: create: a: thing: ok?` - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) - idx := index.NewSpecIndex(&idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) + idx := index.NewSpecIndex(&idxNode) - var n Header - _ = low.BuildModel(idxNode.Content[0], &n) - _ = n.Build(idxNode.Content[0], idx) + var n Header + _ = low.BuildModel(idxNode.Content[0], &n) + _ = n.Build(idxNode.Content[0], idx) - assert.NotNil(t, n.Default.Value) + assert.NotNil(t, n.Default.Value) } func TestHeader_NoDefault(t *testing.T) { - yml := `minimum: 12` + yml := `minimum: 12` - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) - idx := index.NewSpecIndex(&idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) + idx := index.NewSpecIndex(&idxNode) - var n Header - _ = low.BuildModel(idxNode.Content[0], &n) - _ = n.Build(idxNode.Content[0], idx) + var n Header + _ = low.BuildModel(idxNode.Content[0], &n) + _ = n.Build(idxNode.Content[0], idx) - assert.Equal(t, 12, n.Minimum.Value) + assert.Equal(t, 12, n.Minimum.Value) } func TestHeader_Hash_n_Grab(t *testing.T) { - yml := `description: head + yml := `description: head type: string format: left collectionFormat: nice @@ -110,15 +110,15 @@ minItems: 1 uniqueItems: true multipleOf: 12` - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) - idx := index.NewSpecIndex(&idxNode) + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) + idx := index.NewSpecIndex(&idxNode) - var n Header - _ = low.BuildModel(idxNode.Content[0], &n) - _ = n.Build(idxNode.Content[0], idx) + var n Header + _ = low.BuildModel(idxNode.Content[0], &n) + _ = n.Build(idxNode.Content[0], idx) - yml2 := `description: head + yml2 := `description: head items: type: int format: left @@ -142,37 +142,37 @@ x-belly: large pattern: wow ` - var idxNode2 yaml.Node - _ = yaml.Unmarshal([]byte(yml2), &idxNode2) - idx2 := index.NewSpecIndex(&idxNode2) + var idxNode2 yaml.Node + _ = yaml.Unmarshal([]byte(yml2), &idxNode2) + idx2 := index.NewSpecIndex(&idxNode2) - var n2 Header - _ = low.BuildModel(idxNode2.Content[0], &n2) - _ = n2.Build(idxNode2.Content[0], idx2) + var n2 Header + _ = low.BuildModel(idxNode2.Content[0], &n2) + _ = n2.Build(idxNode2.Content[0], idx2) - // hash - assert.Equal(t, n.Hash(), n2.Hash()) + // hash + assert.Equal(t, n.Hash(), n2.Hash()) - // and grab - assert.Equal(t, "string", n.GetType().Value) - assert.Equal(t, "head", n.GetDescription().Value) - assert.Equal(t, "left", n.GetFormat().Value) - assert.Equal(t, "left", n.GetFormat().Value) - assert.Equal(t, "nice", n.GetCollectionFormat().Value) - assert.Equal(t, "shut that door!", n.GetDefault().Value) - assert.Equal(t, 10, n.GetMaximum().Value) - assert.Equal(t, 1, n.GetMinimum().Value) - assert.True(t, n.GetExclusiveMinimum().Value) - assert.True(t, n.GetExclusiveMaximum().Value) - assert.Equal(t, 10, n.GetMaxLength().Value) - assert.Equal(t, 1, n.GetMinLength().Value) - assert.Equal(t, 10, n.GetMaxItems().Value) - assert.Equal(t, 1, n.GetMinItems().Value) - assert.True(t, n.GetUniqueItems().Value) - assert.Equal(t, 12, n.GetMultipleOf().Value) - assert.Equal(t, "wow", n.GetPattern().Value) - assert.Equal(t, "int", n.GetItems().Value.(*Items).Type.Value) - assert.Len(t, n.GetEnum().Value, 2) - assert.Equal(t, "large", n.FindExtension("x-belly").Value) + // and grab + assert.Equal(t, "string", n.GetType().Value) + assert.Equal(t, "head", n.GetDescription().Value) + assert.Equal(t, "left", n.GetFormat().Value) + assert.Equal(t, "left", n.GetFormat().Value) + assert.Equal(t, "nice", n.GetCollectionFormat().Value) + assert.Equal(t, "shut that door!", n.GetDefault().Value) + assert.Equal(t, 10, n.GetMaximum().Value) + assert.Equal(t, 1, n.GetMinimum().Value) + assert.True(t, n.GetExclusiveMinimum().Value) + assert.True(t, n.GetExclusiveMaximum().Value) + assert.Equal(t, 10, n.GetMaxLength().Value) + assert.Equal(t, 1, n.GetMinLength().Value) + assert.Equal(t, 10, n.GetMaxItems().Value) + assert.Equal(t, 1, n.GetMinItems().Value) + assert.True(t, n.GetUniqueItems().Value) + assert.Equal(t, 12, n.GetMultipleOf().Value) + assert.Equal(t, "wow", n.GetPattern().Value) + assert.Equal(t, "int", n.GetItems().Value.(*Items).Type.Value) + assert.Len(t, n.GetEnum().Value, 2) + assert.Equal(t, "large", n.FindExtension("x-belly").Value) } diff --git a/datamodel/low/v2/items.go b/datamodel/low/v2/items.go index 307afa0..53fc0c6 100644 --- a/datamodel/low/v2/items.go +++ b/datamodel/low/v2/items.go @@ -18,7 +18,7 @@ import ( // // Items is a limited subset of JSON-Schema's items object. It is used by parameter definitions that are not // located in "body". Items, is actually identical to a Header, except it does not have description. -// - https://swagger.io/specification/v2/#itemsObject +// - https://swagger.io/specification/v2/#itemsObject type Items struct { Type low.NodeReference[string] Format low.NodeReference[string] diff --git a/datamodel/low/v2/operation.go b/datamodel/low/v2/operation.go index 2783bc9..b5a89cf 100644 --- a/datamodel/low/v2/operation.go +++ b/datamodel/low/v2/operation.go @@ -17,7 +17,7 @@ import ( // Operation represents a low-level Swagger / OpenAPI 2 Operation object. // // It describes a single API operation on a path. -// - https://swagger.io/specification/v2/#operationObject +// - https://swagger.io/specification/v2/#operationObject type Operation struct { Tags low.NodeReference[[]low.ValueReference[string]] Summary low.NodeReference[string] diff --git a/datamodel/low/v2/parameter.go b/datamodel/low/v2/parameter.go index 159532d..2fb63a1 100644 --- a/datamodel/low/v2/parameter.go +++ b/datamodel/low/v2/parameter.go @@ -22,29 +22,39 @@ import ( // There are five possible parameter types. // // Path -// Used together with Path Templating, where the parameter value is actually part of the operation's URL. -// This does not include the host or base path of the API. For example, in /items/{itemId}, the path parameter is itemId. +// +// Used together with Path Templating, where the parameter value is actually part of the operation's URL. +// This does not include the host or base path of the API. For example, in /items/{itemId}, the path parameter is itemId. +// // Query -// Parameters that are appended to the URL. For example, in /items?id=###, the query parameter is id. +// +// Parameters that are appended to the URL. For example, in /items?id=###, the query parameter is id. +// // Header -// Custom headers that are expected as part of the request. +// +// Custom headers that are expected as part of the request. +// // Body -// The payload that's appended to the HTTP request. Since there can only be one payload, there can only be one body parameter. -// The name of the body parameter has no effect on the parameter itself and is used for documentation purposes only. -// Since Form parameters are also in the payload, body and form parameters cannot exist together for the same operation. +// +// The payload that's appended to the HTTP request. Since there can only be one payload, there can only be one body parameter. +// The name of the body parameter has no effect on the parameter itself and is used for documentation purposes only. +// Since Form parameters are also in the payload, body and form parameters cannot exist together for the same operation. +// // Form -// Used to describe the payload of an HTTP request when either application/x-www-form-urlencoded, multipart/form-data -// or both are used as the content type of the request (in Swagger's definition, the consumes property of an operation). -// This is the only parameter type that can be used to send files, thus supporting the file type. Since form parameters -// are sent in the payload, they cannot be declared together with a body parameter for the same operation. Form -// parameters have a different format based on the content-type used (for further details, -// consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4): -// application/x-www-form-urlencoded - Similar to the format of Query parameters but as a payload. For example, -// foo=1&bar=swagger - both foo and bar are form parameters. This is normally used for simple parameters that are -// being transferred. -// multipart/form-data - each parameter takes a section in the payload with an internal header. For example, for -// the header Content-Disposition: form-data; name="submit-name" the name of the parameter is -// submit-name. This type of form parameters is more commonly used for file transfers +// +// Used to describe the payload of an HTTP request when either application/x-www-form-urlencoded, multipart/form-data +// or both are used as the content type of the request (in Swagger's definition, the consumes property of an operation). +// This is the only parameter type that can be used to send files, thus supporting the file type. Since form parameters +// are sent in the payload, they cannot be declared together with a body parameter for the same operation. Form +// parameters have a different format based on the content-type used (for further details, +// consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4): +// application/x-www-form-urlencoded - Similar to the format of Query parameters but as a payload. For example, +// foo=1&bar=swagger - both foo and bar are form parameters. This is normally used for simple parameters that are +// being transferred. +// multipart/form-data - each parameter takes a section in the payload with an internal header. For example, for +// the header Content-Disposition: form-data; name="submit-name" the name of the parameter is +// submit-name. This type of form parameters is more commonly used for file transfers +// // https://swagger.io/specification/v2/#parameterObject type Parameter struct { Name low.NodeReference[string] diff --git a/datamodel/low/v2/path_item.go b/datamodel/low/v2/path_item.go index 649cccd..5c367f3 100644 --- a/datamodel/low/v2/path_item.go +++ b/datamodel/low/v2/path_item.go @@ -20,7 +20,7 @@ import ( // The path itself is still exposed to the tooling, but will not know which operations and parameters // are available. // -// - https://swagger.io/specification/v2/#pathItemObject +// - https://swagger.io/specification/v2/#pathItemObject type PathItem struct { Ref low.NodeReference[string] Get low.NodeReference[*Operation] diff --git a/datamodel/low/v2/response.go b/datamodel/low/v2/response.go index cde0cf1..c202aae 100644 --- a/datamodel/low/v2/response.go +++ b/datamodel/low/v2/response.go @@ -17,7 +17,7 @@ import ( // Response is a representation of a high-level Swagger / OpenAPI 2 Response object, backed by a low-level one. // // Response describes a single response from an API Operation -// - https://swagger.io/specification/v2/#responseObject +// - https://swagger.io/specification/v2/#responseObject type Response struct { Description low.NodeReference[string] Schema low.NodeReference[*base.SchemaProxy] diff --git a/datamodel/low/v2/scopes.go b/datamodel/low/v2/scopes.go index 1908ba2..01b3b69 100644 --- a/datamodel/low/v2/scopes.go +++ b/datamodel/low/v2/scopes.go @@ -17,7 +17,7 @@ import ( // Scopes is a low-level representation of a Swagger / OpenAPI 2 OAuth2 Scopes object. // // Scopes lists the available scopes for an OAuth2 security scheme. -// - https://swagger.io/specification/v2/#scopesObject +// - https://swagger.io/specification/v2/#scopesObject type Scopes struct { Values map[low.KeyReference[string]]low.ValueReference[string] Extensions map[low.KeyReference[string]]low.ValueReference[any] diff --git a/datamodel/low/v2/security_scheme.go b/datamodel/low/v2/security_scheme.go index 952f8b6..c9180e9 100644 --- a/datamodel/low/v2/security_scheme.go +++ b/datamodel/low/v2/security_scheme.go @@ -18,7 +18,7 @@ import ( // SecurityScheme allows the definition of a security scheme that can be used by the operations. Supported schemes are // basic authentication, an API key (either as a header or as a query parameter) and OAuth2's common flows // (implicit, password, application and access code) -// - https://swagger.io/specification/v2/#securityDefinitionsObject +// - https://swagger.io/specification/v2/#securityDefinitionsObject type SecurityScheme struct { Type low.NodeReference[string] Description low.NodeReference[string] diff --git a/datamodel/low/v2/swagger.go b/datamodel/low/v2/swagger.go index 561864c..305f9bb 100644 --- a/datamodel/low/v2/swagger.go +++ b/datamodel/low/v2/swagger.go @@ -12,12 +12,12 @@ package v2 import ( - "github.com/pb33f/libopenapi/datamodel" - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/base" - "github.com/pb33f/libopenapi/index" - "github.com/pb33f/libopenapi/resolver" - "gopkg.in/yaml.v3" + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/resolver" + "gopkg.in/yaml.v3" ) // processes a property of a Swagger document asynchronously using bool and error channels for signals. @@ -26,272 +26,272 @@ type documentFunction func(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, // Swagger represents a high-level Swagger / OpenAPI 2 document. An instance of Swagger is the root of the specification. type Swagger struct { - // Swagger is the version of Swagger / OpenAPI being used, extracted from the 'swagger: 2.x' definition. - Swagger low.ValueReference[string] + // Swagger is the version of Swagger / OpenAPI being used, extracted from the 'swagger: 2.x' definition. + Swagger low.ValueReference[string] - // Info represents a specification Info definition. - // Provides metadata about the API. The metadata can be used by the clients if needed. - // - https://swagger.io/specification/v2/#infoObject - Info low.NodeReference[*base.Info] + // Info represents a specification Info definition. + // Provides metadata about the API. The metadata can be used by the clients if needed. + // - https://swagger.io/specification/v2/#infoObject + Info low.NodeReference[*base.Info] - // Host is The host (name or ip) serving the API. This MUST be the host only and does not include the scheme nor - // sub-paths. It MAY include a port. If the host is not included, the host serving the documentation is to be used - // (including the port). The host does not support path templating. - Host low.NodeReference[string] + // Host is The host (name or ip) serving the API. This MUST be the host only and does not include the scheme nor + // sub-paths. It MAY include a port. If the host is not included, the host serving the documentation is to be used + // (including the port). The host does not support path templating. + Host low.NodeReference[string] - // BasePath is The base path on which the API is served, which is relative to the host. If it is not included, - // the API is served directly under the host. The value MUST start with a leading slash (/). - // The basePath does not support path templating. - BasePath low.NodeReference[string] + // BasePath is The base path on which the API is served, which is relative to the host. If it is not included, + // the API is served directly under the host. The value MUST start with a leading slash (/). + // The basePath does not support path templating. + BasePath low.NodeReference[string] - // Schemes represents the transfer protocol of the API. Requirements MUST be from the list: "http", "https", "ws", "wss". - // If the schemes is not included, the default scheme to be used is the one used to access - // the Swagger definition itself. - Schemes low.NodeReference[[]low.ValueReference[string]] + // Schemes represents the transfer protocol of the API. Requirements MUST be from the list: "http", "https", "ws", "wss". + // If the schemes is not included, the default scheme to be used is the one used to access + // the Swagger definition itself. + Schemes low.NodeReference[[]low.ValueReference[string]] - // Consumes is a list of MIME types the APIs can consume. This is global to all APIs but can be overridden on - // specific API calls. Value MUST be as described under Mime Types. - Consumes low.NodeReference[[]low.ValueReference[string]] + // Consumes is a list of MIME types the APIs can consume. This is global to all APIs but can be overridden on + // specific API calls. Value MUST be as described under Mime Types. + Consumes low.NodeReference[[]low.ValueReference[string]] - // Produces is a list of MIME types the APIs can produce. This is global to all APIs but can be overridden on - // specific API calls. Value MUST be as described under Mime Types. - Produces low.NodeReference[[]low.ValueReference[string]] + // Produces is a list of MIME types the APIs can produce. This is global to all APIs but can be overridden on + // specific API calls. Value MUST be as described under Mime Types. + Produces low.NodeReference[[]low.ValueReference[string]] - // Paths are the paths and operations for the API. Perhaps the most important part of the specification. - // - https://swagger.io/specification/v2/#pathsObject - Paths low.NodeReference[*Paths] + // Paths are the paths and operations for the API. Perhaps the most important part of the specification. + // - https://swagger.io/specification/v2/#pathsObject + Paths low.NodeReference[*Paths] - // Definitions is an object to hold data types produced and consumed by operations. It's composed of Schema instances - // - https://swagger.io/specification/v2/#definitionsObject - Definitions low.NodeReference[*Definitions] + // Definitions is an object to hold data types produced and consumed by operations. It's composed of Schema instances + // - https://swagger.io/specification/v2/#definitionsObject + Definitions low.NodeReference[*Definitions] - // SecurityDefinitions represents security scheme definitions that can be used across the specification. - // - https://swagger.io/specification/v2/#securityDefinitionsObject - SecurityDefinitions low.NodeReference[*SecurityDefinitions] + // SecurityDefinitions represents security scheme definitions that can be used across the specification. + // - https://swagger.io/specification/v2/#securityDefinitionsObject + SecurityDefinitions low.NodeReference[*SecurityDefinitions] - // Parameters is an object to hold parameters that can be used across operations. - // This property does not define global parameters for all operations. - // - https://swagger.io/specification/v2/#parametersDefinitionsObject - Parameters low.NodeReference[*ParameterDefinitions] + // Parameters is an object to hold parameters that can be used across operations. + // This property does not define global parameters for all operations. + // - https://swagger.io/specification/v2/#parametersDefinitionsObject + Parameters low.NodeReference[*ParameterDefinitions] - // Responses is an object to hold responses that can be used across operations. - // This property does not define global responses for all operations. - // - https://swagger.io/specification/v2/#responsesDefinitionsObject - Responses low.NodeReference[*ResponsesDefinitions] + // Responses is an object to hold responses that can be used across operations. + // This property does not define global responses for all operations. + // - https://swagger.io/specification/v2/#responsesDefinitionsObject + Responses low.NodeReference[*ResponsesDefinitions] - // Security is a declaration of which security schemes are applied for the API as a whole. The list of values - // describes alternative security schemes that can be used (that is, there is a logical OR between the security - // requirements). Individual operations can override this definition. - // - https://swagger.io/specification/v2/#securityRequirementObject - Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]] + // Security is a declaration of which security schemes are applied for the API as a whole. The list of values + // describes alternative security schemes that can be used (that is, there is a logical OR between the security + // requirements). Individual operations can override this definition. + // - https://swagger.io/specification/v2/#securityRequirementObject + Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]] - // Tags are A list of tags used by the specification with additional metadata. - // The order of the tags can be used to reflect on their order by the parsing tools. Not all tags that are used - // by the Operation Object must be declared. The tags that are not declared may be organized randomly or based - // on the tools' logic. Each tag name in the list MUST be unique. - // - https://swagger.io/specification/v2/#tagObject - Tags low.NodeReference[[]low.ValueReference[*base.Tag]] + // Tags are A list of tags used by the specification with additional metadata. + // The order of the tags can be used to reflect on their order by the parsing tools. Not all tags that are used + // by the Operation Object must be declared. The tags that are not declared may be organized randomly or based + // on the tools' logic. Each tag name in the list MUST be unique. + // - https://swagger.io/specification/v2/#tagObject + Tags low.NodeReference[[]low.ValueReference[*base.Tag]] - // ExternalDocs is an instance of base.ExternalDoc for.. well, obvious really, innit mate? - ExternalDocs low.NodeReference[*base.ExternalDoc] + // ExternalDocs is an instance of base.ExternalDoc for.. well, obvious really, innit mate? + ExternalDocs low.NodeReference[*base.ExternalDoc] - // Extensions contains all custom extensions defined for the top-level document. - Extensions map[low.KeyReference[string]]low.ValueReference[any] + // Extensions contains all custom extensions defined for the top-level document. + Extensions map[low.KeyReference[string]]low.ValueReference[any] - // Index is a reference to the index.SpecIndex that was created for the document and used - // as a guide when building out the Document. Ideal if further processing is required on the model and - // the original details are required to continue the work. - // - // This property is not a part of the OpenAPI schema, this is custom to libopenapi. - Index *index.SpecIndex + // Index is a reference to the index.SpecIndex that was created for the document and used + // as a guide when building out the Document. Ideal if further processing is required on the model and + // the original details are required to continue the work. + // + // This property is not a part of the OpenAPI schema, this is custom to libopenapi. + Index *index.SpecIndex - // SpecInfo is a reference to the datamodel.SpecInfo instance created when the specification was read. - // - // This property is not a part of the OpenAPI schema, this is custom to libopenapi. - SpecInfo *datamodel.SpecInfo + // SpecInfo is a reference to the datamodel.SpecInfo instance created when the specification was read. + // + // This property is not a part of the OpenAPI schema, this is custom to libopenapi. + SpecInfo *datamodel.SpecInfo } // FindExtension locates an extension from the root of the Swagger document. func (s *Swagger) FindExtension(ext string) *low.ValueReference[any] { - return low.FindItemInMap[any](ext, s.Extensions) + return low.FindItemInMap[any](ext, s.Extensions) } // GetExtensions returns all Swagger/Top level extensions and satisfies the low.HasExtensions interface. func (s *Swagger) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] { - return s.Extensions + return s.Extensions } // CreateDocumentFromConfig will create a new Swagger document from the provided SpecInfo and DocumentConfiguration. func CreateDocumentFromConfig(info *datamodel.SpecInfo, - configuration *datamodel.DocumentConfiguration) (*Swagger, []error) { - return createDocument(info, configuration) + configuration *datamodel.DocumentConfiguration) (*Swagger, []error) { + return createDocument(info, configuration) } // CreateDocument will create a new Swagger document from the provided SpecInfo. // // Deprecated: Use CreateDocumentFromConfig instead. func CreateDocument(info *datamodel.SpecInfo) (*Swagger, []error) { - return createDocument(info, &datamodel.DocumentConfiguration{ - AllowRemoteReferences: true, - AllowFileReferences: true, - }) + return createDocument(info, &datamodel.DocumentConfiguration{ + AllowRemoteReferences: true, + AllowFileReferences: true, + }) } func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfiguration) (*Swagger, []error) { - doc := Swagger{Swagger: low.ValueReference[string]{Value: info.Version, ValueNode: info.RootNode}} - doc.Extensions = low.ExtractExtensions(info.RootNode.Content[0]) + doc := Swagger{Swagger: low.ValueReference[string]{Value: info.Version, ValueNode: info.RootNode}} + doc.Extensions = low.ExtractExtensions(info.RootNode.Content[0]) - // build an index - idx := index.NewSpecIndexWithConfig(info.RootNode, &index.SpecIndexConfig{ - BaseURL: config.BaseURL, - AllowRemoteLookup: config.AllowRemoteReferences, - AllowFileLookup: config.AllowFileReferences, - }) - doc.Index = idx - doc.SpecInfo = info + // build an index + idx := index.NewSpecIndexWithConfig(info.RootNode, &index.SpecIndexConfig{ + BaseURL: config.BaseURL, + AllowRemoteLookup: config.AllowRemoteReferences, + AllowFileLookup: config.AllowFileReferences, + }) + doc.Index = idx + doc.SpecInfo = info - var errors []error + var errors []error - // build out swagger scalar variables. - _ = low.BuildModel(info.RootNode.Content[0], &doc) + // build out swagger scalar variables. + _ = low.BuildModel(info.RootNode.Content[0], &doc) - // extract externalDocs - extDocs, err := low.ExtractObject[*base.ExternalDoc](base.ExternalDocsLabel, info.RootNode, idx) - if err != nil { - errors = append(errors, err) - } + // extract externalDocs + extDocs, err := low.ExtractObject[*base.ExternalDoc](base.ExternalDocsLabel, info.RootNode, idx) + if err != nil { + errors = append(errors, err) + } - doc.ExternalDocs = extDocs + doc.ExternalDocs = extDocs - // create resolver and check for circular references. - resolve := resolver.NewResolver(idx) - resolvingErrors := resolve.CheckForCircularReferences() + // create resolver and check for circular references. + resolve := resolver.NewResolver(idx) + resolvingErrors := resolve.CheckForCircularReferences() - if len(resolvingErrors) > 0 { - for r := range resolvingErrors { - errors = append(errors, resolvingErrors[r]) - } - } + if len(resolvingErrors) > 0 { + for r := range resolvingErrors { + errors = append(errors, resolvingErrors[r]) + } + } - extractionFuncs := []documentFunction{ - extractInfo, - extractPaths, - extractDefinitions, - extractParamDefinitions, - extractResponsesDefinitions, - extractSecurityDefinitions, - extractTags, - extractSecurity, - } - doneChan := make(chan bool) - errChan := make(chan error) - for i := range extractionFuncs { - go extractionFuncs[i](info.RootNode.Content[0], &doc, idx, doneChan, errChan) - } - completedExtractions := 0 - for completedExtractions < len(extractionFuncs) { - select { - case <-doneChan: - completedExtractions++ - case e := <-errChan: - completedExtractions++ - errors = append(errors, e) - } - } + extractionFuncs := []documentFunction{ + extractInfo, + extractPaths, + extractDefinitions, + extractParamDefinitions, + extractResponsesDefinitions, + extractSecurityDefinitions, + extractTags, + extractSecurity, + } + doneChan := make(chan bool) + errChan := make(chan error) + for i := range extractionFuncs { + go extractionFuncs[i](info.RootNode.Content[0], &doc, idx, doneChan, errChan) + } + completedExtractions := 0 + for completedExtractions < len(extractionFuncs) { + select { + case <-doneChan: + completedExtractions++ + case e := <-errChan: + completedExtractions++ + errors = append(errors, e) + } + } - return &doc, errors + return &doc, errors } func (s *Swagger) GetExternalDocs() *low.NodeReference[any] { - return &low.NodeReference[any]{ - KeyNode: s.ExternalDocs.KeyNode, - ValueNode: s.ExternalDocs.ValueNode, - Value: s.ExternalDocs.Value, - } + return &low.NodeReference[any]{ + KeyNode: s.ExternalDocs.KeyNode, + ValueNode: s.ExternalDocs.ValueNode, + Value: s.ExternalDocs.Value, + } } func extractInfo(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) { - info, err := low.ExtractObject[*base.Info](base.InfoLabel, root, idx) - if err != nil { - e <- err - return - } - doc.Info = info - c <- true + info, err := low.ExtractObject[*base.Info](base.InfoLabel, root, idx) + if err != nil { + e <- err + return + } + doc.Info = info + c <- true } func extractPaths(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) { - paths, err := low.ExtractObject[*Paths](PathsLabel, root, idx) - if err != nil { - e <- err - return - } - doc.Paths = paths - c <- true + paths, err := low.ExtractObject[*Paths](PathsLabel, root, idx) + if err != nil { + e <- err + return + } + doc.Paths = paths + c <- true } func extractDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) { - def, err := low.ExtractObject[*Definitions](DefinitionsLabel, root, idx) - if err != nil { - e <- err - return - } - doc.Definitions = def - c <- true + def, err := low.ExtractObject[*Definitions](DefinitionsLabel, root, idx) + if err != nil { + e <- err + return + } + doc.Definitions = def + c <- true } func extractParamDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) { - param, err := low.ExtractObject[*ParameterDefinitions](ParametersLabel, root, idx) - if err != nil { - e <- err - return - } - doc.Parameters = param - c <- true + param, err := low.ExtractObject[*ParameterDefinitions](ParametersLabel, root, idx) + if err != nil { + e <- err + return + } + doc.Parameters = param + c <- true } func extractResponsesDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) { - resp, err := low.ExtractObject[*ResponsesDefinitions](ResponsesLabel, root, idx) - if err != nil { - e <- err - return - } - doc.Responses = resp - c <- true + resp, err := low.ExtractObject[*ResponsesDefinitions](ResponsesLabel, root, idx) + if err != nil { + e <- err + return + } + doc.Responses = resp + c <- true } func extractSecurityDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) { - sec, err := low.ExtractObject[*SecurityDefinitions](SecurityDefinitionsLabel, root, idx) - if err != nil { - e <- err - return - } - doc.SecurityDefinitions = sec - c <- true + sec, err := low.ExtractObject[*SecurityDefinitions](SecurityDefinitionsLabel, root, idx) + if err != nil { + e <- err + return + } + doc.SecurityDefinitions = sec + c <- true } func extractTags(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) { - tags, ln, vn, err := low.ExtractArray[*base.Tag](base.TagsLabel, root, idx) - if err != nil { - e <- err - return - } - doc.Tags = low.NodeReference[[]low.ValueReference[*base.Tag]]{ - Value: tags, - KeyNode: ln, - ValueNode: vn, - } - c <- true + tags, ln, vn, err := low.ExtractArray[*base.Tag](base.TagsLabel, root, idx) + if err != nil { + e <- err + return + } + doc.Tags = low.NodeReference[[]low.ValueReference[*base.Tag]]{ + Value: tags, + KeyNode: ln, + ValueNode: vn, + } + c <- true } func extractSecurity(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) { - sec, ln, vn, err := low.ExtractArray[*base.SecurityRequirement](SecurityLabel, root, idx) - if err != nil { - e <- err - return - } - doc.Security = low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]{ - Value: sec, - KeyNode: ln, - ValueNode: vn, - } - c <- true + sec, ln, vn, err := low.ExtractArray[*base.SecurityRequirement](SecurityLabel, root, idx) + if err != nil { + e <- err + return + } + doc.Security = low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]{ + Value: sec, + KeyNode: ln, + ValueNode: vn, + } + c <- true } diff --git a/datamodel/low/v2/swagger_test.go b/datamodel/low/v2/swagger_test.go index a765985..c099194 100644 --- a/datamodel/low/v2/swagger_test.go +++ b/datamodel/low/v2/swagger_test.go @@ -4,350 +4,350 @@ package v2 import ( - "fmt" - "github.com/pb33f/libopenapi/datamodel" - "github.com/stretchr/testify/assert" - "io/ioutil" - "testing" + "fmt" + "github.com/pb33f/libopenapi/datamodel" + "github.com/stretchr/testify/assert" + "io/ioutil" + "testing" ) var doc *Swagger func initTest() { - if doc != nil { - return - } - data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml") - info, _ := datamodel.ExtractSpecInfo(data) - var err []error - doc, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ - AllowFileReferences: false, - AllowRemoteReferences: false, - }) - wait := true - for wait { - select { - case <-info.JsonParsingChannel: - wait = false - } - } - if err != nil { - fmt.Print(err) - panic(err) - } + if doc != nil { + return + } + data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml") + info, _ := datamodel.ExtractSpecInfo(data) + var err []error + doc, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ + AllowFileReferences: false, + AllowRemoteReferences: false, + }) + wait := true + for wait { + select { + case <-info.JsonParsingChannel: + wait = false + } + } + if err != nil { + fmt.Print(err) + panic(err) + } } func BenchmarkCreateDocument(b *testing.B) { - data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml") - info, _ := datamodel.ExtractSpecInfo(data) - for i := 0; i < b.N; i++ { - doc, _ = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ - AllowFileReferences: false, - AllowRemoteReferences: false, - }) - } + data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml") + info, _ := datamodel.ExtractSpecInfo(data) + for i := 0; i < b.N; i++ { + doc, _ = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ + AllowFileReferences: false, + AllowRemoteReferences: false, + }) + } } func TestCreateDocument(t *testing.T) { - initTest() - doc := doc - assert.Equal(t, "2.0", doc.SpecInfo.Version) - assert.Equal(t, "1.0.6", doc.Info.Value.Version.Value) - assert.Equal(t, "petstore.swagger.io", doc.Host.Value) - assert.Equal(t, "/v2", doc.BasePath.Value) - assert.Len(t, doc.Parameters.Value.Definitions, 1) - assert.Len(t, doc.Tags.Value, 3) - assert.Len(t, doc.Schemes.Value, 2) - assert.Len(t, doc.Definitions.Value.Schemas, 6) - assert.Len(t, doc.SecurityDefinitions.Value.Definitions, 3) - assert.Len(t, doc.Paths.Value.PathItems, 15) - assert.Len(t, doc.Responses.Value.Definitions, 2) - assert.Equal(t, "http://swagger.io", doc.ExternalDocs.Value.URL.Value) - assert.Equal(t, true, doc.FindExtension("x-pet").Value) - assert.Equal(t, true, doc.FindExtension("X-Pet").Value) - assert.NotNil(t, doc.GetExternalDocs()) - assert.Len(t, doc.GetExtensions(), 1) + initTest() + doc := doc + assert.Equal(t, "2.0", doc.SpecInfo.Version) + assert.Equal(t, "1.0.6", doc.Info.Value.Version.Value) + assert.Equal(t, "petstore.swagger.io", doc.Host.Value) + assert.Equal(t, "/v2", doc.BasePath.Value) + assert.Len(t, doc.Parameters.Value.Definitions, 1) + assert.Len(t, doc.Tags.Value, 3) + assert.Len(t, doc.Schemes.Value, 2) + assert.Len(t, doc.Definitions.Value.Schemas, 6) + assert.Len(t, doc.SecurityDefinitions.Value.Definitions, 3) + assert.Len(t, doc.Paths.Value.PathItems, 15) + assert.Len(t, doc.Responses.Value.Definitions, 2) + assert.Equal(t, "http://swagger.io", doc.ExternalDocs.Value.URL.Value) + assert.Equal(t, true, doc.FindExtension("x-pet").Value) + assert.Equal(t, true, doc.FindExtension("X-Pet").Value) + assert.NotNil(t, doc.GetExternalDocs()) + assert.Len(t, doc.GetExtensions(), 1) } func TestCreateDocument_Info(t *testing.T) { - initTest() - assert.Equal(t, "Swagger Petstore", doc.Info.Value.Title.Value) - assert.Equal(t, "apiteam@swagger.io", doc.Info.Value.Contact.Value.Email.Value) - assert.Equal(t, "Apache 2.0", doc.Info.Value.License.Value.Name.Value) + initTest() + assert.Equal(t, "Swagger Petstore", doc.Info.Value.Title.Value) + assert.Equal(t, "apiteam@swagger.io", doc.Info.Value.Contact.Value.Email.Value) + assert.Equal(t, "Apache 2.0", doc.Info.Value.License.Value.Name.Value) } func TestCreateDocument_Parameters(t *testing.T) { - initTest() - simpleParam := doc.Parameters.Value.FindParameter("simpleParam") - assert.NotNil(t, simpleParam) - assert.Equal(t, "simple", simpleParam.Value.Name.Value) - assert.Equal(t, "nuggets", simpleParam.Value.FindExtension("x-chicken").Value) + initTest() + simpleParam := doc.Parameters.Value.FindParameter("simpleParam") + assert.NotNil(t, simpleParam) + assert.Equal(t, "simple", simpleParam.Value.Name.Value) + assert.Equal(t, "nuggets", simpleParam.Value.FindExtension("x-chicken").Value) } func TestCreateDocument_Tags(t *testing.T) { - initTest() - assert.Equal(t, "pet", doc.Tags.Value[0].Value.Name.Value) - assert.Equal(t, "http://swagger.io", doc.Tags.Value[0].Value.ExternalDocs.Value.URL.Value) - assert.Equal(t, "store", doc.Tags.Value[1].Value.Name.Value) - assert.Equal(t, "user", doc.Tags.Value[2].Value.Name.Value) - assert.Equal(t, "http://swagger.io", doc.Tags.Value[2].Value.ExternalDocs.Value.URL.Value) + initTest() + assert.Equal(t, "pet", doc.Tags.Value[0].Value.Name.Value) + assert.Equal(t, "http://swagger.io", doc.Tags.Value[0].Value.ExternalDocs.Value.URL.Value) + assert.Equal(t, "store", doc.Tags.Value[1].Value.Name.Value) + assert.Equal(t, "user", doc.Tags.Value[2].Value.Name.Value) + assert.Equal(t, "http://swagger.io", doc.Tags.Value[2].Value.ExternalDocs.Value.URL.Value) } func TestCreateDocument_SecurityDefinitions(t *testing.T) { - initTest() - apiKey := doc.SecurityDefinitions.Value.FindSecurityDefinition("api_key") - assert.Equal(t, "apiKey", apiKey.Value.Type.Value) - petStoreAuth := doc.SecurityDefinitions.Value.FindSecurityDefinition("petstore_auth") - assert.Equal(t, "oauth2", petStoreAuth.Value.Type.Value) - assert.Equal(t, "implicit", petStoreAuth.Value.Flow.Value) - assert.Len(t, petStoreAuth.Value.Scopes.Value.Values, 2) - assert.Equal(t, "read your pets", petStoreAuth.Value.Scopes.Value.FindScope("read:pets").Value) + initTest() + apiKey := doc.SecurityDefinitions.Value.FindSecurityDefinition("api_key") + assert.Equal(t, "apiKey", apiKey.Value.Type.Value) + petStoreAuth := doc.SecurityDefinitions.Value.FindSecurityDefinition("petstore_auth") + assert.Equal(t, "oauth2", petStoreAuth.Value.Type.Value) + assert.Equal(t, "implicit", petStoreAuth.Value.Flow.Value) + assert.Len(t, petStoreAuth.Value.Scopes.Value.Values, 2) + assert.Equal(t, "read your pets", petStoreAuth.Value.Scopes.Value.FindScope("read:pets").Value) } func TestCreateDocument_Definitions(t *testing.T) { - initTest() - apiResp := doc.Definitions.Value.FindSchema("ApiResponse").Value.Schema() - assert.NotNil(t, apiResp) - assert.Len(t, apiResp.Properties.Value, 3) - assert.Equal(t, "integer", apiResp.FindProperty("code").Value.Schema().Type.Value.A) + initTest() + apiResp := doc.Definitions.Value.FindSchema("ApiResponse").Value.Schema() + assert.NotNil(t, apiResp) + assert.Len(t, apiResp.Properties.Value, 3) + assert.Equal(t, "integer", apiResp.FindProperty("code").Value.Schema().Type.Value.A) - pet := doc.Definitions.Value.FindSchema("Pet").Value.Schema() - assert.NotNil(t, pet) - assert.Len(t, pet.Required.Value, 2) + pet := doc.Definitions.Value.FindSchema("Pet").Value.Schema() + assert.NotNil(t, pet) + assert.Len(t, pet.Required.Value, 2) - // perform a deep inline lookup on a schema to ensure chains work - assert.Equal(t, "Category", pet.FindProperty("category").Value.Schema().XML.Value.Name.Value) + // perform a deep inline lookup on a schema to ensure chains work + assert.Equal(t, "Category", pet.FindProperty("category").Value.Schema().XML.Value.Name.Value) - // check enums - assert.Len(t, pet.FindProperty("status").Value.Schema().Enum.Value, 3) + // check enums + assert.Len(t, pet.FindProperty("status").Value.Schema().Enum.Value, 3) } func TestCreateDocument_ResponseDefinitions(t *testing.T) { - initTest() - apiResp := doc.Responses.Value.FindResponse("200") - assert.NotNil(t, apiResp) - assert.Equal(t, "OK", apiResp.Value.Description.Value) - assert.Equal(t, "morning", apiResp.Value.FindExtension("x-coffee").Value) + initTest() + apiResp := doc.Responses.Value.FindResponse("200") + assert.NotNil(t, apiResp) + assert.Equal(t, "OK", apiResp.Value.Description.Value) + assert.Equal(t, "morning", apiResp.Value.FindExtension("x-coffee").Value) - header := apiResp.Value.FindHeader("noHeader") - assert.NotNil(t, header) - assert.True(t, header.Value.FindExtension("x-empty").Value.(bool)) + header := apiResp.Value.FindHeader("noHeader") + assert.NotNil(t, header) + assert.True(t, header.Value.FindExtension("x-empty").Value.(bool)) - header = apiResp.Value.FindHeader("myHeader") - if k, ok := header.Value.Items.Value.Default.Value.(map[string]interface{}); ok { - assert.Equal(t, "here", k["something"]) - } else { - panic("should not fail.") - } - if k, ok := header.Value.Items.Value.Items.Value.Default.Value.([]interface{}); ok { - assert.Len(t, k, 2) - assert.Equal(t, "two", k[1]) - } else { - panic("should not fail.") - } + header = apiResp.Value.FindHeader("myHeader") + if k, ok := header.Value.Items.Value.Default.Value.(map[string]interface{}); ok { + assert.Equal(t, "here", k["something"]) + } else { + panic("should not fail.") + } + if k, ok := header.Value.Items.Value.Items.Value.Default.Value.([]interface{}); ok { + assert.Len(t, k, 2) + assert.Equal(t, "two", k[1]) + } else { + panic("should not fail.") + } - header = apiResp.Value.FindHeader("yourHeader") - assert.Equal(t, "somethingSimple", header.Value.Items.Value.Default.Value) + header = apiResp.Value.FindHeader("yourHeader") + assert.Equal(t, "somethingSimple", header.Value.Items.Value.Default.Value) - assert.NotNil(t, apiResp.Value.Examples.Value.FindExample("application/json").Value) + assert.NotNil(t, apiResp.Value.Examples.Value.FindExample("application/json").Value) } func TestCreateDocument_Paths(t *testing.T) { - initTest() - uploadImage := doc.Paths.Value.FindPath("/pet/{petId}/uploadImage").Value - assert.NotNil(t, uploadImage) - assert.Nil(t, doc.Paths.Value.FindPath("/nothing-nowhere-nohow")) - assert.Equal(t, "man", uploadImage.FindExtension("x-potato").Value) - assert.Equal(t, "fresh", doc.Paths.Value.FindExtension("x-minty").Value) - assert.Equal(t, "successful operation", - uploadImage.Post.Value.Responses.Value.FindResponseByCode("200").Value.Description.Value) + initTest() + uploadImage := doc.Paths.Value.FindPath("/pet/{petId}/uploadImage").Value + assert.NotNil(t, uploadImage) + assert.Nil(t, doc.Paths.Value.FindPath("/nothing-nowhere-nohow")) + assert.Equal(t, "man", uploadImage.FindExtension("x-potato").Value) + assert.Equal(t, "fresh", doc.Paths.Value.FindExtension("x-minty").Value) + assert.Equal(t, "successful operation", + uploadImage.Post.Value.Responses.Value.FindResponseByCode("200").Value.Description.Value) } func TestCreateDocument_Bad(t *testing.T) { - yml := `swagger: + yml := `swagger: $ref: bork` - info, err := datamodel.ExtractSpecInfo([]byte(yml)) - assert.Nil(t, info) - assert.Error(t, err) + info, err := datamodel.ExtractSpecInfo([]byte(yml)) + assert.Nil(t, info) + assert.Error(t, err) } func TestCreateDocument_ExternalDocsBad(t *testing.T) { - yml := `externalDocs: + yml := `externalDocs: $ref: bork` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - doc, err = CreateDocument(info) - wait := true - for wait { - select { - case <-info.JsonParsingChannel: - wait = false - } - } - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + doc, err = CreateDocument(info) + wait := true + for wait { + select { + case <-info.JsonParsingChannel: + wait = false + } + } + assert.Len(t, err, 1) } func TestCreateDocument_TagsBad(t *testing.T) { - yml := `tags: + yml := `tags: $ref: bork` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - doc, err = CreateDocument(info) - wait := true - for wait { - select { - case <-info.JsonParsingChannel: - wait = false - } - } - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + doc, err = CreateDocument(info) + wait := true + for wait { + select { + case <-info.JsonParsingChannel: + wait = false + } + } + assert.Len(t, err, 1) } func TestCreateDocument_PathsBad(t *testing.T) { - yml := `paths: + yml := `paths: "/hey": post: responses: "200": $ref: bork` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - doc, err = CreateDocument(info) - wait := true - for wait { - select { - case <-info.JsonParsingChannel: - wait = false - } - } - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + doc, err = CreateDocument(info) + wait := true + for wait { + select { + case <-info.JsonParsingChannel: + wait = false + } + } + assert.Len(t, err, 1) } func TestCreateDocument_SecurityBad(t *testing.T) { - yml := `security: + yml := `security: $ref: ` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - doc, err = CreateDocument(info) - wait := true - for wait { - select { - case <-info.JsonParsingChannel: - wait = false - } - } - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + doc, err = CreateDocument(info) + wait := true + for wait { + select { + case <-info.JsonParsingChannel: + wait = false + } + } + assert.Len(t, err, 1) } func TestCreateDocument_SecurityDefinitionsBad(t *testing.T) { - yml := `securityDefinitions: + yml := `securityDefinitions: $ref: ` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - doc, err = CreateDocument(info) - wait := true - for wait { - select { - case <-info.JsonParsingChannel: - wait = false - } - } - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + doc, err = CreateDocument(info) + wait := true + for wait { + select { + case <-info.JsonParsingChannel: + wait = false + } + } + assert.Len(t, err, 1) } func TestCreateDocument_ResponsesBad(t *testing.T) { - yml := `responses: + yml := `responses: $ref: ` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - doc, err = CreateDocument(info) - wait := true - for wait { - select { - case <-info.JsonParsingChannel: - wait = false - } - } - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + doc, err = CreateDocument(info) + wait := true + for wait { + select { + case <-info.JsonParsingChannel: + wait = false + } + } + assert.Len(t, err, 1) } func TestCreateDocument_ParametersBad(t *testing.T) { - yml := `parameters: + yml := `parameters: $ref: ` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - doc, err = CreateDocument(info) - wait := true - for wait { - select { - case <-info.JsonParsingChannel: - wait = false - } - } - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + doc, err = CreateDocument(info) + wait := true + for wait { + select { + case <-info.JsonParsingChannel: + wait = false + } + } + assert.Len(t, err, 1) } func TestCreateDocument_DefinitionsBad(t *testing.T) { - yml := `definitions: + yml := `definitions: $ref: ` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - doc, err = CreateDocument(info) - wait := true - for wait { - select { - case <-info.JsonParsingChannel: - wait = false - } - } - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + doc, err = CreateDocument(info) + wait := true + for wait { + select { + case <-info.JsonParsingChannel: + wait = false + } + } + assert.Len(t, err, 1) } func TestCreateDocument_InfoBad(t *testing.T) { - yml := `info: + yml := `info: $ref: ` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - doc, err = CreateDocument(info) - wait := true - for wait { - select { - case <-info.JsonParsingChannel: - wait = false - } - } - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + doc, err = CreateDocument(info) + wait := true + for wait { + select { + case <-info.JsonParsingChannel: + wait = false + } + } + assert.Len(t, err, 1) } func TestCircularReferenceError(t *testing.T) { - data, _ := ioutil.ReadFile("../../../test_specs/swagger-circular-tests.yaml") - info, _ := datamodel.ExtractSpecInfo(data) - circDoc, err := CreateDocument(info) - assert.NotNil(t, circDoc) - assert.Len(t, err, 3) + data, _ := ioutil.ReadFile("../../../test_specs/swagger-circular-tests.yaml") + info, _ := datamodel.ExtractSpecInfo(data) + circDoc, err := CreateDocument(info) + assert.NotNil(t, circDoc) + assert.Len(t, err, 3) } diff --git a/datamodel/low/v3/callback.go b/datamodel/low/v3/callback.go index 483ddbb..4b2f6d1 100644 --- a/datamodel/low/v3/callback.go +++ b/datamodel/low/v3/callback.go @@ -19,7 +19,7 @@ import ( // PathItem Object that describes a set of requests that may be initiated by the API provider and the expected // responses. The key value used to identify the path item object is an expression, evaluated at runtime, // that identifies a URL to use for the callback operation. -// - https://spec.openapis.org/oas/v3.1.0#callback-object +// - https://spec.openapis.org/oas/v3.1.0#callback-object type Callback struct { Expression low.ValueReference[map[low.KeyReference[string]]low.ValueReference[*PathItem]] Extensions map[low.KeyReference[string]]low.ValueReference[any] @@ -61,9 +61,9 @@ func (cb *Callback) Build(root *yaml.Node, idx *index.SpecIndex) error { Value: currentCB.Value, KeyNode: currentCB, }] = low.ValueReference[*PathItem]{ - Value: callback, - ValueNode: callbackNode, - Reference: rv, + Value: callback, + ValueNode: callbackNode, + Reference: rv, } } if len(callbacks) > 0 { diff --git a/datamodel/low/v3/components.go b/datamodel/low/v3/components.go index 05d5295..c3a8650 100644 --- a/datamodel/low/v3/components.go +++ b/datamodel/low/v3/components.go @@ -19,7 +19,7 @@ import ( // // Holds a set of reusable objects for different aspects of the OAS. All objects defined within the components object // will have no effect on the API unless they are explicitly referenced from properties outside the components object. -// - https://spec.openapis.org/oas/v3.1.0#components-object +// - https://spec.openapis.org/oas/v3.1.0#components-object type Components struct { Schemas low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy]] Responses low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Response]] diff --git a/datamodel/low/v3/encoding.go b/datamodel/low/v3/encoding.go index e9a5d08..82fc444 100644 --- a/datamodel/low/v3/encoding.go +++ b/datamodel/low/v3/encoding.go @@ -13,7 +13,7 @@ import ( ) // Encoding represents a low-level OpenAPI 3+ Encoding object -// - https://spec.openapis.org/oas/v3.1.0#encoding-object +// - https://spec.openapis.org/oas/v3.1.0#encoding-object type Encoding struct { ContentType low.NodeReference[string] Headers low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Header]] diff --git a/datamodel/low/v3/header.go b/datamodel/low/v3/header.go index 9d283d5..188dfd7 100644 --- a/datamodel/low/v3/header.go +++ b/datamodel/low/v3/header.go @@ -16,7 +16,7 @@ import ( ) // Header represents a low-level OpenAPI 3+ Header object. -// - https://spec.openapis.org/oas/v3.1.0#header-object +// - https://spec.openapis.org/oas/v3.1.0#header-object type Header struct { Description low.NodeReference[string] Required low.NodeReference[bool] diff --git a/datamodel/low/v3/link.go b/datamodel/low/v3/link.go index 26166ad..8be2508 100644 --- a/datamodel/low/v3/link.go +++ b/datamodel/low/v3/link.go @@ -24,7 +24,7 @@ import ( // // For computing links, and providing instructions to execute them, a runtime expression is used for accessing values // in an operation and using them as parameters while invoking the linked operation. -// - https://spec.openapis.org/oas/v3.1.0#link-object +// - https://spec.openapis.org/oas/v3.1.0#link-object type Link struct { OperationRef low.NodeReference[string] OperationId low.NodeReference[string] diff --git a/datamodel/low/v3/media_type.go b/datamodel/low/v3/media_type.go index 9aa9702..703bf55 100644 --- a/datamodel/low/v3/media_type.go +++ b/datamodel/low/v3/media_type.go @@ -18,7 +18,7 @@ import ( // MediaType represents a low-level OpenAPI MediaType object. // // Each Media Type Object provides schema and examples for the media type identified by its key. -// - https://spec.openapis.org/oas/v3.1.0#media-type-object +// - https://spec.openapis.org/oas/v3.1.0#media-type-object type MediaType struct { Schema low.NodeReference[*base.SchemaProxy] Example low.NodeReference[any] diff --git a/datamodel/low/v3/oauth_flows.go b/datamodel/low/v3/oauth_flows.go index 5d91666..137ffb2 100644 --- a/datamodel/low/v3/oauth_flows.go +++ b/datamodel/low/v3/oauth_flows.go @@ -14,7 +14,7 @@ import ( ) // OAuthFlows represents a low-level OpenAPI 3+ OAuthFlows object. -// - https://spec.openapis.org/oas/v3.1.0#oauth-flows-object +// - https://spec.openapis.org/oas/v3.1.0#oauth-flows-object type OAuthFlows struct { Implicit low.NodeReference[*OAuthFlow] Password low.NodeReference[*OAuthFlow] @@ -87,7 +87,7 @@ func (o *OAuthFlows) Hash() [32]byte { } // OAuthFlow represents a low-level OpenAPI 3+ OAuthFlow object. -// - https://spec.openapis.org/oas/v3.1.0#oauth-flow-object +// - https://spec.openapis.org/oas/v3.1.0#oauth-flow-object type OAuthFlow struct { AuthorizationUrl low.NodeReference[string] TokenUrl low.NodeReference[string] diff --git a/datamodel/low/v3/operation.go b/datamodel/low/v3/operation.go index e3badf7..c6d798a 100644 --- a/datamodel/low/v3/operation.go +++ b/datamodel/low/v3/operation.go @@ -18,7 +18,7 @@ import ( // // An Operation is perhaps the most important object of the entire specification. Everything of value // happens here. The entire being for existence of this library and the specification, is this Operation. -// - https://spec.openapis.org/oas/v3.1.0#operation-object +// - https://spec.openapis.org/oas/v3.1.0#operation-object type Operation struct { Tags low.NodeReference[[]low.ValueReference[string]] Summary low.NodeReference[string] diff --git a/datamodel/low/v3/parameter.go b/datamodel/low/v3/parameter.go index 1a5bdd0..5f8272d 100644 --- a/datamodel/low/v3/parameter.go +++ b/datamodel/low/v3/parameter.go @@ -18,7 +18,7 @@ import ( // Parameter represents a high-level OpenAPI 3+ Parameter object, that is backed by a low-level one. // // A unique parameter is defined by a combination of a name and location. -// - https://spec.openapis.org/oas/v3.1.0#parameter-object +// - https://spec.openapis.org/oas/v3.1.0#parameter-object type Parameter struct { Name low.NodeReference[string] In low.NodeReference[string] diff --git a/datamodel/low/v3/request_body.go b/datamodel/low/v3/request_body.go index d11c6d8..782fbe3 100644 --- a/datamodel/low/v3/request_body.go +++ b/datamodel/low/v3/request_body.go @@ -14,7 +14,7 @@ import ( ) // RequestBody represents a low-level OpenAPI 3+ RequestBody object. -// - https://spec.openapis.org/oas/v3.1.0#request-body-object +// - https://spec.openapis.org/oas/v3.1.0#request-body-object type RequestBody struct { Description low.NodeReference[string] Content low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*MediaType]] diff --git a/datamodel/low/v3/security_scheme.go b/datamodel/low/v3/security_scheme.go index 468a8c8..373510a 100644 --- a/datamodel/low/v3/security_scheme.go +++ b/datamodel/low/v3/security_scheme.go @@ -22,7 +22,7 @@ import ( // authorization code) as defined in RFC6749 (https://www.rfc-editor.org/rfc/rfc6749), and OpenID Connect Discovery. // Please note that as of 2020, the implicit flow is about to be deprecated by OAuth 2.0 Security Best Current Practice. // Recommended for most use case is Authorization Code Grant flow with PKCE. -// - https://spec.openapis.org/oas/v3.1.0#security-scheme-object +// - https://spec.openapis.org/oas/v3.1.0#security-scheme-object type SecurityScheme struct { Type low.NodeReference[string] Description low.NodeReference[string] diff --git a/datamodel/low/v3/server.go b/datamodel/low/v3/server.go index 92e2210..dc074d2 100644 --- a/datamodel/low/v3/server.go +++ b/datamodel/low/v3/server.go @@ -14,7 +14,7 @@ import ( ) // Server represents a low-level OpenAPI 3+ Server object. -// - https://spec.openapis.org/oas/v3.1.0#server-object +// - https://spec.openapis.org/oas/v3.1.0#server-object type Server struct { URL low.NodeReference[string] Description low.NodeReference[string] diff --git a/datamodel/spec_info_test.go b/datamodel/spec_info_test.go index ceb5e38..ae06c7a 100644 --- a/datamodel/spec_info_test.go +++ b/datamodel/spec_info_test.go @@ -4,31 +4,31 @@ package datamodel import ( - "fmt" - "io/ioutil" - "testing" + "fmt" + "io/ioutil" + "testing" - "github.com/pb33f/libopenapi/utils" - "github.com/stretchr/testify/assert" + "github.com/pb33f/libopenapi/utils" + "github.com/stretchr/testify/assert" ) const ( - // OpenApi3 is used by all OpenAPI 3+ docs - OpenApi3 = "openapi" + // OpenApi3 is used by all OpenAPI 3+ docs + OpenApi3 = "openapi" - // OpenApi2 is used by all OpenAPI 2 docs, formerly known as swagger. - OpenApi2 = "swagger" + // OpenApi2 is used by all OpenAPI 2 docs, formerly known as swagger. + OpenApi2 = "swagger" - // AsyncApi is used by akk AsyncAPI docs, all versions. - AsyncApi = "asyncapi" + // AsyncApi is used by akk AsyncAPI docs, all versions. + AsyncApi = "asyncapi" ) func TestSpecInfo_GetJSONParsingChannel(t *testing.T) { - // dumb, but we need to ensure coverage is as high as we can make it. - bchan := make(chan bool) - si := &SpecInfo{JsonParsingChannel: bchan} - assert.Equal(t, si.GetJSONParsingChannel(), bchan) + // dumb, but we need to ensure coverage is as high as we can make it. + bchan := make(chan bool) + si := &SpecInfo{JsonParsingChannel: bchan} + assert.Equal(t, si.GetJSONParsingChannel(), bchan) } @@ -115,147 +115,147 @@ info: version: '0.1.0'` func TestExtractSpecInfo_ValidJSON(t *testing.T) { - r, e := ExtractSpecInfo([]byte(goodJSON)) - assert.Greater(t, len(*r.SpecJSONBytes), 0) - assert.Error(t, e) + r, e := ExtractSpecInfo([]byte(goodJSON)) + assert.Greater(t, len(*r.SpecJSONBytes), 0) + assert.Error(t, e) } func TestExtractSpecInfo_InvalidJSON(t *testing.T) { - _, e := ExtractSpecInfo([]byte(badJSON)) - assert.Error(t, e) + _, e := ExtractSpecInfo([]byte(badJSON)) + assert.Error(t, e) } func TestExtractSpecInfo_Nothing(t *testing.T) { - _, e := ExtractSpecInfo([]byte("")) - assert.Error(t, e) + _, e := ExtractSpecInfo([]byte("")) + assert.Error(t, e) } func TestExtractSpecInfo_ValidYAML(t *testing.T) { - r, e := ExtractSpecInfo([]byte(goodYAML)) - assert.Greater(t, len(*r.SpecJSONBytes), 0) - assert.Error(t, e) + r, e := ExtractSpecInfo([]byte(goodYAML)) + assert.Greater(t, len(*r.SpecJSONBytes), 0) + assert.Error(t, e) } func TestExtractSpecInfo_InvalidYAML(t *testing.T) { - _, e := ExtractSpecInfo([]byte(badYAML)) - assert.Error(t, e) + _, e := ExtractSpecInfo([]byte(badYAML)) + assert.Error(t, e) } func TestExtractSpecInfo_InvalidOpenAPIVersion(t *testing.T) { - _, e := ExtractSpecInfo([]byte(OpenApiOne)) - assert.Error(t, e) + _, e := ExtractSpecInfo([]byte(OpenApiOne)) + assert.Error(t, e) } func TestExtractSpecInfo_OpenAPI3(t *testing.T) { - r, e := ExtractSpecInfo([]byte(OpenApi3Spec)) - assert.Nil(t, e) - assert.Equal(t, utils.OpenApi3, r.SpecType) - assert.Equal(t, "3.0.1", r.Version) - assert.Greater(t, len(*r.SpecJSONBytes), 0) - assert.Contains(t, r.APISchema, "https://spec.openapis.org/oas/3.0/schema/2021-09-28") + r, e := ExtractSpecInfo([]byte(OpenApi3Spec)) + assert.Nil(t, e) + assert.Equal(t, utils.OpenApi3, r.SpecType) + assert.Equal(t, "3.0.1", r.Version) + assert.Greater(t, len(*r.SpecJSONBytes), 0) + assert.Contains(t, r.APISchema, "https://spec.openapis.org/oas/3.0/schema/2021-09-28") } func TestExtractSpecInfo_OpenAPIWat(t *testing.T) { - r, e := ExtractSpecInfo([]byte(OpenApiWat)) - assert.Nil(t, e) - assert.Equal(t, OpenApi3, r.SpecType) - assert.Equal(t, "3.2", r.Version) + r, e := ExtractSpecInfo([]byte(OpenApiWat)) + assert.Nil(t, e) + assert.Equal(t, OpenApi3, r.SpecType) + assert.Equal(t, "3.2", r.Version) } func TestExtractSpecInfo_OpenAPI31(t *testing.T) { - r, e := ExtractSpecInfo([]byte(OpenApi31)) - assert.Nil(t, e) - assert.Equal(t, OpenApi3, r.SpecType) - assert.Equal(t, "3.1", r.Version) - assert.Contains(t, r.APISchema, "https://spec.openapis.org/oas/3.1/schema/2022-10-07") + r, e := ExtractSpecInfo([]byte(OpenApi31)) + assert.Nil(t, e) + assert.Equal(t, OpenApi3, r.SpecType) + assert.Equal(t, "3.1", r.Version) + assert.Contains(t, r.APISchema, "https://spec.openapis.org/oas/3.1/schema/2022-10-07") } func TestExtractSpecInfo_OpenAPIFalse(t *testing.T) { - spec, e := ExtractSpecInfo([]byte(OpenApiFalse)) - assert.NoError(t, e) - assert.Equal(t, "false", spec.Version) + spec, e := ExtractSpecInfo([]byte(OpenApiFalse)) + assert.NoError(t, e) + assert.Equal(t, "false", spec.Version) } func TestExtractSpecInfo_OpenAPI2(t *testing.T) { - r, e := ExtractSpecInfo([]byte(OpenApi2Spec)) - assert.Nil(t, e) - assert.Equal(t, OpenApi2, r.SpecType) - assert.Equal(t, "2.0.1", r.Version) - assert.Greater(t, len(*r.SpecJSONBytes), 0) - assert.Contains(t, r.APISchema, "http://swagger.io/v2/schema.json#") + r, e := ExtractSpecInfo([]byte(OpenApi2Spec)) + assert.Nil(t, e) + assert.Equal(t, OpenApi2, r.SpecType) + assert.Equal(t, "2.0.1", r.Version) + assert.Greater(t, len(*r.SpecJSONBytes), 0) + assert.Contains(t, r.APISchema, "http://swagger.io/v2/schema.json#") } func TestExtractSpecInfo_OpenAPI2_OddVersion(t *testing.T) { - _, e := ExtractSpecInfo([]byte(OpenApi2SpecOdd)) - assert.NotNil(t, e) - assert.Equal(t, - "spec is defined as a swagger (openapi 2.0) spec, but is an openapi 3 or unknown version", e.Error()) + _, e := ExtractSpecInfo([]byte(OpenApi2SpecOdd)) + assert.NotNil(t, e) + assert.Equal(t, + "spec is defined as a swagger (openapi 2.0) spec, but is an openapi 3 or unknown version", e.Error()) } func TestExtractSpecInfo_AsyncAPI(t *testing.T) { - r, e := ExtractSpecInfo([]byte(AsyncAPISpec)) - assert.Nil(t, e) - assert.Equal(t, AsyncApi, r.SpecType) - assert.Equal(t, "2.0.0", r.Version) - assert.Greater(t, len(*r.SpecJSONBytes), 0) + r, e := ExtractSpecInfo([]byte(AsyncAPISpec)) + assert.Nil(t, e) + assert.Equal(t, AsyncApi, r.SpecType) + assert.Equal(t, "2.0.0", r.Version) + assert.Greater(t, len(*r.SpecJSONBytes), 0) } func TestExtractSpecInfo_AsyncAPI_OddVersion(t *testing.T) { - _, e := ExtractSpecInfo([]byte(AsyncAPISpecOdd)) - assert.NotNil(t, e) - assert.Equal(t, - "spec is defined as asyncapi, but has a major version that is invalid", e.Error()) + _, e := ExtractSpecInfo([]byte(AsyncAPISpecOdd)) + assert.NotNil(t, e) + assert.Equal(t, + "spec is defined as asyncapi, but has a major version that is invalid", e.Error()) } func TestExtractSpecInfo_BadVersion_OpenAPI3(t *testing.T) { - yml := `openapi: + yml := `openapi: should: fail` - _, err := ExtractSpecInfo([]byte(yml)) - assert.Error(t, err) + _, err := ExtractSpecInfo([]byte(yml)) + assert.Error(t, err) } func TestExtractSpecInfo_BadVersion_Swagger(t *testing.T) { - yml := `swagger: + yml := `swagger: should: fail` - _, err := ExtractSpecInfo([]byte(yml)) - assert.Error(t, err) + _, err := ExtractSpecInfo([]byte(yml)) + assert.Error(t, err) } func TestExtractSpecInfo_BadVersion_AsyncAPI(t *testing.T) { - yml := `asyncapi: + yml := `asyncapi: should: fail` - _, err := ExtractSpecInfo([]byte(yml)) - assert.Error(t, err) + _, err := ExtractSpecInfo([]byte(yml)) + assert.Error(t, err) } func ExampleExtractSpecInfo() { - // load bytes from openapi spec file. - bytes, _ := ioutil.ReadFile("../test_specs/petstorev3.json") + // load bytes from openapi spec file. + bytes, _ := ioutil.ReadFile("../test_specs/petstorev3.json") - // create a new *SpecInfo instance from loaded bytes - specInfo, err := ExtractSpecInfo(bytes) - if err != nil { - panic(fmt.Sprintf("cannot extract spec info: %e", err)) - } + // create a new *SpecInfo instance from loaded bytes + specInfo, err := ExtractSpecInfo(bytes) + if err != nil { + panic(fmt.Sprintf("cannot extract spec info: %e", err)) + } - // print out the version, format and filetype - fmt.Printf("the version of the spec is %s, the format is %s and the file type is %s", - specInfo.Version, specInfo.SpecFormat, specInfo.SpecFileType) + // print out the version, format and filetype + fmt.Printf("the version of the spec is %s, the format is %s and the file type is %s", + specInfo.Version, specInfo.SpecFormat, specInfo.SpecFileType) - // Output: the version of the spec is 3.0.2, the format is oas3 and the file type is json + // Output: the version of the spec is 3.0.2, the format is oas3 and the file type is json } diff --git a/document_examples_test.go b/document_examples_test.go index 7c44bb6..5323cc5 100644 --- a/document_examples_test.go +++ b/document_examples_test.go @@ -4,221 +4,221 @@ package libopenapi import ( - "fmt" - "github.com/pb33f/libopenapi/datamodel" - "io/ioutil" - "net/url" - "os" - "strings" - "testing" + "fmt" + "github.com/pb33f/libopenapi/datamodel" + "io/ioutil" + "net/url" + "os" + "strings" + "testing" - "github.com/pb33f/libopenapi/datamodel/high" - v3high "github.com/pb33f/libopenapi/datamodel/high/v3" - low "github.com/pb33f/libopenapi/datamodel/low/base" - v3 "github.com/pb33f/libopenapi/datamodel/low/v3" - "github.com/pb33f/libopenapi/resolver" - "github.com/pb33f/libopenapi/utils" - "github.com/stretchr/testify/assert" + "github.com/pb33f/libopenapi/datamodel/high" + v3high "github.com/pb33f/libopenapi/datamodel/high/v3" + low "github.com/pb33f/libopenapi/datamodel/low/base" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/resolver" + "github.com/pb33f/libopenapi/utils" + "github.com/stretchr/testify/assert" ) func ExampleNewDocument_fromOpenAPI3Document() { - // How to read in an OpenAPI 3 Specification, into a Document. + // How to read in an OpenAPI 3 Specification, into a Document. - // load an OpenAPI 3 specification from bytes - petstore, _ := ioutil.ReadFile("test_specs/petstorev3.json") + // load an OpenAPI 3 specification from bytes + petstore, _ := ioutil.ReadFile("test_specs/petstorev3.json") - // create a new document from specification bytes - document, err := NewDocument(petstore) + // create a new document from specification bytes + document, err := NewDocument(petstore) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } - // because we know this is a v3 spec, we can build a ready to go model from it. - v3Model, errors := document.BuildV3Model() + // because we know this is a v3 spec, we can build a ready to go model from it. + v3Model, errors := document.BuildV3Model() - // if anything went wrong when building the v3 model, a slice of errors will be returned - if len(errors) > 0 { - for i := range errors { - fmt.Printf("error: %e\n", errors[i]) - } - panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors))) - } + // if anything went wrong when building the v3 model, a slice of errors will be returned + if len(errors) > 0 { + for i := range errors { + fmt.Printf("error: %e\n", errors[i]) + } + panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors))) + } - // get a count of the number of paths and schemas. - paths := len(v3Model.Model.Paths.PathItems) - schemas := len(v3Model.Model.Components.Schemas) + // get a count of the number of paths and schemas. + paths := len(v3Model.Model.Paths.PathItems) + schemas := len(v3Model.Model.Components.Schemas) - // print the number of paths and schemas in the document - fmt.Printf("There are %d paths and %d schemas in the document", paths, schemas) - // Output: There are 13 paths and 8 schemas in the document + // print the number of paths and schemas in the document + fmt.Printf("There are %d paths and %d schemas in the document", paths, schemas) + // Output: There are 13 paths and 8 schemas in the document } func ExampleNewDocument_fromWithDocumentConfigurationFailure() { - // This example shows how to create a document that prevents the loading of external references/ - // from files or the network + // This example shows how to create a document that prevents the loading of external references/ + // from files or the network - // load in the Digital Ocean OpenAPI specification - digitalOcean, _ := ioutil.ReadFile("test_specs/digitalocean.yaml") + // load in the Digital Ocean OpenAPI specification + digitalOcean, _ := ioutil.ReadFile("test_specs/digitalocean.yaml") - // create a DocumentConfiguration that prevents loading file and remote references - config := datamodel.DocumentConfiguration{ - AllowFileReferences: false, - AllowRemoteReferences: false, - } + // create a DocumentConfiguration that prevents loading file and remote references + config := datamodel.DocumentConfiguration{ + AllowFileReferences: false, + AllowRemoteReferences: false, + } - // create a new document from specification bytes - doc, err := NewDocumentWithConfiguration(digitalOcean, &config) + // create a new document from specification bytes + doc, err := NewDocumentWithConfiguration(digitalOcean, &config) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } - // only errors will be thrown, so just capture them and print the number of errors. - _, errors := doc.BuildV3Model() + // only errors will be thrown, so just capture them and print the number of errors. + _, errors := doc.BuildV3Model() - // if anything went wrong when building the v3 model, a slice of errors will be returned - if len(errors) > 0 { - fmt.Println("Error building Digital Ocean spec errors reported") - } - // Output: Error building Digital Ocean spec errors reported + // if anything went wrong when building the v3 model, a slice of errors will be returned + if len(errors) > 0 { + fmt.Println("Error building Digital Ocean spec errors reported") + } + // Output: Error building Digital Ocean spec errors reported } func ExampleNewDocument_fromWithDocumentConfigurationSuccess() { - // This example shows how to create a document that prevents the loading of external references/ - // from files or the network + // This example shows how to create a document that prevents the loading of external references/ + // from files or the network - // load in the Digital Ocean OpenAPI specification - digitalOcean, _ := ioutil.ReadFile("test_specs/digitalocean.yaml") + // load in the Digital Ocean OpenAPI specification + digitalOcean, _ := ioutil.ReadFile("test_specs/digitalocean.yaml") - // Digital Ocean needs a baseURL to be set, so we can resolve relative references. - baseURL, _ := url.Parse("https://raw.githubusercontent.com/digitalocean/openapi/main/specification") + // Digital Ocean needs a baseURL to be set, so we can resolve relative references. + baseURL, _ := url.Parse("https://raw.githubusercontent.com/digitalocean/openapi/main/specification") - // create a DocumentConfiguration that allows loading file and remote references, and sets the baseURL - // to somewhere that can resolve the relative references. - config := datamodel.DocumentConfiguration{ - AllowFileReferences: true, - AllowRemoteReferences: true, - BaseURL: baseURL, - } + // create a DocumentConfiguration that allows loading file and remote references, and sets the baseURL + // to somewhere that can resolve the relative references. + config := datamodel.DocumentConfiguration{ + AllowFileReferences: true, + AllowRemoteReferences: true, + BaseURL: baseURL, + } - // create a new document from specification bytes - doc, err := NewDocumentWithConfiguration(digitalOcean, &config) + // create a new document from specification bytes + doc, err := NewDocumentWithConfiguration(digitalOcean, &config) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } - // only errors will be thrown, so just capture them and print the number of errors. - _, errors := doc.BuildV3Model() + // only errors will be thrown, so just capture them and print the number of errors. + _, errors := doc.BuildV3Model() - // if anything went wrong when building the v3 model, a slice of errors will be returned - if len(errors) > 0 { - fmt.Println("Error building Digital Ocean spec errors reported") - } else { - fmt.Println("Digital Ocean spec built successfully") - } - // Output: Digital Ocean spec built successfully + // if anything went wrong when building the v3 model, a slice of errors will be returned + if len(errors) > 0 { + fmt.Println("Error building Digital Ocean spec errors reported") + } else { + fmt.Println("Digital Ocean spec built successfully") + } + // Output: Digital Ocean spec built successfully } func ExampleNewDocument_fromSwaggerDocument() { - // How to read in a Swagger / OpenAPI 2 Specification, into a Document. + // How to read in a Swagger / OpenAPI 2 Specification, into a Document. - // load a Swagger specification from bytes - petstore, _ := ioutil.ReadFile("test_specs/petstorev2.json") + // load a Swagger specification from bytes + petstore, _ := ioutil.ReadFile("test_specs/petstorev2.json") - // create a new document from specification bytes - document, err := NewDocument(petstore) + // create a new document from specification bytes + document, err := NewDocument(petstore) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } - // because we know this is a v2 spec, we can build a ready to go model from it. - v2Model, errors := document.BuildV2Model() + // because we know this is a v2 spec, we can build a ready to go model from it. + v2Model, errors := document.BuildV2Model() - // if anything went wrong when building the v3 model, a slice of errors will be returned - if len(errors) > 0 { - for i := range errors { - fmt.Printf("error: %e\n", errors[i]) - } - panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors))) - } + // if anything went wrong when building the v3 model, a slice of errors will be returned + if len(errors) > 0 { + for i := range errors { + fmt.Printf("error: %e\n", errors[i]) + } + panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors))) + } - // get a count of the number of paths and schemas. - paths := len(v2Model.Model.Paths.PathItems) - schemas := len(v2Model.Model.Definitions.Definitions) + // get a count of the number of paths and schemas. + paths := len(v2Model.Model.Paths.PathItems) + schemas := len(v2Model.Model.Definitions.Definitions) - // print the number of paths and schemas in the document - fmt.Printf("There are %d paths and %d schemas in the document", paths, schemas) - // Output: There are 14 paths and 6 schemas in the document + // print the number of paths and schemas in the document + fmt.Printf("There are %d paths and %d schemas in the document", paths, schemas) + // Output: There are 14 paths and 6 schemas in the document } func ExampleNewDocument_fromUnknownVersion() { - // load an unknown version of an OpenAPI spec - petstore, _ := ioutil.ReadFile("test_specs/burgershop.openapi.yaml") + // load an unknown version of an OpenAPI spec + petstore, _ := ioutil.ReadFile("test_specs/burgershop.openapi.yaml") - // create a new document from specification bytes - document, err := NewDocument(petstore) + // create a new document from specification bytes + document, err := NewDocument(petstore) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } - var paths, schemas int - var errors []error + var paths, schemas int + var errors []error - // We don't know which type of document this is, so we can use the spec info to inform us - if document.GetSpecInfo().SpecType == utils.OpenApi3 { - v3Model, errs := document.BuildV3Model() - if len(errs) > 0 { - errors = errs - } - if len(errors) <= 0 { - paths = len(v3Model.Model.Paths.PathItems) - schemas = len(v3Model.Model.Components.Schemas) - } - } - if document.GetSpecInfo().SpecType == utils.OpenApi2 { - v2Model, errs := document.BuildV2Model() - if len(errs) > 0 { - errors = errs - } - if len(errors) <= 0 { - paths = len(v2Model.Model.Paths.PathItems) - schemas = len(v2Model.Model.Definitions.Definitions) - } - } + // We don't know which type of document this is, so we can use the spec info to inform us + if document.GetSpecInfo().SpecType == utils.OpenApi3 { + v3Model, errs := document.BuildV3Model() + if len(errs) > 0 { + errors = errs + } + if len(errors) <= 0 { + paths = len(v3Model.Model.Paths.PathItems) + schemas = len(v3Model.Model.Components.Schemas) + } + } + if document.GetSpecInfo().SpecType == utils.OpenApi2 { + v2Model, errs := document.BuildV2Model() + if len(errs) > 0 { + errors = errs + } + if len(errors) <= 0 { + paths = len(v2Model.Model.Paths.PathItems) + schemas = len(v2Model.Model.Definitions.Definitions) + } + } - // if anything went wrong when building the model, report errors. - if len(errors) > 0 { - for i := range errors { - fmt.Printf("error: %e\n", errors[i]) - } - panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors))) - } + // if anything went wrong when building the model, report errors. + if len(errors) > 0 { + for i := range errors { + fmt.Printf("error: %e\n", errors[i]) + } + panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors))) + } - // print the number of paths and schemas in the document - fmt.Printf("There are %d paths and %d schemas in the document", paths, schemas) - // Output: There are 5 paths and 6 schemas in the document + // print the number of paths and schemas in the document + fmt.Printf("There are %d paths and %d schemas in the document", paths, schemas) + // Output: There are 5 paths and 6 schemas in the document } func ExampleNewDocument_mutateValuesAndSerialize() { - // How to mutate values in an OpenAPI Specification, without re-ordering original content. + // How to mutate values in an OpenAPI Specification, without re-ordering original content. - // create very small, and useless spec that does nothing useful, except showcase this feature. - spec := ` + // create very small, and useless spec that does nothing useful, except showcase this feature. + spec := ` openapi: 3.1.0 info: title: This is a title @@ -228,155 +228,155 @@ info: license: url: http://some-place-on-the-internet.com/license ` - // create a new document from specification bytes - document, err := NewDocument([]byte(spec)) + // create a new document from specification bytes + document, err := NewDocument([]byte(spec)) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } - // because we know this is a v3 spec, we can build a ready to go model from it. - v3Model, errors := document.BuildV3Model() + // because we know this is a v3 spec, we can build a ready to go model from it. + v3Model, errors := document.BuildV3Model() - // if anything went wrong when building the v3 model, a slice of errors will be returned - if len(errors) > 0 { - for i := range errors { - fmt.Printf("error: %e\n", errors[i]) - } - panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors))) - } + // if anything went wrong when building the v3 model, a slice of errors will be returned + if len(errors) > 0 { + for i := range errors { + fmt.Printf("error: %e\n", errors[i]) + } + panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors))) + } - // mutate the title, to do this we currently need to drop down to the low-level API. - v3Model.Model.GoLow().Info.Value.Title.Mutate("A new title for a useless spec") + // mutate the title, to do this we currently need to drop down to the low-level API. + v3Model.Model.GoLow().Info.Value.Title.Mutate("A new title for a useless spec") - // mutate the email address in the contact object. - v3Model.Model.GoLow().Info.Value.Contact.Value.Email.Mutate("buckaroo@pb33f.io") + // mutate the email address in the contact object. + v3Model.Model.GoLow().Info.Value.Contact.Value.Email.Mutate("buckaroo@pb33f.io") - // mutate the name in the contact object. - v3Model.Model.GoLow().Info.Value.Contact.Value.Name.Mutate("Buckaroo") + // mutate the name in the contact object. + v3Model.Model.GoLow().Info.Value.Contact.Value.Name.Mutate("Buckaroo") - // mutate the URL for the license object. - v3Model.Model.GoLow().Info.Value.License.Value.URL.Mutate("https://pb33f.io/license") + // mutate the URL for the license object. + v3Model.Model.GoLow().Info.Value.License.Value.URL.Mutate("https://pb33f.io/license") - // serialize the document back into the original YAML or JSON - mutatedSpec, serialError := document.Serialize() + // serialize the document back into the original YAML or JSON + mutatedSpec, serialError := document.Serialize() - // if something went wrong serializing - if serialError != nil { - panic(fmt.Sprintf("cannot serialize document: %e", serialError)) - } + // if something went wrong serializing + if serialError != nil { + panic(fmt.Sprintf("cannot serialize document: %e", serialError)) + } - // print our modified spec! - fmt.Println(string(mutatedSpec)) - // Output: openapi: 3.1.0 - //info: - // title: A new title for a useless spec - // contact: - // name: Buckaroo - // email: buckaroo@pb33f.io - // license: - // url: https://pb33f.io/license + // print our modified spec! + fmt.Println(string(mutatedSpec)) + // Output: openapi: 3.1.0 + //info: + // title: A new title for a useless spec + // contact: + // name: Buckaroo + // email: buckaroo@pb33f.io + // license: + // url: https://pb33f.io/license } func ExampleCompareDocuments_openAPI() { - // How to compare two different OpenAPI specifications. + // How to compare two different OpenAPI specifications. - // load an original OpenAPI 3 specification from bytes - burgerShopOriginal, _ := ioutil.ReadFile("test_specs/burgershop.openapi.yaml") + // load an original OpenAPI 3 specification from bytes + burgerShopOriginal, _ := ioutil.ReadFile("test_specs/burgershop.openapi.yaml") - // load an **updated** OpenAPI 3 specification from bytes - burgerShopUpdated, _ := ioutil.ReadFile("test_specs/burgershop.openapi-modified.yaml") + // load an **updated** OpenAPI 3 specification from bytes + burgerShopUpdated, _ := ioutil.ReadFile("test_specs/burgershop.openapi-modified.yaml") - // create a new document from original specification bytes - originalDoc, err := NewDocument(burgerShopOriginal) + // create a new document from original specification bytes + originalDoc, err := NewDocument(burgerShopOriginal) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } - // create a new document from updated specification bytes - updatedDoc, err := NewDocument(burgerShopUpdated) + // create a new document from updated specification bytes + updatedDoc, err := NewDocument(burgerShopUpdated) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } - // Compare documents for all changes made - documentChanges, errs := CompareDocuments(originalDoc, updatedDoc) + // Compare documents for all changes made + documentChanges, errs := CompareDocuments(originalDoc, updatedDoc) - // If anything went wrong when building models for documents. - if len(errs) > 0 { - for i := range errs { - fmt.Printf("error: %e\n", errs[i]) - } - panic(fmt.Sprintf("cannot compare documents: %d errors reported", len(errs))) - } + // If anything went wrong when building models for documents. + if len(errs) > 0 { + for i := range errs { + fmt.Printf("error: %e\n", errs[i]) + } + panic(fmt.Sprintf("cannot compare documents: %d errors reported", len(errs))) + } - // Extract SchemaChanges from components changes. - schemaChanges := documentChanges.ComponentsChanges.SchemaChanges + // Extract SchemaChanges from components changes. + schemaChanges := documentChanges.ComponentsChanges.SchemaChanges - // Print out some interesting stats about the OpenAPI document changes. - fmt.Printf("There are %d changes, of which %d are breaking. %v schemas have changes.", - documentChanges.TotalChanges(), documentChanges.TotalBreakingChanges(), len(schemaChanges)) - //Output: There are 72 changes, of which 17 are breaking. 5 schemas have changes. + // Print out some interesting stats about the OpenAPI document changes. + fmt.Printf("There are %d changes, of which %d are breaking. %v schemas have changes.", + documentChanges.TotalChanges(), documentChanges.TotalBreakingChanges(), len(schemaChanges)) + //Output: There are 72 changes, of which 17 are breaking. 5 schemas have changes. } func ExampleCompareDocuments_swagger() { - // How to compare two different Swagger specifications. + // How to compare two different Swagger specifications. - // load an original OpenAPI 3 specification from bytes - petstoreOriginal, _ := ioutil.ReadFile("test_specs/petstorev2-complete.yaml") + // load an original OpenAPI 3 specification from bytes + petstoreOriginal, _ := ioutil.ReadFile("test_specs/petstorev2-complete.yaml") - // load an **updated** OpenAPI 3 specification from bytes - petstoreUpdated, _ := ioutil.ReadFile("test_specs/petstorev2-complete-modified.yaml") + // load an **updated** OpenAPI 3 specification from bytes + petstoreUpdated, _ := ioutil.ReadFile("test_specs/petstorev2-complete-modified.yaml") - // create a new document from original specification bytes - originalDoc, err := NewDocument(petstoreOriginal) + // create a new document from original specification bytes + originalDoc, err := NewDocument(petstoreOriginal) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } - // create a new document from updated specification bytes - updatedDoc, err := NewDocument(petstoreUpdated) + // create a new document from updated specification bytes + updatedDoc, err := NewDocument(petstoreUpdated) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } - // Compare documents for all changes made - documentChanges, errs := CompareDocuments(originalDoc, updatedDoc) + // Compare documents for all changes made + documentChanges, errs := CompareDocuments(originalDoc, updatedDoc) - // If anything went wrong when building models for documents. - if len(errs) > 0 { - for i := range errs { - fmt.Printf("error: %e\n", errs[i]) - } - panic(fmt.Sprintf("cannot compare documents: %d errors reported", len(errs))) - } + // If anything went wrong when building models for documents. + if len(errs) > 0 { + for i := range errs { + fmt.Printf("error: %e\n", errs[i]) + } + panic(fmt.Sprintf("cannot compare documents: %d errors reported", len(errs))) + } - // Extract SchemaChanges from components changes. - schemaChanges := documentChanges.ComponentsChanges.SchemaChanges + // Extract SchemaChanges from components changes. + schemaChanges := documentChanges.ComponentsChanges.SchemaChanges - // Print out some interesting stats about the Swagger document changes. - fmt.Printf("There are %d changes, of which %d are breaking. %v schemas have changes.", - documentChanges.TotalChanges(), documentChanges.TotalBreakingChanges(), len(schemaChanges)) - //Output: There are 52 changes, of which 27 are breaking. 5 schemas have changes. + // Print out some interesting stats about the Swagger document changes. + fmt.Printf("There are %d changes, of which %d are breaking. %v schemas have changes.", + documentChanges.TotalChanges(), documentChanges.TotalBreakingChanges(), len(schemaChanges)) + //Output: There are 52 changes, of which 27 are breaking. 5 schemas have changes. } func TestDocument_Paths_As_Array(t *testing.T) { - // paths can now be wrapped in an array. - spec := `{ + // paths can now be wrapped in an array. + spec := `{ "openapi": "3.1.0", "paths": [ "/": { @@ -385,15 +385,15 @@ func TestDocument_Paths_As_Array(t *testing.T) { ] } ` - // create a new document from specification bytes - doc, err := NewDocument([]byte(spec)) + // create a new document from specification bytes + doc, err := NewDocument([]byte(spec)) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } - v3Model, _ := doc.BuildV3Model() - assert.NotNil(t, v3Model) + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } + v3Model, _ := doc.BuildV3Model() + assert.NotNil(t, v3Model) } // If you want to know more about circular references that have been found @@ -401,8 +401,8 @@ func TestDocument_Paths_As_Array(t *testing.T) { // []errors thrown which are pointers to *resolver.ResolvingError func ExampleNewDocument_infinite_circular_references() { - // create a specification with an obvious and deliberate circular reference - spec := `openapi: "3.1" + // create a specification with an obvious and deliberate circular reference + spec := `openapi: "3.1" components: schemas: One: @@ -420,45 +420,45 @@ components: required: - testThing ` - // create a new document from specification bytes - doc, err := NewDocument([]byte(spec)) + // create a new document from specification bytes + doc, err := NewDocument([]byte(spec)) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } - _, errs := doc.BuildV3Model() + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } + _, errs := doc.BuildV3Model() - // extract resolving error - resolvingError := errs[0] + // extract resolving error + resolvingError := errs[0] - // resolving error is a pointer to *resolver.ResolvingError - // which provides access to rich details about the error. - circularReference := resolvingError.(*resolver.ResolvingError).CircularReference + // resolving error is a pointer to *resolver.ResolvingError + // which provides access to rich details about the error. + circularReference := resolvingError.(*resolver.ResolvingError).CircularReference - // capture the journey with all details - var buf strings.Builder - for n := range circularReference.Journey { + // capture the journey with all details + var buf strings.Builder + for n := range circularReference.Journey { - // add the full definition name to the journey. - buf.WriteString(circularReference.Journey[n].Definition) - if n < len(circularReference.Journey)-1 { - buf.WriteString(" -> ") - } - } + // add the full definition name to the journey. + buf.WriteString(circularReference.Journey[n].Definition) + if n < len(circularReference.Journey)-1 { + buf.WriteString(" -> ") + } + } - // print out the journey and the loop point. - fmt.Printf("Journey: %s\n", buf.String()) - fmt.Printf("Loop Point: %s", circularReference.LoopPoint.Definition) - // Output: Journey: #/components/schemas/Two -> #/components/schemas/One -> #/components/schemas/Two - // Loop Point: #/components/schemas/Two + // print out the journey and the loop point. + fmt.Printf("Journey: %s\n", buf.String()) + fmt.Printf("Loop Point: %s", circularReference.LoopPoint.Definition) + // Output: Journey: #/components/schemas/Two -> #/components/schemas/One -> #/components/schemas/Two + // Loop Point: #/components/schemas/Two } // This tests checks that circular references which are _not_ marked as required pass correctly func TestNewDocument_terminable_circular_references(t *testing.T) { - // create a specification with an obvious and deliberate circular reference - spec := `openapi: "3.1" + // create a specification with an obvious and deliberate circular reference + spec := `openapi: "3.1" components: schemas: One: @@ -472,16 +472,16 @@ components: testThing: "$ref": "#/components/schemas/One" ` - // create a new document from specification bytes - doc, err := NewDocument([]byte(spec)) + // create a new document from specification bytes + doc, err := NewDocument([]byte(spec)) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } - _, errs := doc.BuildV3Model() + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } + _, errs := doc.BuildV3Model() - assert.Len(t, errs, 0) + assert.Len(t, errs, 0) } // If you're using complex types with OpenAPI Extensions, it's simple to unpack extensions into complex @@ -491,33 +491,33 @@ components: // This example demonstrates how to use the `UnpackExtensions` with custom OpenAPI extensions. func ExampleNewDocument_unpacking_extensions() { - // define an example struct representing a cake - type cake struct { - Candles int `yaml:"candles"` - Frosting string `yaml:"frosting"` - Some_Strange_Var_Name string `yaml:"someStrangeVarName"` - } + // define an example struct representing a cake + type cake struct { + Candles int `yaml:"candles"` + Frosting string `yaml:"frosting"` + Some_Strange_Var_Name string `yaml:"someStrangeVarName"` + } - // define a struct that holds a map of cake pointers. - type cakes struct { - Description string - Cakes map[string]*cake - } + // define a struct that holds a map of cake pointers. + type cakes struct { + Description string + Cakes map[string]*cake + } - // define a struct representing a burger - type burger struct { - Sauce string - Patty string - } + // define a struct representing a burger + type burger struct { + Sauce string + Patty string + } - // define a struct that holds a map of cake pointers - type burgers struct { - Description string - Burgers map[string]*burger - } + // define a struct that holds a map of cake pointers + type burgers struct { + Description string + Burgers map[string]*burger + } - // create a specification with a schema and parameter that use complex custom cakes and burgers extensions. - spec := `openapi: "3.1" + // create a specification with a schema and parameter that use complex custom cakes and burgers extensions. + spec := `openapi: "3.1" components: schemas: SchemaOne: @@ -544,126 +544,126 @@ components: anotherBurger: sauce: mayo patty: lamb` - // create a new document from specification bytes - doc, err := NewDocument([]byte(spec)) + // create a new document from specification bytes + doc, err := NewDocument([]byte(spec)) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } - // build a v3 model. - docModel, errs := doc.BuildV3Model() + // build a v3 model. + docModel, errs := doc.BuildV3Model() - // if anything went wrong building, indexing and resolving the model, an error is thrown - if errs != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } + // if anything went wrong building, indexing and resolving the model, an error is thrown + if errs != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } - // get a reference to SchemaOne and ParameterOne - schemaOne := docModel.Model.Components.Schemas["SchemaOne"].Schema() - parameterOne := docModel.Model.Components.Parameters["ParameterOne"] + // get a reference to SchemaOne and ParameterOne + schemaOne := docModel.Model.Components.Schemas["SchemaOne"].Schema() + parameterOne := docModel.Model.Components.Parameters["ParameterOne"] - // unpack schemaOne extensions into complex `cakes` type - schemaOneExtensions, schemaUnpackErrors := high.UnpackExtensions[cakes, *low.Schema](schemaOne) - if schemaUnpackErrors != nil { - panic(fmt.Sprintf("cannot unpack schema extensions: %e", err)) - } + // unpack schemaOne extensions into complex `cakes` type + schemaOneExtensions, schemaUnpackErrors := high.UnpackExtensions[cakes, *low.Schema](schemaOne) + if schemaUnpackErrors != nil { + panic(fmt.Sprintf("cannot unpack schema extensions: %e", err)) + } - // unpack parameterOne into complex `burgers` type - parameterOneExtensions, paramUnpackErrors := high.UnpackExtensions[burgers, *v3.Parameter](parameterOne) - if paramUnpackErrors != nil { - panic(fmt.Sprintf("cannot unpack parameter extensions: %e", err)) - } + // unpack parameterOne into complex `burgers` type + parameterOneExtensions, paramUnpackErrors := high.UnpackExtensions[burgers, *v3.Parameter](parameterOne) + if paramUnpackErrors != nil { + panic(fmt.Sprintf("cannot unpack parameter extensions: %e", err)) + } - // extract extension by name for schemaOne - customCakes := schemaOneExtensions["x-custom-cakes"] + // extract extension by name for schemaOne + customCakes := schemaOneExtensions["x-custom-cakes"] - // extract extension by name for schemaOne - customBurgers := parameterOneExtensions["x-custom-burgers"] + // extract extension by name for schemaOne + customBurgers := parameterOneExtensions["x-custom-burgers"] - // print out schemaOne complex extension details. - fmt.Printf("schemaOne 'x-custom-cakes' (%s) has %d cakes, 'someCake' has %d candles and %s frosting\n", - customCakes.Description, - len(customCakes.Cakes), - customCakes.Cakes["someCake"].Candles, - customCakes.Cakes["someCake"].Frosting, - ) + // print out schemaOne complex extension details. + fmt.Printf("schemaOne 'x-custom-cakes' (%s) has %d cakes, 'someCake' has %d candles and %s frosting\n", + customCakes.Description, + len(customCakes.Cakes), + customCakes.Cakes["someCake"].Candles, + customCakes.Cakes["someCake"].Frosting, + ) - // print out parameterOne complex extension details. - fmt.Printf("parameterOne 'x-custom-burgers' (%s) has %d burgers, 'anotherBurger' has %s sauce and a %s patty\n", - customBurgers.Description, - len(customBurgers.Burgers), - customBurgers.Burgers["anotherBurger"].Sauce, - customBurgers.Burgers["anotherBurger"].Patty, - ) + // print out parameterOne complex extension details. + fmt.Printf("parameterOne 'x-custom-burgers' (%s) has %d burgers, 'anotherBurger' has %s sauce and a %s patty\n", + customBurgers.Description, + len(customBurgers.Burgers), + customBurgers.Burgers["anotherBurger"].Sauce, + customBurgers.Burgers["anotherBurger"].Patty, + ) - // Output: schemaOne 'x-custom-cakes' (some cakes) has 2 cakes, 'someCake' has 10 candles and blue frosting - //parameterOne 'x-custom-burgers' (some burgers) has 2 burgers, 'anotherBurger' has mayo sauce and a lamb patty + // Output: schemaOne 'x-custom-cakes' (some cakes) has 2 cakes, 'someCake' has 10 candles and blue frosting + //parameterOne 'x-custom-burgers' (some burgers) has 2 burgers, 'anotherBurger' has mayo sauce and a lamb patty } func ExampleNewDocument_modifyAndReRender() { - // How to read in an OpenAPI 3 Specification, into a Document, - // modify the document and then re-render it back to YAML bytes. + // How to read in an OpenAPI 3 Specification, into a Document, + // modify the document and then re-render it back to YAML bytes. - // load an OpenAPI 3 specification from bytes - petstore, _ := os.ReadFile("test_specs/petstorev3.json") + // load an OpenAPI 3 specification from bytes + petstore, _ := os.ReadFile("test_specs/petstorev3.json") - // create a new document from specification bytes - doc, err := NewDocument(petstore) + // create a new document from specification bytes + doc, err := NewDocument(petstore) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } - // because we know this is a v3 spec, we can build a ready to go model from it. - v3Model, errors := doc.BuildV3Model() + // because we know this is a v3 spec, we can build a ready to go model from it. + v3Model, errors := doc.BuildV3Model() - // if anything went wrong when building the v3 model, a slice of errors will be returned - if len(errors) > 0 { - for i := range errors { - fmt.Printf("error: %e\n", errors[i]) - } - panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors))) - } + // if anything went wrong when building the v3 model, a slice of errors will be returned + if len(errors) > 0 { + for i := range errors { + fmt.Printf("error: %e\n", errors[i]) + } + panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors))) + } - // create a new path item and operation. - newPath := &v3high.PathItem{ - Description: "this is a new path item", - Get: &v3high.Operation{ - Description: "this is a get operation", - OperationId: "getNewThing", - RequestBody: &v3high.RequestBody{ - Description: "this is a new request body", - }, - }, - } + // create a new path item and operation. + newPath := &v3high.PathItem{ + Description: "this is a new path item", + Get: &v3high.Operation{ + Description: "this is a get operation", + OperationId: "getNewThing", + RequestBody: &v3high.RequestBody{ + Description: "this is a new request body", + }, + }, + } - // capture original number of paths - originalPaths := len(v3Model.Model.Paths.PathItems) + // capture original number of paths + originalPaths := len(v3Model.Model.Paths.PathItems) - // add the path to the document - v3Model.Model.Paths.PathItems["/new/path"] = newPath + // add the path to the document + v3Model.Model.Paths.PathItems["/new/path"] = newPath - // render out the new path item to YAML - // renderedPathItem, _ := yaml.Marshal(newPath) + // render out the new path item to YAML + // renderedPathItem, _ := yaml.Marshal(newPath) - // render the document back to bytes and reload the model. - rawBytes, _, newModel, errs := doc.RenderAndReload() + // render the document back to bytes and reload the model. + rawBytes, _, newModel, errs := doc.RenderAndReload() - // if anything went wrong when re-rendering the v3 model, a slice of errors will be returned - if len(errors) > 0 { - panic(fmt.Sprintf("cannot re-render document: %d errors reported", len(errs))) - } + // if anything went wrong when re-rendering the v3 model, a slice of errors will be returned + if len(errors) > 0 { + panic(fmt.Sprintf("cannot re-render document: %d errors reported", len(errs))) + } - // capture new number of paths after re-rendering - newPaths := len(newModel.Model.Paths.PathItems) + // capture new number of paths after re-rendering + newPaths := len(newModel.Model.Paths.PathItems) - // print the number of paths and schemas in the document - fmt.Printf("There were %d original paths. There are now %d paths in the document\n", originalPaths, newPaths) - fmt.Printf("The original spec had %d bytes, the new one has %d\n", len(petstore), len(rawBytes)) - // Output: There were 13 original paths. There are now 14 paths in the document - //The original spec had 31143 bytes, the new one has 27841 + // print the number of paths and schemas in the document + fmt.Printf("There were %d original paths. There are now %d paths in the document\n", originalPaths, newPaths) + fmt.Printf("The original spec had %d bytes, the new one has %d\n", len(petstore), len(rawBytes)) + // Output: There were 13 original paths. There are now 14 paths in the document + //The original spec had 31143 bytes, the new one has 27841 } diff --git a/index/index_model_test.go b/index/index_model_test.go index 1f6daab..3bd8e93 100644 --- a/index/index_model_test.go +++ b/index/index_model_test.go @@ -4,22 +4,22 @@ package index import ( - "github.com/stretchr/testify/assert" - "testing" + "github.com/stretchr/testify/assert" + "testing" ) func TestSpecIndex_Children(t *testing.T) { - idx1 := new(SpecIndex) - idx2 := new(SpecIndex) - idx3 := new(SpecIndex) - idx4 := new(SpecIndex) - idx5 := new(SpecIndex) - idx1.AddChild(idx2) - idx1.AddChild(idx3) - idx3.AddChild(idx4) - idx4.AddChild(idx5) - assert.Equal(t, 2, len(idx1.GetChildren())) - assert.Equal(t, 1, len(idx3.GetChildren())) - assert.Equal(t, 1, len(idx4.GetChildren())) - assert.Equal(t, 0, len(idx5.GetChildren())) + idx1 := new(SpecIndex) + idx2 := new(SpecIndex) + idx3 := new(SpecIndex) + idx4 := new(SpecIndex) + idx5 := new(SpecIndex) + idx1.AddChild(idx2) + idx1.AddChild(idx3) + idx3.AddChild(idx4) + idx4.AddChild(idx5) + assert.Equal(t, 2, len(idx1.GetChildren())) + assert.Equal(t, 1, len(idx3.GetChildren())) + assert.Equal(t, 1, len(idx4.GetChildren())) + assert.Equal(t, 0, len(idx5.GetChildren())) } diff --git a/index/index_utils.go b/index/index_utils.go index a1c0fd0..e74400c 100644 --- a/index/index_utils.go +++ b/index/index_utils.go @@ -4,88 +4,88 @@ package index import ( - "gopkg.in/yaml.v3" - "net/http" - "strings" - "time" + "gopkg.in/yaml.v3" + "net/http" + "strings" + "time" ) func isHttpMethod(val string) bool { - switch strings.ToLower(val) { - case methodTypes[0]: - return true - case methodTypes[1]: - return true - case methodTypes[2]: - return true - case methodTypes[3]: - return true - case methodTypes[4]: - return true - case methodTypes[5]: - return true - case methodTypes[6]: - return true - } - return false + switch strings.ToLower(val) { + case methodTypes[0]: + return true + case methodTypes[1]: + return true + case methodTypes[2]: + return true + case methodTypes[3]: + return true + case methodTypes[4]: + return true + case methodTypes[5]: + return true + case methodTypes[6]: + return true + } + return false } func DetermineReferenceResolveType(ref string) int { - if ref != "" && ref[0] == '#' { - return LocalResolve - } - if ref != "" && len(ref) >= 5 && (ref[:5] == "https" || ref[:5] == "http:") { - return HttpResolve - } - if strings.Contains(ref, ".json") || - strings.Contains(ref, ".yaml") || - strings.Contains(ref, ".yml") { - return FileResolve - } - return -1 + if ref != "" && ref[0] == '#' { + return LocalResolve + } + if ref != "" && len(ref) >= 5 && (ref[:5] == "https" || ref[:5] == "http:") { + return HttpResolve + } + if strings.Contains(ref, ".json") || + strings.Contains(ref, ".yaml") || + strings.Contains(ref, ".yml") { + return FileResolve + } + return -1 } func boostrapIndexCollections(rootNode *yaml.Node, index *SpecIndex) { - index.root = rootNode - index.allRefs = make(map[string]*Reference) - index.allMappedRefs = make(map[string]*Reference) - index.refsByLine = make(map[string]map[int]bool) - index.linesWithRefs = make(map[int]bool) - index.pathRefs = make(map[string]map[string]*Reference) - index.paramOpRefs = make(map[string]map[string]map[string][]*Reference) - index.operationTagsRefs = make(map[string]map[string][]*Reference) - index.operationDescriptionRefs = make(map[string]map[string]*Reference) - index.operationSummaryRefs = make(map[string]map[string]*Reference) - index.paramCompRefs = make(map[string]*Reference) - index.paramAllRefs = make(map[string]*Reference) - index.paramInlineDuplicateNames = make(map[string][]*Reference) - index.globalTagRefs = make(map[string]*Reference) - index.securitySchemeRefs = make(map[string]*Reference) - index.requestBodiesRefs = make(map[string]*Reference) - index.responsesRefs = make(map[string]*Reference) - index.headersRefs = make(map[string]*Reference) - index.examplesRefs = make(map[string]*Reference) - index.callbacksRefs = make(map[string]map[string][]*Reference) - index.linksRefs = make(map[string]map[string][]*Reference) - index.callbackRefs = make(map[string]*Reference) - index.externalSpecIndex = make(map[string]*SpecIndex) - index.allComponentSchemaDefinitions = make(map[string]*Reference) - index.allParameters = make(map[string]*Reference) - index.allSecuritySchemes = make(map[string]*Reference) - index.allRequestBodies = make(map[string]*Reference) - index.allResponses = make(map[string]*Reference) - index.allHeaders = make(map[string]*Reference) - index.allExamples = make(map[string]*Reference) - index.allLinks = make(map[string]*Reference) - index.allCallbacks = make(map[string]*Reference) - index.allExternalDocuments = make(map[string]*Reference) - index.securityRequirementRefs = make(map[string]map[string][]*Reference) - index.polymorphicRefs = make(map[string]*Reference) - index.refsWithSiblings = make(map[string]Reference) - index.seenRemoteSources = make(map[string]*yaml.Node) - index.seenLocalSources = make(map[string]*yaml.Node) - index.opServersRefs = make(map[string]map[string][]*Reference) - index.httpClient = &http.Client{Timeout: time.Duration(5) * time.Second} - index.componentIndexChan = make(chan bool) - index.polyComponentIndexChan = make(chan bool) + index.root = rootNode + index.allRefs = make(map[string]*Reference) + index.allMappedRefs = make(map[string]*Reference) + index.refsByLine = make(map[string]map[int]bool) + index.linesWithRefs = make(map[int]bool) + index.pathRefs = make(map[string]map[string]*Reference) + index.paramOpRefs = make(map[string]map[string]map[string][]*Reference) + index.operationTagsRefs = make(map[string]map[string][]*Reference) + index.operationDescriptionRefs = make(map[string]map[string]*Reference) + index.operationSummaryRefs = make(map[string]map[string]*Reference) + index.paramCompRefs = make(map[string]*Reference) + index.paramAllRefs = make(map[string]*Reference) + index.paramInlineDuplicateNames = make(map[string][]*Reference) + index.globalTagRefs = make(map[string]*Reference) + index.securitySchemeRefs = make(map[string]*Reference) + index.requestBodiesRefs = make(map[string]*Reference) + index.responsesRefs = make(map[string]*Reference) + index.headersRefs = make(map[string]*Reference) + index.examplesRefs = make(map[string]*Reference) + index.callbacksRefs = make(map[string]map[string][]*Reference) + index.linksRefs = make(map[string]map[string][]*Reference) + index.callbackRefs = make(map[string]*Reference) + index.externalSpecIndex = make(map[string]*SpecIndex) + index.allComponentSchemaDefinitions = make(map[string]*Reference) + index.allParameters = make(map[string]*Reference) + index.allSecuritySchemes = make(map[string]*Reference) + index.allRequestBodies = make(map[string]*Reference) + index.allResponses = make(map[string]*Reference) + index.allHeaders = make(map[string]*Reference) + index.allExamples = make(map[string]*Reference) + index.allLinks = make(map[string]*Reference) + index.allCallbacks = make(map[string]*Reference) + index.allExternalDocuments = make(map[string]*Reference) + index.securityRequirementRefs = make(map[string]map[string][]*Reference) + index.polymorphicRefs = make(map[string]*Reference) + index.refsWithSiblings = make(map[string]Reference) + index.seenRemoteSources = make(map[string]*yaml.Node) + index.seenLocalSources = make(map[string]*yaml.Node) + index.opServersRefs = make(map[string]map[string][]*Reference) + index.httpClient = &http.Client{Timeout: time.Duration(5) * time.Second} + index.componentIndexChan = make(chan bool) + index.polyComponentIndexChan = make(chan bool) } diff --git a/index/utility_methods.go b/index/utility_methods.go index 8a0cf40..41a08df 100644 --- a/index/utility_methods.go +++ b/index/utility_methods.go @@ -4,447 +4,445 @@ package index import ( - "fmt" - "github.com/pb33f/libopenapi/utils" - "gopkg.in/yaml.v3" - "net/url" - "strings" - "sync" + "fmt" + "github.com/pb33f/libopenapi/utils" + "gopkg.in/yaml.v3" + "net/url" + "strings" + "sync" ) func (index *SpecIndex) extractDefinitionsAndSchemas(schemasNode *yaml.Node, pathPrefix string) { - var name string - for i, schema := range schemasNode.Content { - if i%2 == 0 { - name = schema.Value - continue - } + var name string + for i, schema := range schemasNode.Content { + if i%2 == 0 { + name = schema.Value + continue + } - def := fmt.Sprintf("%s%s", pathPrefix, name) - ref := &Reference{ - Definition: def, - Name: name, - Node: schema, - Path: fmt.Sprintf("$.components.schemas.%s", name), - ParentNode: schemasNode, - RequiredRefProperties: index.extractDefinitionRequiredRefProperties(schemasNode, map[string][]string{}), - } - index.allComponentSchemaDefinitions[def] = ref - } + def := fmt.Sprintf("%s%s", pathPrefix, name) + ref := &Reference{ + Definition: def, + Name: name, + Node: schema, + Path: fmt.Sprintf("$.components.schemas.%s", name), + ParentNode: schemasNode, + RequiredRefProperties: index.extractDefinitionRequiredRefProperties(schemasNode, map[string][]string{}), + } + index.allComponentSchemaDefinitions[def] = ref + } } // extractDefinitionRequiredRefProperties goes through the direct properties of a schema and extracts the map of required definitions from within it func (index *SpecIndex) extractDefinitionRequiredRefProperties(schemaNode *yaml.Node, reqRefProps map[string][]string) map[string][]string { - if schemaNode == nil { - return reqRefProps - } + if schemaNode == nil { + return reqRefProps + } - // If the node we're looking at is a direct ref to another model without any properties, mark it as required, but still continue to look for required properties - isRef, _, defPath := utils.IsNodeRefValue(schemaNode) - if isRef { - if _, ok := reqRefProps[defPath]; !ok { - reqRefProps[defPath] = []string{} - } - } + // If the node we're looking at is a direct ref to another model without any properties, mark it as required, but still continue to look for required properties + isRef, _, defPath := utils.IsNodeRefValue(schemaNode) + if isRef { + if _, ok := reqRefProps[defPath]; !ok { + reqRefProps[defPath] = []string{} + } + } - // Check for a required parameters list, and return if none exists, as any properties will be optional - _, requiredSeqNode := utils.FindKeyNodeTop("required", schemaNode.Content) - if requiredSeqNode == nil { - return reqRefProps - } + // Check for a required parameters list, and return if none exists, as any properties will be optional + _, requiredSeqNode := utils.FindKeyNodeTop("required", schemaNode.Content) + if requiredSeqNode == nil { + return reqRefProps + } - _, propertiesMapNode := utils.FindKeyNodeTop("properties", schemaNode.Content) - if propertiesMapNode == nil { - // TODO: Log a warning on the resolver, because if you have required properties, but no actual properties, something is wrong - return reqRefProps - } + _, propertiesMapNode := utils.FindKeyNodeTop("properties", schemaNode.Content) + if propertiesMapNode == nil { + // TODO: Log a warning on the resolver, because if you have required properties, but no actual properties, something is wrong + return reqRefProps + } - name := "" - for i, param := range propertiesMapNode.Content { - if i%2 == 0 { - name = param.Value - continue - } + name := "" + for i, param := range propertiesMapNode.Content { + if i%2 == 0 { + name = param.Value + continue + } - // Check to see if the current property is directly embedded within the current schema, and handle its properties if so - _, paramPropertiesMapNode := utils.FindKeyNodeTop("properties", param.Content) - if paramPropertiesMapNode != nil { - reqRefProps = index.extractDefinitionRequiredRefProperties(param, reqRefProps) - } + // Check to see if the current property is directly embedded within the current schema, and handle its properties if so + _, paramPropertiesMapNode := utils.FindKeyNodeTop("properties", param.Content) + if paramPropertiesMapNode != nil { + reqRefProps = index.extractDefinitionRequiredRefProperties(param, reqRefProps) + } - // Check to see if the current property is polymorphic, and dive into that model if so - for _, key := range []string{"allOf", "oneOf", "anyOf"} { - _, ofNode := utils.FindKeyNodeTop(key, param.Content) - if ofNode != nil { - for _, ofNodeItem := range ofNode.Content { - reqRefProps = index.extractRequiredReferenceProperties(ofNodeItem, name, reqRefProps) - } - } - } - } + // Check to see if the current property is polymorphic, and dive into that model if so + for _, key := range []string{"allOf", "oneOf", "anyOf"} { + _, ofNode := utils.FindKeyNodeTop(key, param.Content) + if ofNode != nil { + for _, ofNodeItem := range ofNode.Content { + reqRefProps = index.extractRequiredReferenceProperties(ofNodeItem, name, reqRefProps) + } + } + } + } - // Run through each of the required properties and extract _their_ required references - for _, requiredPropertyNode := range requiredSeqNode.Content { - _, requiredPropDefNode := utils.FindKeyNodeTop(requiredPropertyNode.Value, propertiesMapNode.Content) - if requiredPropDefNode == nil { - continue - } + // Run through each of the required properties and extract _their_ required references + for _, requiredPropertyNode := range requiredSeqNode.Content { + _, requiredPropDefNode := utils.FindKeyNodeTop(requiredPropertyNode.Value, propertiesMapNode.Content) + if requiredPropDefNode == nil { + continue + } - reqRefProps = index.extractRequiredReferenceProperties(requiredPropDefNode, requiredPropertyNode.Value, reqRefProps) - } + reqRefProps = index.extractRequiredReferenceProperties(requiredPropDefNode, requiredPropertyNode.Value, reqRefProps) + } - return reqRefProps + return reqRefProps } // extractRequiredReferenceProperties returns a map of definition names to the property or properties which reference it within a node func (index *SpecIndex) extractRequiredReferenceProperties(requiredPropDefNode *yaml.Node, propName string, reqRefProps map[string][]string) map[string][]string { - isRef, _, defPath := utils.IsNodeRefValue(requiredPropDefNode) - if !isRef { - _, defItems := utils.FindKeyNodeTop("items", requiredPropDefNode.Content) - if defItems != nil { - isRef, _, defPath = utils.IsNodeRefValue(defItems) - } - } + isRef, _, defPath := utils.IsNodeRefValue(requiredPropDefNode) + if !isRef { + _, defItems := utils.FindKeyNodeTop("items", requiredPropDefNode.Content) + if defItems != nil { + isRef, _, defPath = utils.IsNodeRefValue(defItems) + } + } - if /* still */ !isRef { - return reqRefProps - } + if /* still */ !isRef { + return reqRefProps + } - if _, ok := reqRefProps[defPath]; !ok { - reqRefProps[defPath] = []string{} - } - reqRefProps[defPath] = append(reqRefProps[defPath], propName) + if _, ok := reqRefProps[defPath]; !ok { + reqRefProps[defPath] = []string{} + } + reqRefProps[defPath] = append(reqRefProps[defPath], propName) - return reqRefProps + return reqRefProps } func (index *SpecIndex) extractComponentParameters(paramsNode *yaml.Node, pathPrefix string) { - var name string - for i, param := range paramsNode.Content { - if i%2 == 0 { - name = param.Value - continue - } - def := fmt.Sprintf("%s%s", pathPrefix, name) - ref := &Reference{ - Definition: def, - Name: name, - Node: param, - } - index.allParameters[def] = ref - } + var name string + for i, param := range paramsNode.Content { + if i%2 == 0 { + name = param.Value + continue + } + def := fmt.Sprintf("%s%s", pathPrefix, name) + ref := &Reference{ + Definition: def, + Name: name, + Node: param, + } + index.allParameters[def] = ref + } } func (index *SpecIndex) extractComponentRequestBodies(requestBodiesNode *yaml.Node, pathPrefix string) { - var name string - for i, reqBod := range requestBodiesNode.Content { - if i%2 == 0 { - name = reqBod.Value - continue - } - def := fmt.Sprintf("%s%s", pathPrefix, name) - ref := &Reference{ - Definition: def, - Name: name, - Node: reqBod, - } - index.allRequestBodies[def] = ref - } + var name string + for i, reqBod := range requestBodiesNode.Content { + if i%2 == 0 { + name = reqBod.Value + continue + } + def := fmt.Sprintf("%s%s", pathPrefix, name) + ref := &Reference{ + Definition: def, + Name: name, + Node: reqBod, + } + index.allRequestBodies[def] = ref + } } func (index *SpecIndex) extractComponentResponses(responsesNode *yaml.Node, pathPrefix string) { - var name string - for i, response := range responsesNode.Content { - if i%2 == 0 { - name = response.Value - continue - } - def := fmt.Sprintf("%s%s", pathPrefix, name) - ref := &Reference{ - Definition: def, - Name: name, - Node: response, - } - index.allResponses[def] = ref - } + var name string + for i, response := range responsesNode.Content { + if i%2 == 0 { + name = response.Value + continue + } + def := fmt.Sprintf("%s%s", pathPrefix, name) + ref := &Reference{ + Definition: def, + Name: name, + Node: response, + } + index.allResponses[def] = ref + } } func (index *SpecIndex) extractComponentHeaders(headersNode *yaml.Node, pathPrefix string) { - var name string - for i, header := range headersNode.Content { - if i%2 == 0 { - name = header.Value - continue - } - def := fmt.Sprintf("%s%s", pathPrefix, name) - ref := &Reference{ - Definition: def, - Name: name, - Node: header, - } - index.allHeaders[def] = ref - } + var name string + for i, header := range headersNode.Content { + if i%2 == 0 { + name = header.Value + continue + } + def := fmt.Sprintf("%s%s", pathPrefix, name) + ref := &Reference{ + Definition: def, + Name: name, + Node: header, + } + index.allHeaders[def] = ref + } } func (index *SpecIndex) extractComponentCallbacks(callbacksNode *yaml.Node, pathPrefix string) { - var name string - for i, callback := range callbacksNode.Content { - if i%2 == 0 { - name = callback.Value - continue - } - def := fmt.Sprintf("%s%s", pathPrefix, name) - ref := &Reference{ - Definition: def, - Name: name, - Node: callback, - } - index.allCallbacks[def] = ref - } + var name string + for i, callback := range callbacksNode.Content { + if i%2 == 0 { + name = callback.Value + continue + } + def := fmt.Sprintf("%s%s", pathPrefix, name) + ref := &Reference{ + Definition: def, + Name: name, + Node: callback, + } + index.allCallbacks[def] = ref + } } func (index *SpecIndex) extractComponentLinks(linksNode *yaml.Node, pathPrefix string) { - var name string - for i, link := range linksNode.Content { - if i%2 == 0 { - name = link.Value - continue - } - def := fmt.Sprintf("%s%s", pathPrefix, name) - ref := &Reference{ - Definition: def, - Name: name, - Node: link, - } - index.allLinks[def] = ref - } + var name string + for i, link := range linksNode.Content { + if i%2 == 0 { + name = link.Value + continue + } + def := fmt.Sprintf("%s%s", pathPrefix, name) + ref := &Reference{ + Definition: def, + Name: name, + Node: link, + } + index.allLinks[def] = ref + } } func (index *SpecIndex) extractComponentExamples(examplesNode *yaml.Node, pathPrefix string) { - var name string - for i, example := range examplesNode.Content { - if i%2 == 0 { - name = example.Value - continue - } - def := fmt.Sprintf("%s%s", pathPrefix, name) - ref := &Reference{ - Definition: def, - Name: name, - Node: example, - } - index.allExamples[def] = ref - } + var name string + for i, example := range examplesNode.Content { + if i%2 == 0 { + name = example.Value + continue + } + def := fmt.Sprintf("%s%s", pathPrefix, name) + ref := &Reference{ + Definition: def, + Name: name, + Node: example, + } + index.allExamples[def] = ref + } } func (index *SpecIndex) extractComponentSecuritySchemes(securitySchemesNode *yaml.Node, pathPrefix string) { - var name string - for i, secScheme := range securitySchemesNode.Content { - if i%2 == 0 { - name = secScheme.Value - continue - } - def := fmt.Sprintf("%s%s", pathPrefix, name) - ref := &Reference{ - Definition: def, - Name: name, - Node: secScheme, - ParentNode: securitySchemesNode, - Path: fmt.Sprintf("$.components.securitySchemes.%s", name), - } - index.allSecuritySchemes[def] = ref - } + var name string + for i, secScheme := range securitySchemesNode.Content { + if i%2 == 0 { + name = secScheme.Value + continue + } + def := fmt.Sprintf("%s%s", pathPrefix, name) + ref := &Reference{ + Definition: def, + Name: name, + Node: secScheme, + ParentNode: securitySchemesNode, + Path: fmt.Sprintf("$.components.securitySchemes.%s", name), + } + index.allSecuritySchemes[def] = ref + } } func (index *SpecIndex) countUniqueInlineDuplicates() int { - if index.componentsInlineParamUniqueCount > 0 { - return index.componentsInlineParamUniqueCount - } - unique := 0 - for _, p := range index.paramInlineDuplicateNames { - if len(p) == 1 { - unique++ - } - } - index.componentsInlineParamUniqueCount = unique - return unique + if index.componentsInlineParamUniqueCount > 0 { + return index.componentsInlineParamUniqueCount + } + unique := 0 + for _, p := range index.paramInlineDuplicateNames { + if len(p) == 1 { + unique++ + } + } + index.componentsInlineParamUniqueCount = unique + return unique } func (index *SpecIndex) scanOperationParams(params []*yaml.Node, pathItemNode *yaml.Node, method string) { - for i, param := range params { - // param is ref - if len(param.Content) > 0 && param.Content[0].Value == "$ref" { + for i, param := range params { + // param is ref + if len(param.Content) > 0 && param.Content[0].Value == "$ref" { - paramRefName := param.Content[1].Value - paramRef := index.allMappedRefs[paramRefName] + paramRefName := param.Content[1].Value + paramRef := index.allMappedRefs[paramRefName] - if index.paramOpRefs[pathItemNode.Value] == nil { - index.paramOpRefs[pathItemNode.Value] = make(map[string]map[string][]*Reference) - index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference) + if index.paramOpRefs[pathItemNode.Value] == nil { + index.paramOpRefs[pathItemNode.Value] = make(map[string]map[string][]*Reference) + index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference) - } - // if we know the path, but it's a new method - if index.paramOpRefs[pathItemNode.Value][method] == nil { - index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference) - } + } + // if we know the path, but it's a new method + if index.paramOpRefs[pathItemNode.Value][method] == nil { + index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference) + } - // if this is a duplicate, add an error and ignore it - if index.paramOpRefs[pathItemNode.Value][method][paramRefName] != nil { - path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i) - if method == "top" { - path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i) - } + // if this is a duplicate, add an error and ignore it + if index.paramOpRefs[pathItemNode.Value][method][paramRefName] != nil { + path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i) + if method == "top" { + path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i) + } - index.operationParamErrors = append(index.operationParamErrors, &IndexingError{ - Err: fmt.Errorf("the `%s` operation parameter at path `%s`, "+ - "index %d has a duplicate ref `%s`", method, pathItemNode.Value, i, paramRefName), - Node: param, - Path: path, - }) - } else { - if paramRef != nil { - index.paramOpRefs[pathItemNode.Value][method][paramRefName] = - append(index.paramOpRefs[pathItemNode.Value][method][paramRefName], paramRef) - } - } + index.operationParamErrors = append(index.operationParamErrors, &IndexingError{ + Err: fmt.Errorf("the `%s` operation parameter at path `%s`, "+ + "index %d has a duplicate ref `%s`", method, pathItemNode.Value, i, paramRefName), + Node: param, + Path: path, + }) + } else { + if paramRef != nil { + index.paramOpRefs[pathItemNode.Value][method][paramRefName] = + append(index.paramOpRefs[pathItemNode.Value][method][paramRefName], paramRef) + } + } - continue + continue - } else { + } else { - // param is inline. - _, vn := utils.FindKeyNode("name", param.Content) + // param is inline. + _, vn := utils.FindKeyNode("name", param.Content) - path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i) - if method == "top" { - path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i) - } + path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i) + if method == "top" { + path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i) + } - if vn == nil { - index.operationParamErrors = append(index.operationParamErrors, &IndexingError{ - Err: fmt.Errorf("the '%s' operation parameter at path '%s', index %d has no 'name' value", - method, pathItemNode.Value, i), - Node: param, - Path: path, - }) - continue - } + if vn == nil { + index.operationParamErrors = append(index.operationParamErrors, &IndexingError{ + Err: fmt.Errorf("the '%s' operation parameter at path '%s', index %d has no 'name' value", + method, pathItemNode.Value, i), + Node: param, + Path: path, + }) + continue + } - ref := &Reference{ - Definition: vn.Value, - Name: vn.Value, - Node: param, - Path: path, - } - if index.paramOpRefs[pathItemNode.Value] == nil { - index.paramOpRefs[pathItemNode.Value] = make(map[string]map[string][]*Reference) - index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference) - } + ref := &Reference{ + Definition: vn.Value, + Name: vn.Value, + Node: param, + Path: path, + } + if index.paramOpRefs[pathItemNode.Value] == nil { + index.paramOpRefs[pathItemNode.Value] = make(map[string]map[string][]*Reference) + index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference) + } - // if we know the path but this is a new method. - if index.paramOpRefs[pathItemNode.Value][method] == nil { - index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference) - } + // if we know the path but this is a new method. + if index.paramOpRefs[pathItemNode.Value][method] == nil { + index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference) + } - // if this is a duplicate name, check if the `in` type is also the same, if so, it's a duplicate. - if len(index.paramOpRefs[pathItemNode.Value][method][ref.Name]) > 0 { + // if this is a duplicate name, check if the `in` type is also the same, if so, it's a duplicate. + if len(index.paramOpRefs[pathItemNode.Value][method][ref.Name]) > 0 { - currentNode := ref.Node - checkNodes := index.paramOpRefs[pathItemNode.Value][method][ref.Name] - _, currentIn := utils.FindKeyNodeTop("in", currentNode.Content) + currentNode := ref.Node + checkNodes := index.paramOpRefs[pathItemNode.Value][method][ref.Name] + _, currentIn := utils.FindKeyNodeTop("in", currentNode.Content) - for _, checkNode := range checkNodes { + for _, checkNode := range checkNodes { - _, checkIn := utils.FindKeyNodeTop("in", checkNode.Node.Content) + _, checkIn := utils.FindKeyNodeTop("in", checkNode.Node.Content) - if currentIn != nil && checkIn != nil && currentIn.Value == checkIn.Value { + if currentIn != nil && checkIn != nil && currentIn.Value == checkIn.Value { - path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i) - if method == "top" { - path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i) - } + path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i) + if method == "top" { + path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i) + } - index.operationParamErrors = append(index.operationParamErrors, &IndexingError{ - Err: fmt.Errorf("the `%s` operation parameter at path `%s`, "+ - "index %d has a duplicate name `%s` and `in` type", method, pathItemNode.Value, i, vn.Value), - Node: param, - Path: path, - }) - } else { - index.paramOpRefs[pathItemNode.Value][method][ref.Name] = - append(index.paramOpRefs[pathItemNode.Value][method][ref.Name], ref) - } - } - } else { - index.paramOpRefs[pathItemNode.Value][method][ref.Name] = - append(index.paramOpRefs[pathItemNode.Value][method][ref.Name], ref) - } - continue - } - } + index.operationParamErrors = append(index.operationParamErrors, &IndexingError{ + Err: fmt.Errorf("the `%s` operation parameter at path `%s`, "+ + "index %d has a duplicate name `%s` and `in` type", method, pathItemNode.Value, i, vn.Value), + Node: param, + Path: path, + }) + } else { + index.paramOpRefs[pathItemNode.Value][method][ref.Name] = + append(index.paramOpRefs[pathItemNode.Value][method][ref.Name], ref) + } + } + } else { + index.paramOpRefs[pathItemNode.Value][method][ref.Name] = + append(index.paramOpRefs[pathItemNode.Value][method][ref.Name], ref) + } + continue + } + } } func runIndexFunction(funcs []func() int, wg *sync.WaitGroup) { - for _, cFunc := range funcs { - go func(wg *sync.WaitGroup, cf func() int) { - cf() - wg.Done() - }(wg, cFunc) - } + for _, cFunc := range funcs { + go func(wg *sync.WaitGroup, cf func() int) { + cf() + wg.Done() + }(wg, cFunc) + } } func GenerateCleanSpecConfigBaseURL(baseURL *url.URL, dir string, includeFile bool) string { - cleanedPath := baseURL.Path // not cleaned yet! + cleanedPath := baseURL.Path // not cleaned yet! - // create a slice of path segments from existing path - pathSegs := strings.Split(cleanedPath, "/") - dirSegs := strings.Split(dir, "/") + // create a slice of path segments from existing path + pathSegs := strings.Split(cleanedPath, "/") + dirSegs := strings.Split(dir, "/") - var cleanedSegs []string - if !includeFile { - dirSegs = dirSegs[:len(dirSegs)-1] - } - - // relative paths are a pain in the ass, damn you digital ocean, use a single spec, and break them - // down into services, please don't blast apart specs into a billion shards. - if strings.Contains(dir, "../") { - for s := range dirSegs { - if dirSegs[s] == ".." { - // chop off the last segment of the base path. - if len(pathSegs) > 0 { - pathSegs = pathSegs[:len(pathSegs)-1] - } - } else { - cleanedSegs = append(cleanedSegs, dirSegs[s]) - } - } - cleanedPath = fmt.Sprintf("%s/%s", strings.Join(pathSegs, "/"), strings.Join(cleanedSegs, "/")) - } else { - if !strings.HasPrefix(dir, "http") { - if len(pathSegs) > 1 || len(dirSegs) > 1 { - cleanedPath = fmt.Sprintf("%s/%s", strings.Join(pathSegs, "/"), strings.Join(dirSegs, "/")) - } - } else { - cleanedPath = strings.Join(dirSegs, "/") - } - } - var p string - if baseURL.Scheme != "" && !strings.HasPrefix(dir, "http") { - p = fmt.Sprintf("%s://%s%s", baseURL.Scheme, baseURL.Host, cleanedPath) - } else { - if !strings.Contains(cleanedPath, "/") { - p = "" - } else { - p = cleanedPath - } + var cleanedSegs []string + if !includeFile { + dirSegs = dirSegs[:len(dirSegs)-1] + } - } - if strings.HasSuffix(p, "/") { - p = p[:len(p)-1] - } - return p + // relative paths are a pain in the ass, damn you digital ocean, use a single spec, and break them + // down into services, please don't blast apart specs into a billion shards. + if strings.Contains(dir, "../") { + for s := range dirSegs { + if dirSegs[s] == ".." { + // chop off the last segment of the base path. + if len(pathSegs) > 0 { + pathSegs = pathSegs[:len(pathSegs)-1] + } + } else { + cleanedSegs = append(cleanedSegs, dirSegs[s]) + } + } + cleanedPath = fmt.Sprintf("%s/%s", strings.Join(pathSegs, "/"), strings.Join(cleanedSegs, "/")) + } else { + if !strings.HasPrefix(dir, "http") { + if len(pathSegs) > 1 || len(dirSegs) > 1 { + cleanedPath = fmt.Sprintf("%s/%s", strings.Join(pathSegs, "/"), strings.Join(dirSegs, "/")) + } + } else { + cleanedPath = strings.Join(dirSegs, "/") + } + } + var p string + if baseURL.Scheme != "" && !strings.HasPrefix(dir, "http") { + p = fmt.Sprintf("%s://%s%s", baseURL.Scheme, baseURL.Host, cleanedPath) + } else { + if !strings.Contains(cleanedPath, "/") { + p = "" + } else { + p = cleanedPath + } + + } + if strings.HasSuffix(p, "/") { + p = p[:len(p)-1] + } + return p } - - diff --git a/index/utility_methods_test.go b/index/utility_methods_test.go index 531149c..aec4506 100644 --- a/index/utility_methods_test.go +++ b/index/utility_methods_test.go @@ -4,52 +4,52 @@ package index import ( - "github.com/stretchr/testify/assert" - "net/url" - "testing" + "github.com/stretchr/testify/assert" + "net/url" + "testing" ) func TestGenerateCleanSpecConfigBaseURL(t *testing.T) { - u, _ := url.Parse("https://pb33f.io/things/stuff") - path := "." - assert.Equal(t, "https://pb33f.io/things/stuff", - GenerateCleanSpecConfigBaseURL(u, path, false)) + u, _ := url.Parse("https://pb33f.io/things/stuff") + path := "." + assert.Equal(t, "https://pb33f.io/things/stuff", + GenerateCleanSpecConfigBaseURL(u, path, false)) } func TestGenerateCleanSpecConfigBaseURL_RelativeDeep(t *testing.T) { - u, _ := url.Parse("https://pb33f.io/things/stuff/jazz/cakes/winter/oil") - path := "../../../../foo/bar/baz/crap.yaml#thang" - assert.Equal(t, "https://pb33f.io/things/stuff/foo/bar/baz", - GenerateCleanSpecConfigBaseURL(u, path, false)) + u, _ := url.Parse("https://pb33f.io/things/stuff/jazz/cakes/winter/oil") + path := "../../../../foo/bar/baz/crap.yaml#thang" + assert.Equal(t, "https://pb33f.io/things/stuff/foo/bar/baz", + GenerateCleanSpecConfigBaseURL(u, path, false)) - assert.Equal(t, "https://pb33f.io/things/stuff/foo/bar/baz/crap.yaml#thang", - GenerateCleanSpecConfigBaseURL(u, path, true)) + assert.Equal(t, "https://pb33f.io/things/stuff/foo/bar/baz/crap.yaml#thang", + GenerateCleanSpecConfigBaseURL(u, path, true)) } func TestGenerateCleanSpecConfigBaseURL_NoBaseURL(t *testing.T) { - u, _ := url.Parse("/things/stuff/jazz/cakes/winter/oil") - path := "../../../../foo/bar/baz/crap.yaml#thang" - assert.Equal(t, "/things/stuff/foo/bar/baz", - GenerateCleanSpecConfigBaseURL(u, path, false)) + u, _ := url.Parse("/things/stuff/jazz/cakes/winter/oil") + path := "../../../../foo/bar/baz/crap.yaml#thang" + assert.Equal(t, "/things/stuff/foo/bar/baz", + GenerateCleanSpecConfigBaseURL(u, path, false)) - assert.Equal(t, "/things/stuff/foo/bar/baz/crap.yaml#thang", - GenerateCleanSpecConfigBaseURL(u, path, true)) + assert.Equal(t, "/things/stuff/foo/bar/baz/crap.yaml#thang", + GenerateCleanSpecConfigBaseURL(u, path, true)) } func TestGenerateCleanSpecConfigBaseURL_HttpStrip(t *testing.T) { - u, _ := url.Parse(".") - path := "http://thing.com/crap.yaml#thang" - assert.Equal(t, "http://thing.com", - GenerateCleanSpecConfigBaseURL(u, path, false)) + u, _ := url.Parse(".") + path := "http://thing.com/crap.yaml#thang" + assert.Equal(t, "http://thing.com", + GenerateCleanSpecConfigBaseURL(u, path, false)) - assert.Equal(t, "", - GenerateCleanSpecConfigBaseURL(u, "crap.yaml#thing", true)) + assert.Equal(t, "", + GenerateCleanSpecConfigBaseURL(u, "crap.yaml#thing", true)) } func TestSpecIndex_extractDefinitionRequiredRefProperties(t *testing.T) { - c := CreateOpenAPIIndexConfig() - idx := NewSpecIndexWithConfig(nil, c) - assert.Nil(t, idx.extractDefinitionRequiredRefProperties(nil, nil)) -} \ No newline at end of file + c := CreateOpenAPIIndexConfig() + idx := NewSpecIndexWithConfig(nil, c) + assert.Nil(t, idx.extractDefinitionRequiredRefProperties(nil, nil)) +} diff --git a/resolver/resolver_test.go b/resolver/resolver_test.go index 2bded19..1db450e 100644 --- a/resolver/resolver_test.go +++ b/resolver/resolver_test.go @@ -1,170 +1,170 @@ package resolver import ( - "errors" - "fmt" - "io/ioutil" - "net/url" - "testing" + "errors" + "fmt" + "io/ioutil" + "net/url" + "testing" - "github.com/pb33f/libopenapi/index" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" + "github.com/pb33f/libopenapi/index" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" ) func TestNewResolver(t *testing.T) { - assert.Nil(t, NewResolver(nil)) + assert.Nil(t, NewResolver(nil)) } func Benchmark_ResolveDocumentStripe(b *testing.B) { - stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml") - for n := 0; n < b.N; n++ { - var rootNode yaml.Node - yaml.Unmarshal(stripe, &rootNode) - index := index.NewSpecIndex(&rootNode) - resolver := NewResolver(index) - resolver.Resolve() - } + stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml") + for n := 0; n < b.N; n++ { + var rootNode yaml.Node + yaml.Unmarshal(stripe, &rootNode) + index := index.NewSpecIndex(&rootNode) + resolver := NewResolver(index) + resolver.Resolve() + } } func TestResolver_ResolveComponents_CircularSpec(t *testing.T) { - circular, _ := ioutil.ReadFile("../test_specs/circular-tests.yaml") - var rootNode yaml.Node - yaml.Unmarshal(circular, &rootNode) + circular, _ := ioutil.ReadFile("../test_specs/circular-tests.yaml") + var rootNode yaml.Node + yaml.Unmarshal(circular, &rootNode) - index := index.NewSpecIndex(&rootNode) + index := index.NewSpecIndex(&rootNode) - resolver := NewResolver(index) - assert.NotNil(t, resolver) + resolver := NewResolver(index) + assert.NotNil(t, resolver) - circ := resolver.Resolve() - assert.Len(t, circ, 3) + circ := resolver.Resolve() + assert.Len(t, circ, 3) - _, err := yaml.Marshal(resolver.resolvedRoot) - assert.NoError(t, err) + _, err := yaml.Marshal(resolver.resolvedRoot) + assert.NoError(t, err) } func TestResolver_CheckForCircularReferences(t *testing.T) { - circular, _ := ioutil.ReadFile("../test_specs/circular-tests.yaml") - var rootNode yaml.Node - yaml.Unmarshal(circular, &rootNode) + circular, _ := ioutil.ReadFile("../test_specs/circular-tests.yaml") + var rootNode yaml.Node + yaml.Unmarshal(circular, &rootNode) - index := index.NewSpecIndex(&rootNode) + index := index.NewSpecIndex(&rootNode) - resolver := NewResolver(index) - assert.NotNil(t, resolver) + resolver := NewResolver(index) + assert.NotNil(t, resolver) - circ := resolver.CheckForCircularReferences() - assert.Len(t, circ, 3) - assert.Len(t, resolver.GetResolvingErrors(), 3) - assert.Len(t, resolver.GetCircularErrors(), 3) + circ := resolver.CheckForCircularReferences() + assert.Len(t, circ, 3) + assert.Len(t, resolver.GetResolvingErrors(), 3) + assert.Len(t, resolver.GetCircularErrors(), 3) - _, err := yaml.Marshal(resolver.resolvedRoot) - assert.NoError(t, err) + _, err := yaml.Marshal(resolver.resolvedRoot) + assert.NoError(t, err) } func TestResolver_CheckForCircularReferences_DigitalOcean(t *testing.T) { - circular, _ := ioutil.ReadFile("../test_specs/digitalocean.yaml") - var rootNode yaml.Node - yaml.Unmarshal(circular, &rootNode) + circular, _ := ioutil.ReadFile("../test_specs/digitalocean.yaml") + var rootNode yaml.Node + yaml.Unmarshal(circular, &rootNode) - baseURL, _ := url.Parse("https://raw.githubusercontent.com/digitalocean/openapi/main/specification") + baseURL, _ := url.Parse("https://raw.githubusercontent.com/digitalocean/openapi/main/specification") - index := index.NewSpecIndexWithConfig(&rootNode, &index.SpecIndexConfig{ - AllowRemoteLookup: true, - AllowFileLookup: true, - BaseURL: baseURL, - }) + index := index.NewSpecIndexWithConfig(&rootNode, &index.SpecIndexConfig{ + AllowRemoteLookup: true, + AllowFileLookup: true, + BaseURL: baseURL, + }) - resolver := NewResolver(index) - assert.NotNil(t, resolver) + resolver := NewResolver(index) + assert.NotNil(t, resolver) - circ := resolver.CheckForCircularReferences() - assert.Len(t, circ, 0) - assert.Len(t, resolver.GetResolvingErrors(), 0) - assert.Len(t, resolver.GetCircularErrors(), 0) + circ := resolver.CheckForCircularReferences() + assert.Len(t, circ, 0) + assert.Len(t, resolver.GetResolvingErrors(), 0) + assert.Len(t, resolver.GetCircularErrors(), 0) - _, err := yaml.Marshal(resolver.resolvedRoot) - assert.NoError(t, err) + _, err := yaml.Marshal(resolver.resolvedRoot) + assert.NoError(t, err) } func TestResolver_CircularReferencesRequiredValid(t *testing.T) { - circular, _ := ioutil.ReadFile("../test_specs/swagger-valid-recursive-model.yaml") - var rootNode yaml.Node - yaml.Unmarshal(circular, &rootNode) + circular, _ := ioutil.ReadFile("../test_specs/swagger-valid-recursive-model.yaml") + var rootNode yaml.Node + yaml.Unmarshal(circular, &rootNode) - index := index.NewSpecIndex(&rootNode) + index := index.NewSpecIndex(&rootNode) - resolver := NewResolver(index) - assert.NotNil(t, resolver) + resolver := NewResolver(index) + assert.NotNil(t, resolver) - circ := resolver.CheckForCircularReferences() - assert.Len(t, circ, 0) + circ := resolver.CheckForCircularReferences() + assert.Len(t, circ, 0) - _, err := yaml.Marshal(resolver.resolvedRoot) - assert.NoError(t, err) + _, err := yaml.Marshal(resolver.resolvedRoot) + assert.NoError(t, err) } func TestResolver_CircularReferencesRequiredInvalid(t *testing.T) { - circular, _ := ioutil.ReadFile("../test_specs/swagger-invalid-recursive-model.yaml") - var rootNode yaml.Node - yaml.Unmarshal(circular, &rootNode) + circular, _ := ioutil.ReadFile("../test_specs/swagger-invalid-recursive-model.yaml") + var rootNode yaml.Node + yaml.Unmarshal(circular, &rootNode) - index := index.NewSpecIndex(&rootNode) + index := index.NewSpecIndex(&rootNode) - resolver := NewResolver(index) - assert.NotNil(t, resolver) + resolver := NewResolver(index) + assert.NotNil(t, resolver) - circ := resolver.CheckForCircularReferences() - assert.Len(t, circ, 2) + circ := resolver.CheckForCircularReferences() + assert.Len(t, circ, 2) - _, err := yaml.Marshal(resolver.resolvedRoot) - assert.NoError(t, err) + _, err := yaml.Marshal(resolver.resolvedRoot) + assert.NoError(t, err) } func TestResolver_DeepJourney(t *testing.T) { - var journey []*index.Reference - for f := 0; f < 200; f++ { - journey = append(journey, nil) - } - index := index.NewSpecIndex(nil) - resolver := NewResolver(index) - assert.Nil(t, resolver.extractRelatives(nil, nil, journey, false)) + var journey []*index.Reference + for f := 0; f < 200; f++ { + journey = append(journey, nil) + } + index := index.NewSpecIndex(nil) + resolver := NewResolver(index) + assert.Nil(t, resolver.extractRelatives(nil, nil, journey, false)) } func TestResolver_ResolveComponents_Stripe(t *testing.T) { - stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml") - var rootNode yaml.Node - yaml.Unmarshal(stripe, &rootNode) + stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml") + var rootNode yaml.Node + yaml.Unmarshal(stripe, &rootNode) - index := index.NewSpecIndex(&rootNode) + index := index.NewSpecIndex(&rootNode) - resolver := NewResolver(index) - assert.NotNil(t, resolver) + resolver := NewResolver(index) + assert.NotNil(t, resolver) - circ := resolver.Resolve() - assert.Len(t, circ, 3) + circ := resolver.Resolve() + assert.Len(t, circ, 3) - assert.Len(t, resolver.GetNonPolymorphicCircularErrors(), 3) - assert.Len(t, resolver.GetPolymorphicCircularErrors(), 0) + assert.Len(t, resolver.GetNonPolymorphicCircularErrors(), 3) + assert.Len(t, resolver.GetPolymorphicCircularErrors(), 0) } func TestResolver_ResolveComponents_BurgerShop(t *testing.T) { - mixedref, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml") - var rootNode yaml.Node - yaml.Unmarshal(mixedref, &rootNode) + mixedref, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml") + var rootNode yaml.Node + yaml.Unmarshal(mixedref, &rootNode) - index := index.NewSpecIndex(&rootNode) + index := index.NewSpecIndex(&rootNode) - resolver := NewResolver(index) - assert.NotNil(t, resolver) + resolver := NewResolver(index) + assert.NotNil(t, resolver) - circ := resolver.Resolve() - assert.Len(t, circ, 0) + circ := resolver.Resolve() + assert.Len(t, circ, 0) } func TestResolver_ResolveComponents_PolyNonCircRef(t *testing.T) { - yml := `paths: + yml := `paths: /hey: get: responses: @@ -184,20 +184,20 @@ components: tea: description: tea` - var rootNode yaml.Node - yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + yaml.Unmarshal([]byte(yml), &rootNode) - index := index.NewSpecIndex(&rootNode) + index := index.NewSpecIndex(&rootNode) - resolver := NewResolver(index) - assert.NotNil(t, resolver) + resolver := NewResolver(index) + assert.NotNil(t, resolver) - circ := resolver.CheckForCircularReferences() - assert.Len(t, circ, 0) + circ := resolver.CheckForCircularReferences() + assert.Len(t, circ, 0) } func TestResolver_ResolveComponents_PolyCircRef(t *testing.T) { - yml := `openapi: 3.1.0 + yml := `openapi: 3.1.0 components: schemas: cheese: @@ -211,24 +211,24 @@ components: tea: description: tea` - var rootNode yaml.Node - yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + yaml.Unmarshal([]byte(yml), &rootNode) - index := index.NewSpecIndex(&rootNode) + index := index.NewSpecIndex(&rootNode) - resolver := NewResolver(index) - assert.NotNil(t, resolver) + resolver := NewResolver(index) + assert.NotNil(t, resolver) - _ = resolver.CheckForCircularReferences() - resolver.circularReferences[0].IsInfiniteLoop = true // override - assert.Len(t, index.GetCircularReferences(), 1) - assert.Len(t, resolver.GetPolymorphicCircularErrors(), 1) - assert.Equal(t, 2, index.GetCircularReferences()[0].LoopIndex) + _ = resolver.CheckForCircularReferences() + resolver.circularReferences[0].IsInfiniteLoop = true // override + assert.Len(t, index.GetCircularReferences(), 1) + assert.Len(t, resolver.GetPolymorphicCircularErrors(), 1) + assert.Equal(t, 2, index.GetCircularReferences()[0].LoopIndex) } func TestResolver_ResolveComponents_Missing(t *testing.T) { - yml := `paths: + yml := `paths: /hey: get: responses: @@ -247,95 +247,95 @@ components: butter: $ref: 'go home, I am drunk'` - var rootNode yaml.Node - yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + yaml.Unmarshal([]byte(yml), &rootNode) - index := index.NewSpecIndex(&rootNode) + index := index.NewSpecIndex(&rootNode) - resolver := NewResolver(index) - assert.NotNil(t, resolver) + resolver := NewResolver(index) + assert.NotNil(t, resolver) - err := resolver.Resolve() - assert.Len(t, err, 1) - assert.Equal(t, "cannot resolve reference `go home, I am drunk`, it's missing: $go home, I am drunk [18:11]", err[0].Error()) + err := resolver.Resolve() + assert.Len(t, err, 1) + assert.Equal(t, "cannot resolve reference `go home, I am drunk`, it's missing: $go home, I am drunk [18:11]", err[0].Error()) } func TestResolver_ResolveComponents_MixedRef(t *testing.T) { - mixedref, _ := ioutil.ReadFile("../test_specs/mixedref-burgershop.openapi.yaml") - var rootNode yaml.Node - yaml.Unmarshal(mixedref, &rootNode) + mixedref, _ := ioutil.ReadFile("../test_specs/mixedref-burgershop.openapi.yaml") + var rootNode yaml.Node + yaml.Unmarshal(mixedref, &rootNode) - b := index.CreateOpenAPIIndexConfig() - idx := index.NewSpecIndexWithConfig(&rootNode, b) + b := index.CreateOpenAPIIndexConfig() + idx := index.NewSpecIndexWithConfig(&rootNode, b) - resolver := NewResolver(idx) - assert.NotNil(t, resolver) + resolver := NewResolver(idx) + assert.NotNil(t, resolver) - circ := resolver.Resolve() - assert.Len(t, circ, 0) - assert.Equal(t, 5, resolver.GetIndexesVisited()) + circ := resolver.Resolve() + assert.Len(t, circ, 0) + assert.Equal(t, 5, resolver.GetIndexesVisited()) - // in v0.8.2 a new check was added when indexing, to prevent re-indexing the same file multiple times. - assert.Equal(t, 191, resolver.GetRelativesSeen()) - assert.Equal(t, 35, resolver.GetJourneysTaken()) - assert.Equal(t, 62, resolver.GetReferenceVisited()) + // in v0.8.2 a new check was added when indexing, to prevent re-indexing the same file multiple times. + assert.Equal(t, 191, resolver.GetRelativesSeen()) + assert.Equal(t, 35, resolver.GetJourneysTaken()) + assert.Equal(t, 62, resolver.GetReferenceVisited()) } func TestResolver_ResolveComponents_k8s(t *testing.T) { - k8s, _ := ioutil.ReadFile("../test_specs/k8s.json") - var rootNode yaml.Node - yaml.Unmarshal(k8s, &rootNode) + k8s, _ := ioutil.ReadFile("../test_specs/k8s.json") + var rootNode yaml.Node + yaml.Unmarshal(k8s, &rootNode) - index := index.NewSpecIndex(&rootNode) + index := index.NewSpecIndex(&rootNode) - resolver := NewResolver(index) - assert.NotNil(t, resolver) + resolver := NewResolver(index) + assert.NotNil(t, resolver) - circ := resolver.Resolve() - assert.Len(t, circ, 0) + circ := resolver.Resolve() + assert.Len(t, circ, 0) } // Example of how to resolve the Stripe OpenAPI specification, and check for circular reference errors func ExampleNewResolver() { - // create a yaml.Node reference as a root node. - var rootNode yaml.Node + // create a yaml.Node reference as a root node. + var rootNode yaml.Node - // load in the Stripe OpenAPI spec (lots of polymorphic complexity in here) - stripeBytes, _ := ioutil.ReadFile("../test_specs/stripe.yaml") + // load in the Stripe OpenAPI spec (lots of polymorphic complexity in here) + stripeBytes, _ := ioutil.ReadFile("../test_specs/stripe.yaml") - // unmarshal bytes into our rootNode. - _ = yaml.Unmarshal(stripeBytes, &rootNode) + // unmarshal bytes into our rootNode. + _ = yaml.Unmarshal(stripeBytes, &rootNode) - // create a new spec index (resolver depends on it) - indexConfig := index.CreateClosedAPIIndexConfig() - index := index.NewSpecIndexWithConfig(&rootNode, indexConfig) + // create a new spec index (resolver depends on it) + indexConfig := index.CreateClosedAPIIndexConfig() + index := index.NewSpecIndexWithConfig(&rootNode, indexConfig) - // create a new resolver using the index. - resolver := NewResolver(index) + // create a new resolver using the index. + resolver := NewResolver(index) - // resolve the document, if there are circular reference errors, they are returned/ - // WARNING: this is a destructive action and the rootNode will be PERMANENTLY altered and cannot be unresolved - circularErrors := resolver.Resolve() + // resolve the document, if there are circular reference errors, they are returned/ + // WARNING: this is a destructive action and the rootNode will be PERMANENTLY altered and cannot be unresolved + circularErrors := resolver.Resolve() - // The Stripe API has a bunch of circular reference problems, mainly from polymorphism. - // So let's print them out. - // - fmt.Printf("There are %d circular reference errors, %d of them are polymorphic errors, %d are not", - len(circularErrors), len(resolver.GetPolymorphicCircularErrors()), len(resolver.GetNonPolymorphicCircularErrors())) - // Output: There are 3 circular reference errors, 0 of them are polymorphic errors, 3 are not + // The Stripe API has a bunch of circular reference problems, mainly from polymorphism. + // So let's print them out. + // + fmt.Printf("There are %d circular reference errors, %d of them are polymorphic errors, %d are not", + len(circularErrors), len(resolver.GetPolymorphicCircularErrors()), len(resolver.GetNonPolymorphicCircularErrors())) + // Output: There are 3 circular reference errors, 0 of them are polymorphic errors, 3 are not } func ExampleResolvingError() { - re := ResolvingError{ - ErrorRef: errors.New("Je suis une erreur"), - Node: &yaml.Node{ - Line: 5, - Column: 21, - }, - Path: "#/definitions/JeSuisUneErreur", - CircularReference: &index.CircularReferenceResult{}, - } + re := ResolvingError{ + ErrorRef: errors.New("Je suis une erreur"), + Node: &yaml.Node{ + Line: 5, + Column: 21, + }, + Path: "#/definitions/JeSuisUneErreur", + CircularReference: &index.CircularReferenceResult{}, + } - fmt.Printf("%s", re.Error()) - // Output: Je suis une erreur: #/definitions/JeSuisUneErreur [5:21] + fmt.Printf("%s", re.Error()) + // Output: Je suis une erreur: #/definitions/JeSuisUneErreur [5:21] } diff --git a/utils/nodes.go b/utils/nodes.go index b4bdee0..d8bc1b6 100644 --- a/utils/nodes.go +++ b/utils/nodes.go @@ -6,63 +6,63 @@ package utils import "gopkg.in/yaml.v3" func CreateRefNode(ref string) *yaml.Node { - m := CreateEmptyMapNode() - nodes := make([]*yaml.Node, 2) - nodes[0] = CreateStringNode("$ref") - nodes[1] = CreateStringNode(ref) - nodes[1].Style = yaml.SingleQuotedStyle - m.Content = nodes - return m + m := CreateEmptyMapNode() + nodes := make([]*yaml.Node, 2) + nodes[0] = CreateStringNode("$ref") + nodes[1] = CreateStringNode(ref) + nodes[1].Style = yaml.SingleQuotedStyle + m.Content = nodes + return m } func CreateEmptyMapNode() *yaml.Node { - n := &yaml.Node{ - Kind: yaml.MappingNode, - Tag: "!!map", - } - return n + n := &yaml.Node{ + Kind: yaml.MappingNode, + Tag: "!!map", + } + return n } func CreateEmptySequenceNode() *yaml.Node { - n := &yaml.Node{ - Kind: yaml.SequenceNode, - Tag: "!!seq", - } - return n + n := &yaml.Node{ + Kind: yaml.SequenceNode, + Tag: "!!seq", + } + return n } func CreateStringNode(str string) *yaml.Node { - n := &yaml.Node{ - Kind: yaml.ScalarNode, - Tag: "!!str", - Value: str, - } - return n + n := &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: str, + } + return n } func CreateBoolNode(str string) *yaml.Node { - n := &yaml.Node{ - Kind: yaml.ScalarNode, - Tag: "!!bool", - Value: str, - } - return n + n := &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!bool", + Value: str, + } + return n } func CreateIntNode(str string) *yaml.Node { - n := &yaml.Node{ - Kind: yaml.ScalarNode, - Tag: "!!int", - Value: str, - } - return n + n := &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!int", + Value: str, + } + return n } func CreateFloatNode(str string) *yaml.Node { - n := &yaml.Node{ - Kind: yaml.ScalarNode, - Tag: "!!float", - Value: str, - } - return n + n := &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!float", + Value: str, + } + return n } diff --git a/utils/nodes_test.go b/utils/nodes_test.go index 81d5ef9..535d965 100644 --- a/utils/nodes_test.go +++ b/utils/nodes_test.go @@ -4,46 +4,46 @@ package utils import ( - "github.com/stretchr/testify/assert" - "testing" + "github.com/stretchr/testify/assert" + "testing" ) func TestCreateBoolNode(t *testing.T) { - b := CreateBoolNode("true") - assert.Equal(t, "!!bool", b.Tag) - assert.Equal(t, "true", b.Value) + b := CreateBoolNode("true") + assert.Equal(t, "!!bool", b.Tag) + assert.Equal(t, "true", b.Value) } func TestCreateEmptyMapNode(t *testing.T) { - m := CreateEmptyMapNode() - assert.Equal(t, "!!map", m.Tag) - assert.Len(t, m.Content, 0) + m := CreateEmptyMapNode() + assert.Equal(t, "!!map", m.Tag) + assert.Len(t, m.Content, 0) } func TestCreateEmptySequenceNode(t *testing.T) { - s := CreateEmptySequenceNode() - assert.Equal(t, "!!seq", s.Tag) - assert.Len(t, s.Content, 0) + s := CreateEmptySequenceNode() + assert.Equal(t, "!!seq", s.Tag) + assert.Len(t, s.Content, 0) } func TestCreateFloatNode(t *testing.T) { - f := CreateFloatNode("3.14") - assert.Equal(t, "!!float", f.Tag) - assert.Equal(t, "3.14", f.Value) + f := CreateFloatNode("3.14") + assert.Equal(t, "!!float", f.Tag) + assert.Equal(t, "3.14", f.Value) } func TestCreateIntNode(t *testing.T) { - i := CreateIntNode("42") - assert.Equal(t, "!!int", i.Tag) - assert.Equal(t, "42", i.Value) + i := CreateIntNode("42") + assert.Equal(t, "!!int", i.Tag) + assert.Equal(t, "42", i.Value) } func TestCreateRefNode(t *testing.T) { - r := CreateRefNode("#/components/schemas/MySchema") - assert.Equal(t, "!!map", r.Tag) - assert.Len(t, r.Content, 2) - assert.Equal(t, "!!str", r.Content[0].Tag) - assert.Equal(t, "$ref", r.Content[0].Value) - assert.Equal(t, "!!str", r.Content[1].Tag) - assert.Equal(t, "#/components/schemas/MySchema", r.Content[1].Value) + r := CreateRefNode("#/components/schemas/MySchema") + assert.Equal(t, "!!map", r.Tag) + assert.Len(t, r.Content, 2) + assert.Equal(t, "!!str", r.Content[0].Tag) + assert.Equal(t, "$ref", r.Content[0].Value) + assert.Equal(t, "!!str", r.Content[1].Tag) + assert.Equal(t, "#/components/schemas/MySchema", r.Content[1].Value) } diff --git a/utils/utils_test.go b/utils/utils_test.go index 009faf1..e848b58 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -1,11 +1,11 @@ package utils import ( - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "io/ioutil" - "sync" - "testing" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "io/ioutil" + "sync" + "testing" ) type petstore []byte @@ -13,561 +13,561 @@ type petstore []byte var once sync.Once var ( - psBytes petstore + psBytes petstore ) func getPetstore() petstore { - once.Do(func() { - psBytes, _ = ioutil.ReadFile("../test_specs/petstorev3.json") - }) - return psBytes + once.Do(func() { + psBytes, _ = ioutil.ReadFile("../test_specs/petstorev3.json") + }) + return psBytes } func TestRenderCodeSnippet(t *testing.T) { - code := []string{"hey", "ho", "let's", "go!"} - startNode := &yaml.Node{ - Line: 1, - } - rendered := RenderCodeSnippet(startNode, code, 1, 3) - assert.Equal(t, "hey\nho\nlet's\n", rendered) + code := []string{"hey", "ho", "let's", "go!"} + startNode := &yaml.Node{ + Line: 1, + } + rendered := RenderCodeSnippet(startNode, code, 1, 3) + assert.Equal(t, "hey\nho\nlet's\n", rendered) } func TestRenderCodeSnippet_BelowStart(t *testing.T) { - code := []string{"hey", "ho", "let's", "go!"} - startNode := &yaml.Node{ - Line: 0, - } - rendered := RenderCodeSnippet(startNode, code, 1, 3) - assert.Equal(t, "hey\nho\nlet's\n", rendered) + code := []string{"hey", "ho", "let's", "go!"} + startNode := &yaml.Node{ + Line: 0, + } + rendered := RenderCodeSnippet(startNode, code, 1, 3) + assert.Equal(t, "hey\nho\nlet's\n", rendered) } func TestFindNodes(t *testing.T) { - nodes, err := FindNodes(getPetstore(), "$.info.contact") - assert.NoError(t, err) - assert.NotNil(t, nodes) - assert.Len(t, nodes, 1) + nodes, err := FindNodes(getPetstore(), "$.info.contact") + assert.NoError(t, err) + assert.NotNil(t, nodes) + assert.Len(t, nodes, 1) } func TestFindNodes_BadPath(t *testing.T) { - nodes, err := FindNodes(getPetstore(), "I am not valid") - assert.Error(t, err) - assert.Nil(t, nodes) + nodes, err := FindNodes(getPetstore(), "I am not valid") + assert.Error(t, err) + assert.Nil(t, nodes) } func TestFindLastChildNode(t *testing.T) { - nodes, _ := FindNodes(getPetstore(), "$.info") - lastNode := FindLastChildNode(nodes[0]) - assert.Equal(t, "1.0.11", lastNode.Value) // should be the version. - lastNodeDouble := FindLastChildNodeWithLevel(nodes[0], 0) - assert.Equal(t, lastNode, lastNodeDouble) + nodes, _ := FindNodes(getPetstore(), "$.info") + lastNode := FindLastChildNode(nodes[0]) + assert.Equal(t, "1.0.11", lastNode.Value) // should be the version. + lastNodeDouble := FindLastChildNodeWithLevel(nodes[0], 0) + assert.Equal(t, lastNode, lastNodeDouble) } func TestFindLastChildNode_WithKids(t *testing.T) { - nodes, _ := FindNodes(getPetstore(), "$.paths./pet") - lastNode := FindLastChildNode(nodes[0]) - lastNodeDouble := FindLastChildNodeWithLevel(nodes[0], 0) - assert.Equal(t, lastNode, lastNodeDouble) - assert.Equal(t, "read:pets", lastNode.Value) + nodes, _ := FindNodes(getPetstore(), "$.paths./pet") + lastNode := FindLastChildNode(nodes[0]) + lastNodeDouble := FindLastChildNodeWithLevel(nodes[0], 0) + assert.Equal(t, lastNode, lastNodeDouble) + assert.Equal(t, "read:pets", lastNode.Value) } func TestFindLastChildNode_NotFound(t *testing.T) { - node := &yaml.Node{ - Value: "same", - } - lastNode := FindLastChildNode(node) - assert.Equal(t, "same", lastNode.Value) // should be the same node - lastNodeDouble := FindLastChildNodeWithLevel(node, 0) - assert.Equal(t, lastNode, lastNodeDouble) + node := &yaml.Node{ + Value: "same", + } + lastNode := FindLastChildNode(node) + assert.Equal(t, "same", lastNode.Value) // should be the same node + lastNodeDouble := FindLastChildNodeWithLevel(node, 0) + assert.Equal(t, lastNode, lastNodeDouble) } func genLoop(count int) *yaml.Node { - if count > 200 { - return nil - } - count++ - return &yaml.Node{ - Value: "same", - Content: []*yaml.Node{ - genLoop(count), - }, - } + if count > 200 { + return nil + } + count++ + return &yaml.Node{ + Value: "same", + Content: []*yaml.Node{ + genLoop(count), + }, + } } func TestFindLastChildNode_TooDeep(t *testing.T) { - node := genLoop(0) - lastNodeDouble := FindLastChildNodeWithLevel(node, 0) - assert.NotNil(t, lastNodeDouble) + node := genLoop(0) + lastNodeDouble := FindLastChildNodeWithLevel(node, 0) + assert.NotNil(t, lastNodeDouble) } func TestBuildPath(t *testing.T) { - assert.Equal(t, "$.fresh.fish.and.chicken.nuggets", - BuildPath("$.fresh.fish", []string{"and", "chicken", "nuggets"})) + assert.Equal(t, "$.fresh.fish.and.chicken.nuggets", + BuildPath("$.fresh.fish", []string{"and", "chicken", "nuggets"})) } func TestBuildPath_WithTrailingPeriod(t *testing.T) { - assert.Equal(t, "$.fresh.fish.and.chicken.nuggets", - BuildPath("$.fresh.fish", []string{"and", "chicken", "nuggets", ""})) + assert.Equal(t, "$.fresh.fish.and.chicken.nuggets", + BuildPath("$.fresh.fish", []string{"and", "chicken", "nuggets", ""})) } func TestFindNodesWithoutDeserializing(t *testing.T) { - root, _ := FindNodes(getPetstore(), "$") - nodes, err := FindNodesWithoutDeserializing(root[0], "$.info.contact") - assert.NoError(t, err) - assert.NotNil(t, nodes) - assert.Len(t, nodes, 1) + root, _ := FindNodes(getPetstore(), "$") + nodes, err := FindNodesWithoutDeserializing(root[0], "$.info.contact") + assert.NoError(t, err) + assert.NotNil(t, nodes) + assert.Len(t, nodes, 1) } func TestFindNodesWithoutDeserializing_InvalidPath(t *testing.T) { - root, _ := FindNodes(getPetstore(), "$") - nodes, err := FindNodesWithoutDeserializing(root[0], "I love a good curry") - assert.Error(t, err) - assert.Nil(t, nodes) + root, _ := FindNodes(getPetstore(), "$") + nodes, err := FindNodesWithoutDeserializing(root[0], "I love a good curry") + assert.Error(t, err) + assert.Nil(t, nodes) } func TestConvertInterfaceIntoStringMap(t *testing.T) { - var d interface{} - n := make(map[string]string) - n["melody"] = "baby girl" - d = n - parsed := ConvertInterfaceIntoStringMap(d) - assert.Equal(t, "baby girl", parsed["melody"]) + var d interface{} + n := make(map[string]string) + n["melody"] = "baby girl" + d = n + parsed := ConvertInterfaceIntoStringMap(d) + assert.Equal(t, "baby girl", parsed["melody"]) } func TestConvertInterfaceIntoStringMap_NoType(t *testing.T) { - var d interface{} - n := make(map[string]interface{}) - n["melody"] = "baby girl" - d = n - parsed := ConvertInterfaceIntoStringMap(d) - assert.Equal(t, "baby girl", parsed["melody"]) + var d interface{} + n := make(map[string]interface{}) + n["melody"] = "baby girl" + d = n + parsed := ConvertInterfaceIntoStringMap(d) + assert.Equal(t, "baby girl", parsed["melody"]) } func TestConvertInterfaceToStringArray(t *testing.T) { - var d interface{} - n := make(map[string][]string) - n["melody"] = []string{"melody", "is", "my", "baby"} - d = n - parsed := ConvertInterfaceToStringArray(d) - assert.Equal(t, "baby", parsed[3]) + var d interface{} + n := make(map[string][]string) + n["melody"] = []string{"melody", "is", "my", "baby"} + d = n + parsed := ConvertInterfaceToStringArray(d) + assert.Equal(t, "baby", parsed[3]) } func TestConvertInterfaceToStringArray_NoType(t *testing.T) { - var d interface{} - m := make([]interface{}, 4) - n := make(map[string]interface{}) - m[0] = "melody" - m[1] = "is" - m[2] = "my" - m[3] = "baby" - n["melody"] = m - d = n - parsed := ConvertInterfaceToStringArray(d) - assert.Equal(t, "baby", parsed[3]) + var d interface{} + m := make([]interface{}, 4) + n := make(map[string]interface{}) + m[0] = "melody" + m[1] = "is" + m[2] = "my" + m[3] = "baby" + n["melody"] = m + d = n + parsed := ConvertInterfaceToStringArray(d) + assert.Equal(t, "baby", parsed[3]) } func TestConvertInterfaceToStringArray_Invalid(t *testing.T) { - var d interface{} - d = "I am a carrot" - parsed := ConvertInterfaceToStringArray(d) - assert.Nil(t, parsed) + var d interface{} + d = "I am a carrot" + parsed := ConvertInterfaceToStringArray(d) + assert.Nil(t, parsed) } func TestConvertInterfaceArrayToStringArray(t *testing.T) { - var d interface{} - m := []string{"maddox", "is", "my", "little", "champion"} - d = m - parsed := ConvertInterfaceArrayToStringArray(d) - assert.Equal(t, "little", parsed[3]) + var d interface{} + m := []string{"maddox", "is", "my", "little", "champion"} + d = m + parsed := ConvertInterfaceArrayToStringArray(d) + assert.Equal(t, "little", parsed[3]) } func TestConvertInterfaceArrayToStringArray_NoType(t *testing.T) { - var d interface{} - m := make([]interface{}, 4) - m[0] = "melody" - m[1] = "is" - m[2] = "my" - m[3] = "baby" - d = m - parsed := ConvertInterfaceArrayToStringArray(d) - assert.Equal(t, "baby", parsed[3]) + var d interface{} + m := make([]interface{}, 4) + m[0] = "melody" + m[1] = "is" + m[2] = "my" + m[3] = "baby" + d = m + parsed := ConvertInterfaceArrayToStringArray(d) + assert.Equal(t, "baby", parsed[3]) } func TestConvertInterfaceArrayToStringArray_Invalid(t *testing.T) { - var d interface{} - d = "weed is good" - parsed := ConvertInterfaceArrayToStringArray(d) - assert.Nil(t, parsed) + var d interface{} + d = "weed is good" + parsed := ConvertInterfaceArrayToStringArray(d) + assert.Nil(t, parsed) } func TestExtractValueFromInterfaceMap(t *testing.T) { - var d interface{} - m := make(map[string][]string) - m["melody"] = []string{"is", "my", "baby"} - d = m - parsed := ExtractValueFromInterfaceMap("melody", d) - assert.Equal(t, "baby", parsed.([]string)[2]) + var d interface{} + m := make(map[string][]string) + m["melody"] = []string{"is", "my", "baby"} + d = m + parsed := ExtractValueFromInterfaceMap("melody", d) + assert.Equal(t, "baby", parsed.([]string)[2]) } func TestExtractValueFromInterfaceMap_NoType(t *testing.T) { - var d interface{} - m := make(map[string]interface{}) - n := make([]interface{}, 3) - n[0] = "maddy" - n[1] = "the" - n[2] = "champion" - m["maddy"] = n - d = m - parsed := ExtractValueFromInterfaceMap("maddy", d) - assert.Equal(t, "champion", parsed.([]interface{})[2]) + var d interface{} + m := make(map[string]interface{}) + n := make([]interface{}, 3) + n[0] = "maddy" + n[1] = "the" + n[2] = "champion" + m["maddy"] = n + d = m + parsed := ExtractValueFromInterfaceMap("maddy", d) + assert.Equal(t, "champion", parsed.([]interface{})[2]) } func TestExtractValueFromInterfaceMap_Flat(t *testing.T) { - var d interface{} - m := make(map[string]interface{}) - m["maddy"] = "niblet" - d = m - parsed := ExtractValueFromInterfaceMap("maddy", d) - assert.Equal(t, "niblet", parsed.(interface{})) + var d interface{} + m := make(map[string]interface{}) + m["maddy"] = "niblet" + d = m + parsed := ExtractValueFromInterfaceMap("maddy", d) + assert.Equal(t, "niblet", parsed.(interface{})) } func TestExtractValueFromInterfaceMap_NotFound(t *testing.T) { - var d interface{} - d = "not a map" - parsed := ExtractValueFromInterfaceMap("melody", d) - assert.Nil(t, parsed) + var d interface{} + d = "not a map" + parsed := ExtractValueFromInterfaceMap("melody", d) + assert.Nil(t, parsed) } func TestFindFirstKeyNode(t *testing.T) { - nodes, _ := FindNodes(getPetstore(), "$") - key, value := FindFirstKeyNode("operationId", nodes, 0) - assert.NotNil(t, key) - assert.NotNil(t, value) - assert.Equal(t, 55, key.Line) + nodes, _ := FindNodes(getPetstore(), "$") + key, value := FindFirstKeyNode("operationId", nodes, 0) + assert.NotNil(t, key) + assert.NotNil(t, value) + assert.Equal(t, 55, key.Line) } func TestFindFirstKeyNode_NotFound(t *testing.T) { - nodes, _ := FindNodes(getPetstore(), "$") - key, value := FindFirstKeyNode("i-do-not-exist-in-the-doc", nodes, 0) - assert.Nil(t, key) - assert.Nil(t, value) + nodes, _ := FindNodes(getPetstore(), "$") + key, value := FindFirstKeyNode("i-do-not-exist-in-the-doc", nodes, 0) + assert.Nil(t, key) + assert.Nil(t, value) } func TestFindFirstKeyNode_TooDeep(t *testing.T) { - a, b := FindFirstKeyNode("", nil, 900) - assert.Nil(t, a) - assert.Nil(t, b) + a, b := FindFirstKeyNode("", nil, 900) + assert.Nil(t, a) + assert.Nil(t, b) } func TestFindFirstKeyNode_ValueIsKey(t *testing.T) { - a := &yaml.Node{ - Value: "chicken", - } + a := &yaml.Node{ + Value: "chicken", + } - b := &yaml.Node{ - Value: "nuggets", - Content: []*yaml.Node{a}, - } + b := &yaml.Node{ + Value: "nuggets", + Content: []*yaml.Node{a}, + } - c, d := FindFirstKeyNode("nuggets", []*yaml.Node{b}, 0) - assert.NotNil(t, c) - assert.NotNil(t, d) - assert.Equal(t, c, d) + c, d := FindFirstKeyNode("nuggets", []*yaml.Node{b}, 0) + assert.NotNil(t, c) + assert.NotNil(t, d) + assert.Equal(t, c, d) } func TestFindFirstKeyNode_Map(t *testing.T) { - nodes, _ := FindNodes(getPetstore(), "$") - key, value := FindFirstKeyNode("pet", nodes, 0) - assert.NotNil(t, key) - assert.NotNil(t, value) - assert.Equal(t, 27, key.Line) + nodes, _ := FindNodes(getPetstore(), "$") + key, value := FindFirstKeyNode("pet", nodes, 0) + assert.NotNil(t, key) + assert.NotNil(t, value) + assert.Equal(t, 27, key.Line) } func TestFindKeyNodeTop(t *testing.T) { - nodes, _ := FindNodes(getPetstore(), "$") - k, v := FindKeyNodeTop("info", nodes[0].Content) - assert.NotNil(t, k) - assert.NotNil(t, v) - assert.Equal(t, 3, k.Line) + nodes, _ := FindNodes(getPetstore(), "$") + k, v := FindKeyNodeTop("info", nodes[0].Content) + assert.NotNil(t, k) + assert.NotNil(t, v) + assert.Equal(t, 3, k.Line) } func TestFindKeyNodeTop_NotFound(t *testing.T) { - nodes, _ := FindNodes(getPetstore(), "$") - k, v := FindKeyNodeTop("i am a giant potato", nodes[0].Content) - assert.Nil(t, k) - assert.Nil(t, v) + nodes, _ := FindNodes(getPetstore(), "$") + k, v := FindKeyNodeTop("i am a giant potato", nodes[0].Content) + assert.Nil(t, k) + assert.Nil(t, v) } func TestFindKeyNode(t *testing.T) { - nodes, _ := FindNodes(getPetstore(), "$") - k, v := FindKeyNode("/pet", nodes[0].Content) - assert.NotNil(t, k) - assert.NotNil(t, v) - assert.Equal(t, 47, k.Line) + nodes, _ := FindNodes(getPetstore(), "$") + k, v := FindKeyNode("/pet", nodes[0].Content) + assert.NotNil(t, k) + assert.NotNil(t, v) + assert.Equal(t, 47, k.Line) } func TestFindKeyNode_ValueIsKey(t *testing.T) { - a := &yaml.Node{ - Value: "chicken", - } + a := &yaml.Node{ + Value: "chicken", + } - b := &yaml.Node{ - Tag: "!!map", - Value: "nuggets", - Content: []*yaml.Node{a}, - } + b := &yaml.Node{ + Tag: "!!map", + Value: "nuggets", + Content: []*yaml.Node{a}, + } - c, d := FindKeyNode("nuggets", []*yaml.Node{b, a}) - assert.Equal(t, "nuggets", c.Value) - assert.Equal(t, "chicken", d.Value) + c, d := FindKeyNode("nuggets", []*yaml.Node{b, a}) + assert.Equal(t, "nuggets", c.Value) + assert.Equal(t, "chicken", d.Value) - e := &yaml.Node{ - Value: "pizza", - } - f := &yaml.Node{ - Value: "pie", - } - b.Content = append(b.Content, e, f) + e := &yaml.Node{ + Value: "pizza", + } + f := &yaml.Node{ + Value: "pie", + } + b.Content = append(b.Content, e, f) - c, d = FindKeyNode("pie", []*yaml.Node{b, a}) - assert.Equal(t, "nuggets", c.Value) - assert.Equal(t, "pie", d.Value) + c, d = FindKeyNode("pie", []*yaml.Node{b, a}) + assert.Equal(t, "nuggets", c.Value) + assert.Equal(t, "pie", d.Value) - b.Tag = "!!seq" + b.Tag = "!!seq" - c, d = FindKeyNode("pie", []*yaml.Node{b, a}) - assert.Equal(t, "nuggets", c.Value) - assert.Equal(t, "pie", d.Value) + c, d = FindKeyNode("pie", []*yaml.Node{b, a}) + assert.Equal(t, "nuggets", c.Value) + assert.Equal(t, "pie", d.Value) } func TestFindExtensionNodes(t *testing.T) { - a := &yaml.Node{ - Value: "x-coffee", - } - b := &yaml.Node{ - Value: "required", - } - c := &yaml.Node{ - Content: []*yaml.Node{a, b}, - } - exts := FindExtensionNodes(c.Content) - assert.Len(t, exts, 1) - assert.Equal(t, "required", exts[0].Value.Value) + a := &yaml.Node{ + Value: "x-coffee", + } + b := &yaml.Node{ + Value: "required", + } + c := &yaml.Node{ + Content: []*yaml.Node{a, b}, + } + exts := FindExtensionNodes(c.Content) + assert.Len(t, exts, 1) + assert.Equal(t, "required", exts[0].Value.Value) } func TestFindKeyNodeFull(t *testing.T) { - a := &yaml.Node{ - Value: "fish", - } - b := &yaml.Node{ - Value: "paste", - } + a := &yaml.Node{ + Value: "fish", + } + b := &yaml.Node{ + Value: "paste", + } - c, d, e := FindKeyNodeFull("fish", []*yaml.Node{a, b}) - assert.Equal(t, "fish", c.Value) - assert.Equal(t, "fish", d.Value) - assert.Equal(t, "paste", e.Value) + c, d, e := FindKeyNodeFull("fish", []*yaml.Node{a, b}) + assert.Equal(t, "fish", c.Value) + assert.Equal(t, "fish", d.Value) + assert.Equal(t, "paste", e.Value) } func TestFindKeyNodeFull_MapValueIsLastNode(t *testing.T) { - f := &yaml.Node{ - Value: "cheese", - } - h := &yaml.Node{ - Tag: "!!map", - Value: "deserts", // this is invalid btw, but helps with mechanical understanding - Content: []*yaml.Node{f}, - } + f := &yaml.Node{ + Value: "cheese", + } + h := &yaml.Node{ + Tag: "!!map", + Value: "deserts", // this is invalid btw, but helps with mechanical understanding + Content: []*yaml.Node{f}, + } - c, d, e := FindKeyNodeFull("cheese", []*yaml.Node{h}) - assert.Equal(t, "deserts", c.Value) - assert.Equal(t, "cheese", d.Value) - assert.Equal(t, "cheese", e.Value) + c, d, e := FindKeyNodeFull("cheese", []*yaml.Node{h}) + assert.Equal(t, "deserts", c.Value) + assert.Equal(t, "cheese", d.Value) + assert.Equal(t, "cheese", e.Value) } func TestFindKeyNodeFull_Map(t *testing.T) { - f := &yaml.Node{ - Value: "cheese", - } - g := &yaml.Node{ - Value: "cake", - } - h := &yaml.Node{ - Tag: "!!map", - Value: "deserts", // this is invalid btw, but helps with mechanical understanding - Content: []*yaml.Node{f, g}, - } + f := &yaml.Node{ + Value: "cheese", + } + g := &yaml.Node{ + Value: "cake", + } + h := &yaml.Node{ + Tag: "!!map", + Value: "deserts", // this is invalid btw, but helps with mechanical understanding + Content: []*yaml.Node{f, g}, + } - c, d, e := FindKeyNodeFull("cheese", []*yaml.Node{h}) - assert.Equal(t, "deserts", c.Value) - assert.Equal(t, "cheese", d.Value) - assert.Equal(t, "cake", e.Value) + c, d, e := FindKeyNodeFull("cheese", []*yaml.Node{h}) + assert.Equal(t, "deserts", c.Value) + assert.Equal(t, "cheese", d.Value) + assert.Equal(t, "cake", e.Value) } func TestFindKeyNodeFull_Array(t *testing.T) { - f := &yaml.Node{ - Value: "cheese", - } - g := &yaml.Node{ - Value: "cake", - } - h := &yaml.Node{ - Tag: "!!seq", - Value: "deserts", // this is invalid btw, but helps with mechanical understanding - Content: []*yaml.Node{f, g}, - } + f := &yaml.Node{ + Value: "cheese", + } + g := &yaml.Node{ + Value: "cake", + } + h := &yaml.Node{ + Tag: "!!seq", + Value: "deserts", // this is invalid btw, but helps with mechanical understanding + Content: []*yaml.Node{f, g}, + } - c, d, e := FindKeyNodeFull("cheese", []*yaml.Node{h}) - assert.Equal(t, "deserts", c.Value) - assert.Equal(t, "cheese", d.Value) - assert.Equal(t, "cheese", e.Value) + c, d, e := FindKeyNodeFull("cheese", []*yaml.Node{h}) + assert.Equal(t, "deserts", c.Value) + assert.Equal(t, "cheese", d.Value) + assert.Equal(t, "cheese", e.Value) } func TestFindKeyNodeFull_Nothing(t *testing.T) { - c, d, e := FindKeyNodeFull("cheese", []*yaml.Node{}) - assert.Nil(t, c) - assert.Nil(t, d) - assert.Nil(t, e) + c, d, e := FindKeyNodeFull("cheese", []*yaml.Node{}) + assert.Nil(t, c) + assert.Nil(t, d) + assert.Nil(t, e) } func TestFindKeyNode_NotFound(t *testing.T) { - nodes, _ := FindNodes(getPetstore(), "$") - k, v := FindKeyNode("I am not anything at all", nodes[0].Content) - assert.Nil(t, k) - assert.Nil(t, v) + nodes, _ := FindNodes(getPetstore(), "$") + k, v := FindKeyNode("I am not anything at all", nodes[0].Content) + assert.Nil(t, k) + assert.Nil(t, v) } func TestFindKeyFullNodeTop(t *testing.T) { - a := &yaml.Node{ - Value: "fish", - } - b := &yaml.Node{ - Value: "paste", - } + a := &yaml.Node{ + Value: "fish", + } + b := &yaml.Node{ + Value: "paste", + } - c, d, e := FindKeyNodeFullTop("fish", []*yaml.Node{a, b}) - assert.Equal(t, "fish", c.Value) - assert.Equal(t, "fish", d.Value) - assert.Equal(t, "paste", e.Value) + c, d, e := FindKeyNodeFullTop("fish", []*yaml.Node{a, b}) + assert.Equal(t, "fish", c.Value) + assert.Equal(t, "fish", d.Value) + assert.Equal(t, "paste", e.Value) } func TestFindKeyFullNode_NotFound(t *testing.T) { - a := &yaml.Node{ - Value: "fish", - } - b := &yaml.Node{ - Value: "paste", - } + a := &yaml.Node{ + Value: "fish", + } + b := &yaml.Node{ + Value: "paste", + } - c, d, e := FindKeyNodeFullTop("lemons", []*yaml.Node{a, b}) - assert.Nil(t, c) - assert.Nil(t, d) - assert.Nil(t, e) + c, d, e := FindKeyNodeFullTop("lemons", []*yaml.Node{a, b}) + assert.Nil(t, c) + assert.Nil(t, d) + assert.Nil(t, e) } func TestMakeTagReadable(t *testing.T) { - n := &yaml.Node{ - Tag: "!!map", - } - assert.Equal(t, ObjectLabel, MakeTagReadable(n)) - n.Tag = "!!seq" - assert.Equal(t, ArrayLabel, MakeTagReadable(n)) - n.Tag = "!!str" - assert.Equal(t, StringLabel, MakeTagReadable(n)) - n.Tag = "!!int" - assert.Equal(t, IntegerLabel, MakeTagReadable(n)) - n.Tag = "!!float" - assert.Equal(t, NumberLabel, MakeTagReadable(n)) - n.Tag = "!!bool" - assert.Equal(t, BooleanLabel, MakeTagReadable(n)) - n.Tag = "mr potato man is here" - assert.Equal(t, "unknown", MakeTagReadable(n)) + n := &yaml.Node{ + Tag: "!!map", + } + assert.Equal(t, ObjectLabel, MakeTagReadable(n)) + n.Tag = "!!seq" + assert.Equal(t, ArrayLabel, MakeTagReadable(n)) + n.Tag = "!!str" + assert.Equal(t, StringLabel, MakeTagReadable(n)) + n.Tag = "!!int" + assert.Equal(t, IntegerLabel, MakeTagReadable(n)) + n.Tag = "!!float" + assert.Equal(t, NumberLabel, MakeTagReadable(n)) + n.Tag = "!!bool" + assert.Equal(t, BooleanLabel, MakeTagReadable(n)) + n.Tag = "mr potato man is here" + assert.Equal(t, "unknown", MakeTagReadable(n)) } func TestIsNodeMap(t *testing.T) { - n := &yaml.Node{ - Tag: "!!map", - } - assert.True(t, IsNodeMap(n)) - n.Tag = "!!pizza" - assert.False(t, IsNodeMap(n)) + n := &yaml.Node{ + Tag: "!!map", + } + assert.True(t, IsNodeMap(n)) + n.Tag = "!!pizza" + assert.False(t, IsNodeMap(n)) } func TestIsNodeMap_Nil(t *testing.T) { - assert.False(t, IsNodeMap(nil)) + assert.False(t, IsNodeMap(nil)) } func TestIsNodePolyMorphic(t *testing.T) { - n := &yaml.Node{ - Content: []*yaml.Node{ - { - Value: "anyOf", - }, - }, - } - assert.True(t, IsNodePolyMorphic(n)) - n.Content[0].Value = "cakes" - assert.False(t, IsNodePolyMorphic(n)) + n := &yaml.Node{ + Content: []*yaml.Node{ + { + Value: "anyOf", + }, + }, + } + assert.True(t, IsNodePolyMorphic(n)) + n.Content[0].Value = "cakes" + assert.False(t, IsNodePolyMorphic(n)) } func TestIsNodeArray(t *testing.T) { - n := &yaml.Node{ - Tag: "!!seq", - } - assert.True(t, IsNodeArray(n)) - n.Tag = "!!pizza" - assert.False(t, IsNodeArray(n)) + n := &yaml.Node{ + Tag: "!!seq", + } + assert.True(t, IsNodeArray(n)) + n.Tag = "!!pizza" + assert.False(t, IsNodeArray(n)) } func TestIsNodeArray_Nil(t *testing.T) { - assert.False(t, IsNodeArray(nil)) + assert.False(t, IsNodeArray(nil)) } func TestIsNodeStringValue(t *testing.T) { - n := &yaml.Node{ - Tag: "!!str", - } - assert.True(t, IsNodeStringValue(n)) - n.Tag = "!!pizza" - assert.False(t, IsNodeStringValue(n)) + n := &yaml.Node{ + Tag: "!!str", + } + assert.True(t, IsNodeStringValue(n)) + n.Tag = "!!pizza" + assert.False(t, IsNodeStringValue(n)) } func TestIsNodeStringValue_Nil(t *testing.T) { - assert.False(t, IsNodeStringValue(nil)) + assert.False(t, IsNodeStringValue(nil)) } func TestIsNodeIntValue(t *testing.T) { - n := &yaml.Node{ - Tag: "!!int", - } - assert.True(t, IsNodeIntValue(n)) - n.Tag = "!!pizza" - assert.False(t, IsNodeIntValue(n)) + n := &yaml.Node{ + Tag: "!!int", + } + assert.True(t, IsNodeIntValue(n)) + n.Tag = "!!pizza" + assert.False(t, IsNodeIntValue(n)) } func TestIsNodeIntValue_Nil(t *testing.T) { - assert.False(t, IsNodeIntValue(nil)) + assert.False(t, IsNodeIntValue(nil)) } func TestIsNodeFloatValue(t *testing.T) { - n := &yaml.Node{ - Tag: "!!float", - } - assert.True(t, IsNodeFloatValue(n)) - n.Tag = "!!pizza" - assert.False(t, IsNodeFloatValue(n)) + n := &yaml.Node{ + Tag: "!!float", + } + assert.True(t, IsNodeFloatValue(n)) + n.Tag = "!!pizza" + assert.False(t, IsNodeFloatValue(n)) } func TestIsNodeNumberValue(t *testing.T) { @@ -588,212 +588,212 @@ func TestIsNodeNumberValue(t *testing.T) { } func TestIsNodeFloatValue_Nil(t *testing.T) { - assert.False(t, IsNodeFloatValue(nil)) + assert.False(t, IsNodeFloatValue(nil)) } func TestIsNodeBoolValue(t *testing.T) { - n := &yaml.Node{ - Tag: "!!bool", - } - assert.True(t, IsNodeBoolValue(n)) - n.Tag = "!!pizza" - assert.False(t, IsNodeBoolValue(n)) + n := &yaml.Node{ + Tag: "!!bool", + } + assert.True(t, IsNodeBoolValue(n)) + n.Tag = "!!pizza" + assert.False(t, IsNodeBoolValue(n)) } func TestIsNodeBoolValue_Nil(t *testing.T) { - assert.False(t, IsNodeBoolValue(nil)) + assert.False(t, IsNodeBoolValue(nil)) } func TestFixContext(t *testing.T) { - assert.Equal(t, "$.nuggets[12].name", FixContext("(root).nuggets.12.name")) + assert.Equal(t, "$.nuggets[12].name", FixContext("(root).nuggets.12.name")) } func TestFixContext_HttpCode(t *testing.T) { - assert.Equal(t, "$.nuggets.404.name", FixContext("(root).nuggets.404.name")) + assert.Equal(t, "$.nuggets.404.name", FixContext("(root).nuggets.404.name")) } func TestIsJSON(t *testing.T) { - assert.True(t, IsJSON("{'hello':'there'}")) - assert.False(t, IsJSON("potato shoes")) - assert.False(t, IsJSON("")) + assert.True(t, IsJSON("{'hello':'there'}")) + assert.False(t, IsJSON("potato shoes")) + assert.False(t, IsJSON("")) } func TestIsYAML(t *testing.T) { - assert.True(t, IsYAML("hello:\n there:\n my-name: is quobix")) - assert.True(t, IsYAML("potato shoes")) - assert.False(t, IsYAML("{'hello':'there'}")) - assert.False(t, IsYAML("")) - assert.False(t, IsYAML("8908: hello: yeah: \n12309812: :123")) + assert.True(t, IsYAML("hello:\n there:\n my-name: is quobix")) + assert.True(t, IsYAML("potato shoes")) + assert.False(t, IsYAML("{'hello':'there'}")) + assert.False(t, IsYAML("")) + assert.False(t, IsYAML("8908: hello: yeah: \n12309812: :123")) } func TestConvertYAMLtoJSON(t *testing.T) { - str, err := ConvertYAMLtoJSON([]byte("hello: there")) - assert.NoError(t, err) - assert.NotNil(t, str) - assert.Equal(t, "{\"hello\":\"there\"}", string(str)) + str, err := ConvertYAMLtoJSON([]byte("hello: there")) + assert.NoError(t, err) + assert.NotNil(t, str) + assert.Equal(t, "{\"hello\":\"there\"}", string(str)) - str, err = ConvertYAMLtoJSON([]byte("gonna: break: you:\nyeah:yeah:yeah")) - assert.Error(t, err) - assert.Nil(t, str) + str, err = ConvertYAMLtoJSON([]byte("gonna: break: you:\nyeah:yeah:yeah")) + assert.Error(t, err) + assert.Nil(t, str) } func TestIsHttpVerb(t *testing.T) { - assert.True(t, IsHttpVerb("get")) - assert.True(t, IsHttpVerb("post")) - assert.False(t, IsHttpVerb("nuggets")) + assert.True(t, IsHttpVerb("get")) + assert.True(t, IsHttpVerb("post")) + assert.False(t, IsHttpVerb("nuggets")) } func TestConvertComponentIdIntoFriendlyPathSearch(t *testing.T) { - segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/chicken/chips/pizza/cake") - assert.Equal(t, "$.chicken.chips.pizza.cake", path) - assert.Equal(t, "cake", segment) + segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/chicken/chips/pizza/cake") + assert.Equal(t, "$.chicken.chips.pizza.cake", path) + assert.Equal(t, "cake", segment) } func TestConvertComponentIdIntoFriendlyPathSearch_SuperCrazy(t *testing.T) { - segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1crazy~1ass~1references/get/responses/404/content/application~1xml;%20charset=utf-8/schema") - assert.Equal(t, "$.paths['/crazy/ass/references'].get.responses['404'].content['application/xml; charset=utf-8'].schema", path) - assert.Equal(t, "schema", segment) + segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1crazy~1ass~1references/get/responses/404/content/application~1xml;%20charset=utf-8/schema") + assert.Equal(t, "$.paths['/crazy/ass/references'].get.responses['404'].content['application/xml; charset=utf-8'].schema", path) + assert.Equal(t, "schema", segment) } func TestConvertComponentIdIntoFriendlyPathSearch_Crazy(t *testing.T) { - segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/components/schemas/gpg-key/properties/subkeys/example/0/expires_at") - assert.Equal(t, "$.components.schemas.gpg-key.properties.subkeys.example[0].expires_at", path) - assert.Equal(t, "expires_at", segment) + segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/components/schemas/gpg-key/properties/subkeys/example/0/expires_at") + assert.Equal(t, "$.components.schemas.gpg-key.properties.subkeys.example[0].expires_at", path) + assert.Equal(t, "expires_at", segment) } func TestConvertComponentIdIntoFriendlyPathSearch_Simple(t *testing.T) { - segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/~1fresh~1pizza/get") - assert.Equal(t, "$['/fresh/pizza'].get", path) - assert.Equal(t, "get", segment) + segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/~1fresh~1pizza/get") + assert.Equal(t, "$['/fresh/pizza'].get", path) + assert.Equal(t, "get", segment) } func TestConvertComponentIdIntoFriendlyPathSearch_Params(t *testing.T) { - segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/why/0") - assert.Equal(t, "$.why[0]", path) - assert.Equal(t, "0", segment) + segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/why/0") + assert.Equal(t, "$.why[0]", path) + assert.Equal(t, "0", segment) } func TestConvertComponentIdIntoFriendlyPathSearch_Crazy_Github(t *testing.T) { - segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1crazy~1ass~1references/get/responses/404/content/application~1xml;%20charset=utf-8/schema") - assert.Equal(t, "$.paths['/crazy/ass/references'].get.responses['404'].content['application/xml; charset=utf-8'].schema", path) - assert.Equal(t, "schema", segment) + segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1crazy~1ass~1references/get/responses/404/content/application~1xml;%20charset=utf-8/schema") + assert.Equal(t, "$.paths['/crazy/ass/references'].get.responses['404'].content['application/xml; charset=utf-8'].schema", path) + assert.Equal(t, "schema", segment) } func TestConvertComponentIdIntoFriendlyPathSearch_Crazy_DigitalOcean(t *testing.T) { - segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1v2~1customers~1my~1invoices~1%7Binvoice_uuid%7D/get/parameters/0") - assert.Equal(t, "$.paths['/v2/customers/my/invoices/{invoice_uuid}'].get.parameters[0]", path) - assert.Equal(t, "0", segment) + segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1v2~1customers~1my~1invoices~1%7Binvoice_uuid%7D/get/parameters/0") + assert.Equal(t, "$.paths['/v2/customers/my/invoices/{invoice_uuid}'].get.parameters[0]", path) + assert.Equal(t, "0", segment) } func TestConvertComponentIdIntoFriendlyPathSearch_Crazy_DigitalOcean_More(t *testing.T) { - segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1v2~1certificates/post/responses/201/content/application~1json/examples/Custom%20Certificate") - assert.Equal(t, "$.paths['/v2/certificates'].post.responses['201'].content['application/json'].examples['Custom Certificate']", path) - assert.Equal(t, "Custom Certificate", segment) + segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1v2~1certificates/post/responses/201/content/application~1json/examples/Custom%20Certificate") + assert.Equal(t, "$.paths['/v2/certificates'].post.responses['201'].content['application/json'].examples['Custom Certificate']", path) + assert.Equal(t, "Custom Certificate", segment) } func TestConvertComponentIdIntoFriendlyPathSearch_CrazyShort(t *testing.T) { - segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1crazy~1ass~1references") - assert.Equal(t, "$.paths['/crazy/ass/references']", path) - assert.Equal(t, "/crazy/ass/references", segment) + segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1crazy~1ass~1references") + assert.Equal(t, "$.paths['/crazy/ass/references']", path) + assert.Equal(t, "/crazy/ass/references", segment) } func TestConvertComponentIdIntoFriendlyPathSearch_Short(t *testing.T) { - segment, path := ConvertComponentIdIntoFriendlyPathSearch("/~1crazy~1ass~1references") - assert.Equal(t, "$['/crazy/ass/references']", path) - assert.Equal(t, "/crazy/ass/references", segment) + segment, path := ConvertComponentIdIntoFriendlyPathSearch("/~1crazy~1ass~1references") + assert.Equal(t, "$['/crazy/ass/references']", path) + assert.Equal(t, "/crazy/ass/references", segment) } func TestConvertComponentIdIntoFriendlyPathSearch_Array(t *testing.T) { - segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1crazy~1ass~1references/get/parameters/0") - assert.Equal(t, "$.paths['/crazy/ass/references'].get.parameters[0]", path) - assert.Equal(t, "0", segment) + segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1crazy~1ass~1references/get/parameters/0") + assert.Equal(t, "$.paths['/crazy/ass/references'].get.parameters[0]", path) + assert.Equal(t, "0", segment) } func TestConvertComponentIdIntoFriendlyPathSearch_HTTPCode(t *testing.T) { - segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1crazy~1ass~1references/get/responses/404") - assert.Equal(t, "$.paths['/crazy/ass/references'].get.responses['404']", path) - assert.Equal(t, "404", segment) + segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1crazy~1ass~1references/get/responses/404") + assert.Equal(t, "$.paths['/crazy/ass/references'].get.responses['404']", path) + assert.Equal(t, "404", segment) } func TestConvertComponentIdIntoPath(t *testing.T) { - segment, path := ConvertComponentIdIntoPath("#/chicken/chips/pizza/cake") - assert.Equal(t, "$.chicken.chips.pizza.cake", path) - assert.Equal(t, "cake", segment) + segment, path := ConvertComponentIdIntoPath("#/chicken/chips/pizza/cake") + assert.Equal(t, "$.chicken.chips.pizza.cake", path) + assert.Equal(t, "cake", segment) } func TestDetectCase(t *testing.T) { - assert.Equal(t, PascalCase, DetectCase("PizzaPie")) - assert.Equal(t, CamelCase, DetectCase("anyoneForTennis")) - assert.Equal(t, ScreamingSnakeCase, DetectCase("I_LOVE_BEER")) - assert.Equal(t, ScreamingKebabCase, DetectCase("I-LOVE-BURGERS")) - assert.Equal(t, SnakeCase, DetectCase("snakes_on_a_plane")) - assert.Equal(t, KebabCase, DetectCase("chicken-be-be-beef-or-pork")) - assert.Equal(t, RegularCase, DetectCase("kebab-TimeIn_london-TOWN")) - assert.Equal(t, UnknownCase, DetectCase("")) + assert.Equal(t, PascalCase, DetectCase("PizzaPie")) + assert.Equal(t, CamelCase, DetectCase("anyoneForTennis")) + assert.Equal(t, ScreamingSnakeCase, DetectCase("I_LOVE_BEER")) + assert.Equal(t, ScreamingKebabCase, DetectCase("I-LOVE-BURGERS")) + assert.Equal(t, SnakeCase, DetectCase("snakes_on_a_plane")) + assert.Equal(t, KebabCase, DetectCase("chicken-be-be-beef-or-pork")) + assert.Equal(t, RegularCase, DetectCase("kebab-TimeIn_london-TOWN")) + assert.Equal(t, UnknownCase, DetectCase("")) } func TestIsNodeRefValue(t *testing.T) { - f := &yaml.Node{ - Value: "$ref", - } - g := &yaml.Node{ - Value: "'#/somewhere/out-there'", - } - h := &yaml.Node{ - Tag: "!!map", - Content: []*yaml.Node{f, g}, - } + f := &yaml.Node{ + Value: "$ref", + } + g := &yaml.Node{ + Value: "'#/somewhere/out-there'", + } + h := &yaml.Node{ + Tag: "!!map", + Content: []*yaml.Node{f, g}, + } - ref, node, val := IsNodeRefValue(h) + ref, node, val := IsNodeRefValue(h) - assert.True(t, ref) - assert.Equal(t, "$ref", node.Value) - assert.Equal(t, "'#/somewhere/out-there'", val) + assert.True(t, ref) + assert.Equal(t, "$ref", node.Value) + assert.Equal(t, "'#/somewhere/out-there'", val) } func TestIsNodeRefValue_False(t *testing.T) { - f := &yaml.Node{ - Value: "woof", - } - g := &yaml.Node{ - Value: "dog", - } - h := &yaml.Node{ - Tag: "!!map", - Content: []*yaml.Node{f, g}, - } + f := &yaml.Node{ + Value: "woof", + } + g := &yaml.Node{ + Value: "dog", + } + h := &yaml.Node{ + Tag: "!!map", + Content: []*yaml.Node{f, g}, + } - ref, node, val := IsNodeRefValue(h) + ref, node, val := IsNodeRefValue(h) - assert.False(t, ref) - assert.Nil(t, node) - assert.Empty(t, val) + assert.False(t, ref) + assert.Nil(t, node) + assert.Empty(t, val) } func TestCheckEnumForDuplicates_Success(t *testing.T) { - yml := "- yes\n- no\n- crisps" - var rootNode yaml.Node - yaml.Unmarshal([]byte(yml), &rootNode) - assert.Len(t, CheckEnumForDuplicates(rootNode.Content[0].Content), 0) + yml := "- yes\n- no\n- crisps" + var rootNode yaml.Node + yaml.Unmarshal([]byte(yml), &rootNode) + assert.Len(t, CheckEnumForDuplicates(rootNode.Content[0].Content), 0) } func TestCheckEnumForDuplicates_Fail(t *testing.T) { - yml := "- yes\n- no\n- crisps\n- no" - var rootNode yaml.Node - yaml.Unmarshal([]byte(yml), &rootNode) - assert.Len(t, CheckEnumForDuplicates(rootNode.Content[0].Content), 1) + yml := "- yes\n- no\n- crisps\n- no" + var rootNode yaml.Node + yaml.Unmarshal([]byte(yml), &rootNode) + assert.Len(t, CheckEnumForDuplicates(rootNode.Content[0].Content), 1) } func TestCheckEnumForDuplicates_FailMultiple(t *testing.T) { - yml := "- yes\n- no\n- crisps\n- no\n- rice\n- yes\n- no" + yml := "- yes\n- no\n- crisps\n- no\n- rice\n- yes\n- no" - var rootNode yaml.Node - yaml.Unmarshal([]byte(yml), &rootNode) - assert.Len(t, CheckEnumForDuplicates(rootNode.Content[0].Content), 3) + var rootNode yaml.Node + yaml.Unmarshal([]byte(yml), &rootNode) + assert.Len(t, CheckEnumForDuplicates(rootNode.Content[0].Content), 3) } diff --git a/what-changed/model/callback.go b/what-changed/model/callback.go index 04dbea0..ca554d3 100644 --- a/what-changed/model/callback.go +++ b/what-changed/model/callback.go @@ -4,110 +4,110 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/v3" ) // CallbackChanges represents all changes made between two Callback OpenAPI objects. type CallbackChanges struct { - *PropertyChanges - ExpressionChanges map[string]*PathItemChanges `json:"expressions,omitempty" yaml:"expressions,omitempty"` - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + *PropertyChanges + ExpressionChanges map[string]*PathItemChanges `json:"expressions,omitempty" yaml:"expressions,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` } // TotalChanges returns a total count of all changes made between Callback objects func (c *CallbackChanges) TotalChanges() int { - d := c.PropertyChanges.TotalChanges() - for k := range c.ExpressionChanges { - d += c.ExpressionChanges[k].TotalChanges() - } - if c.ExtensionChanges != nil { - d += c.ExtensionChanges.TotalChanges() - } - return d + d := c.PropertyChanges.TotalChanges() + for k := range c.ExpressionChanges { + d += c.ExpressionChanges[k].TotalChanges() + } + if c.ExtensionChanges != nil { + d += c.ExtensionChanges.TotalChanges() + } + return d } // GetAllChanges returns a slice of all changes made between Callback objects func (c *CallbackChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, c.Changes...) - for k := range c.ExpressionChanges { - changes = append(changes, c.ExpressionChanges[k].GetAllChanges()...) - } - if c.ExtensionChanges != nil { - changes = append(changes, c.ExtensionChanges.GetAllChanges()...) - } - return changes + var changes []*Change + changes = append(changes, c.Changes...) + for k := range c.ExpressionChanges { + changes = append(changes, c.ExpressionChanges[k].GetAllChanges()...) + } + if c.ExtensionChanges != nil { + changes = append(changes, c.ExtensionChanges.GetAllChanges()...) + } + return changes } // TotalBreakingChanges returns a total count of all changes made between Callback objects func (c *CallbackChanges) TotalBreakingChanges() int { - d := c.PropertyChanges.TotalBreakingChanges() - for k := range c.ExpressionChanges { - d += c.ExpressionChanges[k].TotalBreakingChanges() - } - if c.ExtensionChanges != nil { - d += c.ExtensionChanges.TotalBreakingChanges() - } - return d + d := c.PropertyChanges.TotalBreakingChanges() + for k := range c.ExpressionChanges { + d += c.ExpressionChanges[k].TotalBreakingChanges() + } + if c.ExtensionChanges != nil { + d += c.ExtensionChanges.TotalBreakingChanges() + } + return d } // CompareCallback will compare two Callback objects and return a pointer to CallbackChanges with all the things // that have changed between them. func CompareCallback(l, r *v3.Callback) *CallbackChanges { - cc := new(CallbackChanges) - var changes []*Change + cc := new(CallbackChanges) + var changes []*Change - lHashes := make(map[string]string) - rHashes := make(map[string]string) + lHashes := make(map[string]string) + rHashes := make(map[string]string) - lValues := make(map[string]low.ValueReference[*v3.PathItem]) - rValues := make(map[string]low.ValueReference[*v3.PathItem]) + lValues := make(map[string]low.ValueReference[*v3.PathItem]) + rValues := make(map[string]low.ValueReference[*v3.PathItem]) - for k := range l.Expression.Value { - lHashes[k.Value] = low.GenerateHashString(l.Expression.Value[k].Value) - lValues[k.Value] = l.Expression.Value[k] - } + for k := range l.Expression.Value { + lHashes[k.Value] = low.GenerateHashString(l.Expression.Value[k].Value) + lValues[k.Value] = l.Expression.Value[k] + } - for k := range r.Expression.Value { - rHashes[k.Value] = low.GenerateHashString(r.Expression.Value[k].Value) - rValues[k.Value] = r.Expression.Value[k] - } + for k := range r.Expression.Value { + rHashes[k.Value] = low.GenerateHashString(r.Expression.Value[k].Value) + rValues[k.Value] = r.Expression.Value[k] + } - expChanges := make(map[string]*PathItemChanges) + expChanges := make(map[string]*PathItemChanges) - // check left path item hashes - for k := range lHashes { - rhash := rHashes[k] - if rhash == "" { - CreateChange(&changes, ObjectRemoved, k, - lValues[k].GetValueNode(), nil, true, - lValues[k].GetValue(), nil) - continue - } - if lHashes[k] == rHashes[k] { - continue - } - // run comparison. - expChanges[k] = ComparePathItems(lValues[k].Value, rValues[k].Value) - } + // check left path item hashes + for k := range lHashes { + rhash := rHashes[k] + if rhash == "" { + CreateChange(&changes, ObjectRemoved, k, + lValues[k].GetValueNode(), nil, true, + lValues[k].GetValue(), nil) + continue + } + if lHashes[k] == rHashes[k] { + continue + } + // run comparison. + expChanges[k] = ComparePathItems(lValues[k].Value, rValues[k].Value) + } - //check right path item hashes - for k := range rHashes { - lhash := lHashes[k] - if lhash == "" { - CreateChange(&changes, ObjectAdded, k, - nil, rValues[k].GetValueNode(), false, - nil, rValues[k].GetValue()) - continue - } - } - cc.ExpressionChanges = expChanges - cc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) - cc.PropertyChanges = NewPropertyChanges(changes) - if cc.TotalChanges() <= 0 { - return nil - } - return cc + //check right path item hashes + for k := range rHashes { + lhash := lHashes[k] + if lhash == "" { + CreateChange(&changes, ObjectAdded, k, + nil, rValues[k].GetValueNode(), false, + nil, rValues[k].GetValue()) + continue + } + } + cc.ExpressionChanges = expChanges + cc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) + cc.PropertyChanges = NewPropertyChanges(changes) + if cc.TotalChanges() <= 0 { + return nil + } + return cc } diff --git a/what-changed/model/change_types.go b/what-changed/model/change_types.go index dceb9a2..4f33325 100644 --- a/what-changed/model/change_types.go +++ b/what-changed/model/change_types.go @@ -4,42 +4,42 @@ package model import ( - "gopkg.in/yaml.v3" + "gopkg.in/yaml.v3" ) // Definitions of the possible changes between two items const ( - // Modified means that was a modification of a value was made - Modified = iota + 1 + // Modified means that was a modification of a value was made + Modified = iota + 1 - // PropertyAdded means that a new property to an object was added - PropertyAdded + // PropertyAdded means that a new property to an object was added + PropertyAdded - // ObjectAdded means that a new object was added to a parent object - ObjectAdded + // ObjectAdded means that a new object was added to a parent object + ObjectAdded - // ObjectRemoved means that an object was removed from a parent object - ObjectRemoved + // ObjectRemoved means that an object was removed from a parent object + ObjectRemoved - // PropertyRemoved means that a property of an object was removed - PropertyRemoved + // PropertyRemoved means that a property of an object was removed + PropertyRemoved ) // WhatChanged is a summary object that contains a high level summary of everything changed. type WhatChanged struct { - Added int `json:"added,omitempty" yaml:"added,omitempty"` - Removed int `json:"removed,omitempty" yaml:"removed,omitempty"` - Modified int `json:"modified,omitempty" yaml:"modified,omitempty"` - TotalChanges int `json:"total,omitempty" yaml:"total,omitempty"` + Added int `json:"added,omitempty" yaml:"added,omitempty"` + Removed int `json:"removed,omitempty" yaml:"removed,omitempty"` + Modified int `json:"modified,omitempty" yaml:"modified,omitempty"` + TotalChanges int `json:"total,omitempty" yaml:"total,omitempty"` } // ChangeContext holds a reference to the line and column positions of original and new change. type ChangeContext struct { - OriginalLine *int `json:"originalLine,omitempty" yaml:"originalLine,omitempty"` - OriginalColumn *int `json:"originalColumn,omitempty" yaml:"originalColumn,omitempty"` - NewLine *int `json:"newLine,omitempty" yaml:"newLine,omitempty"` - NewColumn *int `json:"newColumn,omitempty" yaml:"newColumn,omitempty"` + OriginalLine *int `json:"originalLine,omitempty" yaml:"originalLine,omitempty"` + OriginalColumn *int `json:"originalColumn,omitempty" yaml:"originalColumn,omitempty"` + NewLine *int `json:"newLine,omitempty" yaml:"newLine,omitempty"` + NewColumn *int `json:"newColumn,omitempty" yaml:"newColumn,omitempty"` } // HasChanged determines if the line and column numbers of the original and new values have changed. @@ -47,93 +47,93 @@ type ChangeContext struct { // It's worth noting that there is no guarantee to the positions of anything in either left or right, so // considering these values as 'changes' is going to add a considerable amount of noise to results. func (c *ChangeContext) HasChanged() bool { - if c.NewLine != nil && c.OriginalLine != nil && *c.NewLine != *c.OriginalLine { - return true - } - //if c.NewColumn != nil && c.OriginalColumn != nil && *c.NewColumn != *c.OriginalColumn { - // return true - //} - if (c.NewLine == nil && c.OriginalLine != nil) || (c.NewLine != nil && c.OriginalLine == nil) { - return true - } - //if (c.NewColumn == nil && c.OriginalColumn != nil) || (c.NewColumn != nil && c.OriginalColumn == nil) { - // return true - //} - return false + if c.NewLine != nil && c.OriginalLine != nil && *c.NewLine != *c.OriginalLine { + return true + } + //if c.NewColumn != nil && c.OriginalColumn != nil && *c.NewColumn != *c.OriginalColumn { + // return true + //} + if (c.NewLine == nil && c.OriginalLine != nil) || (c.NewLine != nil && c.OriginalLine == nil) { + return true + } + //if (c.NewColumn == nil && c.OriginalColumn != nil) || (c.NewColumn != nil && c.OriginalColumn == nil) { + // return true + //} + return false } // Change represents a change between two different elements inside an OpenAPI specification. type Change struct { - // Context represents the lines and column numbers of the original and new values - // It's worth noting that these values may frequently be different and are not used to calculate - // a change. If the positions change, but values do not, then no change is recorded. - Context *ChangeContext `json:"context,omitempty" yaml:"context,omitempty"` + // Context represents the lines and column numbers of the original and new values + // It's worth noting that these values may frequently be different and are not used to calculate + // a change. If the positions change, but values do not, then no change is recorded. + Context *ChangeContext `json:"context,omitempty" yaml:"context,omitempty"` - // ChangeType represents the type of change that occurred. stored as an integer, defined by constants above. - ChangeType int `json:"change,omitempty" yaml:"change,omitempty"` + // ChangeType represents the type of change that occurred. stored as an integer, defined by constants above. + ChangeType int `json:"change,omitempty" yaml:"change,omitempty"` - // Property is the property name key being changed. - Property string `json:"property,omitempty" yaml:"property,omitempty"` + // Property is the property name key being changed. + Property string `json:"property,omitempty" yaml:"property,omitempty"` - // Original is the original value represented as a string. - Original string `json:"original,omitempty" yaml:"original,omitempty"` + // Original is the original value represented as a string. + Original string `json:"original,omitempty" yaml:"original,omitempty"` - // New is the new value represented as a string. - New string `json:"new,omitempty" yaml:"new,omitempty"` + // New is the new value represented as a string. + New string `json:"new,omitempty" yaml:"new,omitempty"` - // Breaking determines if the change is a breaking one or not. - Breaking bool `json:"breaking" yaml:"breaking"` + // Breaking determines if the change is a breaking one or not. + Breaking bool `json:"breaking" yaml:"breaking"` - // OriginalObject represents the original object that was changed. - OriginalObject any `json:"-" yaml:"-"` + // OriginalObject represents the original object that was changed. + OriginalObject any `json:"-" yaml:"-"` - // NewObject represents the new object that has been modified. - NewObject any `json:"-" yaml:"-"` + // NewObject represents the new object that has been modified. + NewObject any `json:"-" yaml:"-"` } // PropertyChanges holds a slice of Change pointers type PropertyChanges struct { - //Total *int `json:"total,omitempty" yaml:"total,omitempty"` - //Breaking *int `json:"breaking,omitempty" yaml:"breaking,omitempty"` - Changes []*Change `json:"changes,omitempty" yaml:"changes,omitempty"` + //Total *int `json:"total,omitempty" yaml:"total,omitempty"` + //Breaking *int `json:"breaking,omitempty" yaml:"breaking,omitempty"` + Changes []*Change `json:"changes,omitempty" yaml:"changes,omitempty"` } // TotalChanges returns the total number of property changes made. func (p PropertyChanges) TotalChanges() int { - return len(p.Changes) + return len(p.Changes) } // TotalBreakingChanges returns the total number of property breaking changes made. func (p PropertyChanges) TotalBreakingChanges() int { - return CountBreakingChanges(p.Changes) + return CountBreakingChanges(p.Changes) } func NewPropertyChanges(changes []*Change) *PropertyChanges { - return &PropertyChanges{Changes: changes} + return &PropertyChanges{Changes: changes} } // PropertyCheck is used by functions to check the state of left and right values. type PropertyCheck struct { - // Original is the property we're checking on the left - Original any + // Original is the property we're checking on the left + Original any - // New is s the property we're checking on the right - New any + // New is s the property we're checking on the right + New any - // Label is the identifier we're looking for on the left and right hand sides - Label string + // Label is the identifier we're looking for on the left and right hand sides + Label string - // LeftNode is the yaml.Node pointer that holds the original node structure of the value - LeftNode *yaml.Node + // LeftNode is the yaml.Node pointer that holds the original node structure of the value + LeftNode *yaml.Node - // RightNode is the yaml.Node pointer that holds the new node structure of the value - RightNode *yaml.Node + // RightNode is the yaml.Node pointer that holds the new node structure of the value + RightNode *yaml.Node - // Breaking determines if the check is a breaking change (modifications or removals etc.) - Breaking bool + // Breaking determines if the check is a breaking change (modifications or removals etc.) + Breaking bool - // Changes represents a pointer to the slice to contain all changes found. - Changes *[]*Change + // Changes represents a pointer to the slice to contain all changes found. + Changes *[]*Change } diff --git a/what-changed/model/contact.go b/what-changed/model/contact.go index 317b83d..641ea44 100644 --- a/what-changed/model/contact.go +++ b/what-changed/model/contact.go @@ -4,28 +4,28 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low/base" - "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/datamodel/low/v3" ) // ContactChanges Represent changes to a Contact object that is a child of Info, part of an OpenAPI document. type ContactChanges struct { - *PropertyChanges + *PropertyChanges } // GetAllChanges returns a slice of all changes made between Callback objects func (c *ContactChanges) GetAllChanges() []*Change { - return c.Changes + return c.Changes } // TotalChanges represents the total number of changes that have occurred to a Contact object func (c *ContactChanges) TotalChanges() int { - return c.PropertyChanges.TotalChanges() + return c.PropertyChanges.TotalChanges() } // TotalBreakingChanges always returns 0 for Contact objects, they are non-binding. func (c *ContactChanges) TotalBreakingChanges() int { - return 0 + return 0 } // CompareContact will check a left (original) and right (new) Contact object for any changes. If there @@ -33,49 +33,49 @@ func (c *ContactChanges) TotalBreakingChanges() int { // returns nil. func CompareContact(l, r *base.Contact) *ContactChanges { - var changes []*Change - var props []*PropertyCheck + var changes []*Change + var props []*PropertyCheck - // check URL - props = append(props, &PropertyCheck{ - LeftNode: l.URL.ValueNode, - RightNode: r.URL.ValueNode, - Label: v3.URLLabel, - Changes: &changes, - Breaking: false, - Original: l, - New: r, - }) + // check URL + props = append(props, &PropertyCheck{ + LeftNode: l.URL.ValueNode, + RightNode: r.URL.ValueNode, + Label: v3.URLLabel, + Changes: &changes, + Breaking: false, + Original: l, + New: r, + }) - // check name - props = append(props, &PropertyCheck{ - LeftNode: l.Name.ValueNode, - RightNode: r.Name.ValueNode, - Label: v3.NameLabel, - Changes: &changes, - Breaking: false, - Original: l, - New: r, - }) + // check name + props = append(props, &PropertyCheck{ + LeftNode: l.Name.ValueNode, + RightNode: r.Name.ValueNode, + Label: v3.NameLabel, + Changes: &changes, + Breaking: false, + Original: l, + New: r, + }) - // check email - props = append(props, &PropertyCheck{ - LeftNode: l.Email.ValueNode, - RightNode: r.Email.ValueNode, - Label: v3.EmailLabel, - Changes: &changes, - Breaking: false, - Original: l, - New: r, - }) + // check email + props = append(props, &PropertyCheck{ + LeftNode: l.Email.ValueNode, + RightNode: r.Email.ValueNode, + Label: v3.EmailLabel, + Changes: &changes, + Breaking: false, + Original: l, + New: r, + }) - // check everything. - CheckProperties(props) + // check everything. + CheckProperties(props) - dc := new(ContactChanges) - dc.PropertyChanges = NewPropertyChanges(changes) - if dc.TotalChanges() <= 0 { - return nil - } - return dc + dc := new(ContactChanges) + dc.PropertyChanges = NewPropertyChanges(changes) + if dc.TotalChanges() <= 0 { + return nil + } + return dc } diff --git a/what-changed/model/discriminator.go b/what-changed/model/discriminator.go index 3351490..d267541 100644 --- a/what-changed/model/discriminator.go +++ b/what-changed/model/discriminator.go @@ -4,93 +4,93 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low/base" - v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/datamodel/low/base" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" ) // DiscriminatorChanges represents changes made to a Discriminator OpenAPI object type DiscriminatorChanges struct { - *PropertyChanges - MappingChanges []*Change `json:"mappings,omitempty" yaml:"mappings,omitempty"` + *PropertyChanges + MappingChanges []*Change `json:"mappings,omitempty" yaml:"mappings,omitempty"` } // TotalChanges returns a count of everything changed within the Discriminator object func (d *DiscriminatorChanges) TotalChanges() int { - l := 0 - if k := d.PropertyChanges.TotalChanges(); k > 0 { - l += k - } - if k := len(d.MappingChanges); k > 0 { - l += k - } - return l + l := 0 + if k := d.PropertyChanges.TotalChanges(); k > 0 { + l += k + } + if k := len(d.MappingChanges); k > 0 { + l += k + } + return l } // GetAllChanges returns a slice of all changes made between Callback objects func (c *DiscriminatorChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, c.Changes...) - if c.MappingChanges != nil { - changes = append(changes, c.MappingChanges...) - } - return changes + var changes []*Change + changes = append(changes, c.Changes...) + if c.MappingChanges != nil { + changes = append(changes, c.MappingChanges...) + } + return changes } // TotalBreakingChanges returns the number of breaking changes made by the Discriminator func (d *DiscriminatorChanges) TotalBreakingChanges() int { - return d.PropertyChanges.TotalBreakingChanges() + CountBreakingChanges(d.MappingChanges) + return d.PropertyChanges.TotalBreakingChanges() + CountBreakingChanges(d.MappingChanges) } // CompareDiscriminator will check a left (original) and right (new) Discriminator object for changes // and will return a pointer to DiscriminatorChanges func CompareDiscriminator(l, r *base.Discriminator) *DiscriminatorChanges { - dc := new(DiscriminatorChanges) - var changes []*Change - var props []*PropertyCheck - var mappingChanges []*Change + dc := new(DiscriminatorChanges) + var changes []*Change + var props []*PropertyCheck + var mappingChanges []*Change - // Name (breaking change) - props = append(props, &PropertyCheck{ - LeftNode: l.PropertyName.ValueNode, - RightNode: r.PropertyName.ValueNode, - Label: v3.PropertyNameLabel, - Changes: &changes, - Breaking: true, - Original: l, - New: r, - }) + // Name (breaking change) + props = append(props, &PropertyCheck{ + LeftNode: l.PropertyName.ValueNode, + RightNode: r.PropertyName.ValueNode, + Label: v3.PropertyNameLabel, + Changes: &changes, + Breaking: true, + Original: l, + New: r, + }) - // check properties - CheckProperties(props) + // check properties + CheckProperties(props) - // flatten maps - lMap := FlattenLowLevelMap[string](l.Mapping.Value) - rMap := FlattenLowLevelMap[string](r.Mapping.Value) + // flatten maps + lMap := FlattenLowLevelMap[string](l.Mapping.Value) + rMap := FlattenLowLevelMap[string](r.Mapping.Value) - // check for removals, modifications and moves - for i := range lMap { - CheckForObjectAdditionOrRemoval[string](lMap, rMap, i, &mappingChanges, false, true) - // if the existing tag exists, let's check it. - if rMap[i] != nil { - if lMap[i].Value != rMap[i].Value { - CreateChange(&mappingChanges, Modified, i, lMap[i].GetValueNode(), - rMap[i].GetValueNode(), true, lMap[i].GetValue(), rMap[i].GetValue()) - } - } - } + // check for removals, modifications and moves + for i := range lMap { + CheckForObjectAdditionOrRemoval[string](lMap, rMap, i, &mappingChanges, false, true) + // if the existing tag exists, let's check it. + if rMap[i] != nil { + if lMap[i].Value != rMap[i].Value { + CreateChange(&mappingChanges, Modified, i, lMap[i].GetValueNode(), + rMap[i].GetValueNode(), true, lMap[i].GetValue(), rMap[i].GetValue()) + } + } + } - for i := range rMap { - if lMap[i] == nil { - CreateChange(&mappingChanges, ObjectAdded, i, nil, - rMap[i].GetValueNode(), false, nil, rMap[i].GetValue()) - } - } + for i := range rMap { + if lMap[i] == nil { + CreateChange(&mappingChanges, ObjectAdded, i, nil, + rMap[i].GetValueNode(), false, nil, rMap[i].GetValue()) + } + } - dc.PropertyChanges = NewPropertyChanges(changes) - dc.MappingChanges = mappingChanges - if dc.TotalChanges() <= 0 { - return nil - } - return dc + dc.PropertyChanges = NewPropertyChanges(changes) + dc.MappingChanges = mappingChanges + if dc.TotalChanges() <= 0 { + return nil + } + return dc } diff --git a/what-changed/model/encoding.go b/what-changed/model/encoding.go index f467305..78650eb 100644 --- a/what-changed/model/encoding.go +++ b/what-changed/model/encoding.go @@ -4,96 +4,96 @@ package model import ( - v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" ) // EncodingChanges represent all the changes made to an Encoding object type EncodingChanges struct { - *PropertyChanges - HeaderChanges map[string]*HeaderChanges `json:"headers,omitempty" yaml:"headers,omitempty"` + *PropertyChanges + HeaderChanges map[string]*HeaderChanges `json:"headers,omitempty" yaml:"headers,omitempty"` } // GetAllChanges returns a slice of all changes made between Encoding objects func (e *EncodingChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, e.Changes...) - for k := range e.HeaderChanges { - changes = append(changes, e.HeaderChanges[k].GetAllChanges()...) - } - return changes + var changes []*Change + changes = append(changes, e.Changes...) + for k := range e.HeaderChanges { + changes = append(changes, e.HeaderChanges[k].GetAllChanges()...) + } + return changes } // TotalChanges returns the total number of changes made between two Encoding objects func (e *EncodingChanges) TotalChanges() int { - c := e.PropertyChanges.TotalChanges() - if e.HeaderChanges != nil { - for i := range e.HeaderChanges { - c += e.HeaderChanges[i].TotalChanges() - } - } - return c + c := e.PropertyChanges.TotalChanges() + if e.HeaderChanges != nil { + for i := range e.HeaderChanges { + c += e.HeaderChanges[i].TotalChanges() + } + } + return c } // TotalBreakingChanges returns the number of changes made between two Encoding objects that were breaking. func (e *EncodingChanges) TotalBreakingChanges() int { - c := e.PropertyChanges.TotalBreakingChanges() - if e.HeaderChanges != nil { - for i := range e.HeaderChanges { - c += e.HeaderChanges[i].TotalBreakingChanges() - } - } - return c + c := e.PropertyChanges.TotalBreakingChanges() + if e.HeaderChanges != nil { + for i := range e.HeaderChanges { + c += e.HeaderChanges[i].TotalBreakingChanges() + } + } + return c } // CompareEncoding returns a pointer to *EncodingChanges that contain all changes made between a left and right // set of Encoding objects. func CompareEncoding(l, r *v3.Encoding) *EncodingChanges { - var changes []*Change - var props []*PropertyCheck + var changes []*Change + var props []*PropertyCheck - // ContentType - props = append(props, &PropertyCheck{ - LeftNode: l.ContentType.ValueNode, - RightNode: r.ContentType.ValueNode, - Label: v3.ContentTypeLabel, - Changes: &changes, - Breaking: true, - Original: l, - New: r, - }) + // ContentType + props = append(props, &PropertyCheck{ + LeftNode: l.ContentType.ValueNode, + RightNode: r.ContentType.ValueNode, + Label: v3.ContentTypeLabel, + Changes: &changes, + Breaking: true, + Original: l, + New: r, + }) - // Explode - props = append(props, &PropertyCheck{ - LeftNode: l.Explode.ValueNode, - RightNode: r.Explode.ValueNode, - Label: v3.ExplodeLabel, - Changes: &changes, - Breaking: true, - Original: l, - New: r, - }) + // Explode + props = append(props, &PropertyCheck{ + LeftNode: l.Explode.ValueNode, + RightNode: r.Explode.ValueNode, + Label: v3.ExplodeLabel, + Changes: &changes, + Breaking: true, + Original: l, + New: r, + }) - // AllowReserved - props = append(props, &PropertyCheck{ - LeftNode: l.AllowReserved.ValueNode, - RightNode: r.AllowReserved.ValueNode, - Label: v3.AllowReservedLabel, - Changes: &changes, - Breaking: false, - Original: l, - New: r, - }) + // AllowReserved + props = append(props, &PropertyCheck{ + LeftNode: l.AllowReserved.ValueNode, + RightNode: r.AllowReserved.ValueNode, + Label: v3.AllowReservedLabel, + Changes: &changes, + Breaking: false, + Original: l, + New: r, + }) - // check everything. - CheckProperties(props) - ec := new(EncodingChanges) + // check everything. + CheckProperties(props) + ec := new(EncodingChanges) - // headers - ec.HeaderChanges = CheckMapForChanges(l.Headers.Value, r.Headers.Value, &changes, v3.HeadersLabel, CompareHeadersV3) - ec.PropertyChanges = NewPropertyChanges(changes) - if ec.TotalChanges() <= 0 { - return nil - } - return ec + // headers + ec.HeaderChanges = CheckMapForChanges(l.Headers.Value, r.Headers.Value, &changes, v3.HeadersLabel, CompareHeadersV3) + ec.PropertyChanges = NewPropertyChanges(changes) + if ec.TotalChanges() <= 0 { + return nil + } + return ec } diff --git a/what-changed/model/examples.go b/what-changed/model/examples.go index ecd6f94..35539e4 100644 --- a/what-changed/model/examples.go +++ b/what-changed/model/examples.go @@ -4,83 +4,83 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - v2 "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/datamodel/low" + v2 "github.com/pb33f/libopenapi/datamodel/low/v2" ) // ExamplesChanges represents changes made between Swagger Examples objects (Not OpenAPI 3). type ExamplesChanges struct { - *PropertyChanges + *PropertyChanges } // GetAllChanges returns a slice of all changes made between Examples objects func (a *ExamplesChanges) GetAllChanges() []*Change { - return a.Changes + return a.Changes } // TotalChanges represents the total number of changes made between Example instances. func (a *ExamplesChanges) TotalChanges() int { - return a.PropertyChanges.TotalChanges() + return a.PropertyChanges.TotalChanges() } // TotalBreakingChanges will always return 0. Examples cannot break a contract. func (a *ExamplesChanges) TotalBreakingChanges() int { - return 0 // not supported. + return 0 // not supported. } // CompareExamplesV2 compares two Swagger Examples objects, returning a pointer to -//ExamplesChanges if anything was found. +// ExamplesChanges if anything was found. func CompareExamplesV2(l, r *v2.Examples) *ExamplesChanges { - lHashes := make(map[string]string) - rHashes := make(map[string]string) - lValues := make(map[string]low.ValueReference[any]) - rValues := make(map[string]low.ValueReference[any]) + lHashes := make(map[string]string) + rHashes := make(map[string]string) + lValues := make(map[string]low.ValueReference[any]) + rValues := make(map[string]low.ValueReference[any]) - for k := range l.Values { - lHashes[k.Value] = low.GenerateHashString(l.Values[k].Value) - lValues[k.Value] = l.Values[k] - } + for k := range l.Values { + lHashes[k.Value] = low.GenerateHashString(l.Values[k].Value) + lValues[k.Value] = l.Values[k] + } - for k := range r.Values { - rHashes[k.Value] = low.GenerateHashString(r.Values[k].Value) - rValues[k.Value] = r.Values[k] - } - var changes []*Change + for k := range r.Values { + rHashes[k.Value] = low.GenerateHashString(r.Values[k].Value) + rValues[k.Value] = r.Values[k] + } + var changes []*Change - // check left example hashes - for k := range lHashes { - rhash := rHashes[k] - if rhash == "" { - CreateChange(&changes, ObjectRemoved, k, - lValues[k].GetValueNode(), nil, false, - lValues[k].GetValue(), nil) - continue - } - if lHashes[k] == rHashes[k] { - continue - } - CreateChange(&changes, Modified, k, - lValues[k].GetValueNode(), rValues[k].GetValueNode(), false, - lValues[k].GetValue(), lValues[k].GetValue()) + // check left example hashes + for k := range lHashes { + rhash := rHashes[k] + if rhash == "" { + CreateChange(&changes, ObjectRemoved, k, + lValues[k].GetValueNode(), nil, false, + lValues[k].GetValue(), nil) + continue + } + if lHashes[k] == rHashes[k] { + continue + } + CreateChange(&changes, Modified, k, + lValues[k].GetValueNode(), rValues[k].GetValueNode(), false, + lValues[k].GetValue(), lValues[k].GetValue()) - } + } - //check right example hashes - for k := range rHashes { - lhash := lHashes[k] - if lhash == "" { - CreateChange(&changes, ObjectAdded, k, - nil, lValues[k].GetValueNode(), false, - nil, lValues[k].GetValue()) - continue - } - } + //check right example hashes + for k := range rHashes { + lhash := lHashes[k] + if lhash == "" { + CreateChange(&changes, ObjectAdded, k, + nil, lValues[k].GetValueNode(), false, + nil, lValues[k].GetValue()) + continue + } + } - ex := new(ExamplesChanges) - ex.PropertyChanges = NewPropertyChanges(changes) - if ex.TotalChanges() <= 0 { - return nil - } - return ex + ex := new(ExamplesChanges) + ex.PropertyChanges = NewPropertyChanges(changes) + if ex.TotalChanges() <= 0 { + return nil + } + return ex } diff --git a/what-changed/model/examples_test.go b/what-changed/model/examples_test.go index ba81643..18a8e67 100644 --- a/what-changed/model/examples_test.go +++ b/what-changed/model/examples_test.go @@ -4,108 +4,108 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/v2" - "github.com/pb33f/libopenapi/datamodel/low/v3" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "testing" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "testing" ) func TestCompareExamplesV2(t *testing.T) { - left := `summary: magic herbs` - right := `summary: cure all` + left := `summary: magic herbs` + right := `summary: cure all` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v2.Examples - var rDoc v2.Examples - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v2.Examples + var rDoc v2.Examples + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - extChanges := CompareExamplesV2(&lDoc, &rDoc) - assert.Equal(t, extChanges.TotalChanges(), 1) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, 0, extChanges.TotalBreakingChanges()) - assert.Equal(t, Modified, extChanges.Changes[0].ChangeType) - assert.Equal(t, v3.SummaryLabel, extChanges.Changes[0].Property) - assert.Equal(t, "magic herbs", extChanges.Changes[0].Original) - assert.Equal(t, "cure all", extChanges.Changes[0].New) + extChanges := CompareExamplesV2(&lDoc, &rDoc) + assert.Equal(t, extChanges.TotalChanges(), 1) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, 0, extChanges.TotalBreakingChanges()) + assert.Equal(t, Modified, extChanges.Changes[0].ChangeType) + assert.Equal(t, v3.SummaryLabel, extChanges.Changes[0].Property) + assert.Equal(t, "magic herbs", extChanges.Changes[0].Original) + assert.Equal(t, "cure all", extChanges.Changes[0].New) } func TestCompareExamplesV2_Add(t *testing.T) { - left := `summary: magic herbs` - right := `summary: magic herbs + left := `summary: magic herbs` + right := `summary: magic herbs yummy: coffee` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v2.Examples - var rDoc v2.Examples - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v2.Examples + var rDoc v2.Examples + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - extChanges := CompareExamplesV2(&lDoc, &rDoc) - assert.Equal(t, extChanges.TotalChanges(), 1) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, 0, extChanges.TotalBreakingChanges()) - assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType) + extChanges := CompareExamplesV2(&lDoc, &rDoc) + assert.Equal(t, extChanges.TotalChanges(), 1) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, 0, extChanges.TotalBreakingChanges()) + assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType) } func TestCompareExamplesV2_Remove(t *testing.T) { - left := `summary: magic herbs` - right := `summary: magic herbs + left := `summary: magic herbs` + right := `summary: magic herbs yummy: coffee` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v2.Examples - var rDoc v2.Examples - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v2.Examples + var rDoc v2.Examples + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - extChanges := CompareExamplesV2(&rDoc, &lDoc) - assert.Equal(t, extChanges.TotalChanges(), 1) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, 0, extChanges.TotalBreakingChanges()) - assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType) + extChanges := CompareExamplesV2(&rDoc, &lDoc) + assert.Equal(t, extChanges.TotalChanges(), 1) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, 0, extChanges.TotalBreakingChanges()) + assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType) } func TestCompareExamplesV2_Identical(t *testing.T) { - left := `summary: magic herbs` - right := left + left := `summary: magic herbs` + right := left - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v2.Examples - var rDoc v2.Examples - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v2.Examples + var rDoc v2.Examples + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - extChanges := CompareExamplesV2(&rDoc, &lDoc) + extChanges := CompareExamplesV2(&rDoc, &lDoc) assert.Nil(t, extChanges) } diff --git a/what-changed/model/extensions.go b/what-changed/model/extensions.go index a01d878..3bb3bc9 100644 --- a/what-changed/model/extensions.go +++ b/what-changed/model/extensions.go @@ -4,28 +4,28 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "strings" + "github.com/pb33f/libopenapi/datamodel/low" + "strings" ) // ExtensionChanges represents any changes to custom extensions defined for an OpenAPI object. type ExtensionChanges struct { - *PropertyChanges + *PropertyChanges } // GetAllChanges returns a slice of all changes made between Extension objects func (e *ExtensionChanges) GetAllChanges() []*Change { - return e.Changes + return e.Changes } // TotalChanges returns the total number of object extensions that were made. func (e *ExtensionChanges) TotalChanges() int { - return e.PropertyChanges.TotalChanges() + return e.PropertyChanges.TotalChanges() } // TotalBreakingChanges always returns 0 for Extension objects, they are non-binding. func (e *ExtensionChanges) TotalBreakingChanges() int { - return 0 + return 0 } // CompareExtensions will compare a left and right map of Tag/ValueReference models for any changes to @@ -36,62 +36,62 @@ func (e *ExtensionChanges) TotalBreakingChanges() int { // there is currently no support for knowing anything changed - so it is ignored. func CompareExtensions(l, r map[low.KeyReference[string]]low.ValueReference[any]) *ExtensionChanges { - // look at the original and then look through the new. - seenLeft := make(map[string]*low.ValueReference[any]) - seenRight := make(map[string]*low.ValueReference[any]) - for i := range l { - h := l[i] - seenLeft[strings.ToLower(i.Value)] = &h - } - for i := range r { - h := r[i] - seenRight[strings.ToLower(i.Value)] = &h - } + // look at the original and then look through the new. + seenLeft := make(map[string]*low.ValueReference[any]) + seenRight := make(map[string]*low.ValueReference[any]) + for i := range l { + h := l[i] + seenLeft[strings.ToLower(i.Value)] = &h + } + for i := range r { + h := r[i] + seenRight[strings.ToLower(i.Value)] = &h + } - var changes []*Change - for i := range seenLeft { + var changes []*Change + for i := range seenLeft { - CheckForObjectAdditionOrRemoval[any](seenLeft, seenRight, i, &changes, false, true) + CheckForObjectAdditionOrRemoval[any](seenLeft, seenRight, i, &changes, false, true) - if seenRight[i] != nil { - var props []*PropertyCheck + if seenRight[i] != nil { + var props []*PropertyCheck - props = append(props, &PropertyCheck{ - LeftNode: seenLeft[i].ValueNode, - RightNode: seenRight[i].ValueNode, - Label: i, - Changes: &changes, - Breaking: false, - Original: seenLeft[i].Value, - New: seenRight[i].Value, - }) + props = append(props, &PropertyCheck{ + LeftNode: seenLeft[i].ValueNode, + RightNode: seenRight[i].ValueNode, + Label: i, + Changes: &changes, + Breaking: false, + Original: seenLeft[i].Value, + New: seenRight[i].Value, + }) - // check properties - CheckProperties(props) - } - } - for i := range seenRight { - if seenLeft[i] == nil { - CheckForObjectAdditionOrRemoval[any](seenLeft, seenRight, i, &changes, false, true) - } - } - ex := new(ExtensionChanges) - ex.PropertyChanges = NewPropertyChanges(changes) - if ex.TotalChanges() <= 0 { - return nil - } - return ex + // check properties + CheckProperties(props) + } + } + for i := range seenRight { + if seenLeft[i] == nil { + CheckForObjectAdditionOrRemoval[any](seenLeft, seenRight, i, &changes, false, true) + } + } + ex := new(ExtensionChanges) + ex.PropertyChanges = NewPropertyChanges(changes) + if ex.TotalChanges() <= 0 { + return nil + } + return ex } // CheckExtensions is a helper method to un-pack a left and right model that contains extensions. Once unpacked // the extensions are compared and returns a pointer to ExtensionChanges. If nothing changed, nil is returned. func CheckExtensions[T low.HasExtensions[T]](l, r T) *ExtensionChanges { - var lExt, rExt map[low.KeyReference[string]]low.ValueReference[any] - if len(l.GetExtensions()) > 0 { - lExt = l.GetExtensions() - } - if len(r.GetExtensions()) > 0 { - rExt = r.GetExtensions() - } - return CompareExtensions(lExt, rExt) + var lExt, rExt map[low.KeyReference[string]]low.ValueReference[any] + if len(l.GetExtensions()) > 0 { + lExt = l.GetExtensions() + } + if len(r.GetExtensions()) > 0 { + rExt = r.GetExtensions() + } + return CompareExtensions(lExt, rExt) } diff --git a/what-changed/model/external_docs.go b/what-changed/model/external_docs.go index 47b1bd8..324c559 100644 --- a/what-changed/model/external_docs.go +++ b/what-changed/model/external_docs.go @@ -4,79 +4,79 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low/base" - "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/datamodel/low/v3" ) // ExternalDocChanges represents changes made to any ExternalDoc object from an OpenAPI document. type ExternalDocChanges struct { - *PropertyChanges - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + *PropertyChanges + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` } // GetAllChanges returns a slice of all changes made between Example objects func (e *ExternalDocChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, e.Changes...) - if e.ExtensionChanges != nil { - changes = append(changes, e.ExtensionChanges.GetAllChanges()...) - } - return changes + var changes []*Change + changes = append(changes, e.Changes...) + if e.ExtensionChanges != nil { + changes = append(changes, e.ExtensionChanges.GetAllChanges()...) + } + return changes } // TotalChanges returns a count of everything that changed func (e *ExternalDocChanges) TotalChanges() int { - c := e.PropertyChanges.TotalChanges() - if e.ExtensionChanges != nil { - c += e.ExtensionChanges.TotalChanges() - } - return c + c := e.PropertyChanges.TotalChanges() + if e.ExtensionChanges != nil { + c += e.ExtensionChanges.TotalChanges() + } + return c } // TotalBreakingChanges always returns 0 for ExternalDoc objects, they are non-binding. func (e *ExternalDocChanges) TotalBreakingChanges() int { - return 0 + return 0 } // CompareExternalDocs will compare a left (original) and a right (new) slice of ValueReference // nodes for any changes between them. If there are changes, then a pointer to ExternalDocChanges // is returned, otherwise if nothing changed - then nil is returned. func CompareExternalDocs(l, r *base.ExternalDoc) *ExternalDocChanges { - var changes []*Change - var props []*PropertyCheck + var changes []*Change + var props []*PropertyCheck - // URL - props = append(props, &PropertyCheck{ - LeftNode: l.URL.ValueNode, - RightNode: r.URL.ValueNode, - Label: v3.URLLabel, - Changes: &changes, - Breaking: false, - Original: l, - New: r, - }) + // URL + props = append(props, &PropertyCheck{ + LeftNode: l.URL.ValueNode, + RightNode: r.URL.ValueNode, + Label: v3.URLLabel, + Changes: &changes, + Breaking: false, + Original: l, + New: r, + }) - // description. - props = append(props, &PropertyCheck{ - LeftNode: l.Description.ValueNode, - RightNode: r.Description.ValueNode, - Label: v3.DescriptionLabel, - Changes: &changes, - Breaking: false, - Original: l, - New: r, - }) + // description. + props = append(props, &PropertyCheck{ + LeftNode: l.Description.ValueNode, + RightNode: r.Description.ValueNode, + Label: v3.DescriptionLabel, + Changes: &changes, + Breaking: false, + Original: l, + New: r, + }) - // check everything. - CheckProperties(props) + // check everything. + CheckProperties(props) - dc := new(ExternalDocChanges) - dc.PropertyChanges = NewPropertyChanges(changes) + dc := new(ExternalDocChanges) + dc.PropertyChanges = NewPropertyChanges(changes) - // check extensions - dc.ExtensionChanges = CheckExtensions(l, r) - if dc.TotalChanges() <= 0 { - return nil - } - return dc + // check extensions + dc.ExtensionChanges = CheckExtensions(l, r) + if dc.TotalChanges() <= 0 { + return nil + } + return dc } diff --git a/what-changed/model/header.go b/what-changed/model/header.go index 10a9592..c2a63d2 100644 --- a/what-changed/model/header.go +++ b/what-changed/model/header.go @@ -4,277 +4,277 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - v2 "github.com/pb33f/libopenapi/datamodel/low/v2" - "github.com/pb33f/libopenapi/datamodel/low/v3" - "reflect" + "github.com/pb33f/libopenapi/datamodel/low" + v2 "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/datamodel/low/v3" + "reflect" ) // HeaderChanges represents changes made between two Header objects. Supports both Swagger and OpenAPI header // objects, V2 only property Items is broken out into its own. type HeaderChanges struct { - *PropertyChanges - SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"` - ExamplesChanges map[string]*ExampleChanges `json:"examples,omitempty" yaml:"examples,omitempty"` - ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"` - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + *PropertyChanges + SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"` + ExamplesChanges map[string]*ExampleChanges `json:"examples,omitempty" yaml:"examples,omitempty"` + ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` - // Items only supported by Swagger (V2) - ItemsChanges *ItemsChanges `json:"items,omitempty" yaml:"items,omitempty"` + // Items only supported by Swagger (V2) + ItemsChanges *ItemsChanges `json:"items,omitempty" yaml:"items,omitempty"` } // GetAllChanges returns a slice of all changes made between Header objects func (h *HeaderChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, h.Changes...) - for k := range h.ExamplesChanges { - changes = append(changes, h.ExamplesChanges[k].GetAllChanges()...) - } - for k := range h.ContentChanges { - changes = append(changes, h.ContentChanges[k].GetAllChanges()...) - } - if h.ExtensionChanges != nil { - changes = append(changes, h.ExtensionChanges.GetAllChanges()...) - } - if h.SchemaChanges != nil { - changes = append(changes, h.SchemaChanges.GetAllChanges()...) - } - if h.ItemsChanges != nil { - changes = append(changes, h.ItemsChanges.GetAllChanges()...) - } - return changes + var changes []*Change + changes = append(changes, h.Changes...) + for k := range h.ExamplesChanges { + changes = append(changes, h.ExamplesChanges[k].GetAllChanges()...) + } + for k := range h.ContentChanges { + changes = append(changes, h.ContentChanges[k].GetAllChanges()...) + } + if h.ExtensionChanges != nil { + changes = append(changes, h.ExtensionChanges.GetAllChanges()...) + } + if h.SchemaChanges != nil { + changes = append(changes, h.SchemaChanges.GetAllChanges()...) + } + if h.ItemsChanges != nil { + changes = append(changes, h.ItemsChanges.GetAllChanges()...) + } + return changes } // TotalChanges returns the total number of changes made between two Header objects. func (h *HeaderChanges) TotalChanges() int { - c := h.PropertyChanges.TotalChanges() - for k := range h.ExamplesChanges { - c += h.ExamplesChanges[k].TotalChanges() - } - for k := range h.ContentChanges { - c += h.ContentChanges[k].TotalChanges() - } - if h.ExtensionChanges != nil { - c += h.ExtensionChanges.TotalChanges() - } - if h.SchemaChanges != nil { - c += h.SchemaChanges.TotalChanges() - } - if h.ItemsChanges != nil { - c += h.ItemsChanges.TotalChanges() - } - return c + c := h.PropertyChanges.TotalChanges() + for k := range h.ExamplesChanges { + c += h.ExamplesChanges[k].TotalChanges() + } + for k := range h.ContentChanges { + c += h.ContentChanges[k].TotalChanges() + } + if h.ExtensionChanges != nil { + c += h.ExtensionChanges.TotalChanges() + } + if h.SchemaChanges != nil { + c += h.SchemaChanges.TotalChanges() + } + if h.ItemsChanges != nil { + c += h.ItemsChanges.TotalChanges() + } + return c } // TotalBreakingChanges returns the total number of breaking changes made between two Header instances. func (h *HeaderChanges) TotalBreakingChanges() int { - c := h.PropertyChanges.TotalBreakingChanges() - for k := range h.ContentChanges { - c += h.ContentChanges[k].TotalBreakingChanges() - } - if h.ItemsChanges != nil { - c += h.ItemsChanges.TotalBreakingChanges() - } - if h.SchemaChanges != nil { - c += h.SchemaChanges.TotalBreakingChanges() - } - return c + c := h.PropertyChanges.TotalBreakingChanges() + for k := range h.ContentChanges { + c += h.ContentChanges[k].TotalBreakingChanges() + } + if h.ItemsChanges != nil { + c += h.ItemsChanges.TotalBreakingChanges() + } + if h.SchemaChanges != nil { + c += h.SchemaChanges.TotalBreakingChanges() + } + return c } // shared header properties func addOpenAPIHeaderProperties(left, right low.OpenAPIHeader, changes *[]*Change) []*PropertyCheck { - var props []*PropertyCheck + var props []*PropertyCheck - // style - addPropertyCheck(&props, left.GetStyle().ValueNode, right.GetStyle().ValueNode, - left.GetStyle(), right.GetStyle(), changes, v3.StyleLabel, false) + // style + addPropertyCheck(&props, left.GetStyle().ValueNode, right.GetStyle().ValueNode, + left.GetStyle(), right.GetStyle(), changes, v3.StyleLabel, false) - // allow reserved - addPropertyCheck(&props, left.GetAllowReserved().ValueNode, right.GetAllowReserved().ValueNode, - left.GetAllowReserved(), right.GetAllowReserved(), changes, v3.AllowReservedLabel, false) + // allow reserved + addPropertyCheck(&props, left.GetAllowReserved().ValueNode, right.GetAllowReserved().ValueNode, + left.GetAllowReserved(), right.GetAllowReserved(), changes, v3.AllowReservedLabel, false) - // allow empty value - addPropertyCheck(&props, left.GetAllowEmptyValue().ValueNode, right.GetAllowEmptyValue().ValueNode, - left.GetAllowEmptyValue(), right.GetAllowEmptyValue(), changes, v3.AllowEmptyValueLabel, true) + // allow empty value + addPropertyCheck(&props, left.GetAllowEmptyValue().ValueNode, right.GetAllowEmptyValue().ValueNode, + left.GetAllowEmptyValue(), right.GetAllowEmptyValue(), changes, v3.AllowEmptyValueLabel, true) - // explode - addPropertyCheck(&props, left.GetExplode().ValueNode, right.GetExplode().ValueNode, - left.GetExplode(), right.GetExplode(), changes, v3.ExplodeLabel, false) + // explode + addPropertyCheck(&props, left.GetExplode().ValueNode, right.GetExplode().ValueNode, + left.GetExplode(), right.GetExplode(), changes, v3.ExplodeLabel, false) - // example - addPropertyCheck(&props, left.GetExample().ValueNode, right.GetExample().ValueNode, - left.GetExample(), right.GetExample(), changes, v3.ExampleLabel, false) + // example + addPropertyCheck(&props, left.GetExample().ValueNode, right.GetExample().ValueNode, + left.GetExample(), right.GetExample(), changes, v3.ExampleLabel, false) - // deprecated - addPropertyCheck(&props, left.GetDeprecated().ValueNode, right.GetDeprecated().ValueNode, - left.GetDeprecated(), right.GetDeprecated(), changes, v3.DeprecatedLabel, false) + // deprecated + addPropertyCheck(&props, left.GetDeprecated().ValueNode, right.GetDeprecated().ValueNode, + left.GetDeprecated(), right.GetDeprecated(), changes, v3.DeprecatedLabel, false) - // required - addPropertyCheck(&props, left.GetRequired().ValueNode, right.GetRequired().ValueNode, - left.GetRequired(), right.GetRequired(), changes, v3.RequiredLabel, true) + // required + addPropertyCheck(&props, left.GetRequired().ValueNode, right.GetRequired().ValueNode, + left.GetRequired(), right.GetRequired(), changes, v3.RequiredLabel, true) - return props + return props } // swagger only properties func addSwaggerHeaderProperties(left, right low.SwaggerHeader, changes *[]*Change) []*PropertyCheck { - var props []*PropertyCheck + var props []*PropertyCheck - // type - addPropertyCheck(&props, left.GetType().ValueNode, right.GetType().ValueNode, - left.GetType(), right.GetType(), changes, v3.TypeLabel, true) + // type + addPropertyCheck(&props, left.GetType().ValueNode, right.GetType().ValueNode, + left.GetType(), right.GetType(), changes, v3.TypeLabel, true) - // format - addPropertyCheck(&props, left.GetFormat().ValueNode, right.GetFormat().ValueNode, - left.GetFormat(), right.GetFormat(), changes, v3.FormatLabel, true) + // format + addPropertyCheck(&props, left.GetFormat().ValueNode, right.GetFormat().ValueNode, + left.GetFormat(), right.GetFormat(), changes, v3.FormatLabel, true) - // collection format - addPropertyCheck(&props, left.GetCollectionFormat().ValueNode, right.GetCollectionFormat().ValueNode, - left.GetCollectionFormat(), right.GetCollectionFormat(), changes, v3.CollectionFormatLabel, true) + // collection format + addPropertyCheck(&props, left.GetCollectionFormat().ValueNode, right.GetCollectionFormat().ValueNode, + left.GetCollectionFormat(), right.GetCollectionFormat(), changes, v3.CollectionFormatLabel, true) - // maximum - addPropertyCheck(&props, left.GetMaximum().ValueNode, right.GetMaximum().ValueNode, - left.GetMaximum(), right.GetMaximum(), changes, v3.MaximumLabel, true) + // maximum + addPropertyCheck(&props, left.GetMaximum().ValueNode, right.GetMaximum().ValueNode, + left.GetMaximum(), right.GetMaximum(), changes, v3.MaximumLabel, true) - // minimum - addPropertyCheck(&props, left.GetMinimum().ValueNode, right.GetMinimum().ValueNode, - left.GetMinimum(), right.GetMinimum(), changes, v3.MinimumLabel, true) + // minimum + addPropertyCheck(&props, left.GetMinimum().ValueNode, right.GetMinimum().ValueNode, + left.GetMinimum(), right.GetMinimum(), changes, v3.MinimumLabel, true) - // exclusive maximum - addPropertyCheck(&props, left.GetExclusiveMaximum().ValueNode, right.GetExclusiveMaximum().ValueNode, - left.GetExclusiveMaximum(), right.GetExclusiveMaximum(), changes, v3.ExclusiveMaximumLabel, true) + // exclusive maximum + addPropertyCheck(&props, left.GetExclusiveMaximum().ValueNode, right.GetExclusiveMaximum().ValueNode, + left.GetExclusiveMaximum(), right.GetExclusiveMaximum(), changes, v3.ExclusiveMaximumLabel, true) - // exclusive minimum - addPropertyCheck(&props, left.GetExclusiveMinimum().ValueNode, right.GetExclusiveMinimum().ValueNode, - left.GetExclusiveMinimum(), right.GetExclusiveMinimum(), changes, v3.ExclusiveMinimumLabel, true) + // exclusive minimum + addPropertyCheck(&props, left.GetExclusiveMinimum().ValueNode, right.GetExclusiveMinimum().ValueNode, + left.GetExclusiveMinimum(), right.GetExclusiveMinimum(), changes, v3.ExclusiveMinimumLabel, true) - // max length - addPropertyCheck(&props, left.GetMaxLength().ValueNode, right.GetMaxLength().ValueNode, - left.GetMaxLength(), right.GetMaxLength(), changes, v3.MaxLengthLabel, true) + // max length + addPropertyCheck(&props, left.GetMaxLength().ValueNode, right.GetMaxLength().ValueNode, + left.GetMaxLength(), right.GetMaxLength(), changes, v3.MaxLengthLabel, true) - // min length - addPropertyCheck(&props, left.GetMinLength().ValueNode, right.GetMinLength().ValueNode, - left.GetMinLength(), right.GetMinLength(), changes, v3.MinLengthLabel, true) + // min length + addPropertyCheck(&props, left.GetMinLength().ValueNode, right.GetMinLength().ValueNode, + left.GetMinLength(), right.GetMinLength(), changes, v3.MinLengthLabel, true) - // pattern - addPropertyCheck(&props, left.GetPattern().ValueNode, right.GetPattern().ValueNode, - left.GetPattern(), right.GetPattern(), changes, v3.PatternLabel, true) + // pattern + addPropertyCheck(&props, left.GetPattern().ValueNode, right.GetPattern().ValueNode, + left.GetPattern(), right.GetPattern(), changes, v3.PatternLabel, true) - // max items - addPropertyCheck(&props, left.GetMaxItems().ValueNode, right.GetMaxItems().ValueNode, - left.GetMaxItems(), right.GetMaxItems(), changes, v3.MaxItemsLabel, true) + // max items + addPropertyCheck(&props, left.GetMaxItems().ValueNode, right.GetMaxItems().ValueNode, + left.GetMaxItems(), right.GetMaxItems(), changes, v3.MaxItemsLabel, true) - // min items - addPropertyCheck(&props, left.GetMinItems().ValueNode, right.GetMinItems().ValueNode, - left.GetMinItems(), right.GetMinItems(), changes, v3.MinItemsLabel, true) + // min items + addPropertyCheck(&props, left.GetMinItems().ValueNode, right.GetMinItems().ValueNode, + left.GetMinItems(), right.GetMinItems(), changes, v3.MinItemsLabel, true) - // unique items - addPropertyCheck(&props, left.GetUniqueItems().ValueNode, right.GetUniqueItems().ValueNode, - left.GetUniqueItems(), right.GetUniqueItems(), changes, v3.UniqueItemsLabel, true) + // unique items + addPropertyCheck(&props, left.GetUniqueItems().ValueNode, right.GetUniqueItems().ValueNode, + left.GetUniqueItems(), right.GetUniqueItems(), changes, v3.UniqueItemsLabel, true) - // multiple of - addPropertyCheck(&props, left.GetMultipleOf().ValueNode, right.GetMultipleOf().ValueNode, - left.GetMultipleOf(), right.GetMultipleOf(), changes, v3.MultipleOfLabel, true) + // multiple of + addPropertyCheck(&props, left.GetMultipleOf().ValueNode, right.GetMultipleOf().ValueNode, + left.GetMultipleOf(), right.GetMultipleOf(), changes, v3.MultipleOfLabel, true) - return props + return props } // common header properties func addCommonHeaderProperties(left, right low.HasDescription, changes *[]*Change) []*PropertyCheck { - var props []*PropertyCheck + var props []*PropertyCheck - // description - addPropertyCheck(&props, left.GetDescription().ValueNode, right.GetDescription().ValueNode, - left.GetDescription(), right.GetDescription(), changes, v3.DescriptionLabel, false) + // description + addPropertyCheck(&props, left.GetDescription().ValueNode, right.GetDescription().ValueNode, + left.GetDescription(), right.GetDescription(), changes, v3.DescriptionLabel, false) - return props + return props } // CompareHeadersV2 is a Swagger compatible, typed signature used for other generic functions. It simply // wraps CompareHeaders and provides nothing other that a typed interface. func CompareHeadersV2(l, r *v2.Header) *HeaderChanges { - return CompareHeaders(l, r) + return CompareHeaders(l, r) } // CompareHeadersV3 is an OpenAPI 3+ compatible, typed signature used for other generic functions. It simply // wraps CompareHeaders and provides nothing other that a typed interface. func CompareHeadersV3(l, r *v3.Header) *HeaderChanges { - return CompareHeaders(l, r) + return CompareHeaders(l, r) } // CompareHeaders will compare left and right Header objects (any version of Swagger or OpenAPI) and return // a pointer to HeaderChanges with anything that has changed, or nil if nothing changed. func CompareHeaders(l, r any) *HeaderChanges { - var changes []*Change - var props []*PropertyCheck - hc := new(HeaderChanges) + var changes []*Change + var props []*PropertyCheck + hc := new(HeaderChanges) - // handle swagger. - if reflect.TypeOf(&v2.Header{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Header{}) == reflect.TypeOf(r) { - lHeader := l.(*v2.Header) - rHeader := r.(*v2.Header) + // handle swagger. + if reflect.TypeOf(&v2.Header{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Header{}) == reflect.TypeOf(r) { + lHeader := l.(*v2.Header) + rHeader := r.(*v2.Header) - // perform hash check to avoid further processing - if low.AreEqual(lHeader, rHeader) { - return nil - } + // perform hash check to avoid further processing + if low.AreEqual(lHeader, rHeader) { + return nil + } - props = append(props, addCommonHeaderProperties(lHeader, rHeader, &changes)...) - props = append(props, addSwaggerHeaderProperties(lHeader, rHeader, &changes)...) + props = append(props, addCommonHeaderProperties(lHeader, rHeader, &changes)...) + props = append(props, addSwaggerHeaderProperties(lHeader, rHeader, &changes)...) - // enum - if len(lHeader.Enum.Value) > 0 || len(rHeader.Enum.Value) > 0 { - ExtractRawValueSliceChanges(lHeader.Enum.Value, rHeader.Enum.Value, &changes, v3.EnumLabel, true) - } + // enum + if len(lHeader.Enum.Value) > 0 || len(rHeader.Enum.Value) > 0 { + ExtractRawValueSliceChanges(lHeader.Enum.Value, rHeader.Enum.Value, &changes, v3.EnumLabel, true) + } - // items - if !lHeader.Items.IsEmpty() && !rHeader.Items.IsEmpty() { - if !low.AreEqual(lHeader.Items.Value, rHeader.Items.Value) { - hc.ItemsChanges = CompareItems(lHeader.Items.Value, rHeader.Items.Value) - } - } - if lHeader.Items.IsEmpty() && !rHeader.Items.IsEmpty() { - CreateChange(&changes, ObjectAdded, v3.ItemsLabel, nil, - rHeader.Items.ValueNode, true, nil, rHeader.Items.Value) - } - if !lHeader.Items.IsEmpty() && rHeader.Items.IsEmpty() { - CreateChange(&changes, ObjectRemoved, v3.SchemaLabel, lHeader.Items.ValueNode, - nil, true, lHeader.Items.Value, nil) - } - hc.ExtensionChanges = CompareExtensions(lHeader.Extensions, rHeader.Extensions) - } + // items + if !lHeader.Items.IsEmpty() && !rHeader.Items.IsEmpty() { + if !low.AreEqual(lHeader.Items.Value, rHeader.Items.Value) { + hc.ItemsChanges = CompareItems(lHeader.Items.Value, rHeader.Items.Value) + } + } + if lHeader.Items.IsEmpty() && !rHeader.Items.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.ItemsLabel, nil, + rHeader.Items.ValueNode, true, nil, rHeader.Items.Value) + } + if !lHeader.Items.IsEmpty() && rHeader.Items.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.SchemaLabel, lHeader.Items.ValueNode, + nil, true, lHeader.Items.Value, nil) + } + hc.ExtensionChanges = CompareExtensions(lHeader.Extensions, rHeader.Extensions) + } - // handle OpenAPI - if reflect.TypeOf(&v3.Header{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Header{}) == reflect.TypeOf(r) { - lHeader := l.(*v3.Header) - rHeader := r.(*v3.Header) + // handle OpenAPI + if reflect.TypeOf(&v3.Header{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Header{}) == reflect.TypeOf(r) { + lHeader := l.(*v3.Header) + rHeader := r.(*v3.Header) - // perform hash check to avoid further processing - if low.AreEqual(lHeader, rHeader) { - return nil - } + // perform hash check to avoid further processing + if low.AreEqual(lHeader, rHeader) { + return nil + } - props = append(props, addCommonHeaderProperties(lHeader, rHeader, &changes)...) - props = append(props, addOpenAPIHeaderProperties(lHeader, rHeader, &changes)...) + props = append(props, addCommonHeaderProperties(lHeader, rHeader, &changes)...) + props = append(props, addOpenAPIHeaderProperties(lHeader, rHeader, &changes)...) - // header - if !lHeader.Schema.IsEmpty() || !rHeader.Schema.IsEmpty() { - hc.SchemaChanges = CompareSchemas(lHeader.Schema.Value, rHeader.Schema.Value) - } + // header + if !lHeader.Schema.IsEmpty() || !rHeader.Schema.IsEmpty() { + hc.SchemaChanges = CompareSchemas(lHeader.Schema.Value, rHeader.Schema.Value) + } - // examples - hc.ExamplesChanges = CheckMapForChanges(lHeader.Examples.Value, rHeader.Examples.Value, - &changes, v3.ExamplesLabel, CompareExamples) + // examples + hc.ExamplesChanges = CheckMapForChanges(lHeader.Examples.Value, rHeader.Examples.Value, + &changes, v3.ExamplesLabel, CompareExamples) - // content - hc.ContentChanges = CheckMapForChanges(lHeader.Content.Value, rHeader.Content.Value, - &changes, v3.ContentLabel, CompareMediaTypes) + // content + hc.ContentChanges = CheckMapForChanges(lHeader.Content.Value, rHeader.Content.Value, + &changes, v3.ContentLabel, CompareMediaTypes) - hc.ExtensionChanges = CompareExtensions(lHeader.Extensions, rHeader.Extensions) + hc.ExtensionChanges = CompareExtensions(lHeader.Extensions, rHeader.Extensions) - } - CheckProperties(props) - hc.PropertyChanges = NewPropertyChanges(changes) - return hc + } + CheckProperties(props) + hc.PropertyChanges = NewPropertyChanges(changes) + return hc } diff --git a/what-changed/model/info.go b/what-changed/model/info.go index 98021f6..4f93364 100644 --- a/what-changed/model/info.go +++ b/what-changed/model/info.go @@ -17,15 +17,15 @@ type InfoChanges struct { // GetAllChanges returns a slice of all changes made between Info objects func (i *InfoChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, i.Changes...) - if i.ContactChanges != nil { - changes = append(changes, i.ContactChanges.GetAllChanges()...) - } - if i.LicenseChanges != nil { - changes = append(changes, i.LicenseChanges.GetAllChanges()...) - } - return changes + var changes []*Change + changes = append(changes, i.Changes...) + if i.ContactChanges != nil { + changes = append(changes, i.ContactChanges.GetAllChanges()...) + } + if i.LicenseChanges != nil { + changes = append(changes, i.LicenseChanges.GetAllChanges()...) + } + return changes } // TotalChanges represents the total number of changes made to an Info object. diff --git a/what-changed/model/info_test.go b/what-changed/model/info_test.go index 9d42892..1a99097 100644 --- a/what-changed/model/info_test.go +++ b/what-changed/model/info_test.go @@ -4,17 +4,17 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/base" - "github.com/pb33f/libopenapi/datamodel/low/v3" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "testing" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "testing" ) func TestCompareInfo_DescriptionAdded(t *testing.T) { - left := `title: a nice spec + left := `title: a nice spec termsOfService: https://pb33f.io/terms version: '1.2.3' contact: @@ -23,7 +23,7 @@ contact: license: name: MIT` - right := `title: a nice spec + right := `title: a nice spec termsOfService: https://pb33f.io/terms version: '1.2.3' description: this is a description @@ -33,29 +33,29 @@ contact: license: name: MIT` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc base.Info - var rDoc base.Info - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc base.Info + var rDoc base.Info + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareInfo(&lDoc, &rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType) - assert.Equal(t, v3.DescriptionLabel, extChanges.Changes[0].Property) + // compare. + extChanges := CompareInfo(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType) + assert.Equal(t, v3.DescriptionLabel, extChanges.Changes[0].Property) } func TestCompareInfo_TitleRemoved(t *testing.T) { - left := `title: a nice spec + left := `title: a nice spec termsOfService: https://pb33f.io/terms version: '1.2.3' description: this is a description @@ -65,7 +65,7 @@ contact: license: name: MIT` - right := `termsOfService: https://pb33f.io/terms + right := `termsOfService: https://pb33f.io/terms version: '1.2.3' description: this is a description contact: @@ -74,29 +74,29 @@ contact: license: name: MIT` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc base.Info - var rDoc base.Info - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc base.Info + var rDoc base.Info + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareInfo(&lDoc, &rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType) - assert.Equal(t, v3.TitleLabel, extChanges.Changes[0].Property) + // compare. + extChanges := CompareInfo(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType) + assert.Equal(t, v3.TitleLabel, extChanges.Changes[0].Property) } func TestCompareInfo_VersionModified(t *testing.T) { - left := `title: a nice spec + left := `title: a nice spec termsOfService: https://pb33f.io/terms version: '1.2.3' contact: @@ -105,7 +105,7 @@ contact: license: name: MIT` - right := `title: a nice spec + right := `title: a nice spec termsOfService: https://pb33f.io/terms version: '99.99' contact: @@ -114,29 +114,29 @@ contact: license: name: MIT` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc base.Info - var rDoc base.Info - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc base.Info + var rDoc base.Info + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareInfo(&lDoc, &rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, Modified, extChanges.Changes[0].ChangeType) - assert.Equal(t, v3.VersionLabel, extChanges.Changes[0].Property) + // compare. + extChanges := CompareInfo(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, Modified, extChanges.Changes[0].ChangeType) + assert.Equal(t, v3.VersionLabel, extChanges.Changes[0].Property) } func TestCompareInfo_RemoveLicense(t *testing.T) { - left := `title: a nice spec + left := `title: a nice spec termsOfService: https://pb33f.io/terms version: '1.2.3' contact: @@ -145,43 +145,43 @@ contact: license: name: MIT` - right := `title: a nice spec + right := `title: a nice spec termsOfService: https://pb33f.io/terms version: '1.2.3' contact: name: buckaroo email: buckaroo@pb33f.io` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc base.Info - var rDoc base.Info - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc base.Info + var rDoc base.Info + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareInfo(&lDoc, &rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType) - assert.Equal(t, v3.LicenseLabel, extChanges.Changes[0].Property) + // compare. + extChanges := CompareInfo(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType) + assert.Equal(t, v3.LicenseLabel, extChanges.Changes[0].Property) } func TestCompareInfo_AddLicense(t *testing.T) { - left := `title: a nice spec + left := `title: a nice spec termsOfService: https://pb33f.io/terms version: '1.2.3' contact: name: buckaroo email: buckaroo@pb33f.io` - right := `title: a nice spec + right := `title: a nice spec termsOfService: https://pb33f.io/terms version: '1.2.3' contact: @@ -190,29 +190,29 @@ contact: license: name: MIT` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc base.Info - var rDoc base.Info - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc base.Info + var rDoc base.Info + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareInfo(&lDoc, &rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType) - assert.Equal(t, v3.LicenseLabel, extChanges.Changes[0].Property) + // compare. + extChanges := CompareInfo(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType) + assert.Equal(t, v3.LicenseLabel, extChanges.Changes[0].Property) } func TestCompareInfo_LicenseChanged(t *testing.T) { - left := `title: a nice spec + left := `title: a nice spec termsOfService: https://pb33f.io/terms version: '1.2.3' contact: @@ -221,7 +221,7 @@ contact: license: name: MIT` - right := `title: a nice spec + right := `title: a nice spec termsOfService: https://pb33f.io/terms version: '1.2.3' contact: @@ -230,35 +230,35 @@ contact: license: name: Apache` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc base.Info - var rDoc base.Info - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc base.Info + var rDoc base.Info + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareInfo(&lDoc, &rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, Modified, extChanges.LicenseChanges.Changes[0].ChangeType) - assert.Equal(t, v3.NameLabel, extChanges.LicenseChanges.Changes[0].Property) + // compare. + extChanges := CompareInfo(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, Modified, extChanges.LicenseChanges.Changes[0].ChangeType) + assert.Equal(t, v3.NameLabel, extChanges.LicenseChanges.Changes[0].Property) } func TestCompareInfo_AddContact(t *testing.T) { - left := `title: a nice spec + left := `title: a nice spec termsOfService: https://pb33f.io/terms version: '1.2.3' license: name: MIT` - right := `title: a nice spec + right := `title: a nice spec termsOfService: https://pb33f.io/terms version: '1.2.3' contact: @@ -267,29 +267,29 @@ contact: license: name: MIT` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc base.Info - var rDoc base.Info - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc base.Info + var rDoc base.Info + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareInfo(&lDoc, &rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType) - assert.Equal(t, v3.ContactLabel, extChanges.Changes[0].Property) + // compare. + extChanges := CompareInfo(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType) + assert.Equal(t, v3.ContactLabel, extChanges.Changes[0].Property) } func TestCompareInfo_RemoveContact(t *testing.T) { - left := `title: a nice spec + left := `title: a nice spec termsOfService: https://pb33f.io/terms version: '1.2.3' contact: @@ -298,35 +298,35 @@ contact: license: name: MIT` - right := `title: a nice spec + right := `title: a nice spec termsOfService: https://pb33f.io/terms version: '1.2.3' license: name: MIT` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc base.Info - var rDoc base.Info - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc base.Info + var rDoc base.Info + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareInfo(&lDoc, &rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType) - assert.Equal(t, v3.ContactLabel, extChanges.Changes[0].Property) + // compare. + extChanges := CompareInfo(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType) + assert.Equal(t, v3.ContactLabel, extChanges.Changes[0].Property) } func TestCompareInfo_ContactModified(t *testing.T) { - left := `title: a nice spec + left := `title: a nice spec termsOfService: https://pb33f.io/terms version: '1.2.3' contact: @@ -335,7 +335,7 @@ contact: license: name: MIT` - right := `title: a nice spec + right := `title: a nice spec termsOfService: https://pb33f.io/terms version: '1.2.3' contact: @@ -344,30 +344,30 @@ contact: license: name: MIT` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc base.Info - var rDoc base.Info - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc base.Info + var rDoc base.Info + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareInfo(&lDoc, &rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, Modified, extChanges.ContactChanges.Changes[0].ChangeType) - assert.Equal(t, v3.NameLabel, extChanges.ContactChanges.Changes[0].Property) - assert.Equal(t, 0, extChanges.TotalBreakingChanges()) + // compare. + extChanges := CompareInfo(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, Modified, extChanges.ContactChanges.Changes[0].ChangeType) + assert.Equal(t, v3.NameLabel, extChanges.ContactChanges.Changes[0].Property) + assert.Equal(t, 0, extChanges.TotalBreakingChanges()) } func TestCompareInfo_Equal(t *testing.T) { - left := `title: a nice spec + left := `title: a nice spec termsOfService: https://pb33f.io/terms version: '1.2.3' contact: @@ -376,7 +376,7 @@ contact: license: name: MIT` - right := `title: a nice spec + right := `title: a nice spec termsOfService: https://pb33f.io/terms version: '1.2.3' contact: @@ -385,17 +385,17 @@ contact: license: name: MIT` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc base.Info - var rDoc base.Info - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc base.Info + var rDoc base.Info + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) // compare. extChanges := CompareInfo(&lDoc, &rDoc) diff --git a/what-changed/model/items.go b/what-changed/model/items.go index e0da83a..a812caf 100644 --- a/what-changed/model/items.go +++ b/what-changed/model/items.go @@ -4,45 +4,45 @@ package model import ( - v2 "github.com/pb33f/libopenapi/datamodel/low/v2" - v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + v2 "github.com/pb33f/libopenapi/datamodel/low/v2" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" ) // ItemsChanges represent changes found between a left (original) and right (modified) object. Items is only // used by Swagger documents. type ItemsChanges struct { - *PropertyChanges - ItemsChanges *ItemsChanges `json:"items,omitempty" yaml:"items,omitempty"` + *PropertyChanges + ItemsChanges *ItemsChanges `json:"items,omitempty" yaml:"items,omitempty"` } // GetAllChanges returns a slice of all changes made between Items objects func (i *ItemsChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, i.Changes...) - if i.ItemsChanges != nil { - changes = append(changes, i.ItemsChanges.GetAllChanges()...) - } - return changes + var changes []*Change + changes = append(changes, i.Changes...) + if i.ItemsChanges != nil { + changes = append(changes, i.ItemsChanges.GetAllChanges()...) + } + return changes } // TotalChanges returns the total number of changes found between two Items objects // This is a recursive function because Items can contain Items. Be careful! func (i *ItemsChanges) TotalChanges() int { - c := i.PropertyChanges.TotalChanges() - if i.ItemsChanges != nil { - c += i.ItemsChanges.TotalChanges() - } - return c + c := i.PropertyChanges.TotalChanges() + if i.ItemsChanges != nil { + c += i.ItemsChanges.TotalChanges() + } + return c } // TotalBreakingChanges returns the total number of breaking changes found between two Swagger Items objects // This is a recursive method, Items are recursive, be careful! func (i *ItemsChanges) TotalBreakingChanges() int { - c := i.PropertyChanges.TotalBreakingChanges() - if i.ItemsChanges != nil { - c += i.ItemsChanges.TotalBreakingChanges() - } - return c + c := i.PropertyChanges.TotalBreakingChanges() + if i.ItemsChanges != nil { + c += i.ItemsChanges.TotalBreakingChanges() + } + return c } // CompareItems compares two sets of Swagger Item objects. If there are any changes found then a pointer to @@ -52,37 +52,37 @@ func (i *ItemsChanges) TotalBreakingChanges() int { // runaway code if not using the resolver's circular reference checking. func CompareItems(l, r *v2.Items) *ItemsChanges { - var changes []*Change - var props []*PropertyCheck + var changes []*Change + var props []*PropertyCheck - ic := new(ItemsChanges) + ic := new(ItemsChanges) - // header is identical to items, except for a description. - props = append(props, addSwaggerHeaderProperties(l, r, &changes)...) - CheckProperties(props) + // header is identical to items, except for a description. + props = append(props, addSwaggerHeaderProperties(l, r, &changes)...) + CheckProperties(props) - if !l.Items.IsEmpty() && !r.Items.IsEmpty() { - // inline, check hashes, if they don't match, compare. - if l.Items.Value.Hash() != r.Items.Value.Hash() { - // compare. - ic.ItemsChanges = CompareItems(l.Items.Value, r.Items.Value) - } + if !l.Items.IsEmpty() && !r.Items.IsEmpty() { + // inline, check hashes, if they don't match, compare. + if l.Items.Value.Hash() != r.Items.Value.Hash() { + // compare. + ic.ItemsChanges = CompareItems(l.Items.Value, r.Items.Value) + } - } - if l.Items.IsEmpty() && !r.Items.IsEmpty() { - // added items - CreateChange(&changes, PropertyAdded, v3.ItemsLabel, - nil, r.Items.GetValueNode(), true, nil, r.Items.GetValue()) - } - if !l.Items.IsEmpty() && r.Items.IsEmpty() { - // removed items - CreateChange(&changes, PropertyRemoved, v3.ItemsLabel, - l.Items.GetValueNode(), nil, true, l.Items.GetValue(), - nil) - } - ic.PropertyChanges = NewPropertyChanges(changes) - if ic.TotalChanges() <= 0 { - return nil - } - return ic + } + if l.Items.IsEmpty() && !r.Items.IsEmpty() { + // added items + CreateChange(&changes, PropertyAdded, v3.ItemsLabel, + nil, r.Items.GetValueNode(), true, nil, r.Items.GetValue()) + } + if !l.Items.IsEmpty() && r.Items.IsEmpty() { + // removed items + CreateChange(&changes, PropertyRemoved, v3.ItemsLabel, + l.Items.GetValueNode(), nil, true, l.Items.GetValue(), + nil) + } + ic.PropertyChanges = NewPropertyChanges(changes) + if ic.TotalChanges() <= 0 { + return nil + } + return ic } diff --git a/what-changed/model/items_test.go b/what-changed/model/items_test.go index 0ea8d1a..fa5a536 100644 --- a/what-changed/model/items_test.go +++ b/what-changed/model/items_test.go @@ -4,136 +4,136 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/v2" - "github.com/pb33f/libopenapi/datamodel/low/v3" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "testing" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "testing" ) func TestCompareItems(t *testing.T) { - left := `type: string` + left := `type: string` - right := `type: int` + right := `type: int` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v2.Items - var rDoc v2.Items - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v2.Items + var rDoc v2.Items + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - changes := CompareItems(&lDoc, &rDoc) - assert.NotNil(t, changes) - assert.Equal(t, 1, changes.TotalChanges()) - assert.Len(t, changes.GetAllChanges(), 1) - assert.Equal(t, 1, changes.TotalBreakingChanges()) - assert.Equal(t, v3.TypeLabel, changes.Changes[0].Property) + // compare. + changes := CompareItems(&lDoc, &rDoc) + assert.NotNil(t, changes) + assert.Equal(t, 1, changes.TotalChanges()) + assert.Len(t, changes.GetAllChanges(), 1) + assert.Equal(t, 1, changes.TotalBreakingChanges()) + assert.Equal(t, v3.TypeLabel, changes.Changes[0].Property) } func TestCompareItems_RecursiveCheck(t *testing.T) { - left := `type: string + left := `type: string items: type: string` - right := `type: int + right := `type: int items: type: int` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v2.Items - var rDoc v2.Items - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v2.Items + var rDoc v2.Items + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - changes := CompareItems(&lDoc, &rDoc) - assert.NotNil(t, changes) - assert.Equal(t, 2, changes.TotalChanges()) - assert.Len(t, changes.GetAllChanges(), 2) - assert.Equal(t, 2, changes.TotalBreakingChanges()) - assert.Equal(t, 1, changes.ItemsChanges.TotalChanges()) - assert.Equal(t, v3.TypeLabel, changes.Changes[0].Property) + // compare. + changes := CompareItems(&lDoc, &rDoc) + assert.NotNil(t, changes) + assert.Equal(t, 2, changes.TotalChanges()) + assert.Len(t, changes.GetAllChanges(), 2) + assert.Equal(t, 2, changes.TotalBreakingChanges()) + assert.Equal(t, 1, changes.ItemsChanges.TotalChanges()) + assert.Equal(t, v3.TypeLabel, changes.Changes[0].Property) } func TestCompareItems_AddItems(t *testing.T) { - left := `type: int` + left := `type: int` - right := `type: int + right := `type: int items: type: int` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v2.Items - var rDoc v2.Items - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v2.Items + var rDoc v2.Items + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - changes := CompareItems(&lDoc, &rDoc) - assert.NotNil(t, changes) - assert.Equal(t, 1, changes.TotalChanges()) - assert.Len(t, changes.GetAllChanges(), 1) - assert.Equal(t, 1, changes.TotalBreakingChanges()) - assert.Equal(t, v3.ItemsLabel, changes.Changes[0].Property) - assert.Equal(t, PropertyAdded, changes.Changes[0].ChangeType) + // compare. + changes := CompareItems(&lDoc, &rDoc) + assert.NotNil(t, changes) + assert.Equal(t, 1, changes.TotalChanges()) + assert.Len(t, changes.GetAllChanges(), 1) + assert.Equal(t, 1, changes.TotalBreakingChanges()) + assert.Equal(t, v3.ItemsLabel, changes.Changes[0].Property) + assert.Equal(t, PropertyAdded, changes.Changes[0].ChangeType) } func TestCompareItems_RemoveItems(t *testing.T) { - left := `type: int` + left := `type: int` - right := `type: int + right := `type: int items: type: int` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v2.Items - var rDoc v2.Items - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v2.Items + var rDoc v2.Items + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - changes := CompareItems(&rDoc, &lDoc) - assert.NotNil(t, changes) - assert.Equal(t, 1, changes.TotalChanges()) - assert.Len(t, changes.GetAllChanges(), 1) - assert.Equal(t, 1, changes.TotalBreakingChanges()) - assert.Equal(t, v3.ItemsLabel, changes.Changes[0].Property) - assert.Equal(t, PropertyRemoved, changes.Changes[0].ChangeType) + // compare. + changes := CompareItems(&rDoc, &lDoc) + assert.NotNil(t, changes) + assert.Equal(t, 1, changes.TotalChanges()) + assert.Len(t, changes.GetAllChanges(), 1) + assert.Equal(t, 1, changes.TotalBreakingChanges()) + assert.Equal(t, v3.ItemsLabel, changes.Changes[0].Property) + assert.Equal(t, PropertyRemoved, changes.Changes[0].ChangeType) } func TestCompareItems_RefVsInlineIdentical(t *testing.T) { - left := `swagger: 2.0 + left := `swagger: 2.0 definitions: thing: type: string @@ -149,7 +149,7 @@ paths: items: $ref: '#/definitions/thing'` - right := `swagger: 2.0 + right := `swagger: 2.0 definitions: thing: type: string @@ -165,15 +165,15 @@ paths: items: type: bool` - leftDoc, rightDoc := test_BuildDocv2(left, right) + leftDoc, rightDoc := test_BuildDocv2(left, right) - // extract left reference schema and non reference schema. - lItems := leftDoc.Paths.Value.FindPath("/a/path").Value.Get.Value.Parameters. - Value[0].Value.Items.Value - rItems := rightDoc.Paths.Value.FindPath("/a/path").Value.Get.Value.Parameters. - Value[0].Value.Items.Value + // extract left reference schema and non reference schema. + lItems := leftDoc.Paths.Value.FindPath("/a/path").Value.Get.Value.Parameters. + Value[0].Value.Items.Value + rItems := rightDoc.Paths.Value.FindPath("/a/path").Value.Get.Value.Parameters. + Value[0].Value.Items.Value - // compare. - changes := CompareItems(lItems, rItems) + // compare. + changes := CompareItems(lItems, rItems) assert.Nil(t, changes) } diff --git a/what-changed/model/license.go b/what-changed/model/license.go index 6ffbc87..18e587b 100644 --- a/what-changed/model/license.go +++ b/what-changed/model/license.go @@ -4,28 +4,28 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low/base" - "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/datamodel/low/v3" ) // LicenseChanges represent changes to a License object that is a child of Info object. Part of an OpenAPI document type LicenseChanges struct { - *PropertyChanges + *PropertyChanges } // GetAllChanges returns a slice of all changes made between License objects func (l *LicenseChanges) GetAllChanges() []*Change { - return l.Changes + return l.Changes } // TotalChanges represents the total number of changes made to a License instance. func (l *LicenseChanges) TotalChanges() int { - return l.PropertyChanges.TotalChanges() + return l.PropertyChanges.TotalChanges() } // TotalBreakingChanges always returns 0 for License objects, they are non-binding. func (l *LicenseChanges) TotalBreakingChanges() int { - return 0 + return 0 } // CompareLicense will check a left (original) and right (new) License object for any changes. If there @@ -33,38 +33,38 @@ func (l *LicenseChanges) TotalBreakingChanges() int { // returns nil. func CompareLicense(l, r *base.License) *LicenseChanges { - var changes []*Change - var props []*PropertyCheck + var changes []*Change + var props []*PropertyCheck - // check URL - props = append(props, &PropertyCheck{ - LeftNode: l.URL.ValueNode, - RightNode: r.URL.ValueNode, - Label: v3.URLLabel, - Changes: &changes, - Breaking: false, - Original: l, - New: r, - }) + // check URL + props = append(props, &PropertyCheck{ + LeftNode: l.URL.ValueNode, + RightNode: r.URL.ValueNode, + Label: v3.URLLabel, + Changes: &changes, + Breaking: false, + Original: l, + New: r, + }) - // check name - props = append(props, &PropertyCheck{ - LeftNode: l.Name.ValueNode, - RightNode: r.Name.ValueNode, - Label: v3.NameLabel, - Changes: &changes, - Breaking: false, - Original: l, - New: r, - }) + // check name + props = append(props, &PropertyCheck{ + LeftNode: l.Name.ValueNode, + RightNode: r.Name.ValueNode, + Label: v3.NameLabel, + Changes: &changes, + Breaking: false, + Original: l, + New: r, + }) - // check everything. - CheckProperties(props) + // check everything. + CheckProperties(props) - lc := new(LicenseChanges) - lc.PropertyChanges = NewPropertyChanges(changes) - if lc.TotalChanges() <= 0 { - return nil - } - return lc + lc := new(LicenseChanges) + lc.PropertyChanges = NewPropertyChanges(changes) + if lc.TotalChanges() <= 0 { + return nil + } + return lc } diff --git a/what-changed/model/link.go b/what-changed/model/link.go index 478a70e..5a47ac6 100644 --- a/what-changed/model/link.go +++ b/what-changed/model/link.go @@ -4,157 +4,157 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/datamodel/low" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" ) // LinkChanges represent changes made between two OpenAPI Link Objects. type LinkChanges struct { - *PropertyChanges - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` - ServerChanges *ServerChanges `json:"server,omitempty" yaml:"server,omitempty"` + *PropertyChanges + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + ServerChanges *ServerChanges `json:"server,omitempty" yaml:"server,omitempty"` } // GetAllChanges returns a slice of all changes made between Link objects func (l *LinkChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, l.Changes...) - if l.ServerChanges != nil { - changes = append(changes, l.ServerChanges.GetAllChanges()...) - } - if l.ExtensionChanges != nil { - changes = append(changes, l.ExtensionChanges.GetAllChanges()...) - } - return changes + var changes []*Change + changes = append(changes, l.Changes...) + if l.ServerChanges != nil { + changes = append(changes, l.ServerChanges.GetAllChanges()...) + } + if l.ExtensionChanges != nil { + changes = append(changes, l.ExtensionChanges.GetAllChanges()...) + } + return changes } // TotalChanges returns the total changes made between OpenAPI Link objects func (l *LinkChanges) TotalChanges() int { - c := l.PropertyChanges.TotalChanges() - if l.ExtensionChanges != nil { - c += l.ExtensionChanges.TotalChanges() - } - if l.ServerChanges != nil { - c += l.ServerChanges.TotalChanges() - } - return c + c := l.PropertyChanges.TotalChanges() + if l.ExtensionChanges != nil { + c += l.ExtensionChanges.TotalChanges() + } + if l.ServerChanges != nil { + c += l.ServerChanges.TotalChanges() + } + return c } // TotalBreakingChanges returns the number of breaking changes made between two OpenAPI Link Objects func (l *LinkChanges) TotalBreakingChanges() int { - c := l.PropertyChanges.TotalBreakingChanges() - if l.ServerChanges != nil { - c += l.ServerChanges.TotalBreakingChanges() - } - return c + c := l.PropertyChanges.TotalBreakingChanges() + if l.ServerChanges != nil { + c += l.ServerChanges.TotalBreakingChanges() + } + return c } // CompareLinks checks a left and right OpenAPI Link for any changes. If they are found, returns a pointer to // LinkChanges, and returns nil if nothing is found. func CompareLinks(l, r *v3.Link) *LinkChanges { - if low.AreEqual(l, r) { - return nil - } + if low.AreEqual(l, r) { + return nil + } - var props []*PropertyCheck - var changes []*Change + var props []*PropertyCheck + var changes []*Change - // operation ref - props = append(props, &PropertyCheck{ - LeftNode: l.OperationRef.ValueNode, - RightNode: r.OperationRef.ValueNode, - Label: v3.OperationRefLabel, - Changes: &changes, - Breaking: true, - Original: l, - New: r, - }) + // operation ref + props = append(props, &PropertyCheck{ + LeftNode: l.OperationRef.ValueNode, + RightNode: r.OperationRef.ValueNode, + Label: v3.OperationRefLabel, + Changes: &changes, + Breaking: true, + Original: l, + New: r, + }) - // operation id - props = append(props, &PropertyCheck{ - LeftNode: l.OperationId.ValueNode, - RightNode: r.OperationId.ValueNode, - Label: v3.OperationIdLabel, - Changes: &changes, - Breaking: true, - Original: l, - New: r, - }) + // operation id + props = append(props, &PropertyCheck{ + LeftNode: l.OperationId.ValueNode, + RightNode: r.OperationId.ValueNode, + Label: v3.OperationIdLabel, + Changes: &changes, + Breaking: true, + Original: l, + New: r, + }) - // request body - props = append(props, &PropertyCheck{ - LeftNode: l.RequestBody.ValueNode, - RightNode: r.RequestBody.ValueNode, - Label: v3.RequestBodyLabel, - Changes: &changes, - Breaking: true, - Original: l, - New: r, - }) + // request body + props = append(props, &PropertyCheck{ + LeftNode: l.RequestBody.ValueNode, + RightNode: r.RequestBody.ValueNode, + Label: v3.RequestBodyLabel, + Changes: &changes, + Breaking: true, + Original: l, + New: r, + }) - // description - props = append(props, &PropertyCheck{ - LeftNode: l.Description.ValueNode, - RightNode: r.Description.ValueNode, - Label: v3.DescriptionLabel, - Changes: &changes, - Breaking: false, - Original: l, - New: r, - }) + // description + props = append(props, &PropertyCheck{ + LeftNode: l.Description.ValueNode, + RightNode: r.Description.ValueNode, + Label: v3.DescriptionLabel, + Changes: &changes, + Breaking: false, + Original: l, + New: r, + }) - CheckProperties(props) - lc := new(LinkChanges) - lc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) + CheckProperties(props) + lc := new(LinkChanges) + lc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) - // server - if !l.Server.IsEmpty() && !r.Server.IsEmpty() { - if !low.AreEqual(l.Server.Value, r.Server.Value) { - lc.ServerChanges = CompareServers(l.Server.Value, r.Server.Value) - } - } - if !l.Server.IsEmpty() && r.Server.IsEmpty() { - CreateChange(&changes, PropertyRemoved, v3.ServerLabel, - l.Server.ValueNode, nil, true, - l.Server.Value, nil) - } - if l.Server.IsEmpty() && !r.Server.IsEmpty() { - CreateChange(&changes, PropertyAdded, v3.ServerLabel, - nil, r.Server.ValueNode, true, - nil, r.Server.Value) - } + // server + if !l.Server.IsEmpty() && !r.Server.IsEmpty() { + if !low.AreEqual(l.Server.Value, r.Server.Value) { + lc.ServerChanges = CompareServers(l.Server.Value, r.Server.Value) + } + } + if !l.Server.IsEmpty() && r.Server.IsEmpty() { + CreateChange(&changes, PropertyRemoved, v3.ServerLabel, + l.Server.ValueNode, nil, true, + l.Server.Value, nil) + } + if l.Server.IsEmpty() && !r.Server.IsEmpty() { + CreateChange(&changes, PropertyAdded, v3.ServerLabel, + nil, r.Server.ValueNode, true, + nil, r.Server.Value) + } - // parameters - lValues := make(map[string]low.ValueReference[string]) - rValues := make(map[string]low.ValueReference[string]) - for i := range l.Parameters.Value { - lValues[i.Value] = l.Parameters.Value[i] - } - for i := range r.Parameters.Value { - rValues[i.Value] = r.Parameters.Value[i] - } - for k := range lValues { - if _, ok := rValues[k]; !ok { - CreateChange(&changes, ObjectRemoved, v3.ParametersLabel, - lValues[k].ValueNode, nil, true, - k, nil) - continue - } - if lValues[k].Value != rValues[k].Value { - CreateChange(&changes, Modified, v3.ParametersLabel, - lValues[k].ValueNode, rValues[k].ValueNode, true, - k, k) - } + // parameters + lValues := make(map[string]low.ValueReference[string]) + rValues := make(map[string]low.ValueReference[string]) + for i := range l.Parameters.Value { + lValues[i.Value] = l.Parameters.Value[i] + } + for i := range r.Parameters.Value { + rValues[i.Value] = r.Parameters.Value[i] + } + for k := range lValues { + if _, ok := rValues[k]; !ok { + CreateChange(&changes, ObjectRemoved, v3.ParametersLabel, + lValues[k].ValueNode, nil, true, + k, nil) + continue + } + if lValues[k].Value != rValues[k].Value { + CreateChange(&changes, Modified, v3.ParametersLabel, + lValues[k].ValueNode, rValues[k].ValueNode, true, + k, k) + } - } - for k := range rValues { - if _, ok := lValues[k]; !ok { - CreateChange(&changes, ObjectAdded, v3.ParametersLabel, - nil, rValues[k].ValueNode, true, - nil, k) - } - } + } + for k := range rValues { + if _, ok := lValues[k]; !ok { + CreateChange(&changes, ObjectAdded, v3.ParametersLabel, + nil, rValues[k].ValueNode, true, + nil, k) + } + } - lc.PropertyChanges = NewPropertyChanges(changes) - return lc + lc.PropertyChanges = NewPropertyChanges(changes) + return lc } diff --git a/what-changed/model/link_test.go b/what-changed/model/link_test.go index ea83354..aaab5ef 100644 --- a/what-changed/model/link_test.go +++ b/what-changed/model/link_test.go @@ -4,16 +4,16 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/v3" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "testing" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "testing" ) func TestCompareLinks(t *testing.T) { - left := `operationId: someOperation + left := `operationId: someOperation requestBody: expression-says-what description: a nice link server: @@ -21,29 +21,29 @@ server: parameters: nice: rice` - right := left + right := left - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.Link - var rDoc v3.Link - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.Link + var rDoc v3.Link + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareLinks(&lDoc, &rDoc) - assert.Nil(t, extChanges) + // compare. + extChanges := CompareLinks(&lDoc, &rDoc) + assert.Nil(t, extChanges) } func TestCompareLinks_ModifyExtension(t *testing.T) { - left := `operationId: someOperation + left := `operationId: someOperation requestBody: expression-says-what description: a nice link server: @@ -52,7 +52,7 @@ parameters: nice: rice x-cake: tasty` - right := `operationId: someOperation + right := `operationId: someOperation requestBody: expression-says-what description: a nice link server: @@ -61,30 +61,30 @@ parameters: nice: rice x-cake: very tasty` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.Link - var rDoc v3.Link - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.Link + var rDoc v3.Link + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareLinks(&lDoc, &rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, 0, extChanges.TotalBreakingChanges()) - assert.Equal(t, Modified, extChanges.ExtensionChanges.Changes[0].ChangeType) + // compare. + extChanges := CompareLinks(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, 0, extChanges.TotalBreakingChanges()) + assert.Equal(t, Modified, extChanges.ExtensionChanges.Changes[0].ChangeType) } func TestCompareLinks_ModifyServer(t *testing.T) { - left := `operationId: someOperation + left := `operationId: someOperation requestBody: expression-says-what description: a nice link server: @@ -92,7 +92,7 @@ server: parameters: nice: rice` - right := `operationId: someOperation + right := `operationId: someOperation requestBody: expression-says-what description: a nice link server: @@ -100,35 +100,35 @@ server: parameters: nice: rice` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.Link - var rDoc v3.Link - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.Link + var rDoc v3.Link + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareLinks(&lDoc, &rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, 1, extChanges.TotalBreakingChanges()) - assert.Equal(t, Modified, extChanges.ServerChanges.Changes[0].ChangeType) + // compare. + extChanges := CompareLinks(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, 1, extChanges.TotalBreakingChanges()) + assert.Equal(t, Modified, extChanges.ServerChanges.Changes[0].ChangeType) } func TestCompareLinks_AddServer(t *testing.T) { - left := `operationId: someOperation + left := `operationId: someOperation requestBody: expression-says-what description: a nice link parameters: nice: rice` - right := `operationId: someOperation + right := `operationId: someOperation requestBody: expression-says-what description: a nice link server: @@ -136,35 +136,35 @@ server: parameters: nice: rice` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.Link - var rDoc v3.Link - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.Link + var rDoc v3.Link + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareLinks(&lDoc, &rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, 1, extChanges.TotalBreakingChanges()) - assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType) + // compare. + extChanges := CompareLinks(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, 1, extChanges.TotalBreakingChanges()) + assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType) } func TestCompareLinks_RemoveServer(t *testing.T) { - left := `operationId: someOperation + left := `operationId: someOperation requestBody: expression-says-what description: a nice link parameters: nice: rice` - right := `operationId: someOperation + right := `operationId: someOperation requestBody: expression-says-what description: a nice link server: @@ -172,29 +172,29 @@ server: parameters: nice: rice` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.Link - var rDoc v3.Link - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.Link + var rDoc v3.Link + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareLinks(&rDoc, &lDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, 1, extChanges.TotalBreakingChanges()) - assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType) + // compare. + extChanges := CompareLinks(&rDoc, &lDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, 1, extChanges.TotalBreakingChanges()) + assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType) } func TestCompareLinks_ModifyParam(t *testing.T) { - left := `operationId: someOperation + left := `operationId: someOperation requestBody: expression-says-what description: a nice link server: @@ -202,7 +202,7 @@ server: parameters: nice: cake` - right := `operationId: someOperation + right := `operationId: someOperation requestBody: expression-says-what description: a nice link server: @@ -210,32 +210,32 @@ server: parameters: nice: rice` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.Link - var rDoc v3.Link - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.Link + var rDoc v3.Link + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareLinks(&lDoc, &rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, 1, extChanges.TotalBreakingChanges()) - assert.Equal(t, Modified, extChanges.Changes[0].ChangeType) - assert.Equal(t, "nice", extChanges.Changes[0].NewObject) - assert.Equal(t, "cake", extChanges.Changes[0].Original) - assert.Equal(t, "rice", extChanges.Changes[0].New) + // compare. + extChanges := CompareLinks(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, 1, extChanges.TotalBreakingChanges()) + assert.Equal(t, Modified, extChanges.Changes[0].ChangeType) + assert.Equal(t, "nice", extChanges.Changes[0].NewObject) + assert.Equal(t, "cake", extChanges.Changes[0].Original) + assert.Equal(t, "rice", extChanges.Changes[0].New) } func TestCompareLinks_AddParam(t *testing.T) { - left := `operationId: someOperation + left := `operationId: someOperation requestBody: expression-says-what description: a nice link server: @@ -243,7 +243,7 @@ server: parameters: nice: cake` - right := `operationId: someOperation + right := `operationId: someOperation requestBody: expression-says-what description: a nice link server: @@ -252,31 +252,31 @@ parameters: nice: cake hot: pizza` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.Link - var rDoc v3.Link - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.Link + var rDoc v3.Link + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareLinks(&lDoc, &rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, 1, extChanges.TotalBreakingChanges()) - assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType) - assert.Equal(t, "hot", extChanges.Changes[0].NewObject) - assert.Equal(t, "pizza", extChanges.Changes[0].New) + // compare. + extChanges := CompareLinks(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, 1, extChanges.TotalBreakingChanges()) + assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType) + assert.Equal(t, "hot", extChanges.Changes[0].NewObject) + assert.Equal(t, "pizza", extChanges.Changes[0].New) } func TestCompareLinks_RemoveParam(t *testing.T) { - left := `operationId: someOperation + left := `operationId: someOperation requestBody: expression-says-what description: a nice link server: @@ -284,7 +284,7 @@ server: parameters: nice: cake` - right := `operationId: someOperation + right := `operationId: someOperation requestBody: expression-says-what description: a nice link server: @@ -293,24 +293,24 @@ parameters: nice: cake hot: pizza` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.Link - var rDoc v3.Link - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.Link + var rDoc v3.Link + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareLinks(&rDoc, &lDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, 1, extChanges.TotalBreakingChanges()) - assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType) - assert.Equal(t, "hot", extChanges.Changes[0].OriginalObject) + // compare. + extChanges := CompareLinks(&rDoc, &lDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, 1, extChanges.TotalBreakingChanges()) + assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType) + assert.Equal(t, "hot", extChanges.Changes[0].OriginalObject) assert.Equal(t, "pizza", extChanges.Changes[0].Original) } diff --git a/what-changed/model/media_type.go b/what-changed/model/media_type.go index 796d1a7..f4ccd34 100644 --- a/what-changed/model/media_type.go +++ b/what-changed/model/media_type.go @@ -4,146 +4,146 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/v3" - "github.com/pb33f/libopenapi/utils" - "gopkg.in/yaml.v3" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/utils" + "gopkg.in/yaml.v3" ) // MediaTypeChanges represent changes made between two OpenAPI MediaType instances. type MediaTypeChanges struct { - *PropertyChanges - SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"` - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` - ExampleChanges map[string]*ExampleChanges `json:"examples,omitempty" yaml:"examples,omitempty"` - EncodingChanges map[string]*EncodingChanges `json:"encoding,omitempty" yaml:"encoding,omitempty"` + *PropertyChanges + SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + ExampleChanges map[string]*ExampleChanges `json:"examples,omitempty" yaml:"examples,omitempty"` + EncodingChanges map[string]*EncodingChanges `json:"encoding,omitempty" yaml:"encoding,omitempty"` } // GetAllChanges returns a slice of all changes made between MediaType objects func (m *MediaTypeChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, m.Changes...) - if m.SchemaChanges != nil { - changes = append(changes, m.SchemaChanges.GetAllChanges()...) - } - for k := range m.ExampleChanges { - changes = append(changes, m.ExampleChanges[k].GetAllChanges()...) - } - for k := range m.EncodingChanges { - changes = append(changes, m.EncodingChanges[k].GetAllChanges()...) - } - if m.ExtensionChanges != nil { - changes = append(changes, m.ExtensionChanges.GetAllChanges()...) - } - return changes + var changes []*Change + changes = append(changes, m.Changes...) + if m.SchemaChanges != nil { + changes = append(changes, m.SchemaChanges.GetAllChanges()...) + } + for k := range m.ExampleChanges { + changes = append(changes, m.ExampleChanges[k].GetAllChanges()...) + } + for k := range m.EncodingChanges { + changes = append(changes, m.EncodingChanges[k].GetAllChanges()...) + } + if m.ExtensionChanges != nil { + changes = append(changes, m.ExtensionChanges.GetAllChanges()...) + } + return changes } // TotalChanges returns the total number of changes between two MediaType instances. func (m *MediaTypeChanges) TotalChanges() int { - c := m.PropertyChanges.TotalChanges() - for k := range m.ExampleChanges { - c += m.ExampleChanges[k].TotalChanges() - } - if m.SchemaChanges != nil { - c += m.SchemaChanges.TotalChanges() - } - if len(m.EncodingChanges) > 0 { - for i := range m.EncodingChanges { - c += m.EncodingChanges[i].TotalChanges() - } - } - if m.ExtensionChanges != nil { - c += m.ExtensionChanges.TotalChanges() - } - return c + c := m.PropertyChanges.TotalChanges() + for k := range m.ExampleChanges { + c += m.ExampleChanges[k].TotalChanges() + } + if m.SchemaChanges != nil { + c += m.SchemaChanges.TotalChanges() + } + if len(m.EncodingChanges) > 0 { + for i := range m.EncodingChanges { + c += m.EncodingChanges[i].TotalChanges() + } + } + if m.ExtensionChanges != nil { + c += m.ExtensionChanges.TotalChanges() + } + return c } // TotalBreakingChanges returns the total number of breaking changes made between two MediaType instances. func (m *MediaTypeChanges) TotalBreakingChanges() int { - c := m.PropertyChanges.TotalBreakingChanges() - for k := range m.ExampleChanges { - c += m.ExampleChanges[k].TotalBreakingChanges() - } - if m.SchemaChanges != nil { - c += m.SchemaChanges.TotalBreakingChanges() - } - if len(m.EncodingChanges) > 0 { - for i := range m.EncodingChanges { - c += m.EncodingChanges[i].TotalBreakingChanges() - } - } - return c + c := m.PropertyChanges.TotalBreakingChanges() + for k := range m.ExampleChanges { + c += m.ExampleChanges[k].TotalBreakingChanges() + } + if m.SchemaChanges != nil { + c += m.SchemaChanges.TotalBreakingChanges() + } + if len(m.EncodingChanges) > 0 { + for i := range m.EncodingChanges { + c += m.EncodingChanges[i].TotalBreakingChanges() + } + } + return c } // CompareMediaTypes compares a left and a right MediaType object for any changes. If found, a pointer to a // MediaTypeChanges instance is returned, otherwise nothing is returned. func CompareMediaTypes(l, r *v3.MediaType) *MediaTypeChanges { - var props []*PropertyCheck - var changes []*Change + var props []*PropertyCheck + var changes []*Change - mc := new(MediaTypeChanges) + mc := new(MediaTypeChanges) - if low.AreEqual(l, r) { - return nil - } + if low.AreEqual(l, r) { + return nil + } - // Example - if !l.Example.IsEmpty() && !r.Example.IsEmpty() { - if (utils.IsNodeMap(l.Example.ValueNode) && utils.IsNodeMap(r.Example.ValueNode)) || - (utils.IsNodeArray(l.Example.ValueNode) && utils.IsNodeArray(r.Example.ValueNode)) { - render, _ := yaml.Marshal(l.Example.ValueNode) - render, _ = utils.ConvertYAMLtoJSON(render) - l.Example.ValueNode.Value = string(render) - render, _ = yaml.Marshal(r.Example.ValueNode) - render, _ = utils.ConvertYAMLtoJSON(render) - r.Example.ValueNode.Value = string(render) - } - addPropertyCheck(&props, l.Example.ValueNode, r.Example.ValueNode, - l.Example.Value, r.Example.Value, &changes, v3.ExampleLabel, false) + // Example + if !l.Example.IsEmpty() && !r.Example.IsEmpty() { + if (utils.IsNodeMap(l.Example.ValueNode) && utils.IsNodeMap(r.Example.ValueNode)) || + (utils.IsNodeArray(l.Example.ValueNode) && utils.IsNodeArray(r.Example.ValueNode)) { + render, _ := yaml.Marshal(l.Example.ValueNode) + render, _ = utils.ConvertYAMLtoJSON(render) + l.Example.ValueNode.Value = string(render) + render, _ = yaml.Marshal(r.Example.ValueNode) + render, _ = utils.ConvertYAMLtoJSON(render) + r.Example.ValueNode.Value = string(render) + } + addPropertyCheck(&props, l.Example.ValueNode, r.Example.ValueNode, + l.Example.Value, r.Example.Value, &changes, v3.ExampleLabel, false) - } else { + } else { - if utils.IsNodeMap(l.Example.ValueNode) || utils.IsNodeArray(l.Example.ValueNode) { - render, _ := yaml.Marshal(l.Example.ValueNode) - render, _ = utils.ConvertYAMLtoJSON(render) - l.Example.ValueNode.Value = string(render) - } + if utils.IsNodeMap(l.Example.ValueNode) || utils.IsNodeArray(l.Example.ValueNode) { + render, _ := yaml.Marshal(l.Example.ValueNode) + render, _ = utils.ConvertYAMLtoJSON(render) + l.Example.ValueNode.Value = string(render) + } - if utils.IsNodeMap(r.Example.ValueNode) || utils.IsNodeArray(r.Example.ValueNode) { - render, _ := yaml.Marshal(r.Example.ValueNode) - render, _ = utils.ConvertYAMLtoJSON(render) - r.Example.ValueNode.Value = string(render) - } + if utils.IsNodeMap(r.Example.ValueNode) || utils.IsNodeArray(r.Example.ValueNode) { + render, _ := yaml.Marshal(r.Example.ValueNode) + render, _ = utils.ConvertYAMLtoJSON(render) + r.Example.ValueNode.Value = string(render) + } - addPropertyCheck(&props, l.Example.ValueNode, r.Example.ValueNode, - l.Example.Value, r.Example.Value, &changes, v3.ExampleLabel, false) - } + addPropertyCheck(&props, l.Example.ValueNode, r.Example.ValueNode, + l.Example.Value, r.Example.Value, &changes, v3.ExampleLabel, false) + } - CheckProperties(props) + CheckProperties(props) - // schema - if !l.Schema.IsEmpty() && !r.Schema.IsEmpty() { - mc.SchemaChanges = CompareSchemas(l.Schema.Value, r.Schema.Value) - } - if !l.Schema.IsEmpty() && r.Schema.IsEmpty() { - CreateChange(&changes, ObjectRemoved, v3.SchemaLabel, l.Schema.ValueNode, - nil, true, l.Schema.Value, nil) - } - if l.Schema.IsEmpty() && !r.Schema.IsEmpty() { - CreateChange(&changes, ObjectAdded, v3.SchemaLabel, nil, - r.Schema.ValueNode, true, nil, r.Schema.Value) - } + // schema + if !l.Schema.IsEmpty() && !r.Schema.IsEmpty() { + mc.SchemaChanges = CompareSchemas(l.Schema.Value, r.Schema.Value) + } + if !l.Schema.IsEmpty() && r.Schema.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.SchemaLabel, l.Schema.ValueNode, + nil, true, l.Schema.Value, nil) + } + if l.Schema.IsEmpty() && !r.Schema.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.SchemaLabel, nil, + r.Schema.ValueNode, true, nil, r.Schema.Value) + } - // examples - mc.ExampleChanges = CheckMapForChanges(l.Examples.Value, r.Examples.Value, - &changes, v3.ExamplesLabel, CompareExamples) + // examples + mc.ExampleChanges = CheckMapForChanges(l.Examples.Value, r.Examples.Value, + &changes, v3.ExamplesLabel, CompareExamples) - // encoding - mc.EncodingChanges = CheckMapForChanges(l.Encoding.Value, r.Encoding.Value, - &changes, v3.EncodingLabel, CompareEncoding) + // encoding + mc.EncodingChanges = CheckMapForChanges(l.Encoding.Value, r.Encoding.Value, + &changes, v3.EncodingLabel, CompareEncoding) - mc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) - mc.PropertyChanges = NewPropertyChanges(changes) - return mc + mc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) + mc.PropertyChanges = NewPropertyChanges(changes) + return mc } diff --git a/what-changed/model/oauth_flows.go b/what-changed/model/oauth_flows.go index 3045a3b..6b3410b 100644 --- a/what-changed/model/oauth_flows.go +++ b/what-changed/model/oauth_flows.go @@ -4,254 +4,254 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/v3" ) // OAuthFlowsChanges represents changes found between two OpenAPI OAuthFlows objects. type OAuthFlowsChanges struct { - *PropertyChanges - ImplicitChanges *OAuthFlowChanges `json:"implicit,omitempty" yaml:"implicit,omitempty"` - PasswordChanges *OAuthFlowChanges `json:"password,omitempty" yaml:"password,omitempty"` - ClientCredentialsChanges *OAuthFlowChanges `json:"clientCredentials,omitempty" yaml:"clientCredentials,omitempty"` - AuthorizationCodeChanges *OAuthFlowChanges `json:"authCode,omitempty" yaml:"authCode,omitempty"` - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + *PropertyChanges + ImplicitChanges *OAuthFlowChanges `json:"implicit,omitempty" yaml:"implicit,omitempty"` + PasswordChanges *OAuthFlowChanges `json:"password,omitempty" yaml:"password,omitempty"` + ClientCredentialsChanges *OAuthFlowChanges `json:"clientCredentials,omitempty" yaml:"clientCredentials,omitempty"` + AuthorizationCodeChanges *OAuthFlowChanges `json:"authCode,omitempty" yaml:"authCode,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` } // GetAllChanges returns a slice of all changes made between OAuthFlows objects func (o *OAuthFlowsChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, o.Changes...) - if o.ImplicitChanges != nil { - changes = append(changes, o.ImplicitChanges.GetAllChanges()...) - } - if o.PasswordChanges != nil { - changes = append(changes, o.PasswordChanges.GetAllChanges()...) - } - if o.ClientCredentialsChanges != nil { - changes = append(changes, o.ClientCredentialsChanges.GetAllChanges()...) - } - if o.AuthorizationCodeChanges != nil { - changes = append(changes, o.AuthorizationCodeChanges.GetAllChanges()...) - } - if o.ExtensionChanges != nil { - changes = append(changes, o.ImplicitChanges.GetAllChanges()...) - } - return changes + var changes []*Change + changes = append(changes, o.Changes...) + if o.ImplicitChanges != nil { + changes = append(changes, o.ImplicitChanges.GetAllChanges()...) + } + if o.PasswordChanges != nil { + changes = append(changes, o.PasswordChanges.GetAllChanges()...) + } + if o.ClientCredentialsChanges != nil { + changes = append(changes, o.ClientCredentialsChanges.GetAllChanges()...) + } + if o.AuthorizationCodeChanges != nil { + changes = append(changes, o.AuthorizationCodeChanges.GetAllChanges()...) + } + if o.ExtensionChanges != nil { + changes = append(changes, o.ImplicitChanges.GetAllChanges()...) + } + return changes } // TotalChanges returns the number of changes made between two OAuthFlows instances. func (o *OAuthFlowsChanges) TotalChanges() int { - c := o.PropertyChanges.TotalChanges() - if o.ImplicitChanges != nil { - c += o.ImplicitChanges.TotalChanges() - } - if o.PasswordChanges != nil { - c += o.PasswordChanges.TotalChanges() - } - if o.ClientCredentialsChanges != nil { - c += o.ClientCredentialsChanges.TotalChanges() - } - if o.AuthorizationCodeChanges != nil { - c += o.AuthorizationCodeChanges.TotalChanges() - } - if o.ExtensionChanges != nil { - c += o.ExtensionChanges.TotalChanges() - } - return c + c := o.PropertyChanges.TotalChanges() + if o.ImplicitChanges != nil { + c += o.ImplicitChanges.TotalChanges() + } + if o.PasswordChanges != nil { + c += o.PasswordChanges.TotalChanges() + } + if o.ClientCredentialsChanges != nil { + c += o.ClientCredentialsChanges.TotalChanges() + } + if o.AuthorizationCodeChanges != nil { + c += o.AuthorizationCodeChanges.TotalChanges() + } + if o.ExtensionChanges != nil { + c += o.ExtensionChanges.TotalChanges() + } + return c } // TotalBreakingChanges returns the number of breaking changes made between two OAuthFlows objects. func (o *OAuthFlowsChanges) TotalBreakingChanges() int { - c := o.PropertyChanges.TotalBreakingChanges() - if o.ImplicitChanges != nil { - c += o.ImplicitChanges.TotalBreakingChanges() - } - if o.PasswordChanges != nil { - c += o.PasswordChanges.TotalBreakingChanges() - } - if o.ClientCredentialsChanges != nil { - c += o.ClientCredentialsChanges.TotalBreakingChanges() - } - if o.AuthorizationCodeChanges != nil { - c += o.AuthorizationCodeChanges.TotalBreakingChanges() - } - return c + c := o.PropertyChanges.TotalBreakingChanges() + if o.ImplicitChanges != nil { + c += o.ImplicitChanges.TotalBreakingChanges() + } + if o.PasswordChanges != nil { + c += o.PasswordChanges.TotalBreakingChanges() + } + if o.ClientCredentialsChanges != nil { + c += o.ClientCredentialsChanges.TotalBreakingChanges() + } + if o.AuthorizationCodeChanges != nil { + c += o.AuthorizationCodeChanges.TotalBreakingChanges() + } + return c } // CompareOAuthFlows compares a left and right OAuthFlows object. If changes are found a pointer to *OAuthFlowsChanges // is returned, otherwise nil is returned. func CompareOAuthFlows(l, r *v3.OAuthFlows) *OAuthFlowsChanges { - if low.AreEqual(l, r) { - return nil - } + if low.AreEqual(l, r) { + return nil + } - oa := new(OAuthFlowsChanges) - var changes []*Change + oa := new(OAuthFlowsChanges) + var changes []*Change - // client credentials - if !l.ClientCredentials.IsEmpty() && !r.ClientCredentials.IsEmpty() { - oa.ClientCredentialsChanges = CompareOAuthFlow(l.ClientCredentials.Value, r.ClientCredentials.Value) - } - if !l.ClientCredentials.IsEmpty() && r.ClientCredentials.IsEmpty() { - CreateChange(&changes, ObjectRemoved, v3.ClientCredentialsLabel, - l.ClientCredentials.ValueNode, nil, true, - l.ClientCredentials.Value, nil) - } - if l.ClientCredentials.IsEmpty() && !r.ClientCredentials.IsEmpty() { - CreateChange(&changes, ObjectAdded, v3.ClientCredentialsLabel, - nil, r.ClientCredentials.ValueNode, false, - nil, r.ClientCredentials.Value) - } + // client credentials + if !l.ClientCredentials.IsEmpty() && !r.ClientCredentials.IsEmpty() { + oa.ClientCredentialsChanges = CompareOAuthFlow(l.ClientCredentials.Value, r.ClientCredentials.Value) + } + if !l.ClientCredentials.IsEmpty() && r.ClientCredentials.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.ClientCredentialsLabel, + l.ClientCredentials.ValueNode, nil, true, + l.ClientCredentials.Value, nil) + } + if l.ClientCredentials.IsEmpty() && !r.ClientCredentials.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.ClientCredentialsLabel, + nil, r.ClientCredentials.ValueNode, false, + nil, r.ClientCredentials.Value) + } - // implicit - if !l.Implicit.IsEmpty() && !r.Implicit.IsEmpty() { - oa.ImplicitChanges = CompareOAuthFlow(l.Implicit.Value, r.Implicit.Value) - } - if !l.Implicit.IsEmpty() && r.Implicit.IsEmpty() { - CreateChange(&changes, ObjectRemoved, v3.ImplicitLabel, - l.Implicit.ValueNode, nil, true, - l.Implicit.Value, nil) - } - if l.Implicit.IsEmpty() && !r.Implicit.IsEmpty() { - CreateChange(&changes, ObjectAdded, v3.ImplicitLabel, - nil, r.Implicit.ValueNode, false, - nil, r.Implicit.Value) - } + // implicit + if !l.Implicit.IsEmpty() && !r.Implicit.IsEmpty() { + oa.ImplicitChanges = CompareOAuthFlow(l.Implicit.Value, r.Implicit.Value) + } + if !l.Implicit.IsEmpty() && r.Implicit.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.ImplicitLabel, + l.Implicit.ValueNode, nil, true, + l.Implicit.Value, nil) + } + if l.Implicit.IsEmpty() && !r.Implicit.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.ImplicitLabel, + nil, r.Implicit.ValueNode, false, + nil, r.Implicit.Value) + } - // password - if !l.Password.IsEmpty() && !r.Password.IsEmpty() { - oa.PasswordChanges = CompareOAuthFlow(l.Password.Value, r.Password.Value) - } - if !l.Password.IsEmpty() && r.Password.IsEmpty() { - CreateChange(&changes, ObjectRemoved, v3.PasswordLabel, - l.Password.ValueNode, nil, true, - l.Password.Value, nil) - } - if l.Password.IsEmpty() && !r.Password.IsEmpty() { - CreateChange(&changes, ObjectAdded, v3.PasswordLabel, - nil, r.Password.ValueNode, false, - nil, r.Password.Value) - } + // password + if !l.Password.IsEmpty() && !r.Password.IsEmpty() { + oa.PasswordChanges = CompareOAuthFlow(l.Password.Value, r.Password.Value) + } + if !l.Password.IsEmpty() && r.Password.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.PasswordLabel, + l.Password.ValueNode, nil, true, + l.Password.Value, nil) + } + if l.Password.IsEmpty() && !r.Password.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.PasswordLabel, + nil, r.Password.ValueNode, false, + nil, r.Password.Value) + } - // auth code - if !l.AuthorizationCode.IsEmpty() && !r.AuthorizationCode.IsEmpty() { - oa.AuthorizationCodeChanges = CompareOAuthFlow(l.AuthorizationCode.Value, r.AuthorizationCode.Value) - } - if !l.AuthorizationCode.IsEmpty() && r.AuthorizationCode.IsEmpty() { - CreateChange(&changes, ObjectRemoved, v3.AuthorizationCodeLabel, - l.AuthorizationCode.ValueNode, nil, true, - l.AuthorizationCode.Value, nil) - } - if l.AuthorizationCode.IsEmpty() && !r.AuthorizationCode.IsEmpty() { - CreateChange(&changes, ObjectAdded, v3.AuthorizationCodeLabel, - nil, r.AuthorizationCode.ValueNode, false, - nil, r.AuthorizationCode.Value) - } - oa.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) - oa.PropertyChanges = NewPropertyChanges(changes) - return oa + // auth code + if !l.AuthorizationCode.IsEmpty() && !r.AuthorizationCode.IsEmpty() { + oa.AuthorizationCodeChanges = CompareOAuthFlow(l.AuthorizationCode.Value, r.AuthorizationCode.Value) + } + if !l.AuthorizationCode.IsEmpty() && r.AuthorizationCode.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.AuthorizationCodeLabel, + l.AuthorizationCode.ValueNode, nil, true, + l.AuthorizationCode.Value, nil) + } + if l.AuthorizationCode.IsEmpty() && !r.AuthorizationCode.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.AuthorizationCodeLabel, + nil, r.AuthorizationCode.ValueNode, false, + nil, r.AuthorizationCode.Value) + } + oa.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) + oa.PropertyChanges = NewPropertyChanges(changes) + return oa } // OAuthFlowChanges represents an OpenAPI OAuthFlow object. type OAuthFlowChanges struct { - *PropertyChanges - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + *PropertyChanges + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` } // GetAllChanges returns a slice of all changes made between OAuthFlow objects func (o *OAuthFlowChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, o.Changes...) - if o.ExtensionChanges != nil { - changes = append(changes, o.ExtensionChanges.GetAllChanges()...) - } - return changes + var changes []*Change + changes = append(changes, o.Changes...) + if o.ExtensionChanges != nil { + changes = append(changes, o.ExtensionChanges.GetAllChanges()...) + } + return changes } // TotalChanges returns the total number of changes made between two OAuthFlow objects func (o *OAuthFlowChanges) TotalChanges() int { - c := o.PropertyChanges.TotalChanges() - if o.ExtensionChanges != nil { - c += o.ExtensionChanges.TotalChanges() - } - return c + c := o.PropertyChanges.TotalChanges() + if o.ExtensionChanges != nil { + c += o.ExtensionChanges.TotalChanges() + } + return c } // TotalBreakingChanges returns the total number of breaking changes made between two OAuthFlow objects func (o *OAuthFlowChanges) TotalBreakingChanges() int { - return o.PropertyChanges.TotalBreakingChanges() + return o.PropertyChanges.TotalBreakingChanges() } // CompareOAuthFlow checks a left and a right OAuthFlow object for changes. If found, returns a pointer to // an OAuthFlowChanges instance, or nil if nothing is found. func CompareOAuthFlow(l, r *v3.OAuthFlow) *OAuthFlowChanges { - if low.AreEqual(l, r) { - return nil - } + if low.AreEqual(l, r) { + return nil + } - var changes []*Change - var props []*PropertyCheck + var changes []*Change + var props []*PropertyCheck - // authorization url - props = append(props, &PropertyCheck{ - LeftNode: l.AuthorizationUrl.ValueNode, - RightNode: r.AuthorizationUrl.ValueNode, - Label: v3.AuthorizationUrlLabel, - Changes: &changes, - Breaking: true, - Original: l, - New: r, - }) + // authorization url + props = append(props, &PropertyCheck{ + LeftNode: l.AuthorizationUrl.ValueNode, + RightNode: r.AuthorizationUrl.ValueNode, + Label: v3.AuthorizationUrlLabel, + Changes: &changes, + Breaking: true, + Original: l, + New: r, + }) - // token url - props = append(props, &PropertyCheck{ - LeftNode: l.TokenUrl.ValueNode, - RightNode: r.TokenUrl.ValueNode, - Label: v3.TokenUrlLabel, - Changes: &changes, - Breaking: true, - Original: l, - New: r, - }) + // token url + props = append(props, &PropertyCheck{ + LeftNode: l.TokenUrl.ValueNode, + RightNode: r.TokenUrl.ValueNode, + Label: v3.TokenUrlLabel, + Changes: &changes, + Breaking: true, + Original: l, + New: r, + }) - // refresh url - props = append(props, &PropertyCheck{ - LeftNode: l.RefreshUrl.ValueNode, - RightNode: r.RefreshUrl.ValueNode, - Label: v3.RefreshUrlLabel, - Changes: &changes, - Breaking: true, - Original: l, - New: r, - }) + // refresh url + props = append(props, &PropertyCheck{ + LeftNode: l.RefreshUrl.ValueNode, + RightNode: r.RefreshUrl.ValueNode, + Label: v3.RefreshUrlLabel, + Changes: &changes, + Breaking: true, + Original: l, + New: r, + }) - CheckProperties(props) + CheckProperties(props) - for v := range l.Scopes.Value { - if r != nil && r.FindScope(v.Value) == nil { - CreateChange(&changes, ObjectRemoved, v3.Scopes, - l.Scopes.Value[v].ValueNode, nil, true, - v.Value, nil) - continue - } - if r != nil && r.FindScope(v.Value) != nil { - if l.Scopes.Value[v].Value != r.FindScope(v.Value).Value { - CreateChange(&changes, Modified, v3.Scopes, - l.Scopes.Value[v].ValueNode, r.FindScope(v.Value).ValueNode, true, - l.Scopes.Value[v].Value, r.FindScope(v.Value).Value) - } - } - } - for v := range r.Scopes.Value { - if l != nil && l.FindScope(v.Value) == nil { - CreateChange(&changes, ObjectAdded, v3.Scopes, - nil, r.Scopes.Value[v].ValueNode, false, - nil, v.Value) - } - } - oa := new(OAuthFlowChanges) - oa.PropertyChanges = NewPropertyChanges(changes) - oa.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) - return oa + for v := range l.Scopes.Value { + if r != nil && r.FindScope(v.Value) == nil { + CreateChange(&changes, ObjectRemoved, v3.Scopes, + l.Scopes.Value[v].ValueNode, nil, true, + v.Value, nil) + continue + } + if r != nil && r.FindScope(v.Value) != nil { + if l.Scopes.Value[v].Value != r.FindScope(v.Value).Value { + CreateChange(&changes, Modified, v3.Scopes, + l.Scopes.Value[v].ValueNode, r.FindScope(v.Value).ValueNode, true, + l.Scopes.Value[v].Value, r.FindScope(v.Value).Value) + } + } + } + for v := range r.Scopes.Value { + if l != nil && l.FindScope(v.Value) == nil { + CreateChange(&changes, ObjectAdded, v3.Scopes, + nil, r.Scopes.Value[v].ValueNode, false, + nil, v.Value) + } + } + oa := new(OAuthFlowChanges) + oa.PropertyChanges = NewPropertyChanges(changes) + oa.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) + return oa } diff --git a/what-changed/model/oauth_flows_test.go b/what-changed/model/oauth_flows_test.go index e25b898..268d94e 100644 --- a/what-changed/model/oauth_flows_test.go +++ b/what-changed/model/oauth_flows_test.go @@ -4,90 +4,90 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - v3 "github.com/pb33f/libopenapi/datamodel/low/v3" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "testing" + "github.com/pb33f/libopenapi/datamodel/low" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "testing" ) func TestCompareOAuthFlow(t *testing.T) { - left := `authorizationUrl: cheese + left := `authorizationUrl: cheese tokenUrl: biscuits refreshUrl: cake scopes: riff: raff` - right := `authorizationUrl: cheese + right := `authorizationUrl: cheese tokenUrl: biscuits refreshUrl: cake scopes: riff: raff` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.OAuthFlow - var rDoc v3.OAuthFlow - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.OAuthFlow + var rDoc v3.OAuthFlow + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare - extChanges := CompareOAuthFlow(&lDoc, &rDoc) - assert.Nil(t, extChanges) + // compare + extChanges := CompareOAuthFlow(&lDoc, &rDoc) + assert.Nil(t, extChanges) } func TestCompareOAuthFlow_Modified(t *testing.T) { - left := `authorizationUrl: toast + left := `authorizationUrl: toast tokenUrl: biscuits refreshUrl: roast scopes: riff: raff x-burgers: nice` - right := `authorizationUrl: cheese + right := `authorizationUrl: cheese tokenUrl: biscuits refreshUrl: cake scopes: riff: raff x-burgers: crispy` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.OAuthFlow - var rDoc v3.OAuthFlow - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.OAuthFlow + var rDoc v3.OAuthFlow + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare - extChanges := CompareOAuthFlow(&lDoc, &rDoc) - assert.Equal(t, 3, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 3) - assert.Equal(t, 2, extChanges.TotalBreakingChanges()) + // compare + extChanges := CompareOAuthFlow(&lDoc, &rDoc) + assert.Equal(t, 3, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 3) + assert.Equal(t, 2, extChanges.TotalBreakingChanges()) } func TestCompareOAuthFlow_AddScope(t *testing.T) { - left := `authorizationUrl: toast + left := `authorizationUrl: toast tokenUrl: biscuits refreshUrl: roast scopes: riff: raff x-burgers: nice` - right := `authorizationUrl: toast + right := `authorizationUrl: toast tokenUrl: biscuits refreshUrl: roast scopes: @@ -95,37 +95,37 @@ scopes: tiff: taff x-burgers: nice` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.OAuthFlow - var rDoc v3.OAuthFlow - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.OAuthFlow + var rDoc v3.OAuthFlow + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare - extChanges := CompareOAuthFlow(&lDoc, &rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, 0, extChanges.TotalBreakingChanges()) - assert.Equal(t, "taff", extChanges.Changes[0].New) - assert.Equal(t, "tiff", extChanges.Changes[0].NewObject) + // compare + extChanges := CompareOAuthFlow(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, 0, extChanges.TotalBreakingChanges()) + assert.Equal(t, "taff", extChanges.Changes[0].New) + assert.Equal(t, "tiff", extChanges.Changes[0].NewObject) } func TestCompareOAuthFlow_RemoveScope(t *testing.T) { - left := `authorizationUrl: toast + left := `authorizationUrl: toast tokenUrl: biscuits refreshUrl: roast scopes: riff: raff x-burgers: nice` - right := `authorizationUrl: toast + right := `authorizationUrl: toast tokenUrl: biscuits refreshUrl: roast scopes: @@ -133,68 +133,68 @@ scopes: tiff: taff x-burgers: nice` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.OAuthFlow - var rDoc v3.OAuthFlow - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.OAuthFlow + var rDoc v3.OAuthFlow + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare - extChanges := CompareOAuthFlow(&rDoc, &lDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, 1, extChanges.TotalBreakingChanges()) - assert.Equal(t, "taff", extChanges.Changes[0].Original) - assert.Equal(t, "tiff", extChanges.Changes[0].OriginalObject) + // compare + extChanges := CompareOAuthFlow(&rDoc, &lDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, 1, extChanges.TotalBreakingChanges()) + assert.Equal(t, "taff", extChanges.Changes[0].Original) + assert.Equal(t, "tiff", extChanges.Changes[0].OriginalObject) } func TestCompareOAuthFlow_ModifyScope(t *testing.T) { - left := `authorizationUrl: toast + left := `authorizationUrl: toast tokenUrl: biscuits refreshUrl: roast scopes: riff: ruffles x-burgers: nice` - right := `authorizationUrl: toast + right := `authorizationUrl: toast tokenUrl: biscuits refreshUrl: roast scopes: riff: raff x-burgers: nice` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.OAuthFlow - var rDoc v3.OAuthFlow - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.OAuthFlow + var rDoc v3.OAuthFlow + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare - extChanges := CompareOAuthFlow(&lDoc, &rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, 1, extChanges.TotalBreakingChanges()) - assert.Equal(t, "raff", extChanges.Changes[0].New) - assert.Equal(t, "raff", extChanges.Changes[0].NewObject) - assert.Equal(t, "ruffles", extChanges.Changes[0].Original) - assert.Equal(t, "ruffles", extChanges.Changes[0].OriginalObject) + // compare + extChanges := CompareOAuthFlow(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, 1, extChanges.TotalBreakingChanges()) + assert.Equal(t, "raff", extChanges.Changes[0].New) + assert.Equal(t, "raff", extChanges.Changes[0].NewObject) + assert.Equal(t, "ruffles", extChanges.Changes[0].Original) + assert.Equal(t, "ruffles", extChanges.Changes[0].OriginalObject) } func TestCompareOAuthFlows(t *testing.T) { - left := `implicit: + left := `implicit: authorizationUrl: cheese password: authorizationUrl: cake @@ -204,7 +204,7 @@ authorizationCode: authorizationUrl: chalk x-coke: cola` - right := `implicit: + right := `implicit: authorizationUrl: cheese password: authorizationUrl: cake @@ -214,27 +214,27 @@ authorizationCode: authorizationUrl: chalk x-coke: cola` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.OAuthFlows - var rDoc v3.OAuthFlows - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.OAuthFlows + var rDoc v3.OAuthFlows + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare - extChanges := CompareOAuthFlows(&lDoc, &rDoc) - assert.Nil(t, extChanges) + // compare + extChanges := CompareOAuthFlows(&lDoc, &rDoc) + assert.Nil(t, extChanges) } func TestCompareOAuthFlows_AddEverything(t *testing.T) { - left := `x-coke: cola` + left := `x-coke: cola` - right := `implicit: + right := `implicit: authorizationUrl: cheese password: authorizationUrl: cake @@ -244,29 +244,29 @@ authorizationCode: authorizationUrl: chalk x-coke: cola` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.OAuthFlows - var rDoc v3.OAuthFlows - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.OAuthFlows + var rDoc v3.OAuthFlows + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare - extChanges := CompareOAuthFlows(&lDoc, &rDoc) - assert.Equal(t, 4, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 4) - assert.Equal(t, 0, extChanges.TotalBreakingChanges()) + // compare + extChanges := CompareOAuthFlows(&lDoc, &rDoc) + assert.Equal(t, 4, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 4) + assert.Equal(t, 0, extChanges.TotalBreakingChanges()) } func TestCompareOAuthFlows_RemoveEverything(t *testing.T) { - left := `x-coke: cola` + left := `x-coke: cola` - right := `implicit: + right := `implicit: authorizationUrl: cheese password: authorizationUrl: cake @@ -276,27 +276,27 @@ authorizationCode: authorizationUrl: chalk x-coke: cola` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.OAuthFlows - var rDoc v3.OAuthFlows - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.OAuthFlows + var rDoc v3.OAuthFlows + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare - extChanges := CompareOAuthFlows(&rDoc, &lDoc) - assert.Equal(t, 4, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 4) - assert.Equal(t, 4, extChanges.TotalBreakingChanges()) + // compare + extChanges := CompareOAuthFlows(&rDoc, &lDoc) + assert.Equal(t, 4, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 4) + assert.Equal(t, 4, extChanges.TotalBreakingChanges()) } func TestCompareOAuthFlows_ModifyEverything(t *testing.T) { - left := `implicit: + left := `implicit: authorizationUrl: cheese password: authorizationUrl: cake @@ -306,7 +306,7 @@ authorizationCode: authorizationUrl: chalk x-coke: cola` - right := `implicit: + right := `implicit: authorizationUrl: herbs password: authorizationUrl: coffee @@ -316,21 +316,21 @@ authorizationCode: authorizationUrl: pasta x-coke: cherry` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.OAuthFlows - var rDoc v3.OAuthFlows - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.OAuthFlows + var rDoc v3.OAuthFlows + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare - extChanges := CompareOAuthFlows(&lDoc, &rDoc) - assert.Equal(t, 5, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 5) - assert.Equal(t, 4, extChanges.TotalBreakingChanges()) + // compare + extChanges := CompareOAuthFlows(&lDoc, &rDoc) + assert.Equal(t, 5, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 5) + assert.Equal(t, 4, extChanges.TotalBreakingChanges()) } diff --git a/what-changed/model/parameter.go b/what-changed/model/parameter.go index b926884..c6ec78b 100644 --- a/what-changed/model/parameter.go +++ b/what-changed/model/parameter.go @@ -4,351 +4,351 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/base" - v2 "github.com/pb33f/libopenapi/datamodel/low/v2" - v3 "github.com/pb33f/libopenapi/datamodel/low/v3" - "gopkg.in/yaml.v3" - "reflect" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + v2 "github.com/pb33f/libopenapi/datamodel/low/v2" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "gopkg.in/yaml.v3" + "reflect" ) // ParameterChanges represents changes found between Swagger or OpenAPI Parameter objects. type ParameterChanges struct { - *PropertyChanges - SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"` - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + *PropertyChanges + SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` - // Swagger supports Items. - ItemsChanges *ItemsChanges `json:"items,omitempty" yaml:"items,omitempty"` + // Swagger supports Items. + ItemsChanges *ItemsChanges `json:"items,omitempty" yaml:"items,omitempty"` - // OpenAPI supports examples and content types. - ExamplesChanges map[string]*ExampleChanges `json:"examples,omitempty" yaml:"examples,omitempty"` - ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"` + // OpenAPI supports examples and content types. + ExamplesChanges map[string]*ExampleChanges `json:"examples,omitempty" yaml:"examples,omitempty"` + ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"` } // GetAllChanges returns a slice of all changes made between Parameter objects func (p *ParameterChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, p.Changes...) - if p.SchemaChanges != nil { - changes = append(changes, p.SchemaChanges.GetAllChanges()...) - } - for i := range p.ExamplesChanges { - changes = append(changes, p.ExamplesChanges[i].GetAllChanges()...) - } - if p.ItemsChanges != nil { - changes = append(changes, p.ItemsChanges.GetAllChanges()...) - } - if p.ExtensionChanges != nil { - changes = append(changes, p.ExtensionChanges.GetAllChanges()...) - } - for i := range p.ContentChanges { - changes = append(changes, p.ContentChanges[i].GetAllChanges()...) - } - return changes + var changes []*Change + changes = append(changes, p.Changes...) + if p.SchemaChanges != nil { + changes = append(changes, p.SchemaChanges.GetAllChanges()...) + } + for i := range p.ExamplesChanges { + changes = append(changes, p.ExamplesChanges[i].GetAllChanges()...) + } + if p.ItemsChanges != nil { + changes = append(changes, p.ItemsChanges.GetAllChanges()...) + } + if p.ExtensionChanges != nil { + changes = append(changes, p.ExtensionChanges.GetAllChanges()...) + } + for i := range p.ContentChanges { + changes = append(changes, p.ContentChanges[i].GetAllChanges()...) + } + return changes } // TotalChanges returns a count of everything that changed func (p *ParameterChanges) TotalChanges() int { - c := p.PropertyChanges.TotalChanges() - if p.SchemaChanges != nil { - c += p.SchemaChanges.TotalChanges() - } - for i := range p.ExamplesChanges { - c += p.ExamplesChanges[i].TotalChanges() - } - if p.ItemsChanges != nil { - c += p.ItemsChanges.TotalChanges() - } - if p.ExtensionChanges != nil { - c += p.ExtensionChanges.TotalChanges() - } - for i := range p.ContentChanges { - c += p.ContentChanges[i].TotalChanges() - } - return c + c := p.PropertyChanges.TotalChanges() + if p.SchemaChanges != nil { + c += p.SchemaChanges.TotalChanges() + } + for i := range p.ExamplesChanges { + c += p.ExamplesChanges[i].TotalChanges() + } + if p.ItemsChanges != nil { + c += p.ItemsChanges.TotalChanges() + } + if p.ExtensionChanges != nil { + c += p.ExtensionChanges.TotalChanges() + } + for i := range p.ContentChanges { + c += p.ContentChanges[i].TotalChanges() + } + return c } // TotalBreakingChanges always returns 0 for ExternalDoc objects, they are non-binding. func (p *ParameterChanges) TotalBreakingChanges() int { - c := p.PropertyChanges.TotalBreakingChanges() - if p.SchemaChanges != nil { - c += p.SchemaChanges.TotalBreakingChanges() - } - if p.ItemsChanges != nil { - c += p.ItemsChanges.TotalBreakingChanges() - } - for i := range p.ContentChanges { - c += p.ContentChanges[i].TotalBreakingChanges() - } - return c + c := p.PropertyChanges.TotalBreakingChanges() + if p.SchemaChanges != nil { + c += p.SchemaChanges.TotalBreakingChanges() + } + if p.ItemsChanges != nil { + c += p.ItemsChanges.TotalBreakingChanges() + } + for i := range p.ContentChanges { + c += p.ContentChanges[i].TotalBreakingChanges() + } + return c } func addPropertyCheck(props *[]*PropertyCheck, - lvn, rvn *yaml.Node, lv, rv any, changes *[]*Change, label string, breaking bool) { - *props = append(*props, &PropertyCheck{ - LeftNode: lvn, - RightNode: rvn, - Label: label, - Changes: changes, - Breaking: breaking, - Original: lv, - New: rv, - }) + lvn, rvn *yaml.Node, lv, rv any, changes *[]*Change, label string, breaking bool) { + *props = append(*props, &PropertyCheck{ + LeftNode: lvn, + RightNode: rvn, + Label: label, + Changes: changes, + Breaking: breaking, + Original: lv, + New: rv, + }) } func addOpenAPIParameterProperties(left, right low.OpenAPIParameter, changes *[]*Change) []*PropertyCheck { - var props []*PropertyCheck + var props []*PropertyCheck - // style - addPropertyCheck(&props, left.GetStyle().ValueNode, right.GetStyle().ValueNode, - left.GetStyle(), right.GetStyle(), changes, v3.StyleLabel, false) + // style + addPropertyCheck(&props, left.GetStyle().ValueNode, right.GetStyle().ValueNode, + left.GetStyle(), right.GetStyle(), changes, v3.StyleLabel, false) - // allow reserved - addPropertyCheck(&props, left.GetAllowReserved().ValueNode, right.GetAllowReserved().ValueNode, - left.GetAllowReserved(), right.GetAllowReserved(), changes, v3.AllowReservedLabel, true) + // allow reserved + addPropertyCheck(&props, left.GetAllowReserved().ValueNode, right.GetAllowReserved().ValueNode, + left.GetAllowReserved(), right.GetAllowReserved(), changes, v3.AllowReservedLabel, true) - // explode - addPropertyCheck(&props, left.GetExplode().ValueNode, right.GetExplode().ValueNode, - left.GetExplode(), right.GetExplode(), changes, v3.ExplodeLabel, false) + // explode + addPropertyCheck(&props, left.GetExplode().ValueNode, right.GetExplode().ValueNode, + left.GetExplode(), right.GetExplode(), changes, v3.ExplodeLabel, false) - // deprecated - addPropertyCheck(&props, left.GetDeprecated().ValueNode, right.GetDeprecated().ValueNode, - left.GetDeprecated(), right.GetDeprecated(), changes, v3.DeprecatedLabel, false) + // deprecated + addPropertyCheck(&props, left.GetDeprecated().ValueNode, right.GetDeprecated().ValueNode, + left.GetDeprecated(), right.GetDeprecated(), changes, v3.DeprecatedLabel, false) - // example - addPropertyCheck(&props, left.GetExample().ValueNode, right.GetExample().ValueNode, - left.GetExample(), right.GetExample(), changes, v3.ExampleLabel, false) + // example + addPropertyCheck(&props, left.GetExample().ValueNode, right.GetExample().ValueNode, + left.GetExample(), right.GetExample(), changes, v3.ExampleLabel, false) - return props + return props } func addSwaggerParameterProperties(left, right low.SwaggerParameter, changes *[]*Change) []*PropertyCheck { - var props []*PropertyCheck + var props []*PropertyCheck - // type - addPropertyCheck(&props, left.GetType().ValueNode, right.GetType().ValueNode, - left.GetType(), right.GetType(), changes, v3.TypeLabel, true) + // type + addPropertyCheck(&props, left.GetType().ValueNode, right.GetType().ValueNode, + left.GetType(), right.GetType(), changes, v3.TypeLabel, true) - // format - addPropertyCheck(&props, left.GetFormat().ValueNode, right.GetFormat().ValueNode, - left.GetFormat(), right.GetFormat(), changes, v3.FormatLabel, true) + // format + addPropertyCheck(&props, left.GetFormat().ValueNode, right.GetFormat().ValueNode, + left.GetFormat(), right.GetFormat(), changes, v3.FormatLabel, true) - // collection format - addPropertyCheck(&props, left.GetCollectionFormat().ValueNode, right.GetCollectionFormat().ValueNode, - left.GetCollectionFormat(), right.GetCollectionFormat(), changes, v3.CollectionFormatLabel, true) + // collection format + addPropertyCheck(&props, left.GetCollectionFormat().ValueNode, right.GetCollectionFormat().ValueNode, + left.GetCollectionFormat(), right.GetCollectionFormat(), changes, v3.CollectionFormatLabel, true) - // maximum - addPropertyCheck(&props, left.GetMaximum().ValueNode, right.GetMaximum().ValueNode, - left.GetMaximum(), right.GetMaximum(), changes, v3.MaximumLabel, true) + // maximum + addPropertyCheck(&props, left.GetMaximum().ValueNode, right.GetMaximum().ValueNode, + left.GetMaximum(), right.GetMaximum(), changes, v3.MaximumLabel, true) - // minimum - addPropertyCheck(&props, left.GetMinimum().ValueNode, right.GetMinimum().ValueNode, - left.GetMinimum(), right.GetMinimum(), changes, v3.MinimumLabel, true) + // minimum + addPropertyCheck(&props, left.GetMinimum().ValueNode, right.GetMinimum().ValueNode, + left.GetMinimum(), right.GetMinimum(), changes, v3.MinimumLabel, true) - // exclusive maximum - addPropertyCheck(&props, left.GetExclusiveMaximum().ValueNode, right.GetExclusiveMaximum().ValueNode, - left.GetExclusiveMaximum(), right.GetExclusiveMaximum(), changes, v3.ExclusiveMaximumLabel, true) + // exclusive maximum + addPropertyCheck(&props, left.GetExclusiveMaximum().ValueNode, right.GetExclusiveMaximum().ValueNode, + left.GetExclusiveMaximum(), right.GetExclusiveMaximum(), changes, v3.ExclusiveMaximumLabel, true) - // exclusive minimum - addPropertyCheck(&props, left.GetExclusiveMinimum().ValueNode, right.GetExclusiveMinimum().ValueNode, - left.GetExclusiveMinimum(), right.GetExclusiveMinimum(), changes, v3.ExclusiveMinimumLabel, true) + // exclusive minimum + addPropertyCheck(&props, left.GetExclusiveMinimum().ValueNode, right.GetExclusiveMinimum().ValueNode, + left.GetExclusiveMinimum(), right.GetExclusiveMinimum(), changes, v3.ExclusiveMinimumLabel, true) - // max length - addPropertyCheck(&props, left.GetMaxLength().ValueNode, right.GetMaxLength().ValueNode, - left.GetMaxLength(), right.GetMaxLength(), changes, v3.MaxLengthLabel, true) + // max length + addPropertyCheck(&props, left.GetMaxLength().ValueNode, right.GetMaxLength().ValueNode, + left.GetMaxLength(), right.GetMaxLength(), changes, v3.MaxLengthLabel, true) - // min length - addPropertyCheck(&props, left.GetMinLength().ValueNode, right.GetMinLength().ValueNode, - left.GetMinLength(), right.GetMinLength(), changes, v3.MinLengthLabel, true) + // min length + addPropertyCheck(&props, left.GetMinLength().ValueNode, right.GetMinLength().ValueNode, + left.GetMinLength(), right.GetMinLength(), changes, v3.MinLengthLabel, true) - // pattern - addPropertyCheck(&props, left.GetPattern().ValueNode, right.GetPattern().ValueNode, - left.GetPattern(), right.GetPattern(), changes, v3.PatternLabel, true) + // pattern + addPropertyCheck(&props, left.GetPattern().ValueNode, right.GetPattern().ValueNode, + left.GetPattern(), right.GetPattern(), changes, v3.PatternLabel, true) - // max items - addPropertyCheck(&props, left.GetMaxItems().ValueNode, right.GetMaxItems().ValueNode, - left.GetMaxItems(), right.GetMaxItems(), changes, v3.MaxItemsLabel, true) + // max items + addPropertyCheck(&props, left.GetMaxItems().ValueNode, right.GetMaxItems().ValueNode, + left.GetMaxItems(), right.GetMaxItems(), changes, v3.MaxItemsLabel, true) - // min items - addPropertyCheck(&props, left.GetMinItems().ValueNode, right.GetMinItems().ValueNode, - left.GetMinItems(), right.GetMinItems(), changes, v3.MinItemsLabel, true) + // min items + addPropertyCheck(&props, left.GetMinItems().ValueNode, right.GetMinItems().ValueNode, + left.GetMinItems(), right.GetMinItems(), changes, v3.MinItemsLabel, true) - // unique items - addPropertyCheck(&props, left.GetUniqueItems().ValueNode, right.GetUniqueItems().ValueNode, - left.GetUniqueItems(), right.GetUniqueItems(), changes, v3.UniqueItemsLabel, true) + // unique items + addPropertyCheck(&props, left.GetUniqueItems().ValueNode, right.GetUniqueItems().ValueNode, + left.GetUniqueItems(), right.GetUniqueItems(), changes, v3.UniqueItemsLabel, true) - // default - addPropertyCheck(&props, left.GetDefault().ValueNode, right.GetDefault().ValueNode, - left.GetDefault(), right.GetDefault(), changes, v3.DefaultLabel, true) + // default + addPropertyCheck(&props, left.GetDefault().ValueNode, right.GetDefault().ValueNode, + left.GetDefault(), right.GetDefault(), changes, v3.DefaultLabel, true) - // multiple of - addPropertyCheck(&props, left.GetMultipleOf().ValueNode, right.GetMultipleOf().ValueNode, - left.GetMultipleOf(), right.GetMultipleOf(), changes, v3.MultipleOfLabel, true) + // multiple of + addPropertyCheck(&props, left.GetMultipleOf().ValueNode, right.GetMultipleOf().ValueNode, + left.GetMultipleOf(), right.GetMultipleOf(), changes, v3.MultipleOfLabel, true) - return props + return props } func addCommonParameterProperties(left, right low.SharedParameters, changes *[]*Change) []*PropertyCheck { - var props []*PropertyCheck + var props []*PropertyCheck - addPropertyCheck(&props, left.GetName().ValueNode, right.GetName().ValueNode, - left.GetName(), right.GetName(), changes, v3.NameLabel, true) + addPropertyCheck(&props, left.GetName().ValueNode, right.GetName().ValueNode, + left.GetName(), right.GetName(), changes, v3.NameLabel, true) - // in - addPropertyCheck(&props, left.GetIn().ValueNode, right.GetIn().ValueNode, - left.GetIn(), right.GetIn(), changes, v3.InLabel, true) + // in + addPropertyCheck(&props, left.GetIn().ValueNode, right.GetIn().ValueNode, + left.GetIn(), right.GetIn(), changes, v3.InLabel, true) - // description - addPropertyCheck(&props, left.GetDescription().ValueNode, right.GetDescription().ValueNode, - left.GetDescription(), right.GetDescription(), changes, v3.DescriptionLabel, false) + // description + addPropertyCheck(&props, left.GetDescription().ValueNode, right.GetDescription().ValueNode, + left.GetDescription(), right.GetDescription(), changes, v3.DescriptionLabel, false) - // required - addPropertyCheck(&props, left.GetRequired().ValueNode, right.GetRequired().ValueNode, - left.GetRequired(), right.GetRequired(), changes, v3.RequiredLabel, true) + // required + addPropertyCheck(&props, left.GetRequired().ValueNode, right.GetRequired().ValueNode, + left.GetRequired(), right.GetRequired(), changes, v3.RequiredLabel, true) - // allow empty value - addPropertyCheck(&props, left.GetAllowEmptyValue().ValueNode, right.GetAllowEmptyValue().ValueNode, - left.GetAllowEmptyValue(), right.GetAllowEmptyValue(), changes, v3.AllowEmptyValueLabel, true) + // allow empty value + addPropertyCheck(&props, left.GetAllowEmptyValue().ValueNode, right.GetAllowEmptyValue().ValueNode, + left.GetAllowEmptyValue(), right.GetAllowEmptyValue(), changes, v3.AllowEmptyValueLabel, true) - return props + return props } // CompareParametersV3 is an OpenAPI type safe proxy for CompareParameters func CompareParametersV3(l, r *v3.Parameter) *ParameterChanges { - return CompareParameters(l, r) + return CompareParameters(l, r) } // CompareParameters compares a left and right Swagger or OpenAPI Parameter object for any changes. If found returns // a pointer to ParameterChanges. If nothing is found, returns nil. func CompareParameters(l, r any) *ParameterChanges { - var changes []*Change - var props []*PropertyCheck + var changes []*Change + var props []*PropertyCheck - pc := new(ParameterChanges) - var lSchema *base.SchemaProxy - var rSchema *base.SchemaProxy - var lext, rext map[low.KeyReference[string]]low.ValueReference[any] + pc := new(ParameterChanges) + var lSchema *base.SchemaProxy + var rSchema *base.SchemaProxy + var lext, rext map[low.KeyReference[string]]low.ValueReference[any] - if reflect.TypeOf(&v2.Parameter{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Parameter{}) == reflect.TypeOf(r) { - lParam := l.(*v2.Parameter) - rParam := r.(*v2.Parameter) + if reflect.TypeOf(&v2.Parameter{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Parameter{}) == reflect.TypeOf(r) { + lParam := l.(*v2.Parameter) + rParam := r.(*v2.Parameter) - // perform hash check to avoid further processing - if low.AreEqual(lParam, rParam) { - return nil - } + // perform hash check to avoid further processing + if low.AreEqual(lParam, rParam) { + return nil + } - props = append(props, addSwaggerParameterProperties(lParam, rParam, &changes)...) - props = append(props, addCommonParameterProperties(lParam, rParam, &changes)...) + props = append(props, addSwaggerParameterProperties(lParam, rParam, &changes)...) + props = append(props, addCommonParameterProperties(lParam, rParam, &changes)...) - // extract schema - if lParam != nil { - lSchema = lParam.Schema.Value - lext = lParam.Extensions - } - if rParam != nil { - rext = rParam.Extensions - rSchema = rParam.Schema.Value - } + // extract schema + if lParam != nil { + lSchema = lParam.Schema.Value + lext = lParam.Extensions + } + if rParam != nil { + rext = rParam.Extensions + rSchema = rParam.Schema.Value + } - // items - if !lParam.Items.IsEmpty() && !rParam.Items.IsEmpty() { - if lParam.Items.Value.Hash() != rParam.Items.Value.Hash() { - pc.ItemsChanges = CompareItems(lParam.Items.Value, rParam.Items.Value) - } - } - if lParam.Items.IsEmpty() && !rParam.Items.IsEmpty() { - CreateChange(&changes, ObjectAdded, v3.ItemsLabel, - nil, rParam.Items.ValueNode, true, nil, - rParam.Items.Value) - } - if !lParam.Items.IsEmpty() && rParam.Items.IsEmpty() { - CreateChange(&changes, ObjectRemoved, v3.ItemsLabel, - lParam.Items.ValueNode, nil, true, lParam.Items.Value, - nil) - } + // items + if !lParam.Items.IsEmpty() && !rParam.Items.IsEmpty() { + if lParam.Items.Value.Hash() != rParam.Items.Value.Hash() { + pc.ItemsChanges = CompareItems(lParam.Items.Value, rParam.Items.Value) + } + } + if lParam.Items.IsEmpty() && !rParam.Items.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.ItemsLabel, + nil, rParam.Items.ValueNode, true, nil, + rParam.Items.Value) + } + if !lParam.Items.IsEmpty() && rParam.Items.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.ItemsLabel, + lParam.Items.ValueNode, nil, true, lParam.Items.Value, + nil) + } - // enum - if len(lParam.Enum.Value) > 0 || len(rParam.Enum.Value) > 0 { - ExtractRawValueSliceChanges(lParam.Enum.Value, rParam.Enum.Value, &changes, v3.EnumLabel, true) - } - } + // enum + if len(lParam.Enum.Value) > 0 || len(rParam.Enum.Value) > 0 { + ExtractRawValueSliceChanges(lParam.Enum.Value, rParam.Enum.Value, &changes, v3.EnumLabel, true) + } + } - // OpenAPI - if reflect.TypeOf(&v3.Parameter{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Parameter{}) == reflect.TypeOf(r) { + // OpenAPI + if reflect.TypeOf(&v3.Parameter{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Parameter{}) == reflect.TypeOf(r) { - lParam := l.(*v3.Parameter) - rParam := r.(*v3.Parameter) + lParam := l.(*v3.Parameter) + rParam := r.(*v3.Parameter) - // perform hash check to avoid further processing - if low.AreEqual(lParam, rParam) { - return nil - } + // perform hash check to avoid further processing + if low.AreEqual(lParam, rParam) { + return nil + } - props = append(props, addOpenAPIParameterProperties(lParam, rParam, &changes)...) - props = append(props, addCommonParameterProperties(lParam, rParam, &changes)...) - if lParam != nil { - lext = lParam.Extensions - lSchema = lParam.Schema.Value - } - if rParam != nil { - rext = rParam.Extensions - rSchema = rParam.Schema.Value - } + props = append(props, addOpenAPIParameterProperties(lParam, rParam, &changes)...) + props = append(props, addCommonParameterProperties(lParam, rParam, &changes)...) + if lParam != nil { + lext = lParam.Extensions + lSchema = lParam.Schema.Value + } + if rParam != nil { + rext = rParam.Extensions + rSchema = rParam.Schema.Value + } - // example - checkParameterExample(lParam.Example, rParam.Example, changes) + // example + checkParameterExample(lParam.Example, rParam.Example, changes) - // examples - pc.ExamplesChanges = CheckMapForChanges(lParam.Examples.Value, rParam.Examples.Value, - &changes, v3.ExamplesLabel, CompareExamples) + // examples + pc.ExamplesChanges = CheckMapForChanges(lParam.Examples.Value, rParam.Examples.Value, + &changes, v3.ExamplesLabel, CompareExamples) - // content - pc.ContentChanges = CheckMapForChanges(lParam.Content.Value, rParam.Content.Value, - &changes, v3.ContentLabel, CompareMediaTypes) - } - CheckProperties(props) + // content + pc.ContentChanges = CheckMapForChanges(lParam.Content.Value, rParam.Content.Value, + &changes, v3.ContentLabel, CompareMediaTypes) + } + CheckProperties(props) - if lSchema != nil && rSchema != nil { - pc.SchemaChanges = CompareSchemas(lSchema, rSchema) - } - if lSchema != nil && rSchema == nil { - CreateChange(&changes, ObjectRemoved, v3.SchemaLabel, - lSchema.GetValueNode(), nil, true, lSchema, - nil) - } + if lSchema != nil && rSchema != nil { + pc.SchemaChanges = CompareSchemas(lSchema, rSchema) + } + if lSchema != nil && rSchema == nil { + CreateChange(&changes, ObjectRemoved, v3.SchemaLabel, + lSchema.GetValueNode(), nil, true, lSchema, + nil) + } - if lSchema == nil && rSchema != nil { - CreateChange(&changes, ObjectAdded, v3.SchemaLabel, - nil, rSchema.GetValueNode(), true, nil, - rSchema) - } + if lSchema == nil && rSchema != nil { + CreateChange(&changes, ObjectAdded, v3.SchemaLabel, + nil, rSchema.GetValueNode(), true, nil, + rSchema) + } - pc.PropertyChanges = NewPropertyChanges(changes) - pc.ExtensionChanges = CompareExtensions(lext, rext) - return pc + pc.PropertyChanges = NewPropertyChanges(changes) + pc.ExtensionChanges = CompareExtensions(lext, rext) + return pc } func checkParameterExample(expLeft, expRight low.NodeReference[any], changes []*Change) { - if !expLeft.IsEmpty() && !expRight.IsEmpty() { - if low.GenerateHashString(expLeft.GetValue()) != low.GenerateHashString(expRight.GetValue()) { - CreateChange(&changes, Modified, v3.ExampleLabel, - expLeft.GetValueNode(), expRight.GetValueNode(), false, - expLeft.GetValue(), expRight.GetValue()) - } - } - if expLeft.Value == nil && expRight.Value != nil { - CreateChange(&changes, PropertyAdded, v3.ExampleLabel, - nil, expRight.GetValueNode(), false, - nil, expRight.GetValue()) + if !expLeft.IsEmpty() && !expRight.IsEmpty() { + if low.GenerateHashString(expLeft.GetValue()) != low.GenerateHashString(expRight.GetValue()) { + CreateChange(&changes, Modified, v3.ExampleLabel, + expLeft.GetValueNode(), expRight.GetValueNode(), false, + expLeft.GetValue(), expRight.GetValue()) + } + } + if expLeft.Value == nil && expRight.Value != nil { + CreateChange(&changes, PropertyAdded, v3.ExampleLabel, + nil, expRight.GetValueNode(), false, + nil, expRight.GetValue()) - } - if expLeft.Value != nil && expRight.Value == nil { - CreateChange(&changes, PropertyRemoved, v3.ExampleLabel, - expLeft.GetValueNode(), nil, false, - expLeft.GetValue(), nil) + } + if expLeft.Value != nil && expRight.Value == nil { + CreateChange(&changes, PropertyRemoved, v3.ExampleLabel, + expLeft.GetValueNode(), nil, false, + expLeft.GetValue(), nil) - } + } } diff --git a/what-changed/model/paths.go b/what-changed/model/paths.go index 2c54d14..5d63aa2 100644 --- a/what-changed/model/paths.go +++ b/what-changed/model/paths.go @@ -4,203 +4,203 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/v2" - v3 "github.com/pb33f/libopenapi/datamodel/low/v3" - "reflect" - "sync" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/v2" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "reflect" + "sync" ) // PathsChanges represents changes found between two Swagger or OpenAPI Paths Objects. type PathsChanges struct { - *PropertyChanges - PathItemsChanges map[string]*PathItemChanges `json:"pathItems,omitempty" yaml:"pathItems,omitempty"` - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + *PropertyChanges + PathItemsChanges map[string]*PathItemChanges `json:"pathItems,omitempty" yaml:"pathItems,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` } // GetAllChanges returns a slice of all changes made between Paths objects func (p *PathsChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, p.Changes...) - for k := range p.PathItemsChanges { - changes = append(changes, p.PathItemsChanges[k].GetAllChanges()...) - } - if p.ExtensionChanges != nil { - changes = append(changes, p.ExtensionChanges.GetAllChanges()...) - } - return changes + var changes []*Change + changes = append(changes, p.Changes...) + for k := range p.PathItemsChanges { + changes = append(changes, p.PathItemsChanges[k].GetAllChanges()...) + } + if p.ExtensionChanges != nil { + changes = append(changes, p.ExtensionChanges.GetAllChanges()...) + } + return changes } // TotalChanges returns the total number of changes between two Swagger or OpenAPI Paths Objects func (p *PathsChanges) TotalChanges() int { - c := p.PropertyChanges.TotalChanges() - for k := range p.PathItemsChanges { - c += p.PathItemsChanges[k].TotalChanges() - } - if p.ExtensionChanges != nil { - c += p.ExtensionChanges.TotalChanges() - } - return c + c := p.PropertyChanges.TotalChanges() + for k := range p.PathItemsChanges { + c += p.PathItemsChanges[k].TotalChanges() + } + if p.ExtensionChanges != nil { + c += p.ExtensionChanges.TotalChanges() + } + return c } // TotalBreakingChanges returns tht total number of changes found between two Swagger or OpenAPI Path Objects func (p *PathsChanges) TotalBreakingChanges() int { - c := p.PropertyChanges.TotalBreakingChanges() - for k := range p.PathItemsChanges { - c += p.PathItemsChanges[k].TotalBreakingChanges() - } - return c + c := p.PropertyChanges.TotalBreakingChanges() + for k := range p.PathItemsChanges { + c += p.PathItemsChanges[k].TotalBreakingChanges() + } + return c } // ComparePaths compares a left and right Swagger or OpenAPI Paths Object for changes. If found, returns a pointer // to a PathsChanges instance. Returns nil if nothing is found. func ComparePaths(l, r any) *PathsChanges { - var changes []*Change + var changes []*Change - pc := new(PathsChanges) - pathChanges := make(map[string]*PathItemChanges) + pc := new(PathsChanges) + pathChanges := make(map[string]*PathItemChanges) - // Swagger - if reflect.TypeOf(&v2.Paths{}) == reflect.TypeOf(l) && - reflect.TypeOf(&v2.Paths{}) == reflect.TypeOf(r) { + // Swagger + if reflect.TypeOf(&v2.Paths{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v2.Paths{}) == reflect.TypeOf(r) { - lPath := l.(*v2.Paths) - rPath := r.(*v2.Paths) + lPath := l.(*v2.Paths) + rPath := r.(*v2.Paths) - // perform hash check to avoid further processing - if low.AreEqual(lPath, rPath) { - return nil - } + // perform hash check to avoid further processing + if low.AreEqual(lPath, rPath) { + return nil + } - lKeys := make(map[string]low.ValueReference[*v2.PathItem]) - rKeys := make(map[string]low.ValueReference[*v2.PathItem]) - for k := range lPath.PathItems { - lKeys[k.Value] = lPath.PathItems[k] - } - for k := range rPath.PathItems { - rKeys[k.Value] = rPath.PathItems[k] - } + lKeys := make(map[string]low.ValueReference[*v2.PathItem]) + rKeys := make(map[string]low.ValueReference[*v2.PathItem]) + for k := range lPath.PathItems { + lKeys[k.Value] = lPath.PathItems[k] + } + for k := range rPath.PathItems { + rKeys[k.Value] = rPath.PathItems[k] + } - // run every comparison in a thread. - var mLock sync.Mutex - compare := func(path string, pChanges map[string]*PathItemChanges, l, r *v2.PathItem, doneChan chan bool) { - if !low.AreEqual(l, r) { - mLock.Lock() - pathChanges[path] = ComparePathItems(l, r) - mLock.Unlock() - } - doneChan <- true - } + // run every comparison in a thread. + var mLock sync.Mutex + compare := func(path string, pChanges map[string]*PathItemChanges, l, r *v2.PathItem, doneChan chan bool) { + if !low.AreEqual(l, r) { + mLock.Lock() + pathChanges[path] = ComparePathItems(l, r) + mLock.Unlock() + } + doneChan <- true + } - doneChan := make(chan bool) - pathsChecked := 0 + doneChan := make(chan bool) + pathsChecked := 0 - for k := range lKeys { - if _, ok := rKeys[k]; ok { - go compare(k, pathChanges, lKeys[k].Value, rKeys[k].Value, doneChan) - pathsChecked++ - continue - } - g, p := lPath.FindPathAndKey(k) - CreateChange(&changes, ObjectRemoved, v3.PathLabel, - g.KeyNode, nil, true, - p.Value, nil) - } + for k := range lKeys { + if _, ok := rKeys[k]; ok { + go compare(k, pathChanges, lKeys[k].Value, rKeys[k].Value, doneChan) + pathsChecked++ + continue + } + g, p := lPath.FindPathAndKey(k) + CreateChange(&changes, ObjectRemoved, v3.PathLabel, + g.KeyNode, nil, true, + p.Value, nil) + } - for k := range rKeys { - if _, ok := lKeys[k]; !ok { - g, p := rPath.FindPathAndKey(k) - CreateChange(&changes, ObjectAdded, v3.PathLabel, - nil, g.KeyNode, false, - nil, p.Value) - } - } + for k := range rKeys { + if _, ok := lKeys[k]; !ok { + g, p := rPath.FindPathAndKey(k) + CreateChange(&changes, ObjectAdded, v3.PathLabel, + nil, g.KeyNode, false, + nil, p.Value) + } + } - // wait for the things to be done. - completedChecks := 0 - for completedChecks < pathsChecked { - select { - case <-doneChan: - completedChecks++ - } - } - if len(pathChanges) > 0 { - pc.PathItemsChanges = pathChanges - } + // wait for the things to be done. + completedChecks := 0 + for completedChecks < pathsChecked { + select { + case <-doneChan: + completedChecks++ + } + } + if len(pathChanges) > 0 { + pc.PathItemsChanges = pathChanges + } - pc.ExtensionChanges = CompareExtensions(lPath.Extensions, rPath.Extensions) - } + pc.ExtensionChanges = CompareExtensions(lPath.Extensions, rPath.Extensions) + } - // OpenAPI - if reflect.TypeOf(&v3.Paths{}) == reflect.TypeOf(l) && - reflect.TypeOf(&v3.Paths{}) == reflect.TypeOf(r) { + // OpenAPI + if reflect.TypeOf(&v3.Paths{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v3.Paths{}) == reflect.TypeOf(r) { - lPath := l.(*v3.Paths) - rPath := r.(*v3.Paths) + lPath := l.(*v3.Paths) + rPath := r.(*v3.Paths) - // perform hash check to avoid further processing - if low.AreEqual(lPath, rPath) { - return nil - } + // perform hash check to avoid further processing + if low.AreEqual(lPath, rPath) { + return nil + } - lKeys := make(map[string]low.ValueReference[*v3.PathItem]) - rKeys := make(map[string]low.ValueReference[*v3.PathItem]) - for k := range lPath.PathItems { - lKeys[k.Value] = lPath.PathItems[k] - } - for k := range rPath.PathItems { - rKeys[k.Value] = rPath.PathItems[k] - } + lKeys := make(map[string]low.ValueReference[*v3.PathItem]) + rKeys := make(map[string]low.ValueReference[*v3.PathItem]) + for k := range lPath.PathItems { + lKeys[k.Value] = lPath.PathItems[k] + } + for k := range rPath.PathItems { + rKeys[k.Value] = rPath.PathItems[k] + } - // run every comparison in a thread. - var mLock sync.Mutex - compare := func(path string, pChanges map[string]*PathItemChanges, l, r *v3.PathItem, doneChan chan bool) { - if !low.AreEqual(l, r) { - mLock.Lock() - pathChanges[path] = ComparePathItems(l, r) - mLock.Unlock() - } - doneChan <- true - } + // run every comparison in a thread. + var mLock sync.Mutex + compare := func(path string, pChanges map[string]*PathItemChanges, l, r *v3.PathItem, doneChan chan bool) { + if !low.AreEqual(l, r) { + mLock.Lock() + pathChanges[path] = ComparePathItems(l, r) + mLock.Unlock() + } + doneChan <- true + } - doneChan := make(chan bool) - pathsChecked := 0 + doneChan := make(chan bool) + pathsChecked := 0 - for k := range lKeys { - if _, ok := rKeys[k]; ok { - go compare(k, pathChanges, lKeys[k].Value, rKeys[k].Value, doneChan) - pathsChecked++ - continue - } - g, p := lPath.FindPathAndKey(k) - CreateChange(&changes, ObjectRemoved, v3.PathLabel, - g.KeyNode, nil, true, - p.Value, nil) - } + for k := range lKeys { + if _, ok := rKeys[k]; ok { + go compare(k, pathChanges, lKeys[k].Value, rKeys[k].Value, doneChan) + pathsChecked++ + continue + } + g, p := lPath.FindPathAndKey(k) + CreateChange(&changes, ObjectRemoved, v3.PathLabel, + g.KeyNode, nil, true, + p.Value, nil) + } - for k := range rKeys { - if _, ok := lKeys[k]; !ok { - g, p := rPath.FindPathAndKey(k) - CreateChange(&changes, ObjectAdded, v3.PathLabel, - nil, g.KeyNode, false, - nil, p.Value) - } - } - // wait for the things to be done. - completedChecks := 0 - for completedChecks < pathsChecked { - select { - case <-doneChan: - completedChecks++ - } - } - if len(pathChanges) > 0 { - pc.PathItemsChanges = pathChanges - } + for k := range rKeys { + if _, ok := lKeys[k]; !ok { + g, p := rPath.FindPathAndKey(k) + CreateChange(&changes, ObjectAdded, v3.PathLabel, + nil, g.KeyNode, false, + nil, p.Value) + } + } + // wait for the things to be done. + completedChecks := 0 + for completedChecks < pathsChecked { + select { + case <-doneChan: + completedChecks++ + } + } + if len(pathChanges) > 0 { + pc.PathItemsChanges = pathChanges + } - pc.ExtensionChanges = CompareExtensions(lPath.Extensions, rPath.Extensions) - } - pc.PropertyChanges = NewPropertyChanges(changes) - return pc + pc.ExtensionChanges = CompareExtensions(lPath.Extensions, rPath.Extensions) + } + pc.PropertyChanges = NewPropertyChanges(changes) + return pc } diff --git a/what-changed/model/request_body.go b/what-changed/model/request_body.go index 1f209fa..0002db9 100644 --- a/what-changed/model/request_body.go +++ b/what-changed/model/request_body.go @@ -4,90 +4,90 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/v3" ) // RequestBodyChanges represents changes made between two OpenAPI RequestBody Objects type RequestBodyChanges struct { - *PropertyChanges - ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"` - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + *PropertyChanges + ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` } // GetAllChanges returns a slice of all changes made between RequestBody objects func (rb *RequestBodyChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, rb.Changes...) - for k := range rb.ContentChanges { - changes = append(changes, rb.ContentChanges[k].GetAllChanges()...) - } - if rb.ExtensionChanges != nil { - changes = append(changes, rb.ExtensionChanges.GetAllChanges()...) - } - return changes + var changes []*Change + changes = append(changes, rb.Changes...) + for k := range rb.ContentChanges { + changes = append(changes, rb.ContentChanges[k].GetAllChanges()...) + } + if rb.ExtensionChanges != nil { + changes = append(changes, rb.ExtensionChanges.GetAllChanges()...) + } + return changes } // TotalChanges returns the total number of changes found between two OpenAPI RequestBody objects func (rb *RequestBodyChanges) TotalChanges() int { - c := rb.PropertyChanges.TotalChanges() - for k := range rb.ContentChanges { - c += rb.ContentChanges[k].TotalChanges() - } - if rb.ExtensionChanges != nil { - c += rb.ExtensionChanges.TotalChanges() - } - return c + c := rb.PropertyChanges.TotalChanges() + for k := range rb.ContentChanges { + c += rb.ContentChanges[k].TotalChanges() + } + if rb.ExtensionChanges != nil { + c += rb.ExtensionChanges.TotalChanges() + } + return c } // TotalBreakingChanges returns the total number of breaking changes found between OpenAPI RequestBody objects func (rb *RequestBodyChanges) TotalBreakingChanges() int { - c := rb.PropertyChanges.TotalBreakingChanges() - for k := range rb.ContentChanges { - c += rb.ContentChanges[k].TotalBreakingChanges() - } - return c + c := rb.PropertyChanges.TotalBreakingChanges() + for k := range rb.ContentChanges { + c += rb.ContentChanges[k].TotalBreakingChanges() + } + return c } // CompareRequestBodies compares a left and right OpenAPI RequestBody object for changes. If found returns a pointer // to a RequestBodyChanges instance. Returns nil if nothing was found. func CompareRequestBodies(l, r *v3.RequestBody) *RequestBodyChanges { - if low.AreEqual(l, r) { - return nil - } + if low.AreEqual(l, r) { + return nil + } - var changes []*Change - var props []*PropertyCheck + var changes []*Change + var props []*PropertyCheck - // description - props = append(props, &PropertyCheck{ - LeftNode: l.Description.ValueNode, - RightNode: r.Description.ValueNode, - Label: v3.DescriptionLabel, - Changes: &changes, - Breaking: false, - Original: l, - New: r, - }) + // description + props = append(props, &PropertyCheck{ + LeftNode: l.Description.ValueNode, + RightNode: r.Description.ValueNode, + Label: v3.DescriptionLabel, + Changes: &changes, + Breaking: false, + Original: l, + New: r, + }) - // required - props = append(props, &PropertyCheck{ - LeftNode: l.Required.ValueNode, - RightNode: r.Required.ValueNode, - Label: v3.RequiredLabel, - Changes: &changes, - Breaking: true, - Original: l, - New: r, - }) + // required + props = append(props, &PropertyCheck{ + LeftNode: l.Required.ValueNode, + RightNode: r.Required.ValueNode, + Label: v3.RequiredLabel, + Changes: &changes, + Breaking: true, + Original: l, + New: r, + }) - CheckProperties(props) + CheckProperties(props) - rbc := new(RequestBodyChanges) - rbc.ContentChanges = CheckMapForChanges(l.Content.Value, r.Content.Value, - &changes, v3.ContentLabel, CompareMediaTypes) - rbc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) - rbc.PropertyChanges = NewPropertyChanges(changes) + rbc := new(RequestBodyChanges) + rbc.ContentChanges = CheckMapForChanges(l.Content.Value, r.Content.Value, + &changes, v3.ContentLabel, CompareMediaTypes) + rbc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) + rbc.PropertyChanges = NewPropertyChanges(changes) - return rbc + return rbc } diff --git a/what-changed/model/request_body_test.go b/what-changed/model/request_body_test.go index cd85d8b..6d25cb1 100644 --- a/what-changed/model/request_body_test.go +++ b/what-changed/model/request_body_test.go @@ -4,49 +4,49 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/v3" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "testing" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "testing" ) func TestCompareRequestBodies(t *testing.T) { - left := `description: something + left := `description: something required: true content: application/json: schema: type: int` - right := `description: something + right := `description: something required: true content: application/json: schema: type: int` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.RequestBody - var rDoc v3.RequestBody - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.RequestBody + var rDoc v3.RequestBody + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareRequestBodies(&lDoc, &rDoc) - assert.Nil(t, extChanges) + // compare. + extChanges := CompareRequestBodies(&lDoc, &rDoc) + assert.Nil(t, extChanges) } func TestCompareRequestBodies_Modified(t *testing.T) { - left := `description: something + left := `description: something required: true x-pizza: thin content: @@ -54,7 +54,7 @@ content: schema: type: int` - right := `x-pizza: oven + right := `x-pizza: oven description: nothing required: false content: @@ -62,22 +62,22 @@ content: schema: type: string` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.RequestBody - var rDoc v3.RequestBody - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.RequestBody + var rDoc v3.RequestBody + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareRequestBodies(&lDoc, &rDoc) + // compare. + extChanges := CompareRequestBodies(&lDoc, &rDoc) - assert.Equal(t, 4, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 4) - assert.Equal(t, 2, extChanges.TotalBreakingChanges()) + assert.Equal(t, 4, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 4) + assert.Equal(t, 2, extChanges.TotalBreakingChanges()) } diff --git a/what-changed/model/response.go b/what-changed/model/response.go index beef347..ac860a9 100644 --- a/what-changed/model/response.go +++ b/what-changed/model/response.go @@ -4,196 +4,196 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - v3 "github.com/pb33f/libopenapi/datamodel/low/v3" - "reflect" + "github.com/pb33f/libopenapi/datamodel/low" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "reflect" ) import ( - "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/datamodel/low/v2" ) // ResponseChanges represents changes found between two Swagger or OpenAPI Response objects. type ResponseChanges struct { - *PropertyChanges - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` - HeadersChanges map[string]*HeaderChanges `json:"headers,omitempty" yaml:"headers,omitempty"` + *PropertyChanges + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + HeadersChanges map[string]*HeaderChanges `json:"headers,omitempty" yaml:"headers,omitempty"` - // Swagger Response Properties. - SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"` - ExamplesChanges *ExamplesChanges `json:"examples,omitempty" yaml:"examples,omitempty"` + // Swagger Response Properties. + SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"` + ExamplesChanges *ExamplesChanges `json:"examples,omitempty" yaml:"examples,omitempty"` - // OpenAPI Response Properties. - ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"` - LinkChanges map[string]*LinkChanges `json:"links,omitempty" yaml:"links,omitempty"` + // OpenAPI Response Properties. + ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"` + LinkChanges map[string]*LinkChanges `json:"links,omitempty" yaml:"links,omitempty"` } // GetAllChanges returns a slice of all changes made between RequestBody objects func (r *ResponseChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, r.Changes...) - if r.ExtensionChanges != nil { - changes = append(changes, r.ExtensionChanges.GetAllChanges()...) - } - if r.SchemaChanges != nil { - changes = append(changes, r.SchemaChanges.GetAllChanges()...) - } - if r.ExamplesChanges != nil { - changes = append(changes, r.ExamplesChanges.GetAllChanges()...) - } - for k := range r.HeadersChanges { - changes = append(changes, r.HeadersChanges[k].GetAllChanges()...) - } - for k := range r.ContentChanges { - changes = append(changes, r.ContentChanges[k].GetAllChanges()...) - } - for k := range r.LinkChanges { - changes = append(changes, r.LinkChanges[k].GetAllChanges()...) - } - return changes + var changes []*Change + changes = append(changes, r.Changes...) + if r.ExtensionChanges != nil { + changes = append(changes, r.ExtensionChanges.GetAllChanges()...) + } + if r.SchemaChanges != nil { + changes = append(changes, r.SchemaChanges.GetAllChanges()...) + } + if r.ExamplesChanges != nil { + changes = append(changes, r.ExamplesChanges.GetAllChanges()...) + } + for k := range r.HeadersChanges { + changes = append(changes, r.HeadersChanges[k].GetAllChanges()...) + } + for k := range r.ContentChanges { + changes = append(changes, r.ContentChanges[k].GetAllChanges()...) + } + for k := range r.LinkChanges { + changes = append(changes, r.LinkChanges[k].GetAllChanges()...) + } + return changes } // TotalChanges returns the total number of changes found between two Swagger or OpenAPI Response Objects func (r *ResponseChanges) TotalChanges() int { - c := r.PropertyChanges.TotalChanges() - if r.ExtensionChanges != nil { - c += r.ExtensionChanges.TotalChanges() - } - if r.SchemaChanges != nil { - c += r.SchemaChanges.TotalChanges() - } - if r.ExamplesChanges != nil { - c += r.ExamplesChanges.TotalChanges() - } - for k := range r.HeadersChanges { - c += r.HeadersChanges[k].TotalChanges() - } - for k := range r.ContentChanges { - c += r.ContentChanges[k].TotalChanges() - } - for k := range r.LinkChanges { - c += r.LinkChanges[k].TotalChanges() - } - return c + c := r.PropertyChanges.TotalChanges() + if r.ExtensionChanges != nil { + c += r.ExtensionChanges.TotalChanges() + } + if r.SchemaChanges != nil { + c += r.SchemaChanges.TotalChanges() + } + if r.ExamplesChanges != nil { + c += r.ExamplesChanges.TotalChanges() + } + for k := range r.HeadersChanges { + c += r.HeadersChanges[k].TotalChanges() + } + for k := range r.ContentChanges { + c += r.ContentChanges[k].TotalChanges() + } + for k := range r.LinkChanges { + c += r.LinkChanges[k].TotalChanges() + } + return c } // TotalBreakingChanges returns the total number of breaking changes found between two swagger or OpenAPI // Response Objects func (r *ResponseChanges) TotalBreakingChanges() int { - c := r.PropertyChanges.TotalBreakingChanges() - if r.SchemaChanges != nil { - c += r.SchemaChanges.TotalBreakingChanges() - } - for k := range r.HeadersChanges { - c += r.HeadersChanges[k].TotalBreakingChanges() - } - for k := range r.ContentChanges { - c += r.ContentChanges[k].TotalBreakingChanges() - } - for k := range r.LinkChanges { - c += r.LinkChanges[k].TotalBreakingChanges() - } - return c + c := r.PropertyChanges.TotalBreakingChanges() + if r.SchemaChanges != nil { + c += r.SchemaChanges.TotalBreakingChanges() + } + for k := range r.HeadersChanges { + c += r.HeadersChanges[k].TotalBreakingChanges() + } + for k := range r.ContentChanges { + c += r.ContentChanges[k].TotalBreakingChanges() + } + for k := range r.LinkChanges { + c += r.LinkChanges[k].TotalBreakingChanges() + } + return c } // CompareResponseV2 is a Swagger type safe proxy for CompareResponse func CompareResponseV2(l, r *v2.Response) *ResponseChanges { - return CompareResponse(l, r) + return CompareResponse(l, r) } // CompareResponseV3 is an OpenAPI type safe proxy for CompareResponse func CompareResponseV3(l, r *v3.Response) *ResponseChanges { - return CompareResponse(l, r) + return CompareResponse(l, r) } // CompareResponse compares a left and right Swagger or OpenAPI Response object. If anything is found // a pointer to a ResponseChanges is returned, otherwise it returns nil. func CompareResponse(l, r any) *ResponseChanges { - var changes []*Change - var props []*PropertyCheck + var changes []*Change + var props []*PropertyCheck - rc := new(ResponseChanges) + rc := new(ResponseChanges) - if reflect.TypeOf(&v2.Response{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Response{}) == reflect.TypeOf(r) { + if reflect.TypeOf(&v2.Response{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Response{}) == reflect.TypeOf(r) { - lResponse := l.(*v2.Response) - rResponse := r.(*v2.Response) + lResponse := l.(*v2.Response) + rResponse := r.(*v2.Response) - // perform hash check to avoid further processing - if low.AreEqual(lResponse, rResponse) { - return nil - } + // perform hash check to avoid further processing + if low.AreEqual(lResponse, rResponse) { + return nil + } - // description - addPropertyCheck(&props, lResponse.Description.ValueNode, rResponse.Description.ValueNode, - lResponse.Description.Value, rResponse.Description.Value, &changes, v3.DescriptionLabel, false) + // description + addPropertyCheck(&props, lResponse.Description.ValueNode, rResponse.Description.ValueNode, + lResponse.Description.Value, rResponse.Description.Value, &changes, v3.DescriptionLabel, false) - if !lResponse.Schema.IsEmpty() && !rResponse.Schema.IsEmpty() { - rc.SchemaChanges = CompareSchemas(lResponse.Schema.Value, rResponse.Schema.Value) - } - if !lResponse.Schema.IsEmpty() && rResponse.Schema.IsEmpty() { - CreateChange(&changes, ObjectRemoved, v3.SchemaLabel, - lResponse.Schema.ValueNode, nil, true, - lResponse.Schema.Value, nil) - } - if lResponse.Schema.IsEmpty() && !rResponse.Schema.IsEmpty() { - CreateChange(&changes, ObjectAdded, v3.SchemaLabel, - nil, rResponse.Schema.ValueNode, true, - nil, rResponse.Schema.Value) - } + if !lResponse.Schema.IsEmpty() && !rResponse.Schema.IsEmpty() { + rc.SchemaChanges = CompareSchemas(lResponse.Schema.Value, rResponse.Schema.Value) + } + if !lResponse.Schema.IsEmpty() && rResponse.Schema.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.SchemaLabel, + lResponse.Schema.ValueNode, nil, true, + lResponse.Schema.Value, nil) + } + if lResponse.Schema.IsEmpty() && !rResponse.Schema.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.SchemaLabel, + nil, rResponse.Schema.ValueNode, true, + nil, rResponse.Schema.Value) + } - rc.HeadersChanges = - CheckMapForChanges(lResponse.Headers.Value, rResponse.Headers.Value, - &changes, v3.HeadersLabel, CompareHeadersV2) + rc.HeadersChanges = + CheckMapForChanges(lResponse.Headers.Value, rResponse.Headers.Value, + &changes, v3.HeadersLabel, CompareHeadersV2) - if !lResponse.Examples.IsEmpty() && !rResponse.Examples.IsEmpty() { - rc.ExamplesChanges = CompareExamplesV2(lResponse.Examples.Value, rResponse.Examples.Value) - } - if !lResponse.Examples.IsEmpty() && rResponse.Examples.IsEmpty() { - CreateChange(&changes, PropertyRemoved, v3.ExamplesLabel, - lResponse.Schema.ValueNode, nil, false, - lResponse.Schema.Value, nil) - } - if lResponse.Examples.IsEmpty() && !rResponse.Examples.IsEmpty() { - CreateChange(&changes, ObjectAdded, v3.ExamplesLabel, - nil, rResponse.Schema.ValueNode, false, - nil, lResponse.Schema.Value) - } + if !lResponse.Examples.IsEmpty() && !rResponse.Examples.IsEmpty() { + rc.ExamplesChanges = CompareExamplesV2(lResponse.Examples.Value, rResponse.Examples.Value) + } + if !lResponse.Examples.IsEmpty() && rResponse.Examples.IsEmpty() { + CreateChange(&changes, PropertyRemoved, v3.ExamplesLabel, + lResponse.Schema.ValueNode, nil, false, + lResponse.Schema.Value, nil) + } + if lResponse.Examples.IsEmpty() && !rResponse.Examples.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.ExamplesLabel, + nil, rResponse.Schema.ValueNode, false, + nil, lResponse.Schema.Value) + } - rc.ExtensionChanges = CompareExtensions(lResponse.Extensions, rResponse.Extensions) - } + rc.ExtensionChanges = CompareExtensions(lResponse.Extensions, rResponse.Extensions) + } - if reflect.TypeOf(&v3.Response{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Response{}) == reflect.TypeOf(r) { + if reflect.TypeOf(&v3.Response{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Response{}) == reflect.TypeOf(r) { - lResponse := l.(*v3.Response) - rResponse := r.(*v3.Response) + lResponse := l.(*v3.Response) + rResponse := r.(*v3.Response) - // perform hash check to avoid further processing - if low.AreEqual(lResponse, rResponse) { - return nil - } + // perform hash check to avoid further processing + if low.AreEqual(lResponse, rResponse) { + return nil + } - // description - addPropertyCheck(&props, lResponse.Description.ValueNode, rResponse.Description.ValueNode, - lResponse.Description.Value, lResponse.Description.Value, &changes, v3.DescriptionLabel, false) + // description + addPropertyCheck(&props, lResponse.Description.ValueNode, rResponse.Description.ValueNode, + lResponse.Description.Value, lResponse.Description.Value, &changes, v3.DescriptionLabel, false) - rc.HeadersChanges = - CheckMapForChanges(lResponse.Headers.Value, rResponse.Headers.Value, - &changes, v3.HeadersLabel, CompareHeadersV3) + rc.HeadersChanges = + CheckMapForChanges(lResponse.Headers.Value, rResponse.Headers.Value, + &changes, v3.HeadersLabel, CompareHeadersV3) - rc.ContentChanges = - CheckMapForChanges(lResponse.Content.Value, rResponse.Content.Value, - &changes, v3.ContentLabel, CompareMediaTypes) + rc.ContentChanges = + CheckMapForChanges(lResponse.Content.Value, rResponse.Content.Value, + &changes, v3.ContentLabel, CompareMediaTypes) - rc.LinkChanges = - CheckMapForChanges(lResponse.Links.Value, rResponse.Links.Value, - &changes, v3.LinksLabel, CompareLinks) + rc.LinkChanges = + CheckMapForChanges(lResponse.Links.Value, rResponse.Links.Value, + &changes, v3.LinksLabel, CompareLinks) - rc.ExtensionChanges = CompareExtensions(lResponse.Extensions, rResponse.Extensions) - } + rc.ExtensionChanges = CompareExtensions(lResponse.Extensions, rResponse.Extensions) + } - CheckProperties(props) - rc.PropertyChanges = NewPropertyChanges(changes) - return rc + CheckProperties(props) + rc.PropertyChanges = NewPropertyChanges(changes) + return rc } diff --git a/what-changed/model/responses.go b/what-changed/model/responses.go index 03c8812..e47c36b 100644 --- a/what-changed/model/responses.go +++ b/what-changed/model/responses.go @@ -4,137 +4,137 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/v2" - "github.com/pb33f/libopenapi/datamodel/low/v3" - "reflect" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/datamodel/low/v3" + "reflect" ) // ResponsesChanges represents changes made between two Swagger or OpenAPI Responses objects. type ResponsesChanges struct { - *PropertyChanges - ResponseChanges map[string]*ResponseChanges `json:"response,omitempty" yaml:"response,omitempty"` - DefaultChanges *ResponseChanges `json:"default,omitempty" yaml:"default,omitempty"` - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + *PropertyChanges + ResponseChanges map[string]*ResponseChanges `json:"response,omitempty" yaml:"response,omitempty"` + DefaultChanges *ResponseChanges `json:"default,omitempty" yaml:"default,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` } // GetAllChanges returns a slice of all changes made between Responses objects func (r *ResponsesChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, r.Changes...) - for k := range r.ResponseChanges { - changes = append(changes, r.ResponseChanges[k].GetAllChanges()...) - } - if r.DefaultChanges != nil { - changes = append(changes, r.DefaultChanges.GetAllChanges()...) - } - if r.ExtensionChanges != nil { - changes = append(changes, r.ExtensionChanges.GetAllChanges()...) - } - return changes + var changes []*Change + changes = append(changes, r.Changes...) + for k := range r.ResponseChanges { + changes = append(changes, r.ResponseChanges[k].GetAllChanges()...) + } + if r.DefaultChanges != nil { + changes = append(changes, r.DefaultChanges.GetAllChanges()...) + } + if r.ExtensionChanges != nil { + changes = append(changes, r.ExtensionChanges.GetAllChanges()...) + } + return changes } // TotalChanges returns the total number of changes found between two Swagger or OpenAPI Responses objects func (r *ResponsesChanges) TotalChanges() int { - c := r.PropertyChanges.TotalChanges() - for k := range r.ResponseChanges { - c += r.ResponseChanges[k].TotalChanges() - } - if r.DefaultChanges != nil { - c += r.DefaultChanges.TotalChanges() - } - if r.ExtensionChanges != nil { - c += r.ExtensionChanges.TotalChanges() - } - return c + c := r.PropertyChanges.TotalChanges() + for k := range r.ResponseChanges { + c += r.ResponseChanges[k].TotalChanges() + } + if r.DefaultChanges != nil { + c += r.DefaultChanges.TotalChanges() + } + if r.ExtensionChanges != nil { + c += r.ExtensionChanges.TotalChanges() + } + return c } // TotalBreakingChanges returns the total number of changes found between two Swagger or OpenAPI // Responses Objects func (r *ResponsesChanges) TotalBreakingChanges() int { - c := r.PropertyChanges.TotalBreakingChanges() - for k := range r.ResponseChanges { - c += r.ResponseChanges[k].TotalBreakingChanges() - } - if r.DefaultChanges != nil { - c += r.DefaultChanges.TotalBreakingChanges() - } - return c + c := r.PropertyChanges.TotalBreakingChanges() + for k := range r.ResponseChanges { + c += r.ResponseChanges[k].TotalBreakingChanges() + } + if r.DefaultChanges != nil { + c += r.DefaultChanges.TotalBreakingChanges() + } + return c } // CompareResponses compares a left and right Swagger or OpenAPI Responses object for any changes. If found // returns a pointer to ResponsesChanges, or returns nil. func CompareResponses(l, r any) *ResponsesChanges { - var changes []*Change + var changes []*Change - rc := new(ResponsesChanges) + rc := new(ResponsesChanges) - // swagger - if reflect.TypeOf(&v2.Responses{}) == reflect.TypeOf(l) && - reflect.TypeOf(&v2.Responses{}) == reflect.TypeOf(r) { + // swagger + if reflect.TypeOf(&v2.Responses{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v2.Responses{}) == reflect.TypeOf(r) { - lResponses := l.(*v2.Responses) - rResponses := r.(*v2.Responses) + lResponses := l.(*v2.Responses) + rResponses := r.(*v2.Responses) - // perform hash check to avoid further processing - if low.AreEqual(lResponses, rResponses) { - return nil - } + // perform hash check to avoid further processing + if low.AreEqual(lResponses, rResponses) { + return nil + } - if !lResponses.Default.IsEmpty() && !rResponses.Default.IsEmpty() { - rc.DefaultChanges = CompareResponse(lResponses.Default.Value, rResponses.Default.Value) - } - if !lResponses.Default.IsEmpty() && rResponses.Default.IsEmpty() { - CreateChange(&changes, ObjectRemoved, v3.DefaultLabel, - lResponses.Default.ValueNode, nil, true, - lResponses.Default.Value, nil) - } - if lResponses.Default.IsEmpty() && !rResponses.Default.IsEmpty() { - CreateChange(&changes, ObjectAdded, v3.DefaultLabel, - nil, rResponses.Default.ValueNode, false, - nil, lResponses.Default.Value) - } + if !lResponses.Default.IsEmpty() && !rResponses.Default.IsEmpty() { + rc.DefaultChanges = CompareResponse(lResponses.Default.Value, rResponses.Default.Value) + } + if !lResponses.Default.IsEmpty() && rResponses.Default.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.DefaultLabel, + lResponses.Default.ValueNode, nil, true, + lResponses.Default.Value, nil) + } + if lResponses.Default.IsEmpty() && !rResponses.Default.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.DefaultLabel, + nil, rResponses.Default.ValueNode, false, + nil, lResponses.Default.Value) + } - rc.ResponseChanges = CheckMapForChanges(lResponses.Codes, rResponses.Codes, - &changes, v3.CodesLabel, CompareResponseV2) + rc.ResponseChanges = CheckMapForChanges(lResponses.Codes, rResponses.Codes, + &changes, v3.CodesLabel, CompareResponseV2) - rc.ExtensionChanges = CompareExtensions(lResponses.Extensions, rResponses.Extensions) - } + rc.ExtensionChanges = CompareExtensions(lResponses.Extensions, rResponses.Extensions) + } - // openapi - if reflect.TypeOf(&v3.Responses{}) == reflect.TypeOf(l) && - reflect.TypeOf(&v3.Responses{}) == reflect.TypeOf(r) { + // openapi + if reflect.TypeOf(&v3.Responses{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v3.Responses{}) == reflect.TypeOf(r) { - lResponses := l.(*v3.Responses) - rResponses := r.(*v3.Responses) + lResponses := l.(*v3.Responses) + rResponses := r.(*v3.Responses) - //perform hash check to avoid further processing - if low.AreEqual(lResponses, rResponses) { - return nil - } + //perform hash check to avoid further processing + if low.AreEqual(lResponses, rResponses) { + return nil + } - if !lResponses.Default.IsEmpty() && !rResponses.Default.IsEmpty() { - rc.DefaultChanges = CompareResponse(lResponses.Default.Value, rResponses.Default.Value) - } - if !lResponses.Default.IsEmpty() && rResponses.Default.IsEmpty() { - CreateChange(&changes, ObjectRemoved, v3.DefaultLabel, - lResponses.Default.ValueNode, nil, true, - lResponses.Default.Value, nil) - } - if lResponses.Default.IsEmpty() && !rResponses.Default.IsEmpty() { - CreateChange(&changes, ObjectAdded, v3.DefaultLabel, - nil, rResponses.Default.ValueNode, false, - nil, lResponses.Default.Value) - } + if !lResponses.Default.IsEmpty() && !rResponses.Default.IsEmpty() { + rc.DefaultChanges = CompareResponse(lResponses.Default.Value, rResponses.Default.Value) + } + if !lResponses.Default.IsEmpty() && rResponses.Default.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.DefaultLabel, + lResponses.Default.ValueNode, nil, true, + lResponses.Default.Value, nil) + } + if lResponses.Default.IsEmpty() && !rResponses.Default.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.DefaultLabel, + nil, rResponses.Default.ValueNode, false, + nil, lResponses.Default.Value) + } - rc.ResponseChanges = CheckMapForChanges(lResponses.Codes, rResponses.Codes, - &changes, v3.CodesLabel, CompareResponseV3) + rc.ResponseChanges = CheckMapForChanges(lResponses.Codes, rResponses.Codes, + &changes, v3.CodesLabel, CompareResponseV3) - rc.ExtensionChanges = CompareExtensions(lResponses.Extensions, rResponses.Extensions) + rc.ExtensionChanges = CompareExtensions(lResponses.Extensions, rResponses.Extensions) - } + } - rc.PropertyChanges = NewPropertyChanges(changes) - return rc + rc.PropertyChanges = NewPropertyChanges(changes) + return rc } diff --git a/what-changed/model/schema.go b/what-changed/model/schema.go index a24d723..582f57f 100644 --- a/what-changed/model/schema.go +++ b/what-changed/model/schema.go @@ -4,14 +4,14 @@ package model import ( - "fmt" - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/base" - v3 "github.com/pb33f/libopenapi/datamodel/low/v3" - "github.com/pb33f/libopenapi/utils" - "gopkg.in/yaml.v3" - "sort" - "sync" + "fmt" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/utils" + "gopkg.in/yaml.v3" + "sort" + "sync" ) // SchemaChanges represent all changes to a base.Schema OpenAPI object. These changes are represented @@ -21,1217 +21,1233 @@ import ( // changes, and not the child for example, adding a new schema to `anyOf` will create a new change result in // PropertyChanges.Changes, and not in the AnyOfChanges property. type SchemaChanges struct { - *PropertyChanges - DiscriminatorChanges *DiscriminatorChanges `json:"discriminator,omitempty" yaml:"discriminator,omitempty"` - AllOfChanges []*SchemaChanges `json:"allOf,omitempty" yaml:"allOf,omitempty"` - AnyOfChanges []*SchemaChanges `json:"anyOf,omitempty" yaml:"anyOf,omitempty"` - OneOfChanges []*SchemaChanges `json:"oneOf,omitempty" yaml:"oneOf,omitempty"` - NotChanges *SchemaChanges `json:"not,omitempty" yaml:"not,omitempty"` - ItemsChanges *SchemaChanges `json:"items,omitempty" yaml:"items,omitempty"` - SchemaPropertyChanges map[string]*SchemaChanges `json:"properties,omitempty" yaml:"properties,omitempty"` - ExternalDocChanges *ExternalDocChanges `json:"externalDoc,omitempty" yaml:"externalDoc,omitempty"` - XMLChanges *XMLChanges `json:"xml,omitempty" yaml:"xml,omitempty"` - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + *PropertyChanges + DiscriminatorChanges *DiscriminatorChanges `json:"discriminator,omitempty" yaml:"discriminator,omitempty"` + AllOfChanges []*SchemaChanges `json:"allOf,omitempty" yaml:"allOf,omitempty"` + AnyOfChanges []*SchemaChanges `json:"anyOf,omitempty" yaml:"anyOf,omitempty"` + OneOfChanges []*SchemaChanges `json:"oneOf,omitempty" yaml:"oneOf,omitempty"` + NotChanges *SchemaChanges `json:"not,omitempty" yaml:"not,omitempty"` + ItemsChanges *SchemaChanges `json:"items,omitempty" yaml:"items,omitempty"` + SchemaPropertyChanges map[string]*SchemaChanges `json:"properties,omitempty" yaml:"properties,omitempty"` + ExternalDocChanges *ExternalDocChanges `json:"externalDoc,omitempty" yaml:"externalDoc,omitempty"` + XMLChanges *XMLChanges `json:"xml,omitempty" yaml:"xml,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` - // 3.1 specifics - IfChanges *SchemaChanges `json:"if,omitempty" yaml:"if,omitempty"` - ElseChanges *SchemaChanges `json:"else,omitempty" yaml:"else,omitempty"` - ThenChanges *SchemaChanges `json:"then,omitempty" yaml:"then,omitempty"` - PropertyNamesChanges *SchemaChanges `json:"propertyNames,omitempty" yaml:"propertyNames,omitempty"` - ContainsChanges *SchemaChanges `json:"contains,omitempty" yaml:"contains,omitempty"` - UnevaluatedItemsChanges *SchemaChanges `json:"unevaluatedItems,omitempty" yaml:"unevaluatedItems,omitempty"` - UnevaluatedPropertiesChanges *SchemaChanges `json:"unevaluatedProperties,omitempty" yaml:"unevaluatedProperties,omitempty"` - DependentSchemasChanges map[string]*SchemaChanges `json:"dependentSchemas,omitempty" yaml:"dependentSchemas,omitempty"` - PatternPropertiesChanges map[string]*SchemaChanges `json:"patternProperties,omitempty" yaml:"patternProperties,omitempty"` + // 3.1 specifics + IfChanges *SchemaChanges `json:"if,omitempty" yaml:"if,omitempty"` + ElseChanges *SchemaChanges `json:"else,omitempty" yaml:"else,omitempty"` + ThenChanges *SchemaChanges `json:"then,omitempty" yaml:"then,omitempty"` + PropertyNamesChanges *SchemaChanges `json:"propertyNames,omitempty" yaml:"propertyNames,omitempty"` + ContainsChanges *SchemaChanges `json:"contains,omitempty" yaml:"contains,omitempty"` + UnevaluatedItemsChanges *SchemaChanges `json:"unevaluatedItems,omitempty" yaml:"unevaluatedItems,omitempty"` + UnevaluatedPropertiesChanges *SchemaChanges `json:"unevaluatedProperties,omitempty" yaml:"unevaluatedProperties,omitempty"` + DependentSchemasChanges map[string]*SchemaChanges `json:"dependentSchemas,omitempty" yaml:"dependentSchemas,omitempty"` + PatternPropertiesChanges map[string]*SchemaChanges `json:"patternProperties,omitempty" yaml:"patternProperties,omitempty"` } // GetAllChanges returns a slice of all changes made between Responses objects func (s *SchemaChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, s.Changes...) - if s.DiscriminatorChanges != nil { - changes = append(changes, s.DiscriminatorChanges.GetAllChanges()...) - } - if len(s.AllOfChanges) > 0 { - for n := range s.AllOfChanges { - if s.AllOfChanges[n] != nil { - changes = append(changes, s.AllOfChanges[n].GetAllChanges()...) - } - } - } - if len(s.AnyOfChanges) > 0 { - for n := range s.AnyOfChanges { - if s.AnyOfChanges[n] != nil { - changes = append(changes, s.AnyOfChanges[n].GetAllChanges()...) - } - } - } - if len(s.OneOfChanges) > 0 { - for n := range s.OneOfChanges { - if s.OneOfChanges[n] != nil { - changes = append(changes, s.OneOfChanges[n].GetAllChanges()...) - } - } - } - if s.NotChanges != nil { - changes = append(changes, s.NotChanges.GetAllChanges()...) - } - if s.ItemsChanges != nil { - changes = append(changes, s.ItemsChanges.GetAllChanges()...) - } - if s.IfChanges != nil { - changes = append(changes, s.IfChanges.GetAllChanges()...) - } - if s.ElseChanges != nil { - changes = append(changes, s.ElseChanges.GetAllChanges()...) - } - if s.ThenChanges != nil { - changes = append(changes, s.ThenChanges.GetAllChanges()...) - } - if s.PropertyNamesChanges != nil { - changes = append(changes, s.PropertyNamesChanges.GetAllChanges()...) - } - if s.ContainsChanges != nil { - changes = append(changes, s.ContainsChanges.GetAllChanges()...) - } - if s.UnevaluatedItemsChanges != nil { - changes = append(changes, s.UnevaluatedItemsChanges.GetAllChanges()...) - } - if s.UnevaluatedPropertiesChanges != nil { - changes = append(changes, s.UnevaluatedPropertiesChanges.GetAllChanges()...) - } - if s.SchemaPropertyChanges != nil { - for n := range s.SchemaPropertyChanges { - if s.SchemaPropertyChanges[n] != nil { - changes = append(changes, s.SchemaPropertyChanges[n].GetAllChanges()...) - } - } - } - if s.DependentSchemasChanges != nil { - for n := range s.DependentSchemasChanges { - if s.DependentSchemasChanges[n] != nil { - changes = append(changes, s.DependentSchemasChanges[n].GetAllChanges()...) - } - } - } - if s.PatternPropertiesChanges != nil { - for n := range s.PatternPropertiesChanges { - if s.PatternPropertiesChanges[n] != nil { - changes = append(changes, s.PatternPropertiesChanges[n].GetAllChanges()...) - } - } - } - if s.ExternalDocChanges != nil { - changes = append(changes, s.ExternalDocChanges.GetAllChanges()...) - } - if s.XMLChanges != nil { - changes = append(changes, s.XMLChanges.GetAllChanges()...) - } - if s.ExtensionChanges != nil { - changes = append(changes, s.ExtensionChanges.GetAllChanges()...) - } - return changes + var changes []*Change + changes = append(changes, s.Changes...) + if s.DiscriminatorChanges != nil { + changes = append(changes, s.DiscriminatorChanges.GetAllChanges()...) + } + if len(s.AllOfChanges) > 0 { + for n := range s.AllOfChanges { + if s.AllOfChanges[n] != nil { + changes = append(changes, s.AllOfChanges[n].GetAllChanges()...) + } + } + } + if len(s.AnyOfChanges) > 0 { + for n := range s.AnyOfChanges { + if s.AnyOfChanges[n] != nil { + changes = append(changes, s.AnyOfChanges[n].GetAllChanges()...) + } + } + } + if len(s.OneOfChanges) > 0 { + for n := range s.OneOfChanges { + if s.OneOfChanges[n] != nil { + changes = append(changes, s.OneOfChanges[n].GetAllChanges()...) + } + } + } + if s.NotChanges != nil { + changes = append(changes, s.NotChanges.GetAllChanges()...) + } + if s.ItemsChanges != nil { + changes = append(changes, s.ItemsChanges.GetAllChanges()...) + } + if s.IfChanges != nil { + changes = append(changes, s.IfChanges.GetAllChanges()...) + } + if s.ElseChanges != nil { + changes = append(changes, s.ElseChanges.GetAllChanges()...) + } + if s.ThenChanges != nil { + changes = append(changes, s.ThenChanges.GetAllChanges()...) + } + if s.PropertyNamesChanges != nil { + changes = append(changes, s.PropertyNamesChanges.GetAllChanges()...) + } + if s.ContainsChanges != nil { + changes = append(changes, s.ContainsChanges.GetAllChanges()...) + } + if s.UnevaluatedItemsChanges != nil { + changes = append(changes, s.UnevaluatedItemsChanges.GetAllChanges()...) + } + if s.UnevaluatedPropertiesChanges != nil { + changes = append(changes, s.UnevaluatedPropertiesChanges.GetAllChanges()...) + } + if s.SchemaPropertyChanges != nil { + for n := range s.SchemaPropertyChanges { + if s.SchemaPropertyChanges[n] != nil { + changes = append(changes, s.SchemaPropertyChanges[n].GetAllChanges()...) + } + } + } + if s.DependentSchemasChanges != nil { + for n := range s.DependentSchemasChanges { + if s.DependentSchemasChanges[n] != nil { + changes = append(changes, s.DependentSchemasChanges[n].GetAllChanges()...) + } + } + } + if s.PatternPropertiesChanges != nil { + for n := range s.PatternPropertiesChanges { + if s.PatternPropertiesChanges[n] != nil { + changes = append(changes, s.PatternPropertiesChanges[n].GetAllChanges()...) + } + } + } + if s.ExternalDocChanges != nil { + changes = append(changes, s.ExternalDocChanges.GetAllChanges()...) + } + if s.XMLChanges != nil { + changes = append(changes, s.XMLChanges.GetAllChanges()...) + } + if s.ExtensionChanges != nil { + changes = append(changes, s.ExtensionChanges.GetAllChanges()...) + } + return changes } // TotalChanges returns a count of the total number of changes made to this schema and all sub-schemas func (s *SchemaChanges) TotalChanges() int { - t := s.PropertyChanges.TotalChanges() - if s.DiscriminatorChanges != nil { - t += s.DiscriminatorChanges.TotalChanges() - } - if len(s.AllOfChanges) > 0 { - for n := range s.AllOfChanges { - t += s.AllOfChanges[n].TotalChanges() - } - } - if len(s.AnyOfChanges) > 0 { - for n := range s.AnyOfChanges { - if s.AnyOfChanges[n] != nil { - t += s.AnyOfChanges[n].TotalChanges() - } - } - } - if len(s.OneOfChanges) > 0 { - for n := range s.OneOfChanges { - t += s.OneOfChanges[n].TotalChanges() - } - } - if s.NotChanges != nil { - t += s.NotChanges.TotalChanges() - } - if s.ItemsChanges != nil { - t += s.ItemsChanges.TotalChanges() - } - if s.IfChanges != nil { - t += s.IfChanges.TotalChanges() - } - if s.ElseChanges != nil { - t += s.ElseChanges.TotalChanges() - } - if s.ThenChanges != nil { - t += s.ThenChanges.TotalChanges() - } - if s.PropertyNamesChanges != nil { - t += s.PropertyNamesChanges.TotalChanges() - } - if s.ContainsChanges != nil { - t += s.ContainsChanges.TotalChanges() - } - if s.UnevaluatedItemsChanges != nil { - t += s.UnevaluatedItemsChanges.TotalChanges() - } - if s.UnevaluatedPropertiesChanges != nil { - t += s.UnevaluatedPropertiesChanges.TotalChanges() - } - if s.SchemaPropertyChanges != nil { - for n := range s.SchemaPropertyChanges { - if s.SchemaPropertyChanges[n] != nil { - t += s.SchemaPropertyChanges[n].TotalChanges() - } - } - } - if s.DependentSchemasChanges != nil { - for n := range s.DependentSchemasChanges { - t += s.DependentSchemasChanges[n].TotalChanges() - } - } - if s.PatternPropertiesChanges != nil { - for n := range s.PatternPropertiesChanges { - t += s.PatternPropertiesChanges[n].TotalChanges() - } - } - if s.ExternalDocChanges != nil { - t += s.ExternalDocChanges.TotalChanges() - } - if s.XMLChanges != nil { - t += s.XMLChanges.TotalChanges() - } - if s.ExtensionChanges != nil { - t += s.ExtensionChanges.TotalChanges() - } - return t + t := s.PropertyChanges.TotalChanges() + if s.DiscriminatorChanges != nil { + t += s.DiscriminatorChanges.TotalChanges() + } + if len(s.AllOfChanges) > 0 { + for n := range s.AllOfChanges { + t += s.AllOfChanges[n].TotalChanges() + } + } + if len(s.AnyOfChanges) > 0 { + for n := range s.AnyOfChanges { + if s.AnyOfChanges[n] != nil { + t += s.AnyOfChanges[n].TotalChanges() + } + } + } + if len(s.OneOfChanges) > 0 { + for n := range s.OneOfChanges { + t += s.OneOfChanges[n].TotalChanges() + } + } + if s.NotChanges != nil { + t += s.NotChanges.TotalChanges() + } + if s.ItemsChanges != nil { + t += s.ItemsChanges.TotalChanges() + } + if s.IfChanges != nil { + t += s.IfChanges.TotalChanges() + } + if s.ElseChanges != nil { + t += s.ElseChanges.TotalChanges() + } + if s.ThenChanges != nil { + t += s.ThenChanges.TotalChanges() + } + if s.PropertyNamesChanges != nil { + t += s.PropertyNamesChanges.TotalChanges() + } + if s.ContainsChanges != nil { + t += s.ContainsChanges.TotalChanges() + } + if s.UnevaluatedItemsChanges != nil { + t += s.UnevaluatedItemsChanges.TotalChanges() + } + if s.UnevaluatedPropertiesChanges != nil { + t += s.UnevaluatedPropertiesChanges.TotalChanges() + } + if s.SchemaPropertyChanges != nil { + for n := range s.SchemaPropertyChanges { + if s.SchemaPropertyChanges[n] != nil { + t += s.SchemaPropertyChanges[n].TotalChanges() + } + } + } + if s.DependentSchemasChanges != nil { + for n := range s.DependentSchemasChanges { + t += s.DependentSchemasChanges[n].TotalChanges() + } + } + if s.PatternPropertiesChanges != nil { + for n := range s.PatternPropertiesChanges { + t += s.PatternPropertiesChanges[n].TotalChanges() + } + } + if s.ExternalDocChanges != nil { + t += s.ExternalDocChanges.TotalChanges() + } + if s.XMLChanges != nil { + t += s.XMLChanges.TotalChanges() + } + if s.ExtensionChanges != nil { + t += s.ExtensionChanges.TotalChanges() + } + return t } // TotalBreakingChanges returns the total number of breaking changes made to this schema and all sub-schemas. func (s *SchemaChanges) TotalBreakingChanges() int { - t := s.PropertyChanges.TotalBreakingChanges() - if s.DiscriminatorChanges != nil { - t += s.DiscriminatorChanges.TotalBreakingChanges() - } - if len(s.AllOfChanges) > 0 { - for n := range s.AllOfChanges { - t += s.AllOfChanges[n].TotalBreakingChanges() - } - } - if len(s.AllOfChanges) > 0 { - for n := range s.AllOfChanges { - t += s.AllOfChanges[n].TotalBreakingChanges() - } - } - if len(s.AnyOfChanges) > 0 { - for n := range s.AnyOfChanges { - t += s.AnyOfChanges[n].TotalBreakingChanges() - } - } - if len(s.OneOfChanges) > 0 { - for n := range s.OneOfChanges { - t += s.OneOfChanges[n].TotalBreakingChanges() - } - } - if s.NotChanges != nil { - t += s.NotChanges.TotalBreakingChanges() - } - if s.ItemsChanges != nil { - t += s.ItemsChanges.TotalBreakingChanges() - } - if s.IfChanges != nil { - t += s.IfChanges.TotalBreakingChanges() - } - if s.ElseChanges != nil { - t += s.ElseChanges.TotalBreakingChanges() - } - if s.ThenChanges != nil { - t += s.ThenChanges.TotalBreakingChanges() - } - if s.PropertyNamesChanges != nil { - t += s.PropertyNamesChanges.TotalBreakingChanges() - } - if s.ContainsChanges != nil { - t += s.ContainsChanges.TotalBreakingChanges() - } - if s.UnevaluatedItemsChanges != nil { - t += s.UnevaluatedItemsChanges.TotalBreakingChanges() - } - if s.UnevaluatedPropertiesChanges != nil { - t += s.UnevaluatedPropertiesChanges.TotalBreakingChanges() - } - if s.DependentSchemasChanges != nil { - for n := range s.DependentSchemasChanges { - t += s.DependentSchemasChanges[n].TotalBreakingChanges() - } - } - if s.PatternPropertiesChanges != nil { - for n := range s.PatternPropertiesChanges { - t += s.PatternPropertiesChanges[n].TotalBreakingChanges() - } - } - if s.XMLChanges != nil { - t += s.XMLChanges.TotalBreakingChanges() - } - if s.SchemaPropertyChanges != nil { - for n := range s.SchemaPropertyChanges { - t += s.SchemaPropertyChanges[n].TotalBreakingChanges() - } - } - return t + t := s.PropertyChanges.TotalBreakingChanges() + if s.DiscriminatorChanges != nil { + t += s.DiscriminatorChanges.TotalBreakingChanges() + } + if len(s.AllOfChanges) > 0 { + for n := range s.AllOfChanges { + t += s.AllOfChanges[n].TotalBreakingChanges() + } + } + if len(s.AllOfChanges) > 0 { + for n := range s.AllOfChanges { + t += s.AllOfChanges[n].TotalBreakingChanges() + } + } + if len(s.AnyOfChanges) > 0 { + for n := range s.AnyOfChanges { + t += s.AnyOfChanges[n].TotalBreakingChanges() + } + } + if len(s.OneOfChanges) > 0 { + for n := range s.OneOfChanges { + t += s.OneOfChanges[n].TotalBreakingChanges() + } + } + if s.NotChanges != nil { + t += s.NotChanges.TotalBreakingChanges() + } + if s.ItemsChanges != nil { + t += s.ItemsChanges.TotalBreakingChanges() + } + if s.IfChanges != nil { + t += s.IfChanges.TotalBreakingChanges() + } + if s.ElseChanges != nil { + t += s.ElseChanges.TotalBreakingChanges() + } + if s.ThenChanges != nil { + t += s.ThenChanges.TotalBreakingChanges() + } + if s.PropertyNamesChanges != nil { + t += s.PropertyNamesChanges.TotalBreakingChanges() + } + if s.ContainsChanges != nil { + t += s.ContainsChanges.TotalBreakingChanges() + } + if s.UnevaluatedItemsChanges != nil { + t += s.UnevaluatedItemsChanges.TotalBreakingChanges() + } + if s.UnevaluatedPropertiesChanges != nil { + t += s.UnevaluatedPropertiesChanges.TotalBreakingChanges() + } + if s.DependentSchemasChanges != nil { + for n := range s.DependentSchemasChanges { + t += s.DependentSchemasChanges[n].TotalBreakingChanges() + } + } + if s.PatternPropertiesChanges != nil { + for n := range s.PatternPropertiesChanges { + t += s.PatternPropertiesChanges[n].TotalBreakingChanges() + } + } + if s.XMLChanges != nil { + t += s.XMLChanges.TotalBreakingChanges() + } + if s.SchemaPropertyChanges != nil { + for n := range s.SchemaPropertyChanges { + t += s.SchemaPropertyChanges[n].TotalBreakingChanges() + } + } + return t } // CompareSchemas accepts a left and right SchemaProxy and checks for changes. If anything is found, returns // a pointer to SchemaChanges, otherwise returns nil func CompareSchemas(l, r *base.SchemaProxy) *SchemaChanges { - sc := new(SchemaChanges) - var changes []*Change + sc := new(SchemaChanges) + var changes []*Change - // Added - if l == nil && r != nil { - CreateChange(&changes, ObjectAdded, v3.SchemaLabel, - nil, nil, true, nil, r) - sc.PropertyChanges = NewPropertyChanges(changes) - } + // Added + if l == nil && r != nil { + CreateChange(&changes, ObjectAdded, v3.SchemaLabel, + nil, nil, true, nil, r) + sc.PropertyChanges = NewPropertyChanges(changes) + } - // Removed - if l != nil && r == nil { - CreateChange(&changes, ObjectRemoved, v3.SchemaLabel, - nil, nil, true, l, nil) - sc.PropertyChanges = NewPropertyChanges(changes) - } + // Removed + if l != nil && r == nil { + CreateChange(&changes, ObjectRemoved, v3.SchemaLabel, + nil, nil, true, l, nil) + sc.PropertyChanges = NewPropertyChanges(changes) + } - if l != nil && r != nil { + if l != nil && r != nil { - // if left proxy is a reference and right is a reference (we won't recurse into them) - if l.IsSchemaReference() && r.IsSchemaReference() { - // points to the same schema - if l.GetSchemaReference() == r.GetSchemaReference() { - // there is nothing to be done at this point. - return nil - } else { - // references are different, that's all we care to know. - CreateChange(&changes, Modified, v3.RefLabel, - l.GetValueNode().Content[1], r.GetValueNode().Content[1], true, l.GetSchemaReference(), - r.GetSchemaReference()) - sc.PropertyChanges = NewPropertyChanges(changes) - return sc - } - } + // if left proxy is a reference and right is a reference (we won't recurse into them) + if l.IsSchemaReference() && r.IsSchemaReference() { + // points to the same schema + if l.GetSchemaReference() == r.GetSchemaReference() { + // there is nothing to be done at this point. + return nil + } else { + // references are different, that's all we care to know. + CreateChange(&changes, Modified, v3.RefLabel, + l.GetValueNode().Content[1], r.GetValueNode().Content[1], true, l.GetSchemaReference(), + r.GetSchemaReference()) + sc.PropertyChanges = NewPropertyChanges(changes) + return sc + } + } - // changed from inline to ref - if !l.IsSchemaReference() && r.IsSchemaReference() { - CreateChange(&changes, Modified, v3.RefLabel, - l.GetValueNode(), r.GetValueNode().Content[1], true, l, r.GetSchemaReference()) - sc.PropertyChanges = NewPropertyChanges(changes) - return sc // we're done here - } + // changed from inline to ref + if !l.IsSchemaReference() && r.IsSchemaReference() { + CreateChange(&changes, Modified, v3.RefLabel, + l.GetValueNode(), r.GetValueNode().Content[1], true, l, r.GetSchemaReference()) + sc.PropertyChanges = NewPropertyChanges(changes) + return sc // we're done here + } - // changed from ref to inline - if l.IsSchemaReference() && !r.IsSchemaReference() { - CreateChange(&changes, Modified, v3.RefLabel, - l.GetValueNode().Content[1], r.GetValueNode(), true, l.GetSchemaReference(), r) - sc.PropertyChanges = NewPropertyChanges(changes) - return sc // done, nothing else to do. - } + // changed from ref to inline + if l.IsSchemaReference() && !r.IsSchemaReference() { + CreateChange(&changes, Modified, v3.RefLabel, + l.GetValueNode().Content[1], r.GetValueNode(), true, l.GetSchemaReference(), r) + sc.PropertyChanges = NewPropertyChanges(changes) + return sc // done, nothing else to do. + } - lSchema := l.Schema() - rSchema := r.Schema() + lSchema := l.Schema() + rSchema := r.Schema() - if low.AreEqual(lSchema, rSchema) { - // there is no point going on, we know nothing changed! - return nil - } + if low.AreEqual(lSchema, rSchema) { + // there is no point going on, we know nothing changed! + return nil + } - // check XML - checkSchemaXML(lSchema, rSchema, &changes, sc) + // check XML + checkSchemaXML(lSchema, rSchema, &changes, sc) - // check examples - checkExamples(lSchema, rSchema, &changes) + // check examples + checkExamples(lSchema, rSchema, &changes) - // check schema core properties for changes. - checkSchemaPropertyChanges(lSchema, rSchema, &changes, sc) + // check schema core properties for changes. + checkSchemaPropertyChanges(lSchema, rSchema, &changes, sc) - // now for the confusing part, there is also a schema's 'properties' property to parse. - // inception, eat your heart out. - doneChan := make(chan bool) - props, totalProperties := checkMappedSchemaOfASchema(lSchema.Properties.Value, rSchema.Properties.Value, &changes, doneChan) - sc.SchemaPropertyChanges = props + // now for the confusing part, there is also a schema's 'properties' property to parse. + // inception, eat your heart out. + doneChan := make(chan bool) + props, totalProperties := checkMappedSchemaOfASchema(lSchema.Properties.Value, rSchema.Properties.Value, &changes, doneChan) + sc.SchemaPropertyChanges = props - deps, depsTotal := checkMappedSchemaOfASchema(lSchema.DependentSchemas.Value, rSchema.DependentSchemas.Value, &changes, doneChan) - sc.DependentSchemasChanges = deps + deps, depsTotal := checkMappedSchemaOfASchema(lSchema.DependentSchemas.Value, rSchema.DependentSchemas.Value, &changes, doneChan) + sc.DependentSchemasChanges = deps - patterns, patternsTotal := checkMappedSchemaOfASchema(lSchema.PatternProperties.Value, rSchema.PatternProperties.Value, &changes, doneChan) - sc.PatternPropertiesChanges = patterns + patterns, patternsTotal := checkMappedSchemaOfASchema(lSchema.PatternProperties.Value, rSchema.PatternProperties.Value, &changes, doneChan) + sc.PatternPropertiesChanges = patterns - // check polymorphic and multi-values async for speed. - go extractSchemaChanges(lSchema.OneOf.Value, rSchema.OneOf.Value, v3.OneOfLabel, - &sc.OneOfChanges, &changes, doneChan) + // check polymorphic and multi-values async for speed. + go extractSchemaChanges(lSchema.OneOf.Value, rSchema.OneOf.Value, v3.OneOfLabel, + &sc.OneOfChanges, &changes, doneChan) - go extractSchemaChanges(lSchema.AllOf.Value, rSchema.AllOf.Value, v3.AllOfLabel, - &sc.AllOfChanges, &changes, doneChan) + go extractSchemaChanges(lSchema.AllOf.Value, rSchema.AllOf.Value, v3.AllOfLabel, + &sc.AllOfChanges, &changes, doneChan) - go extractSchemaChanges(lSchema.AnyOf.Value, rSchema.AnyOf.Value, v3.AnyOfLabel, - &sc.AnyOfChanges, &changes, doneChan) + go extractSchemaChanges(lSchema.AnyOf.Value, rSchema.AnyOf.Value, v3.AnyOfLabel, + &sc.AnyOfChanges, &changes, doneChan) - totalChecks := totalProperties + depsTotal + patternsTotal + 3 - completedChecks := 0 - for completedChecks < totalChecks { - select { - case <-doneChan: - completedChecks++ - } - } - } - // done - if changes != nil { - sc.PropertyChanges = NewPropertyChanges(changes) - } else { - sc.PropertyChanges = NewPropertyChanges(nil) - } - if sc.TotalChanges() > 0 { - return sc - } - return nil + totalChecks := totalProperties + depsTotal + patternsTotal + 3 + completedChecks := 0 + for completedChecks < totalChecks { + select { + case <-doneChan: + completedChecks++ + } + } + } + // done + if changes != nil { + sc.PropertyChanges = NewPropertyChanges(changes) + } else { + sc.PropertyChanges = NewPropertyChanges(nil) + } + if sc.TotalChanges() > 0 { + return sc + } + return nil } func checkSchemaXML(lSchema *base.Schema, rSchema *base.Schema, changes *[]*Change, sc *SchemaChanges) { - // XML removed - if lSchema.XML.Value != nil && rSchema.XML.Value == nil { - CreateChange(changes, ObjectRemoved, v3.XMLLabel, - lSchema.XML.GetValueNode(), nil, true, lSchema.XML.GetValue(), nil) - } - // XML added - if lSchema.XML.Value == nil && rSchema.XML.Value != nil { - CreateChange(changes, ObjectAdded, v3.XMLLabel, - nil, rSchema.XML.GetValueNode(), false, nil, rSchema.XML.GetValue()) - } + // XML removed + if lSchema.XML.Value != nil && rSchema.XML.Value == nil { + CreateChange(changes, ObjectRemoved, v3.XMLLabel, + lSchema.XML.GetValueNode(), nil, true, lSchema.XML.GetValue(), nil) + } + // XML added + if lSchema.XML.Value == nil && rSchema.XML.Value != nil { + CreateChange(changes, ObjectAdded, v3.XMLLabel, + nil, rSchema.XML.GetValueNode(), false, nil, rSchema.XML.GetValue()) + } - // compare XML - if lSchema.XML.Value != nil && rSchema.XML.Value != nil { - if !low.AreEqual(lSchema.XML.Value, rSchema.XML.Value) { - sc.XMLChanges = CompareXML(lSchema.XML.Value, rSchema.XML.Value) - } - } + // compare XML + if lSchema.XML.Value != nil && rSchema.XML.Value != nil { + if !low.AreEqual(lSchema.XML.Value, rSchema.XML.Value) { + sc.XMLChanges = CompareXML(lSchema.XML.Value, rSchema.XML.Value) + } + } } func checkMappedSchemaOfASchema( - lSchema, - rSchema map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy], - changes *[]*Change, - doneChan chan bool) (map[string]*SchemaChanges, int) { + lSchema, + rSchema map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy], + changes *[]*Change, + doneChan chan bool) (map[string]*SchemaChanges, int) { - propChanges := make(map[string]*SchemaChanges) + propChanges := make(map[string]*SchemaChanges) - var lProps []string - lEntities := make(map[string]*base.SchemaProxy) - lKeyNodes := make(map[string]*yaml.Node) - var rProps []string - rEntities := make(map[string]*base.SchemaProxy) - rKeyNodes := make(map[string]*yaml.Node) + var lProps []string + lEntities := make(map[string]*base.SchemaProxy) + lKeyNodes := make(map[string]*yaml.Node) + var rProps []string + rEntities := make(map[string]*base.SchemaProxy) + rKeyNodes := make(map[string]*yaml.Node) - for w := range lSchema { - lProps = append(lProps, w.Value) - lEntities[w.Value] = lSchema[w].Value - lKeyNodes[w.Value] = w.KeyNode - } - for w := range rSchema { - rProps = append(rProps, w.Value) - rEntities[w.Value] = rSchema[w].Value - rKeyNodes[w.Value] = w.KeyNode - } - sort.Strings(lProps) - sort.Strings(rProps) - totalProperties := buildProperty(lProps, rProps, lEntities, rEntities, propChanges, doneChan, changes, rKeyNodes, lKeyNodes) - return propChanges, totalProperties + for w := range lSchema { + lProps = append(lProps, w.Value) + lEntities[w.Value] = lSchema[w].Value + lKeyNodes[w.Value] = w.KeyNode + } + for w := range rSchema { + rProps = append(rProps, w.Value) + rEntities[w.Value] = rSchema[w].Value + rKeyNodes[w.Value] = w.KeyNode + } + sort.Strings(lProps) + sort.Strings(rProps) + totalProperties := buildProperty(lProps, rProps, lEntities, rEntities, propChanges, doneChan, changes, rKeyNodes, lKeyNodes) + return propChanges, totalProperties } func buildProperty(lProps, rProps []string, lEntities, rEntities map[string]*base.SchemaProxy, - propChanges map[string]*SchemaChanges, doneChan chan bool, changes *[]*Change, rKeyNodes, lKeyNodes map[string]*yaml.Node) int { - var propLock sync.Mutex - checkProperty := func(key string, lp, rp *base.SchemaProxy, propChanges map[string]*SchemaChanges, done chan bool) { - if lp != nil && rp != nil { - if low.AreEqual(lp, rp) { - done <- true - return - } - s := CompareSchemas(lp, rp) - propLock.Lock() - propChanges[key] = s - propLock.Unlock() - done <- true - } - } + propChanges map[string]*SchemaChanges, doneChan chan bool, changes *[]*Change, rKeyNodes, lKeyNodes map[string]*yaml.Node) int { + var propLock sync.Mutex + checkProperty := func(key string, lp, rp *base.SchemaProxy, propChanges map[string]*SchemaChanges, done chan bool) { + if lp != nil && rp != nil { + if low.AreEqual(lp, rp) { + done <- true + return + } + s := CompareSchemas(lp, rp) + propLock.Lock() + propChanges[key] = s + propLock.Unlock() + done <- true + } + } - totalProperties := 0 - if len(lProps) == len(rProps) { - for w := range lProps { - lp := lEntities[lProps[w]] - rp := rEntities[rProps[w]] - if lProps[w] == rProps[w] && lp != nil && rp != nil { - totalProperties++ - go checkProperty(lProps[w], lp, rp, propChanges, doneChan) - } + totalProperties := 0 + if len(lProps) == len(rProps) { + for w := range lProps { + lp := lEntities[lProps[w]] + rp := rEntities[rProps[w]] + if lProps[w] == rProps[w] && lp != nil && rp != nil { + totalProperties++ + go checkProperty(lProps[w], lp, rp, propChanges, doneChan) + } - // keys do not match, even after sorting, means a like for like replacement. - if lProps[w] != rProps[w] { + // keys do not match, even after sorting, means a like for like replacement. + if lProps[w] != rProps[w] { - // old removed, new added. - CreateChange(changes, ObjectAdded, v3.PropertiesLabel, - nil, rKeyNodes[rProps[w]], false, nil, rEntities[rProps[w]]) + // old removed, new added. + CreateChange(changes, ObjectAdded, v3.PropertiesLabel, + nil, rKeyNodes[rProps[w]], false, nil, rEntities[rProps[w]]) - CreateChange(changes, ObjectRemoved, v3.PropertiesLabel, - lKeyNodes[lProps[w]], nil, true, lEntities[lProps[w]], nil) - } + CreateChange(changes, ObjectRemoved, v3.PropertiesLabel, + lKeyNodes[lProps[w]], nil, true, lEntities[lProps[w]], nil) + } - } - } + } + } - // something removed - if len(lProps) > len(rProps) { - for w := range lProps { + // something removed + if len(lProps) > len(rProps) { + for w := range lProps { - if rEntities[lProps[w]] != nil { - totalProperties++ - go checkProperty(lProps[w], lEntities[lProps[w]], rEntities[lProps[w]], propChanges, doneChan) - continue - } else { - CreateChange(changes, ObjectRemoved, v3.PropertiesLabel, - lKeyNodes[lProps[w]], nil, true, lEntities[lProps[w]], nil) - continue - } - } - } + if rEntities[lProps[w]] != nil { + totalProperties++ + go checkProperty(lProps[w], lEntities[lProps[w]], rEntities[lProps[w]], propChanges, doneChan) + continue + } else { + CreateChange(changes, ObjectRemoved, v3.PropertiesLabel, + lKeyNodes[lProps[w]], nil, true, lEntities[lProps[w]], nil) + continue + } + } + } - // something added - if len(rProps) > len(lProps) { - for w := range rProps { - if lEntities[rProps[w]] != nil { - totalProperties++ - go checkProperty(rProps[w], lEntities[rProps[w]], rEntities[rProps[w]], propChanges, doneChan) - } else { - CreateChange(changes, ObjectAdded, v3.PropertiesLabel, - nil, rKeyNodes[rProps[w]], false, nil, rEntities[rProps[w]]) - continue - } - } - } - return totalProperties + // something added + if len(rProps) > len(lProps) { + for w := range rProps { + if lEntities[rProps[w]] != nil { + totalProperties++ + go checkProperty(rProps[w], lEntities[rProps[w]], rEntities[rProps[w]], propChanges, doneChan) + } else { + CreateChange(changes, ObjectAdded, v3.PropertiesLabel, + nil, rKeyNodes[rProps[w]], false, nil, rEntities[rProps[w]]) + continue + } + } + } + return totalProperties } func checkSchemaPropertyChanges( - lSchema *base.Schema, - rSchema *base.Schema, - changes *[]*Change, sc *SchemaChanges) { + lSchema *base.Schema, + rSchema *base.Schema, + changes *[]*Change, sc *SchemaChanges) { - var props []*PropertyCheck + var props []*PropertyCheck - // $schema (breaking change) - props = append(props, &PropertyCheck{ - LeftNode: lSchema.SchemaTypeRef.ValueNode, - RightNode: rSchema.SchemaTypeRef.ValueNode, - Label: v3.SchemaDialectLabel, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // $schema (breaking change) + props = append(props, &PropertyCheck{ + LeftNode: lSchema.SchemaTypeRef.ValueNode, + RightNode: rSchema.SchemaTypeRef.ValueNode, + Label: v3.SchemaDialectLabel, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // ExclusiveMaximum - props = append(props, &PropertyCheck{ - LeftNode: lSchema.ExclusiveMaximum.ValueNode, - RightNode: rSchema.ExclusiveMaximum.ValueNode, - Label: v3.ExclusiveMaximumLabel, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // ExclusiveMaximum + props = append(props, &PropertyCheck{ + LeftNode: lSchema.ExclusiveMaximum.ValueNode, + RightNode: rSchema.ExclusiveMaximum.ValueNode, + Label: v3.ExclusiveMaximumLabel, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // ExclusiveMinimum - props = append(props, &PropertyCheck{ - LeftNode: lSchema.ExclusiveMinimum.ValueNode, - RightNode: rSchema.ExclusiveMinimum.ValueNode, - Label: v3.ExclusiveMinimumLabel, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // ExclusiveMinimum + props = append(props, &PropertyCheck{ + LeftNode: lSchema.ExclusiveMinimum.ValueNode, + RightNode: rSchema.ExclusiveMinimum.ValueNode, + Label: v3.ExclusiveMinimumLabel, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // Type - props = append(props, &PropertyCheck{ - LeftNode: lSchema.Type.ValueNode, - RightNode: rSchema.Type.ValueNode, - Label: v3.TypeLabel, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // Type + props = append(props, &PropertyCheck{ + LeftNode: lSchema.Type.ValueNode, + RightNode: rSchema.Type.ValueNode, + Label: v3.TypeLabel, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // Title - props = append(props, &PropertyCheck{ - LeftNode: lSchema.Title.ValueNode, - RightNode: rSchema.Title.ValueNode, - Label: v3.TitleLabel, - Changes: changes, - Breaking: false, - Original: lSchema, - New: rSchema, - }) + // Title + props = append(props, &PropertyCheck{ + LeftNode: lSchema.Title.ValueNode, + RightNode: rSchema.Title.ValueNode, + Label: v3.TitleLabel, + Changes: changes, + Breaking: false, + Original: lSchema, + New: rSchema, + }) - // MultipleOf - props = append(props, &PropertyCheck{ - LeftNode: lSchema.MultipleOf.ValueNode, - RightNode: rSchema.MultipleOf.ValueNode, - Label: v3.MultipleOfLabel, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // MultipleOf + props = append(props, &PropertyCheck{ + LeftNode: lSchema.MultipleOf.ValueNode, + RightNode: rSchema.MultipleOf.ValueNode, + Label: v3.MultipleOfLabel, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // Maximum - props = append(props, &PropertyCheck{ - LeftNode: lSchema.Maximum.ValueNode, - RightNode: rSchema.Maximum.ValueNode, - Label: v3.MaximumLabel, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // Maximum + props = append(props, &PropertyCheck{ + LeftNode: lSchema.Maximum.ValueNode, + RightNode: rSchema.Maximum.ValueNode, + Label: v3.MaximumLabel, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // Minimum - props = append(props, &PropertyCheck{ - LeftNode: lSchema.Minimum.ValueNode, - RightNode: rSchema.Minimum.ValueNode, - Label: v3.MinimumLabel, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // Minimum + props = append(props, &PropertyCheck{ + LeftNode: lSchema.Minimum.ValueNode, + RightNode: rSchema.Minimum.ValueNode, + Label: v3.MinimumLabel, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // MaxLength - props = append(props, &PropertyCheck{ - LeftNode: lSchema.MaxLength.ValueNode, - RightNode: rSchema.MaxLength.ValueNode, - Label: v3.MaxLengthLabel, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // MaxLength + props = append(props, &PropertyCheck{ + LeftNode: lSchema.MaxLength.ValueNode, + RightNode: rSchema.MaxLength.ValueNode, + Label: v3.MaxLengthLabel, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // MinLength - props = append(props, &PropertyCheck{ - LeftNode: lSchema.MinLength.ValueNode, - RightNode: rSchema.MinLength.ValueNode, - Label: v3.MinLengthLabel, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // MinLength + props = append(props, &PropertyCheck{ + LeftNode: lSchema.MinLength.ValueNode, + RightNode: rSchema.MinLength.ValueNode, + Label: v3.MinLengthLabel, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // Pattern - props = append(props, &PropertyCheck{ - LeftNode: lSchema.Pattern.ValueNode, - RightNode: rSchema.Pattern.ValueNode, - Label: v3.PatternLabel, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // Pattern + props = append(props, &PropertyCheck{ + LeftNode: lSchema.Pattern.ValueNode, + RightNode: rSchema.Pattern.ValueNode, + Label: v3.PatternLabel, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // Format - props = append(props, &PropertyCheck{ - LeftNode: lSchema.Format.ValueNode, - RightNode: rSchema.Format.ValueNode, - Label: v3.FormatLabel, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // Format + props = append(props, &PropertyCheck{ + LeftNode: lSchema.Format.ValueNode, + RightNode: rSchema.Format.ValueNode, + Label: v3.FormatLabel, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // MaxItems - props = append(props, &PropertyCheck{ - LeftNode: lSchema.MaxItems.ValueNode, - RightNode: rSchema.MaxItems.ValueNode, - Label: v3.MaxItemsLabel, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // MaxItems + props = append(props, &PropertyCheck{ + LeftNode: lSchema.MaxItems.ValueNode, + RightNode: rSchema.MaxItems.ValueNode, + Label: v3.MaxItemsLabel, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // MinItems - props = append(props, &PropertyCheck{ - LeftNode: lSchema.MinItems.ValueNode, - RightNode: rSchema.MinItems.ValueNode, - Label: v3.MinItemsLabel, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // MinItems + props = append(props, &PropertyCheck{ + LeftNode: lSchema.MinItems.ValueNode, + RightNode: rSchema.MinItems.ValueNode, + Label: v3.MinItemsLabel, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // MaxProperties - props = append(props, &PropertyCheck{ - LeftNode: lSchema.MaxProperties.ValueNode, - RightNode: rSchema.MaxProperties.ValueNode, - Label: v3.MaxPropertiesLabel, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // MaxProperties + props = append(props, &PropertyCheck{ + LeftNode: lSchema.MaxProperties.ValueNode, + RightNode: rSchema.MaxProperties.ValueNode, + Label: v3.MaxPropertiesLabel, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // MinProperties - props = append(props, &PropertyCheck{ - LeftNode: lSchema.MinProperties.ValueNode, - RightNode: rSchema.MinProperties.ValueNode, - Label: v3.MinPropertiesLabel, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // MinProperties + props = append(props, &PropertyCheck{ + LeftNode: lSchema.MinProperties.ValueNode, + RightNode: rSchema.MinProperties.ValueNode, + Label: v3.MinPropertiesLabel, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // UniqueItems - props = append(props, &PropertyCheck{ - LeftNode: lSchema.UniqueItems.ValueNode, - RightNode: rSchema.UniqueItems.ValueNode, - Label: v3.UniqueItemsLabel, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // UniqueItems + props = append(props, &PropertyCheck{ + LeftNode: lSchema.UniqueItems.ValueNode, + RightNode: rSchema.UniqueItems.ValueNode, + Label: v3.UniqueItemsLabel, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // AdditionalProperties (only if not an object) - if !utils.IsNodeMap(lSchema.AdditionalProperties.ValueNode) && - !utils.IsNodeMap(rSchema.AdditionalProperties.ValueNode) { - props = append(props, &PropertyCheck{ - LeftNode: lSchema.AdditionalProperties.ValueNode, - RightNode: rSchema.AdditionalProperties.ValueNode, - Label: v3.AdditionalPropertiesLabel, - Changes: changes, - Breaking: false, - Original: lSchema, - New: rSchema, - }) - } + // AdditionalProperties (only if not an object) + if !utils.IsNodeMap(lSchema.AdditionalProperties.ValueNode) && + !utils.IsNodeMap(rSchema.AdditionalProperties.ValueNode) { + props = append(props, &PropertyCheck{ + LeftNode: lSchema.AdditionalProperties.ValueNode, + RightNode: rSchema.AdditionalProperties.ValueNode, + Label: v3.AdditionalPropertiesLabel, + Changes: changes, + Breaking: false, + Original: lSchema, + New: rSchema, + }) + } - //Description - props = append(props, &PropertyCheck{ - LeftNode: lSchema.Description.ValueNode, - RightNode: rSchema.Description.ValueNode, - Label: v3.DescriptionLabel, - Changes: changes, - Breaking: false, - Original: lSchema, - New: rSchema, - }) + //Description + props = append(props, &PropertyCheck{ + LeftNode: lSchema.Description.ValueNode, + RightNode: rSchema.Description.ValueNode, + Label: v3.DescriptionLabel, + Changes: changes, + Breaking: false, + Original: lSchema, + New: rSchema, + }) - // ContentEncoding - props = append(props, &PropertyCheck{ - LeftNode: lSchema.ContentEncoding.ValueNode, - RightNode: rSchema.ContentEncoding.ValueNode, - Label: v3.ContentEncodingLabel, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // ContentEncoding + props = append(props, &PropertyCheck{ + LeftNode: lSchema.ContentEncoding.ValueNode, + RightNode: rSchema.ContentEncoding.ValueNode, + Label: v3.ContentEncodingLabel, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // ContentMediaType - props = append(props, &PropertyCheck{ - LeftNode: lSchema.ContentMediaType.ValueNode, - RightNode: rSchema.ContentMediaType.ValueNode, - Label: v3.ContentMediaType, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // ContentMediaType + props = append(props, &PropertyCheck{ + LeftNode: lSchema.ContentMediaType.ValueNode, + RightNode: rSchema.ContentMediaType.ValueNode, + Label: v3.ContentMediaType, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // Default - props = append(props, &PropertyCheck{ - LeftNode: lSchema.Default.ValueNode, - RightNode: rSchema.Default.ValueNode, - Label: v3.DefaultLabel, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // Default + props = append(props, &PropertyCheck{ + LeftNode: lSchema.Default.ValueNode, + RightNode: rSchema.Default.ValueNode, + Label: v3.DefaultLabel, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // Nullable - props = append(props, &PropertyCheck{ - LeftNode: lSchema.Nullable.ValueNode, - RightNode: rSchema.Nullable.ValueNode, - Label: v3.NullableLabel, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // Nullable + props = append(props, &PropertyCheck{ + LeftNode: lSchema.Nullable.ValueNode, + RightNode: rSchema.Nullable.ValueNode, + Label: v3.NullableLabel, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // ReadOnly - props = append(props, &PropertyCheck{ - LeftNode: lSchema.ReadOnly.ValueNode, - RightNode: rSchema.ReadOnly.ValueNode, - Label: v3.ReadOnlyLabel, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // ReadOnly + props = append(props, &PropertyCheck{ + LeftNode: lSchema.ReadOnly.ValueNode, + RightNode: rSchema.ReadOnly.ValueNode, + Label: v3.ReadOnlyLabel, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // WriteOnly - props = append(props, &PropertyCheck{ - LeftNode: lSchema.WriteOnly.ValueNode, - RightNode: rSchema.WriteOnly.ValueNode, - Label: v3.WriteOnlyLabel, - Changes: changes, - Breaking: true, - Original: lSchema, - New: rSchema, - }) + // WriteOnly + props = append(props, &PropertyCheck{ + LeftNode: lSchema.WriteOnly.ValueNode, + RightNode: rSchema.WriteOnly.ValueNode, + Label: v3.WriteOnlyLabel, + Changes: changes, + Breaking: true, + Original: lSchema, + New: rSchema, + }) - // Example - props = append(props, &PropertyCheck{ - LeftNode: lSchema.Example.ValueNode, - RightNode: rSchema.Example.ValueNode, - Label: v3.ExampleLabel, - Changes: changes, - Breaking: false, - Original: lSchema, - New: rSchema, - }) + // Example + props = append(props, &PropertyCheck{ + LeftNode: lSchema.Example.ValueNode, + RightNode: rSchema.Example.ValueNode, + Label: v3.ExampleLabel, + Changes: changes, + Breaking: false, + Original: lSchema, + New: rSchema, + }) - // Deprecated - props = append(props, &PropertyCheck{ - LeftNode: lSchema.Deprecated.ValueNode, - RightNode: rSchema.Deprecated.ValueNode, - Label: v3.DeprecatedLabel, - Changes: changes, - Breaking: false, - Original: lSchema, - New: rSchema, - }) + // Deprecated + props = append(props, &PropertyCheck{ + LeftNode: lSchema.Deprecated.ValueNode, + RightNode: rSchema.Deprecated.ValueNode, + Label: v3.DeprecatedLabel, + Changes: changes, + Breaking: false, + Original: lSchema, + New: rSchema, + }) - // Required - j := make(map[string]int) - k := make(map[string]int) - for i := range lSchema.Required.Value { - j[lSchema.Required.Value[i].Value] = i - } - for i := range rSchema.Required.Value { - k[rSchema.Required.Value[i].Value] = i - } - for g := range k { - if _, ok := j[g]; !ok { - CreateChange(changes, PropertyAdded, v3.RequiredLabel, - nil, rSchema.Required.Value[k[g]].GetValueNode(), true, nil, - rSchema.Required.Value[k[g]].GetValue) - } - } - for g := range j { - if _, ok := k[g]; !ok { - CreateChange(changes, PropertyRemoved, v3.RequiredLabel, - lSchema.Required.Value[j[g]].GetValueNode(), nil, true, lSchema.Required.Value[j[g]].GetValue, - nil) - } - } + // Required + j := make(map[string]int) + k := make(map[string]int) + for i := range lSchema.Required.Value { + j[lSchema.Required.Value[i].Value] = i + } + for i := range rSchema.Required.Value { + k[rSchema.Required.Value[i].Value] = i + } + for g := range k { + if _, ok := j[g]; !ok { + CreateChange(changes, PropertyAdded, v3.RequiredLabel, + nil, rSchema.Required.Value[k[g]].GetValueNode(), true, nil, + rSchema.Required.Value[k[g]].GetValue) + } + } + for g := range j { + if _, ok := k[g]; !ok { + CreateChange(changes, PropertyRemoved, v3.RequiredLabel, + lSchema.Required.Value[j[g]].GetValueNode(), nil, true, lSchema.Required.Value[j[g]].GetValue, + nil) + } + } - // Enums - j = make(map[string]int) - k = make(map[string]int) - for i := range lSchema.Enum.Value { - j[fmt.Sprint(lSchema.Enum.Value[i].Value)] = i - } - for i := range rSchema.Enum.Value { - k[fmt.Sprint(rSchema.Enum.Value[i].Value)] = i - } - for g := range k { - if _, ok := j[g]; !ok { - CreateChange(changes, PropertyAdded, v3.EnumLabel, - nil, rSchema.Enum.Value[k[g]].GetValueNode(), false, nil, - rSchema.Enum.Value[k[g]].GetValue) - } - } - for g := range j { - if _, ok := k[g]; !ok { - CreateChange(changes, PropertyRemoved, v3.EnumLabel, - lSchema.Enum.Value[j[g]].GetValueNode(), nil, true, lSchema.Enum.Value[j[g]].GetValue, - nil) - } - } + // Enums + j = make(map[string]int) + k = make(map[string]int) + for i := range lSchema.Enum.Value { + j[fmt.Sprint(lSchema.Enum.Value[i].Value)] = i + } + for i := range rSchema.Enum.Value { + k[fmt.Sprint(rSchema.Enum.Value[i].Value)] = i + } + for g := range k { + if _, ok := j[g]; !ok { + CreateChange(changes, PropertyAdded, v3.EnumLabel, + nil, rSchema.Enum.Value[k[g]].GetValueNode(), false, nil, + rSchema.Enum.Value[k[g]].GetValue) + } + } + for g := range j { + if _, ok := k[g]; !ok { + CreateChange(changes, PropertyRemoved, v3.EnumLabel, + lSchema.Enum.Value[j[g]].GetValueNode(), nil, true, lSchema.Enum.Value[j[g]].GetValue, + nil) + } + } - // Discriminator - if lSchema.Discriminator.Value != nil && rSchema.Discriminator.Value != nil { - // check if hash matches, if not then compare. - if lSchema.Discriminator.Value.Hash() != rSchema.Discriminator.Value.Hash() { - sc.DiscriminatorChanges = CompareDiscriminator(lSchema.Discriminator.Value, rSchema.Discriminator.Value) - } - } - // added Discriminator - if lSchema.Discriminator.Value == nil && rSchema.Discriminator.Value != nil { - CreateChange(changes, ObjectAdded, v3.DiscriminatorLabel, - nil, rSchema.Discriminator.ValueNode, true, nil, rSchema.Discriminator.Value) - } - // removed Discriminator - if lSchema.Discriminator.Value != nil && rSchema.Discriminator.Value == nil { - CreateChange(changes, ObjectRemoved, v3.DiscriminatorLabel, - lSchema.Discriminator.ValueNode, nil, true, lSchema.Discriminator.Value, nil) - } + // Discriminator + if lSchema.Discriminator.Value != nil && rSchema.Discriminator.Value != nil { + // check if hash matches, if not then compare. + if lSchema.Discriminator.Value.Hash() != rSchema.Discriminator.Value.Hash() { + sc.DiscriminatorChanges = CompareDiscriminator(lSchema.Discriminator.Value, rSchema.Discriminator.Value) + } + } + // added Discriminator + if lSchema.Discriminator.Value == nil && rSchema.Discriminator.Value != nil { + CreateChange(changes, ObjectAdded, v3.DiscriminatorLabel, + nil, rSchema.Discriminator.ValueNode, true, nil, rSchema.Discriminator.Value) + } + // removed Discriminator + if lSchema.Discriminator.Value != nil && rSchema.Discriminator.Value == nil { + CreateChange(changes, ObjectRemoved, v3.DiscriminatorLabel, + lSchema.Discriminator.ValueNode, nil, true, lSchema.Discriminator.Value, nil) + } - // ExternalDocs - if lSchema.ExternalDocs.Value != nil && rSchema.ExternalDocs.Value != nil { - // check if hash matches, if not then compare. - if lSchema.ExternalDocs.Value.Hash() != rSchema.ExternalDocs.Value.Hash() { - sc.ExternalDocChanges = CompareExternalDocs(lSchema.ExternalDocs.Value, rSchema.ExternalDocs.Value) - } - } - // added ExternalDocs - if lSchema.ExternalDocs.Value == nil && rSchema.ExternalDocs.Value != nil { - CreateChange(changes, ObjectAdded, v3.ExternalDocsLabel, - nil, rSchema.ExternalDocs.ValueNode, false, nil, rSchema.ExternalDocs.Value) - } - // removed ExternalDocs - if lSchema.ExternalDocs.Value != nil && rSchema.ExternalDocs.Value == nil { - CreateChange(changes, ObjectRemoved, v3.ExternalDocsLabel, - lSchema.ExternalDocs.ValueNode, nil, false, lSchema.ExternalDocs.Value, nil) - } + // ExternalDocs + if lSchema.ExternalDocs.Value != nil && rSchema.ExternalDocs.Value != nil { + // check if hash matches, if not then compare. + if lSchema.ExternalDocs.Value.Hash() != rSchema.ExternalDocs.Value.Hash() { + sc.ExternalDocChanges = CompareExternalDocs(lSchema.ExternalDocs.Value, rSchema.ExternalDocs.Value) + } + } + // added ExternalDocs + if lSchema.ExternalDocs.Value == nil && rSchema.ExternalDocs.Value != nil { + CreateChange(changes, ObjectAdded, v3.ExternalDocsLabel, + nil, rSchema.ExternalDocs.ValueNode, false, nil, rSchema.ExternalDocs.Value) + } + // removed ExternalDocs + if lSchema.ExternalDocs.Value != nil && rSchema.ExternalDocs.Value == nil { + CreateChange(changes, ObjectRemoved, v3.ExternalDocsLabel, + lSchema.ExternalDocs.ValueNode, nil, false, lSchema.ExternalDocs.Value, nil) + } - // 3.1 properties - // If - if lSchema.If.Value != nil && rSchema.If.Value != nil { - if !low.AreEqual(lSchema.If.Value, rSchema.If.Value) { - sc.IfChanges = CompareSchemas(lSchema.If.Value, rSchema.If.Value) - } - } - // added If - if lSchema.If.Value == nil && rSchema.If.Value != nil { - CreateChange(changes, ObjectAdded, v3.IfLabel, - nil, rSchema.If.ValueNode, true, nil, rSchema.If.Value) - } - // removed If - if lSchema.If.Value != nil && rSchema.If.Value == nil { - CreateChange(changes, ObjectRemoved, v3.IfLabel, - lSchema.If.ValueNode, nil, true, lSchema.If.Value, nil) - } - // Else - if lSchema.Else.Value != nil && rSchema.Else.Value != nil { - if !low.AreEqual(lSchema.Else.Value, rSchema.Else.Value) { - sc.ElseChanges = CompareSchemas(lSchema.Else.Value, rSchema.Else.Value) - } - } - // added Else - if lSchema.Else.Value == nil && rSchema.Else.Value != nil { - CreateChange(changes, ObjectAdded, v3.ElseLabel, - nil, rSchema.Else.ValueNode, true, nil, rSchema.Else.Value) - } - // removed Else - if lSchema.Else.Value != nil && rSchema.Else.Value == nil { - CreateChange(changes, ObjectRemoved, v3.ElseLabel, - lSchema.Else.ValueNode, nil, true, lSchema.Else.Value, nil) - } - // Then - if lSchema.Then.Value != nil && rSchema.Then.Value != nil { - if !low.AreEqual(lSchema.Then.Value, rSchema.Then.Value) { - sc.ThenChanges = CompareSchemas(lSchema.Then.Value, rSchema.Then.Value) - } - } - // added Then - if lSchema.Then.Value == nil && rSchema.Then.Value != nil { - CreateChange(changes, ObjectAdded, v3.ThenLabel, - nil, rSchema.Then.ValueNode, true, nil, rSchema.Then.Value) - } - // removed Then - if lSchema.Then.Value != nil && rSchema.Then.Value == nil { - CreateChange(changes, ObjectRemoved, v3.ThenLabel, - lSchema.Then.ValueNode, nil, true, lSchema.Then.Value, nil) - } - // PropertyNames - if lSchema.PropertyNames.Value != nil && rSchema.PropertyNames.Value != nil { - if !low.AreEqual(lSchema.PropertyNames.Value, rSchema.PropertyNames.Value) { - sc.PropertyNamesChanges = CompareSchemas(lSchema.PropertyNames.Value, rSchema.PropertyNames.Value) - } - } - // added PropertyNames - if lSchema.PropertyNames.Value == nil && rSchema.PropertyNames.Value != nil { - CreateChange(changes, ObjectAdded, v3.PropertyNamesLabel, - nil, rSchema.PropertyNames.ValueNode, true, nil, rSchema.PropertyNames.Value) - } - // removed PropertyNames - if lSchema.PropertyNames.Value != nil && rSchema.PropertyNames.Value == nil { - CreateChange(changes, ObjectRemoved, v3.PropertyNamesLabel, - lSchema.PropertyNames.ValueNode, nil, true, lSchema.PropertyNames.Value, nil) - } - // Contains - if lSchema.Contains.Value != nil && rSchema.Contains.Value != nil { - if !low.AreEqual(lSchema.Contains.Value, rSchema.Contains.Value) { - sc.ContainsChanges = CompareSchemas(lSchema.Contains.Value, rSchema.Contains.Value) - } - } - // added Contains - if lSchema.Contains.Value == nil && rSchema.Contains.Value != nil { - CreateChange(changes, ObjectAdded, v3.ContainsLabel, - nil, rSchema.Contains.ValueNode, true, nil, rSchema.Contains.Value) - } - // removed Contains - if lSchema.Contains.Value != nil && rSchema.Contains.Value == nil { - CreateChange(changes, ObjectRemoved, v3.ContainsLabel, - lSchema.Contains.ValueNode, nil, true, lSchema.Contains.Value, nil) - } - // UnevaluatedItems - if lSchema.UnevaluatedItems.Value != nil && rSchema.UnevaluatedItems.Value != nil { - if !low.AreEqual(lSchema.UnevaluatedItems.Value, rSchema.UnevaluatedItems.Value) { - sc.UnevaluatedItemsChanges = CompareSchemas(lSchema.UnevaluatedItems.Value, rSchema.UnevaluatedItems.Value) - } - } - // added UnevaluatedItems - if lSchema.UnevaluatedItems.Value == nil && rSchema.UnevaluatedItems.Value != nil { - CreateChange(changes, ObjectAdded, v3.UnevaluatedItemsLabel, - nil, rSchema.UnevaluatedItems.ValueNode, true, nil, rSchema.UnevaluatedItems.Value) - } - // removed UnevaluatedItems - if lSchema.UnevaluatedItems.Value != nil && rSchema.UnevaluatedItems.Value == nil { - CreateChange(changes, ObjectRemoved, v3.UnevaluatedItemsLabel, - lSchema.UnevaluatedItems.ValueNode, nil, true, lSchema.UnevaluatedItems.Value, nil) - } - // UnevaluatedProperties - if lSchema.UnevaluatedProperties.Value != nil && rSchema.UnevaluatedProperties.Value != nil { - if !low.AreEqual(lSchema.UnevaluatedProperties.Value, rSchema.UnevaluatedProperties.Value) { - sc.UnevaluatedPropertiesChanges = CompareSchemas(lSchema.UnevaluatedProperties.Value, rSchema.UnevaluatedProperties.Value) - } - } - // added UnevaluatedProperties - if lSchema.UnevaluatedProperties.Value == nil && rSchema.UnevaluatedProperties.Value != nil { - CreateChange(changes, ObjectAdded, v3.UnevaluatedPropertiesLabel, - nil, rSchema.UnevaluatedProperties.ValueNode, true, nil, rSchema.UnevaluatedProperties.Value) - } - // removed UnevaluatedProperties - if lSchema.UnevaluatedProperties.Value != nil && rSchema.UnevaluatedProperties.Value == nil { - CreateChange(changes, ObjectRemoved, v3.UnevaluatedPropertiesLabel, - lSchema.UnevaluatedProperties.ValueNode, nil, true, lSchema.UnevaluatedProperties.Value, nil) - } + // 3.1 properties + // If + if lSchema.If.Value != nil && rSchema.If.Value != nil { + if !low.AreEqual(lSchema.If.Value, rSchema.If.Value) { + sc.IfChanges = CompareSchemas(lSchema.If.Value, rSchema.If.Value) + } + } + // added If + if lSchema.If.Value == nil && rSchema.If.Value != nil { + CreateChange(changes, ObjectAdded, v3.IfLabel, + nil, rSchema.If.ValueNode, true, nil, rSchema.If.Value) + } + // removed If + if lSchema.If.Value != nil && rSchema.If.Value == nil { + CreateChange(changes, ObjectRemoved, v3.IfLabel, + lSchema.If.ValueNode, nil, true, lSchema.If.Value, nil) + } + // Else + if lSchema.Else.Value != nil && rSchema.Else.Value != nil { + if !low.AreEqual(lSchema.Else.Value, rSchema.Else.Value) { + sc.ElseChanges = CompareSchemas(lSchema.Else.Value, rSchema.Else.Value) + } + } + // added Else + if lSchema.Else.Value == nil && rSchema.Else.Value != nil { + CreateChange(changes, ObjectAdded, v3.ElseLabel, + nil, rSchema.Else.ValueNode, true, nil, rSchema.Else.Value) + } + // removed Else + if lSchema.Else.Value != nil && rSchema.Else.Value == nil { + CreateChange(changes, ObjectRemoved, v3.ElseLabel, + lSchema.Else.ValueNode, nil, true, lSchema.Else.Value, nil) + } + // Then + if lSchema.Then.Value != nil && rSchema.Then.Value != nil { + if !low.AreEqual(lSchema.Then.Value, rSchema.Then.Value) { + sc.ThenChanges = CompareSchemas(lSchema.Then.Value, rSchema.Then.Value) + } + } + // added Then + if lSchema.Then.Value == nil && rSchema.Then.Value != nil { + CreateChange(changes, ObjectAdded, v3.ThenLabel, + nil, rSchema.Then.ValueNode, true, nil, rSchema.Then.Value) + } + // removed Then + if lSchema.Then.Value != nil && rSchema.Then.Value == nil { + CreateChange(changes, ObjectRemoved, v3.ThenLabel, + lSchema.Then.ValueNode, nil, true, lSchema.Then.Value, nil) + } + // PropertyNames + if lSchema.PropertyNames.Value != nil && rSchema.PropertyNames.Value != nil { + if !low.AreEqual(lSchema.PropertyNames.Value, rSchema.PropertyNames.Value) { + sc.PropertyNamesChanges = CompareSchemas(lSchema.PropertyNames.Value, rSchema.PropertyNames.Value) + } + } + // added PropertyNames + if lSchema.PropertyNames.Value == nil && rSchema.PropertyNames.Value != nil { + CreateChange(changes, ObjectAdded, v3.PropertyNamesLabel, + nil, rSchema.PropertyNames.ValueNode, true, nil, rSchema.PropertyNames.Value) + } + // removed PropertyNames + if lSchema.PropertyNames.Value != nil && rSchema.PropertyNames.Value == nil { + CreateChange(changes, ObjectRemoved, v3.PropertyNamesLabel, + lSchema.PropertyNames.ValueNode, nil, true, lSchema.PropertyNames.Value, nil) + } + // Contains + if lSchema.Contains.Value != nil && rSchema.Contains.Value != nil { + if !low.AreEqual(lSchema.Contains.Value, rSchema.Contains.Value) { + sc.ContainsChanges = CompareSchemas(lSchema.Contains.Value, rSchema.Contains.Value) + } + } + // added Contains + if lSchema.Contains.Value == nil && rSchema.Contains.Value != nil { + CreateChange(changes, ObjectAdded, v3.ContainsLabel, + nil, rSchema.Contains.ValueNode, true, nil, rSchema.Contains.Value) + } + // removed Contains + if lSchema.Contains.Value != nil && rSchema.Contains.Value == nil { + CreateChange(changes, ObjectRemoved, v3.ContainsLabel, + lSchema.Contains.ValueNode, nil, true, lSchema.Contains.Value, nil) + } + // UnevaluatedItems + if lSchema.UnevaluatedItems.Value != nil && rSchema.UnevaluatedItems.Value != nil { + if !low.AreEqual(lSchema.UnevaluatedItems.Value, rSchema.UnevaluatedItems.Value) { + sc.UnevaluatedItemsChanges = CompareSchemas(lSchema.UnevaluatedItems.Value, rSchema.UnevaluatedItems.Value) + } + } + // added UnevaluatedItems + if lSchema.UnevaluatedItems.Value == nil && rSchema.UnevaluatedItems.Value != nil { + CreateChange(changes, ObjectAdded, v3.UnevaluatedItemsLabel, + nil, rSchema.UnevaluatedItems.ValueNode, true, nil, rSchema.UnevaluatedItems.Value) + } + // removed UnevaluatedItems + if lSchema.UnevaluatedItems.Value != nil && rSchema.UnevaluatedItems.Value == nil { + CreateChange(changes, ObjectRemoved, v3.UnevaluatedItemsLabel, + lSchema.UnevaluatedItems.ValueNode, nil, true, lSchema.UnevaluatedItems.Value, nil) + } - // Not - if lSchema.Not.Value != nil && rSchema.Not.Value != nil { - if !low.AreEqual(lSchema.Not.Value, rSchema.Not.Value) { - sc.NotChanges = CompareSchemas(lSchema.Not.Value, rSchema.Not.Value) - } - } - // added Not - if lSchema.Not.Value == nil && rSchema.Not.Value != nil { - CreateChange(changes, ObjectAdded, v3.NotLabel, - nil, rSchema.Not.ValueNode, true, nil, rSchema.Not.Value) - } - // removed not - if lSchema.Not.Value != nil && rSchema.Not.Value == nil { - CreateChange(changes, ObjectRemoved, v3.NotLabel, - lSchema.Not.ValueNode, nil, true, lSchema.Not.Value, nil) - } + // UnevaluatedProperties + if lSchema.UnevaluatedProperties.Value != nil && rSchema.UnevaluatedProperties.Value != nil { + if lSchema.UnevaluatedProperties.Value.IsA() && rSchema.UnevaluatedProperties.Value.IsA() { + if !low.AreEqual(lSchema.UnevaluatedProperties.Value.A, rSchema.UnevaluatedProperties.Value.A) { + sc.UnevaluatedPropertiesChanges = CompareSchemas(lSchema.UnevaluatedProperties.Value.A, rSchema.UnevaluatedProperties.Value.A) + } + } else { + if lSchema.UnevaluatedProperties.Value.IsB() && rSchema.UnevaluatedProperties.Value.IsB() { + if lSchema.UnevaluatedProperties.Value.B != rSchema.UnevaluatedProperties.Value.B { + CreateChange(changes, Modified, v3.UnevaluatedPropertiesLabel, + lSchema.UnevaluatedProperties.ValueNode, rSchema.UnevaluatedProperties.ValueNode, true, + lSchema.UnevaluatedProperties.Value.B, rSchema.UnevaluatedProperties.Value.B) + } + } else { + CreateChange(changes, Modified, v3.UnevaluatedPropertiesLabel, + lSchema.UnevaluatedProperties.ValueNode, rSchema.UnevaluatedProperties.ValueNode, true, + lSchema.UnevaluatedProperties.Value.B, rSchema.UnevaluatedProperties.Value.B) + } + } + } - // items - if lSchema.Items.Value != nil && rSchema.Items.Value != nil { - if lSchema.Items.Value.IsA() && rSchema.Items.Value.IsA() { - if !low.AreEqual(lSchema.Items.Value.A, rSchema.Items.Value.A) { - sc.ItemsChanges = CompareSchemas(lSchema.Items.Value.A, rSchema.Items.Value.A) - } - } else { - CreateChange(changes, Modified, v3.ItemsLabel, - lSchema.Items.ValueNode, rSchema.Items.ValueNode, true, lSchema.Items.Value.B, rSchema.Items.Value.B) - } - } - // added Items - if lSchema.Items.Value == nil && rSchema.Items.Value != nil { - CreateChange(changes, ObjectAdded, v3.ItemsLabel, - nil, rSchema.Items.ValueNode, true, nil, rSchema.Items.Value) - } - // removed Items - if lSchema.Items.Value != nil && rSchema.Items.Value == nil { - CreateChange(changes, ObjectRemoved, v3.ItemsLabel, - lSchema.Items.ValueNode, nil, true, lSchema.Items.Value, nil) - } + // added UnevaluatedProperties + if lSchema.UnevaluatedProperties.Value == nil && rSchema.UnevaluatedProperties.Value != nil { + CreateChange(changes, ObjectAdded, v3.UnevaluatedPropertiesLabel, + nil, rSchema.UnevaluatedProperties.ValueNode, true, nil, rSchema.UnevaluatedProperties.Value) + } + // removed UnevaluatedProperties + if lSchema.UnevaluatedProperties.Value != nil && rSchema.UnevaluatedProperties.Value == nil { + CreateChange(changes, ObjectRemoved, v3.UnevaluatedPropertiesLabel, + lSchema.UnevaluatedProperties.ValueNode, nil, true, lSchema.UnevaluatedProperties.Value, nil) + } - // check extensions - sc.ExtensionChanges = CompareExtensions(lSchema.Extensions, rSchema.Extensions) + // Not + if lSchema.Not.Value != nil && rSchema.Not.Value != nil { + if !low.AreEqual(lSchema.Not.Value, rSchema.Not.Value) { + sc.NotChanges = CompareSchemas(lSchema.Not.Value, rSchema.Not.Value) + } + } + // added Not + if lSchema.Not.Value == nil && rSchema.Not.Value != nil { + CreateChange(changes, ObjectAdded, v3.NotLabel, + nil, rSchema.Not.ValueNode, true, nil, rSchema.Not.Value) + } + // removed not + if lSchema.Not.Value != nil && rSchema.Not.Value == nil { + CreateChange(changes, ObjectRemoved, v3.NotLabel, + lSchema.Not.ValueNode, nil, true, lSchema.Not.Value, nil) + } - // if additional properties is an object, then hash it - // AdditionalProperties (only if not an object) - if utils.IsNodeMap(lSchema.AdditionalProperties.ValueNode) || - utils.IsNodeMap(rSchema.AdditionalProperties.ValueNode) { + // items + if lSchema.Items.Value != nil && rSchema.Items.Value != nil { + if lSchema.Items.Value.IsA() && rSchema.Items.Value.IsA() { + if !low.AreEqual(lSchema.Items.Value.A, rSchema.Items.Value.A) { + sc.ItemsChanges = CompareSchemas(lSchema.Items.Value.A, rSchema.Items.Value.A) + } + } else { + CreateChange(changes, Modified, v3.ItemsLabel, + lSchema.Items.ValueNode, rSchema.Items.ValueNode, true, lSchema.Items.Value.B, rSchema.Items.Value.B) + } + } + // added Items + if lSchema.Items.Value == nil && rSchema.Items.Value != nil { + CreateChange(changes, ObjectAdded, v3.ItemsLabel, + nil, rSchema.Items.ValueNode, true, nil, rSchema.Items.Value) + } + // removed Items + if lSchema.Items.Value != nil && rSchema.Items.Value == nil { + CreateChange(changes, ObjectRemoved, v3.ItemsLabel, + lSchema.Items.ValueNode, nil, true, lSchema.Items.Value, nil) + } - lHash := low.GenerateHashString(lSchema.AdditionalProperties.ValueNode) - rHash := low.GenerateHashString(rSchema.AdditionalProperties.ValueNode) - if lHash != rHash { - CreateChange(changes, Modified, v3.AdditionalPropertiesLabel, - lSchema.AdditionalProperties.ValueNode, rSchema.AdditionalProperties.ValueNode, false, - lSchema.AdditionalProperties.Value, rSchema.AdditionalProperties.Value) - } - } + // check extensions + sc.ExtensionChanges = CompareExtensions(lSchema.Extensions, rSchema.Extensions) - // check core properties - CheckProperties(props) + // if additional properties is an object, then hash it + // AdditionalProperties (only if not an object) + if utils.IsNodeMap(lSchema.AdditionalProperties.ValueNode) || + utils.IsNodeMap(rSchema.AdditionalProperties.ValueNode) { + + lHash := low.GenerateHashString(lSchema.AdditionalProperties.ValueNode) + rHash := low.GenerateHashString(rSchema.AdditionalProperties.ValueNode) + if lHash != rHash { + CreateChange(changes, Modified, v3.AdditionalPropertiesLabel, + lSchema.AdditionalProperties.ValueNode, rSchema.AdditionalProperties.ValueNode, false, + lSchema.AdditionalProperties.Value, rSchema.AdditionalProperties.Value) + } + } + + // check core properties + CheckProperties(props) } func checkExamples(lSchema *base.Schema, rSchema *base.Schema, changes *[]*Change) { - // check examples (3.1+) - var lExampKey, rExampKey []string - lExampN := make(map[string]*yaml.Node) - rExampN := make(map[string]*yaml.Node) - lExampVal := make(map[string]any) - rExampVal := make(map[string]any) + // check examples (3.1+) + var lExampKey, rExampKey []string + lExampN := make(map[string]*yaml.Node) + rExampN := make(map[string]*yaml.Node) + lExampVal := make(map[string]any) + rExampVal := make(map[string]any) - // create keys by hashing values - if lSchema.Examples.ValueNode != nil { - for i := range lSchema.Examples.ValueNode.Content { - key := low.GenerateHashString(lSchema.Examples.ValueNode.Content[i].Value) - lExampKey = append(lExampKey, key) - lExampVal[key] = lSchema.Examples.ValueNode.Content[i].Value - lExampN[key] = lSchema.Examples.ValueNode.Content[i] + // create keys by hashing values + if lSchema.Examples.ValueNode != nil { + for i := range lSchema.Examples.ValueNode.Content { + key := low.GenerateHashString(lSchema.Examples.ValueNode.Content[i].Value) + lExampKey = append(lExampKey, key) + lExampVal[key] = lSchema.Examples.ValueNode.Content[i].Value + lExampN[key] = lSchema.Examples.ValueNode.Content[i] - } - } - if rSchema.Examples.ValueNode != nil { - for i := range rSchema.Examples.ValueNode.Content { - key := low.GenerateHashString(rSchema.Examples.ValueNode.Content[i].Value) - rExampKey = append(rExampKey, key) - rExampVal[key] = rSchema.Examples.ValueNode.Content[i].Value - rExampN[key] = rSchema.Examples.ValueNode.Content[i] - } - } + } + } + if rSchema.Examples.ValueNode != nil { + for i := range rSchema.Examples.ValueNode.Content { + key := low.GenerateHashString(rSchema.Examples.ValueNode.Content[i].Value) + rExampKey = append(rExampKey, key) + rExampVal[key] = rSchema.Examples.ValueNode.Content[i].Value + rExampN[key] = rSchema.Examples.ValueNode.Content[i] + } + } - // if examples equal lengths, check for equality - if len(lExampKey) == len(rExampKey) { - for i := range lExampKey { - if lExampKey[i] != rExampKey[i] { - CreateChange(changes, Modified, v3.ExamplesLabel, - lExampN[lExampKey[i]], rExampN[rExampKey[i]], false, - lExampVal[lExampKey[i]], rExampVal[rExampKey[i]]) - } - } - } - // examples were removed. - if len(lExampKey) > len(rExampKey) { - for i := range lExampKey { - if i < len(rExampKey) && lExampKey[i] != rExampKey[i] { - CreateChange(changes, Modified, v3.ExamplesLabel, - lExampN[lExampKey[i]], rExampN[rExampKey[i]], false, - lExampVal[lExampKey[i]], rExampVal[rExampKey[i]]) - } - if i >= len(rExampKey) { - CreateChange(changes, ObjectRemoved, v3.ExamplesLabel, - lExampN[lExampKey[i]], nil, false, - lExampVal[lExampKey[i]], nil) - } - } - } + // if examples equal lengths, check for equality + if len(lExampKey) == len(rExampKey) { + for i := range lExampKey { + if lExampKey[i] != rExampKey[i] { + CreateChange(changes, Modified, v3.ExamplesLabel, + lExampN[lExampKey[i]], rExampN[rExampKey[i]], false, + lExampVal[lExampKey[i]], rExampVal[rExampKey[i]]) + } + } + } + // examples were removed. + if len(lExampKey) > len(rExampKey) { + for i := range lExampKey { + if i < len(rExampKey) && lExampKey[i] != rExampKey[i] { + CreateChange(changes, Modified, v3.ExamplesLabel, + lExampN[lExampKey[i]], rExampN[rExampKey[i]], false, + lExampVal[lExampKey[i]], rExampVal[rExampKey[i]]) + } + if i >= len(rExampKey) { + CreateChange(changes, ObjectRemoved, v3.ExamplesLabel, + lExampN[lExampKey[i]], nil, false, + lExampVal[lExampKey[i]], nil) + } + } + } - // examples were added - if len(lExampKey) < len(rExampKey) { - for i := range rExampKey { - if i < len(lExampKey) && lExampKey[i] != rExampKey[i] { - CreateChange(changes, Modified, v3.ExamplesLabel, - lExampN[lExampKey[i]], rExampN[rExampKey[i]], false, - lExampVal[lExampKey[i]], rExampVal[rExampKey[i]]) - } - if i >= len(lExampKey) { - CreateChange(changes, ObjectAdded, v3.ExamplesLabel, - nil, rExampN[rExampKey[i]], false, - nil, rExampVal[rExampKey[i]]) - } - } - } + // examples were added + if len(lExampKey) < len(rExampKey) { + for i := range rExampKey { + if i < len(lExampKey) && lExampKey[i] != rExampKey[i] { + CreateChange(changes, Modified, v3.ExamplesLabel, + lExampN[lExampKey[i]], rExampN[rExampKey[i]], false, + lExampVal[lExampKey[i]], rExampVal[rExampKey[i]]) + } + if i >= len(lExampKey) { + CreateChange(changes, ObjectAdded, v3.ExamplesLabel, + nil, rExampN[rExampKey[i]], false, + nil, rExampVal[rExampKey[i]]) + } + } + } } func extractSchemaChanges( - lSchema []low.ValueReference[*base.SchemaProxy], - rSchema []low.ValueReference[*base.SchemaProxy], - label string, - sc *[]*SchemaChanges, - changes *[]*Change, - done chan bool) { + lSchema []low.ValueReference[*base.SchemaProxy], + rSchema []low.ValueReference[*base.SchemaProxy], + label string, + sc *[]*SchemaChanges, + changes *[]*Change, + done chan bool) { - // if there is nothing here, there is nothing to do. - if lSchema == nil && rSchema == nil { - done <- true - return - } + // if there is nothing here, there is nothing to do. + if lSchema == nil && rSchema == nil { + done <- true + return + } - x := "%x" - // create hash key maps to check equality - lKeys := make([]string, 0, len(lSchema)) - rKeys := make([]string, 0, len(rSchema)) - lEntities := make(map[string]*base.SchemaProxy) - rEntities := make(map[string]*base.SchemaProxy) - for h := range lSchema { - q := lSchema[h].Value - z := fmt.Sprintf(x, q.Hash()) - lKeys = append(lKeys, z) - lEntities[z] = q + x := "%x" + // create hash key maps to check equality + lKeys := make([]string, 0, len(lSchema)) + rKeys := make([]string, 0, len(rSchema)) + lEntities := make(map[string]*base.SchemaProxy) + rEntities := make(map[string]*base.SchemaProxy) + for h := range lSchema { + q := lSchema[h].Value + z := fmt.Sprintf(x, q.Hash()) + lKeys = append(lKeys, z) + lEntities[z] = q - } - for h := range rSchema { - q := rSchema[h].Value - z := fmt.Sprintf(x, q.Hash()) - rKeys = append(rKeys, z) - rEntities[z] = q + } + for h := range rSchema { + q := rSchema[h].Value + z := fmt.Sprintf(x, q.Hash()) + rKeys = append(rKeys, z) + rEntities[z] = q - } + } - // sort slices so that like for like is all sequenced. - sort.Strings(lKeys) - sort.Strings(rKeys) + // sort slices so that like for like is all sequenced. + sort.Strings(lKeys) + sort.Strings(rKeys) - // check for identical lengths - if len(lKeys) == len(rKeys) { - for w := range lKeys { - // keys are different, which means there are changes. - if lKeys[w] != rKeys[w] { - *sc = append(*sc, CompareSchemas(lEntities[lKeys[w]], rEntities[rKeys[w]])) - } - } - } + // check for identical lengths + if len(lKeys) == len(rKeys) { + for w := range lKeys { + // keys are different, which means there are changes. + if lKeys[w] != rKeys[w] { + *sc = append(*sc, CompareSchemas(lEntities[lKeys[w]], rEntities[rKeys[w]])) + } + } + } - // things were removed - if len(lKeys) > len(rKeys) { - for w := range lKeys { - if w < len(rKeys) && lKeys[w] != rKeys[w] { - *sc = append(*sc, CompareSchemas(lEntities[lKeys[w]], rEntities[rKeys[w]])) - } - if w >= len(rKeys) { - CreateChange(changes, ObjectRemoved, label, - lEntities[lKeys[w]].GetValueNode(), nil, true, lEntities[lKeys[w]], nil) - } - } - } + // things were removed + if len(lKeys) > len(rKeys) { + for w := range lKeys { + if w < len(rKeys) && lKeys[w] != rKeys[w] { + *sc = append(*sc, CompareSchemas(lEntities[lKeys[w]], rEntities[rKeys[w]])) + } + if w >= len(rKeys) { + CreateChange(changes, ObjectRemoved, label, + lEntities[lKeys[w]].GetValueNode(), nil, true, lEntities[lKeys[w]], nil) + } + } + } - // things were added - if len(rKeys) > len(lKeys) { - for w := range rKeys { - if w < len(lKeys) && rKeys[w] != lKeys[w] { - *sc = append(*sc, CompareSchemas(lEntities[lKeys[w]], rEntities[rKeys[w]])) - } - if w >= len(lKeys) { - CreateChange(changes, ObjectAdded, label, - nil, rEntities[rKeys[w]].GetValueNode(), false, nil, rEntities[rKeys[w]]) - } - } - } - done <- true + // things were added + if len(rKeys) > len(lKeys) { + for w := range rKeys { + if w < len(lKeys) && rKeys[w] != lKeys[w] { + *sc = append(*sc, CompareSchemas(lEntities[lKeys[w]], rEntities[rKeys[w]])) + } + if w >= len(lKeys) { + CreateChange(changes, ObjectAdded, label, + nil, rEntities[rKeys[w]].GetValueNode(), false, nil, rEntities[rKeys[w]]) + } + } + } + done <- true } diff --git a/what-changed/model/scopes.go b/what-changed/model/scopes.go index b960846..341ccaf 100644 --- a/what-changed/model/scopes.go +++ b/what-changed/model/scopes.go @@ -4,73 +4,73 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/v2" - v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/v2" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" ) // ScopesChanges represents changes between two Swagger Scopes Objects type ScopesChanges struct { - *PropertyChanges - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + *PropertyChanges + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` } // GetAllChanges returns a slice of all changes made between Scopes objects func (s *ScopesChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, s.Changes...) - if s.ExtensionChanges != nil { - changes = append(changes, s.ExtensionChanges.GetAllChanges()...) - } - return changes + var changes []*Change + changes = append(changes, s.Changes...) + if s.ExtensionChanges != nil { + changes = append(changes, s.ExtensionChanges.GetAllChanges()...) + } + return changes } // TotalChanges returns the total changes found between two Swagger Scopes objects. func (s *ScopesChanges) TotalChanges() int { - c := s.PropertyChanges.TotalChanges() - if s.ExtensionChanges != nil { - c += s.ExtensionChanges.TotalChanges() - } - return c + c := s.PropertyChanges.TotalChanges() + if s.ExtensionChanges != nil { + c += s.ExtensionChanges.TotalChanges() + } + return c } // TotalBreakingChanges returns the total number of breaking changes between two Swagger Scopes objects. func (s *ScopesChanges) TotalBreakingChanges() int { - return s.PropertyChanges.TotalBreakingChanges() + return s.PropertyChanges.TotalBreakingChanges() } // CompareScopes compares a left and right Swagger Scopes objects for changes. If anything is found, returns // a pointer to ScopesChanges, or returns nil if nothing is found. func CompareScopes(l, r *v2.Scopes) *ScopesChanges { - if low.AreEqual(l, r) { - return nil - } - var changes []*Change - for v := range l.Values { - if r != nil && r.FindScope(v.Value) == nil { - CreateChange(&changes, ObjectRemoved, v3.Scopes, - l.Values[v].ValueNode, nil, true, - v.Value, nil) - continue - } - if r != nil && r.FindScope(v.Value) != nil { - if l.Values[v].Value != r.FindScope(v.Value).Value { - CreateChange(&changes, Modified, v3.Scopes, - l.Values[v].ValueNode, r.FindScope(v.Value).ValueNode, true, - l.Values[v].Value, r.FindScope(v.Value).Value) - } - } - } - for v := range r.Values { - if l != nil && l.FindScope(v.Value) == nil { - CreateChange(&changes, ObjectAdded, v3.Scopes, - nil, r.Values[v].ValueNode, false, - nil, v.Value) - } - } + if low.AreEqual(l, r) { + return nil + } + var changes []*Change + for v := range l.Values { + if r != nil && r.FindScope(v.Value) == nil { + CreateChange(&changes, ObjectRemoved, v3.Scopes, + l.Values[v].ValueNode, nil, true, + v.Value, nil) + continue + } + if r != nil && r.FindScope(v.Value) != nil { + if l.Values[v].Value != r.FindScope(v.Value).Value { + CreateChange(&changes, Modified, v3.Scopes, + l.Values[v].ValueNode, r.FindScope(v.Value).ValueNode, true, + l.Values[v].Value, r.FindScope(v.Value).Value) + } + } + } + for v := range r.Values { + if l != nil && l.FindScope(v.Value) == nil { + CreateChange(&changes, ObjectAdded, v3.Scopes, + nil, r.Values[v].ValueNode, false, + nil, v.Value) + } + } - sc := new(ScopesChanges) - sc.PropertyChanges = NewPropertyChanges(changes) - sc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) - return sc + sc := new(ScopesChanges) + sc.PropertyChanges = NewPropertyChanges(changes) + sc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) + return sc } diff --git a/what-changed/model/security_requirement.go b/what-changed/model/security_requirement.go index 06f392e..6794f72 100644 --- a/what-changed/model/security_requirement.go +++ b/what-changed/model/security_requirement.go @@ -4,147 +4,147 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/base" - "github.com/pb33f/libopenapi/datamodel/low/v3" - "gopkg.in/yaml.v3" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/datamodel/low/v3" + "gopkg.in/yaml.v3" ) // SecurityRequirementChanges represents changes found between two SecurityRequirement Objects. type SecurityRequirementChanges struct { - *PropertyChanges + *PropertyChanges } // GetAllChanges returns a slice of all changes made between SecurityRequirement objects func (s *SecurityRequirementChanges) GetAllChanges() []*Change { - return s.Changes + return s.Changes } // TotalChanges returns the total number of changes between two SecurityRequirement Objects. func (s *SecurityRequirementChanges) TotalChanges() int { - return s.PropertyChanges.TotalChanges() + return s.PropertyChanges.TotalChanges() } // TotalBreakingChanges returns the total number of breaking changes between two SecurityRequirement Objects. func (s *SecurityRequirementChanges) TotalBreakingChanges() int { - return s.PropertyChanges.TotalBreakingChanges() + return s.PropertyChanges.TotalBreakingChanges() } // CompareSecurityRequirement compares left and right SecurityRequirement objects for changes. If anything // is found, then a pointer to SecurityRequirementChanges is returned, otherwise nil. func CompareSecurityRequirement(l, r *base.SecurityRequirement) *SecurityRequirementChanges { - var changes []*Change - sc := new(SecurityRequirementChanges) + var changes []*Change + sc := new(SecurityRequirementChanges) - if low.AreEqual(l, r) { - return nil - } - checkSecurityRequirement(l.Requirements.Value, r.Requirements.Value, &changes) - sc.PropertyChanges = NewPropertyChanges(changes) - return sc + if low.AreEqual(l, r) { + return nil + } + checkSecurityRequirement(l.Requirements.Value, r.Requirements.Value, &changes) + sc.PropertyChanges = NewPropertyChanges(changes) + return sc } func removedSecurityRequirement(vn *yaml.Node, name string, changes *[]*Change) { - CreateChange(changes, ObjectRemoved, v3.SecurityLabel, - vn, nil, true, name, nil) + CreateChange(changes, ObjectRemoved, v3.SecurityLabel, + vn, nil, true, name, nil) } func addedSecurityRequirement(vn *yaml.Node, name string, changes *[]*Change) { - CreateChange(changes, ObjectAdded, v3.SecurityLabel, - nil, vn, false, nil, name) + CreateChange(changes, ObjectAdded, v3.SecurityLabel, + nil, vn, false, nil, name) } // tricky to do this correctly, this is my solution. func checkSecurityRequirement(lSec, rSec map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]], - changes *[]*Change) { + changes *[]*Change) { - lKeys := make([]string, len(lSec)) - rKeys := make([]string, len(rSec)) - lValues := make(map[string]low.ValueReference[[]low.ValueReference[string]]) - rValues := make(map[string]low.ValueReference[[]low.ValueReference[string]]) - var n, z int - for i := range lSec { - lKeys[n] = i.Value - lValues[i.Value] = lSec[i] - n++ - } - for i := range rSec { - rKeys[z] = i.Value - rValues[i.Value] = rSec[i] - z++ - } + lKeys := make([]string, len(lSec)) + rKeys := make([]string, len(rSec)) + lValues := make(map[string]low.ValueReference[[]low.ValueReference[string]]) + rValues := make(map[string]low.ValueReference[[]low.ValueReference[string]]) + var n, z int + for i := range lSec { + lKeys[n] = i.Value + lValues[i.Value] = lSec[i] + n++ + } + for i := range rSec { + rKeys[z] = i.Value + rValues[i.Value] = rSec[i] + z++ + } - for z = range lKeys { - if z < len(rKeys) { - if _, ok := rValues[lKeys[z]]; !ok { - removedSecurityRequirement(lValues[lKeys[z]].ValueNode, lKeys[z], changes) - continue - } + for z = range lKeys { + if z < len(rKeys) { + if _, ok := rValues[lKeys[z]]; !ok { + removedSecurityRequirement(lValues[lKeys[z]].ValueNode, lKeys[z], changes) + continue + } - lValue := lValues[lKeys[z]].Value - rValue := rValues[lKeys[z]].Value + lValue := lValues[lKeys[z]].Value + rValue := rValues[lKeys[z]].Value - // check if actual values match up - lRoleKeys := make([]string, len(lValue)) - rRoleKeys := make([]string, len(rValue)) - lRoleValues := make(map[string]low.ValueReference[string]) - rRoleValues := make(map[string]low.ValueReference[string]) - var t, k int - for i := range lValue { - lRoleKeys[t] = lValue[i].Value - lRoleValues[lValue[i].Value] = lValue[i] - t++ - } - for i := range rValue { - rRoleKeys[k] = rValue[i].Value - rRoleValues[rValue[i].Value] = rValue[i] - k++ - } + // check if actual values match up + lRoleKeys := make([]string, len(lValue)) + rRoleKeys := make([]string, len(rValue)) + lRoleValues := make(map[string]low.ValueReference[string]) + rRoleValues := make(map[string]low.ValueReference[string]) + var t, k int + for i := range lValue { + lRoleKeys[t] = lValue[i].Value + lRoleValues[lValue[i].Value] = lValue[i] + t++ + } + for i := range rValue { + rRoleKeys[k] = rValue[i].Value + rRoleValues[rValue[i].Value] = rValue[i] + k++ + } - for t = range lRoleKeys { - if t < len(rRoleKeys) { - if _, ok := rRoleValues[lRoleKeys[t]]; !ok { - removedSecurityRequirement(lRoleValues[lRoleKeys[t]].ValueNode, lRoleKeys[t], changes) - continue - } - } - if t >= len(rRoleKeys) { - if _, ok := rRoleValues[lRoleKeys[t]]; !ok { - removedSecurityRequirement(lRoleValues[lRoleKeys[t]].ValueNode, lRoleKeys[t], changes) - } - } - } - for t = range rRoleKeys { - if t < len(lRoleKeys) { - if _, ok := lRoleValues[rRoleKeys[t]]; !ok { - addedSecurityRequirement(rRoleValues[rRoleKeys[t]].ValueNode, rRoleKeys[t], changes) - continue - } - } - if t >= len(lRoleKeys) { - addedSecurityRequirement(rRoleValues[rRoleKeys[t]].ValueNode, rRoleKeys[t], changes) - } - } + for t = range lRoleKeys { + if t < len(rRoleKeys) { + if _, ok := rRoleValues[lRoleKeys[t]]; !ok { + removedSecurityRequirement(lRoleValues[lRoleKeys[t]].ValueNode, lRoleKeys[t], changes) + continue + } + } + if t >= len(rRoleKeys) { + if _, ok := rRoleValues[lRoleKeys[t]]; !ok { + removedSecurityRequirement(lRoleValues[lRoleKeys[t]].ValueNode, lRoleKeys[t], changes) + } + } + } + for t = range rRoleKeys { + if t < len(lRoleKeys) { + if _, ok := lRoleValues[rRoleKeys[t]]; !ok { + addedSecurityRequirement(rRoleValues[rRoleKeys[t]].ValueNode, rRoleKeys[t], changes) + continue + } + } + if t >= len(lRoleKeys) { + addedSecurityRequirement(rRoleValues[rRoleKeys[t]].ValueNode, rRoleKeys[t], changes) + } + } - } - if z >= len(rKeys) { - if _, ok := rValues[lKeys[z]]; !ok { - removedSecurityRequirement(lValues[lKeys[z]].ValueNode, lKeys[z], changes) - } - } - } - for z = range rKeys { - if z < len(lKeys) { - if _, ok := lValues[rKeys[z]]; !ok { - addedSecurityRequirement(rValues[rKeys[z]].ValueNode, rKeys[z], changes) - continue - } - } - if z >= len(lKeys) { - if _, ok := lValues[rKeys[z]]; !ok { - addedSecurityRequirement(rValues[rKeys[z]].ValueNode, rKeys[z], changes) - } - } - } + } + if z >= len(rKeys) { + if _, ok := rValues[lKeys[z]]; !ok { + removedSecurityRequirement(lValues[lKeys[z]].ValueNode, lKeys[z], changes) + } + } + } + for z = range rKeys { + if z < len(lKeys) { + if _, ok := lValues[rKeys[z]]; !ok { + addedSecurityRequirement(rValues[rKeys[z]].ValueNode, rKeys[z], changes) + continue + } + } + if z >= len(lKeys) { + if _, ok := lValues[rKeys[z]]; !ok { + addedSecurityRequirement(rValues[rKeys[z]].ValueNode, rKeys[z], changes) + } + } + } } diff --git a/what-changed/model/security_scheme.go b/what-changed/model/security_scheme.go index 218f926..fb24b71 100644 --- a/what-changed/model/security_scheme.go +++ b/what-changed/model/security_scheme.go @@ -4,178 +4,178 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/v2" - "github.com/pb33f/libopenapi/datamodel/low/v3" - "reflect" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/datamodel/low/v3" + "reflect" ) // SecuritySchemeChanges represents changes made between Swagger or OpenAPI SecurityScheme Objects. type SecuritySchemeChanges struct { - *PropertyChanges - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + *PropertyChanges + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` - // OpenAPI Version - OAuthFlowChanges *OAuthFlowsChanges `json:"oAuthFlow,omitempty" yaml:"oAuthFlow,omitempty"` + // OpenAPI Version + OAuthFlowChanges *OAuthFlowsChanges `json:"oAuthFlow,omitempty" yaml:"oAuthFlow,omitempty"` - // Swagger Version - ScopesChanges *ScopesChanges `json:"scopes,omitempty" yaml:"scopes,omitempty"` + // Swagger Version + ScopesChanges *ScopesChanges `json:"scopes,omitempty" yaml:"scopes,omitempty"` } // GetAllChanges returns a slice of all changes made between SecurityRequirement objects func (ss *SecuritySchemeChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, ss.Changes...) - if ss.OAuthFlowChanges != nil { - changes = append(changes, ss.OAuthFlowChanges.GetAllChanges()...) - } - if ss.ScopesChanges != nil { - changes = append(changes, ss.ScopesChanges.GetAllChanges()...) - } - if ss.ExtensionChanges != nil { - changes = append(changes, ss.ExtensionChanges.GetAllChanges()...) - } - return changes + var changes []*Change + changes = append(changes, ss.Changes...) + if ss.OAuthFlowChanges != nil { + changes = append(changes, ss.OAuthFlowChanges.GetAllChanges()...) + } + if ss.ScopesChanges != nil { + changes = append(changes, ss.ScopesChanges.GetAllChanges()...) + } + if ss.ExtensionChanges != nil { + changes = append(changes, ss.ExtensionChanges.GetAllChanges()...) + } + return changes } // TotalChanges represents total changes found between two Swagger or OpenAPI SecurityScheme instances. func (ss *SecuritySchemeChanges) TotalChanges() int { - c := ss.PropertyChanges.TotalChanges() - if ss.OAuthFlowChanges != nil { - c += ss.OAuthFlowChanges.TotalChanges() - } - if ss.ScopesChanges != nil { - c += ss.ScopesChanges.TotalChanges() - } - if ss.ExtensionChanges != nil { - c += ss.ExtensionChanges.TotalChanges() - } - return c + c := ss.PropertyChanges.TotalChanges() + if ss.OAuthFlowChanges != nil { + c += ss.OAuthFlowChanges.TotalChanges() + } + if ss.ScopesChanges != nil { + c += ss.ScopesChanges.TotalChanges() + } + if ss.ExtensionChanges != nil { + c += ss.ExtensionChanges.TotalChanges() + } + return c } // TotalBreakingChanges returns total number of breaking changes between two SecurityScheme Objects. func (ss *SecuritySchemeChanges) TotalBreakingChanges() int { - c := ss.PropertyChanges.TotalBreakingChanges() - if ss.OAuthFlowChanges != nil { - c += ss.OAuthFlowChanges.TotalBreakingChanges() - } - if ss.ScopesChanges != nil { - c += ss.ScopesChanges.TotalBreakingChanges() - } - return c + c := ss.PropertyChanges.TotalBreakingChanges() + if ss.OAuthFlowChanges != nil { + c += ss.OAuthFlowChanges.TotalBreakingChanges() + } + if ss.ScopesChanges != nil { + c += ss.ScopesChanges.TotalBreakingChanges() + } + return c } // CompareSecuritySchemesV2 is a Swagger type safe proxy for CompareSecuritySchemes func CompareSecuritySchemesV2(l, r *v2.SecurityScheme) *SecuritySchemeChanges { - return CompareSecuritySchemes(l, r) + return CompareSecuritySchemes(l, r) } // CompareSecuritySchemesV3 is an OpenAPI type safe proxt for CompareSecuritySchemes func CompareSecuritySchemesV3(l, r *v3.SecurityScheme) *SecuritySchemeChanges { - return CompareSecuritySchemes(l, r) + return CompareSecuritySchemes(l, r) } // CompareSecuritySchemes compares left and right Swagger or OpenAPI Security Scheme objects for changes. // If anything is found, returns a pointer to *SecuritySchemeChanges or nil if nothing is found. func CompareSecuritySchemes(l, r any) *SecuritySchemeChanges { - var props []*PropertyCheck - var changes []*Change + var props []*PropertyCheck + var changes []*Change - sc := new(SecuritySchemeChanges) - if reflect.TypeOf(&v2.SecurityScheme{}) == reflect.TypeOf(l) && - reflect.TypeOf(&v2.SecurityScheme{}) == reflect.TypeOf(r) { + sc := new(SecuritySchemeChanges) + if reflect.TypeOf(&v2.SecurityScheme{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v2.SecurityScheme{}) == reflect.TypeOf(r) { - lSS := l.(*v2.SecurityScheme) - rSS := r.(*v2.SecurityScheme) + lSS := l.(*v2.SecurityScheme) + rSS := r.(*v2.SecurityScheme) - if low.AreEqual(lSS, rSS) { - return nil - } - addPropertyCheck(&props, lSS.Type.ValueNode, rSS.Type.ValueNode, - lSS.Type.Value, rSS.Type.Value, &changes, v3.TypeLabel, true) + if low.AreEqual(lSS, rSS) { + return nil + } + addPropertyCheck(&props, lSS.Type.ValueNode, rSS.Type.ValueNode, + lSS.Type.Value, rSS.Type.Value, &changes, v3.TypeLabel, true) - addPropertyCheck(&props, lSS.Description.ValueNode, rSS.Description.ValueNode, - lSS.Description.Value, rSS.Description.Value, &changes, v3.DescriptionLabel, false) + addPropertyCheck(&props, lSS.Description.ValueNode, rSS.Description.ValueNode, + lSS.Description.Value, rSS.Description.Value, &changes, v3.DescriptionLabel, false) - addPropertyCheck(&props, lSS.Name.ValueNode, rSS.Name.ValueNode, - lSS.Name.Value, rSS.Name.Value, &changes, v3.NameLabel, true) + addPropertyCheck(&props, lSS.Name.ValueNode, rSS.Name.ValueNode, + lSS.Name.Value, rSS.Name.Value, &changes, v3.NameLabel, true) - addPropertyCheck(&props, lSS.In.ValueNode, rSS.In.ValueNode, - lSS.In.Value, rSS.In.Value, &changes, v3.InLabel, true) + addPropertyCheck(&props, lSS.In.ValueNode, rSS.In.ValueNode, + lSS.In.Value, rSS.In.Value, &changes, v3.InLabel, true) - addPropertyCheck(&props, lSS.Flow.ValueNode, rSS.Flow.ValueNode, - lSS.Flow.Value, rSS.Flow.Value, &changes, v3.FlowLabel, true) + addPropertyCheck(&props, lSS.Flow.ValueNode, rSS.Flow.ValueNode, + lSS.Flow.Value, rSS.Flow.Value, &changes, v3.FlowLabel, true) - addPropertyCheck(&props, lSS.AuthorizationUrl.ValueNode, rSS.AuthorizationUrl.ValueNode, - lSS.AuthorizationUrl.Value, rSS.AuthorizationUrl.Value, &changes, v3.AuthorizationUrlLabel, true) + addPropertyCheck(&props, lSS.AuthorizationUrl.ValueNode, rSS.AuthorizationUrl.ValueNode, + lSS.AuthorizationUrl.Value, rSS.AuthorizationUrl.Value, &changes, v3.AuthorizationUrlLabel, true) - addPropertyCheck(&props, lSS.TokenUrl.ValueNode, rSS.TokenUrl.ValueNode, - lSS.TokenUrl.Value, rSS.TokenUrl.Value, &changes, v3.TokenUrlLabel, true) + addPropertyCheck(&props, lSS.TokenUrl.ValueNode, rSS.TokenUrl.ValueNode, + lSS.TokenUrl.Value, rSS.TokenUrl.Value, &changes, v3.TokenUrlLabel, true) - if !lSS.Scopes.IsEmpty() && !rSS.Scopes.IsEmpty() { - if !low.AreEqual(lSS.Scopes.Value, rSS.Scopes.Value) { - sc.ScopesChanges = CompareScopes(lSS.Scopes.Value, rSS.Scopes.Value) - } - } - if lSS.Scopes.IsEmpty() && !rSS.Scopes.IsEmpty() { - CreateChange(&changes, ObjectAdded, v3.ScopesLabel, - nil, rSS.Scopes.ValueNode, false, nil, rSS.Scopes.Value) - } - if !lSS.Scopes.IsEmpty() && rSS.Scopes.IsEmpty() { - CreateChange(&changes, ObjectRemoved, v3.ScopesLabel, - lSS.Scopes.ValueNode, nil, true, lSS.Scopes.Value, nil) - } + if !lSS.Scopes.IsEmpty() && !rSS.Scopes.IsEmpty() { + if !low.AreEqual(lSS.Scopes.Value, rSS.Scopes.Value) { + sc.ScopesChanges = CompareScopes(lSS.Scopes.Value, rSS.Scopes.Value) + } + } + if lSS.Scopes.IsEmpty() && !rSS.Scopes.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.ScopesLabel, + nil, rSS.Scopes.ValueNode, false, nil, rSS.Scopes.Value) + } + if !lSS.Scopes.IsEmpty() && rSS.Scopes.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.ScopesLabel, + lSS.Scopes.ValueNode, nil, true, lSS.Scopes.Value, nil) + } - sc.ExtensionChanges = CompareExtensions(lSS.Extensions, rSS.Extensions) - } + sc.ExtensionChanges = CompareExtensions(lSS.Extensions, rSS.Extensions) + } - if reflect.TypeOf(&v3.SecurityScheme{}) == reflect.TypeOf(l) && - reflect.TypeOf(&v3.SecurityScheme{}) == reflect.TypeOf(r) { + if reflect.TypeOf(&v3.SecurityScheme{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v3.SecurityScheme{}) == reflect.TypeOf(r) { - lSS := l.(*v3.SecurityScheme) - rSS := r.(*v3.SecurityScheme) + lSS := l.(*v3.SecurityScheme) + rSS := r.(*v3.SecurityScheme) - if low.AreEqual(lSS, rSS) { - return nil - } - addPropertyCheck(&props, lSS.Type.ValueNode, rSS.Type.ValueNode, - lSS.Type.Value, rSS.Type.Value, &changes, v3.TypeLabel, true) + if low.AreEqual(lSS, rSS) { + return nil + } + addPropertyCheck(&props, lSS.Type.ValueNode, rSS.Type.ValueNode, + lSS.Type.Value, rSS.Type.Value, &changes, v3.TypeLabel, true) - addPropertyCheck(&props, lSS.Description.ValueNode, rSS.Description.ValueNode, - lSS.Description.Value, rSS.Description.Value, &changes, v3.DescriptionLabel, false) + addPropertyCheck(&props, lSS.Description.ValueNode, rSS.Description.ValueNode, + lSS.Description.Value, rSS.Description.Value, &changes, v3.DescriptionLabel, false) - addPropertyCheck(&props, lSS.Name.ValueNode, rSS.Name.ValueNode, - lSS.Name.Value, rSS.Name.Value, &changes, v3.NameLabel, true) + addPropertyCheck(&props, lSS.Name.ValueNode, rSS.Name.ValueNode, + lSS.Name.Value, rSS.Name.Value, &changes, v3.NameLabel, true) - addPropertyCheck(&props, lSS.In.ValueNode, rSS.In.ValueNode, - lSS.In.Value, rSS.In.Value, &changes, v3.InLabel, true) + addPropertyCheck(&props, lSS.In.ValueNode, rSS.In.ValueNode, + lSS.In.Value, rSS.In.Value, &changes, v3.InLabel, true) - addPropertyCheck(&props, lSS.Scheme.ValueNode, rSS.Scheme.ValueNode, - lSS.Scheme.Value, rSS.Scheme.Value, &changes, v3.SchemeLabel, true) + addPropertyCheck(&props, lSS.Scheme.ValueNode, rSS.Scheme.ValueNode, + lSS.Scheme.Value, rSS.Scheme.Value, &changes, v3.SchemeLabel, true) - addPropertyCheck(&props, lSS.BearerFormat.ValueNode, rSS.BearerFormat.ValueNode, - lSS.BearerFormat.Value, rSS.BearerFormat.Value, &changes, v3.SchemeLabel, false) + addPropertyCheck(&props, lSS.BearerFormat.ValueNode, rSS.BearerFormat.ValueNode, + lSS.BearerFormat.Value, rSS.BearerFormat.Value, &changes, v3.SchemeLabel, false) - addPropertyCheck(&props, lSS.OpenIdConnectUrl.ValueNode, rSS.OpenIdConnectUrl.ValueNode, - lSS.OpenIdConnectUrl.Value, rSS.OpenIdConnectUrl.Value, &changes, v3.OpenIdConnectUrlLabel, false) + addPropertyCheck(&props, lSS.OpenIdConnectUrl.ValueNode, rSS.OpenIdConnectUrl.ValueNode, + lSS.OpenIdConnectUrl.Value, rSS.OpenIdConnectUrl.Value, &changes, v3.OpenIdConnectUrlLabel, false) - if !lSS.Flows.IsEmpty() && !rSS.Flows.IsEmpty() { - if !low.AreEqual(lSS.Flows.Value, rSS.Flows.Value) { - sc.OAuthFlowChanges = CompareOAuthFlows(lSS.Flows.Value, rSS.Flows.Value) - } - } - if lSS.Flows.IsEmpty() && !rSS.Flows.IsEmpty() { - CreateChange(&changes, ObjectAdded, v3.FlowsLabel, - nil, rSS.Flows.ValueNode, false, nil, rSS.Flows.Value) - } - if !lSS.Flows.IsEmpty() && rSS.Flows.IsEmpty() { - CreateChange(&changes, ObjectRemoved, v3.ScopesLabel, - lSS.Flows.ValueNode, nil, true, lSS.Flows.Value, nil) - } - sc.ExtensionChanges = CompareExtensions(lSS.Extensions, rSS.Extensions) - } - CheckProperties(props) - sc.PropertyChanges = NewPropertyChanges(changes) - return sc + if !lSS.Flows.IsEmpty() && !rSS.Flows.IsEmpty() { + if !low.AreEqual(lSS.Flows.Value, rSS.Flows.Value) { + sc.OAuthFlowChanges = CompareOAuthFlows(lSS.Flows.Value, rSS.Flows.Value) + } + } + if lSS.Flows.IsEmpty() && !rSS.Flows.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.FlowsLabel, + nil, rSS.Flows.ValueNode, false, nil, rSS.Flows.Value) + } + if !lSS.Flows.IsEmpty() && rSS.Flows.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.ScopesLabel, + lSS.Flows.ValueNode, nil, true, lSS.Flows.Value, nil) + } + sc.ExtensionChanges = CompareExtensions(lSS.Extensions, rSS.Extensions) + } + CheckProperties(props) + sc.PropertyChanges = NewPropertyChanges(changes) + return sc } diff --git a/what-changed/model/server.go b/what-changed/model/server.go index 8266dde..7619707 100644 --- a/what-changed/model/server.go +++ b/what-changed/model/server.go @@ -4,79 +4,79 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/v3" ) // ServerChanges represents changes found between two OpenAPI Server Objects type ServerChanges struct { - *PropertyChanges - ServerVariableChanges map[string]*ServerVariableChanges `json:"serverVariables,omitempty" yaml:"serverVariables,omitempty"` + *PropertyChanges + ServerVariableChanges map[string]*ServerVariableChanges `json:"serverVariables,omitempty" yaml:"serverVariables,omitempty"` } // GetAllChanges returns a slice of all changes made between SecurityRequirement objects func (s *ServerChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, s.Changes...) - for k := range s.ServerVariableChanges { - changes = append(changes, s.ServerVariableChanges[k].GetAllChanges()...) - } - return changes + var changes []*Change + changes = append(changes, s.Changes...) + for k := range s.ServerVariableChanges { + changes = append(changes, s.ServerVariableChanges[k].GetAllChanges()...) + } + return changes } // TotalChanges returns total changes found between two OpenAPI Server Objects func (s *ServerChanges) TotalChanges() int { - c := s.PropertyChanges.TotalChanges() - for k := range s.ServerVariableChanges { - c += s.ServerVariableChanges[k].TotalChanges() - } - return c + c := s.PropertyChanges.TotalChanges() + for k := range s.ServerVariableChanges { + c += s.ServerVariableChanges[k].TotalChanges() + } + return c } // TotalBreakingChanges returns the total number of breaking changes found between two OpenAPI Server objects. func (s *ServerChanges) TotalBreakingChanges() int { - c := s.PropertyChanges.TotalBreakingChanges() - for k := range s.ServerVariableChanges { - c += s.ServerVariableChanges[k].TotalBreakingChanges() - } - return c + c := s.PropertyChanges.TotalBreakingChanges() + for k := range s.ServerVariableChanges { + c += s.ServerVariableChanges[k].TotalBreakingChanges() + } + return c } // CompareServers compares two OpenAPI Server objects for any changes. If anything is found, returns a pointer // to a ServerChanges instance, or returns nil if nothing is found. func CompareServers(l, r *v3.Server) *ServerChanges { - if low.AreEqual(l, r) { - return nil - } - var changes []*Change - var props []*PropertyCheck + if low.AreEqual(l, r) { + return nil + } + var changes []*Change + var props []*PropertyCheck - // URL - props = append(props, &PropertyCheck{ - LeftNode: l.URL.ValueNode, - RightNode: r.URL.ValueNode, - Label: v3.URLLabel, - Changes: &changes, - Breaking: true, - Original: l, - New: r, - }) - // Description - props = append(props, &PropertyCheck{ - LeftNode: l.Description.ValueNode, - RightNode: r.Description.ValueNode, - Label: v3.DescriptionLabel, - Changes: &changes, - Breaking: false, - Original: l, - New: r, - }) + // URL + props = append(props, &PropertyCheck{ + LeftNode: l.URL.ValueNode, + RightNode: r.URL.ValueNode, + Label: v3.URLLabel, + Changes: &changes, + Breaking: true, + Original: l, + New: r, + }) + // Description + props = append(props, &PropertyCheck{ + LeftNode: l.Description.ValueNode, + RightNode: r.Description.ValueNode, + Label: v3.DescriptionLabel, + Changes: &changes, + Breaking: false, + Original: l, + New: r, + }) - CheckProperties(props) - sc := new(ServerChanges) - sc.PropertyChanges = NewPropertyChanges(changes) - sc.ServerVariableChanges = CheckMapForChanges(l.Variables.Value, r.Variables.Value, - &changes, v3.VariablesLabel, CompareServerVariables) + CheckProperties(props) + sc := new(ServerChanges) + sc.PropertyChanges = NewPropertyChanges(changes) + sc.ServerVariableChanges = CheckMapForChanges(l.Variables.Value, r.Variables.Value, + &changes, v3.VariablesLabel, CompareServerVariables) - return sc + return sc } diff --git a/what-changed/model/server_test.go b/what-changed/model/server_test.go index 9bff2e0..0effbba 100644 --- a/what-changed/model/server_test.go +++ b/what-changed/model/server_test.go @@ -4,16 +4,16 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - v3 "github.com/pb33f/libopenapi/datamodel/low/v3" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "testing" + "github.com/pb33f/libopenapi/datamodel/low" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "testing" ) func TestCompareServers(t *testing.T) { - left := `url: https://pb33f.io + left := `url: https://pb33f.io description: a server variables: thing: @@ -22,7 +22,7 @@ variables: - biccy default: choccy` - right := `url: https://pb33f.io + right := `url: https://pb33f.io description: a server variables: thing: @@ -31,26 +31,26 @@ variables: - biccy default: choccy` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.Server - var rDoc v3.Server - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.Server + var rDoc v3.Server + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareServers(&lDoc, &rDoc) - assert.Nil(t, extChanges) + // compare. + extChanges := CompareServers(&lDoc, &rDoc) + assert.Nil(t, extChanges) } func TestCompareServers_Modified(t *testing.T) { - left := `url: https://pb33f.io + left := `url: https://pb33f.io description: a server variables: thing: @@ -59,7 +59,7 @@ variables: - biccy default: choccy` - right := `url: https://pb33f.io/hotness + right := `url: https://pb33f.io/hotness description: a server that is not variables: thing: @@ -68,27 +68,27 @@ variables: - biccy default: biccy` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.Server - var rDoc v3.Server - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.Server + var rDoc v3.Server + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareServers(&lDoc, &rDoc) - assert.Equal(t, 3, extChanges.TotalChanges()) + // compare. + extChanges := CompareServers(&lDoc, &rDoc) + assert.Equal(t, 3, extChanges.TotalChanges()) - assert.Equal(t, 2, extChanges.TotalBreakingChanges()) + assert.Equal(t, 2, extChanges.TotalBreakingChanges()) } func TestCompareServers_Added(t *testing.T) { - left := `url: https://pb33f.io + left := `url: https://pb33f.io variables: thing: enum: @@ -96,7 +96,7 @@ variables: - biccy default: choccy` - right := `url: https://pb33f.io + right := `url: https://pb33f.io description: a server variables: thing: @@ -106,29 +106,29 @@ variables: - tea default: choccy` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.Server - var rDoc v3.Server - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.Server + var rDoc v3.Server + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareServers(&lDoc, &rDoc) - assert.Equal(t, 2, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 2) - assert.Equal(t, 0, extChanges.TotalBreakingChanges()) - assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType) - assert.Equal(t, ObjectAdded, extChanges.ServerVariableChanges["thing"].Changes[0].ChangeType) + // compare. + extChanges := CompareServers(&lDoc, &rDoc) + assert.Equal(t, 2, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 2) + assert.Equal(t, 0, extChanges.TotalBreakingChanges()) + assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType) + assert.Equal(t, ObjectAdded, extChanges.ServerVariableChanges["thing"].Changes[0].ChangeType) } func TestCompareServers_Removed(t *testing.T) { - left := `url: https://pb33f.io + left := `url: https://pb33f.io variables: thing: enum: @@ -136,7 +136,7 @@ variables: - biccy default: choccy` - right := `url: https://pb33f.io + right := `url: https://pb33f.io description: a server variables: thing: @@ -146,23 +146,23 @@ variables: - tea default: choccy` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc v3.Server - var rDoc v3.Server - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc v3.Server + var rDoc v3.Server + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareServers(&rDoc, &lDoc) - assert.Equal(t, 2, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 2) - assert.Equal(t, 1, extChanges.TotalBreakingChanges()) - assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType) - assert.Equal(t, ObjectRemoved, extChanges.ServerVariableChanges["thing"].Changes[0].ChangeType) + // compare. + extChanges := CompareServers(&rDoc, &lDoc) + assert.Equal(t, 2, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 2) + assert.Equal(t, 1, extChanges.TotalBreakingChanges()) + assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType) + assert.Equal(t, ObjectRemoved, extChanges.ServerVariableChanges["thing"].Changes[0].ChangeType) } diff --git a/what-changed/model/server_variable.go b/what-changed/model/server_variable.go index 74a2b7e..06e737c 100644 --- a/what-changed/model/server_variable.go +++ b/what-changed/model/server_variable.go @@ -4,79 +4,79 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/v3" ) // ServerVariableChanges represents changes found between two OpenAPI ServerVariable Objects type ServerVariableChanges struct { - *PropertyChanges + *PropertyChanges } // GetAllChanges returns a slice of all changes made between SecurityRequirement objects func (s *ServerVariableChanges) GetAllChanges() []*Change { - return s.Changes + return s.Changes } // CompareServerVariables compares a left and right OpenAPI ServerVariable object for changes. // If anything is found, returns a pointer to a ServerVariableChanges instance, otherwise returns nil. func CompareServerVariables(l, r *v3.ServerVariable) *ServerVariableChanges { - if low.AreEqual(l, r) { - return nil - } + if low.AreEqual(l, r) { + return nil + } - var props []*PropertyCheck - var changes []*Change + var props []*PropertyCheck + var changes []*Change - lValues := make(map[string]low.NodeReference[string]) - rValues := make(map[string]low.NodeReference[string]) - for i := range l.Enum { - lValues[l.Enum[i].Value] = l.Enum[i] - } - for i := range r.Enum { - rValues[r.Enum[i].Value] = r.Enum[i] - } - for k := range lValues { - if _, ok := rValues[k]; !ok { - CreateChange(&changes, ObjectRemoved, v3.EnumLabel, - lValues[k].ValueNode, nil, true, - lValues[k].Value, nil) - continue - } - } - for k := range rValues { - if _, ok := lValues[k]; !ok { - CreateChange(&changes, ObjectAdded, v3.EnumLabel, - lValues[k].ValueNode, rValues[k].ValueNode, false, - lValues[k].Value, rValues[k].Value) - } - } + lValues := make(map[string]low.NodeReference[string]) + rValues := make(map[string]low.NodeReference[string]) + for i := range l.Enum { + lValues[l.Enum[i].Value] = l.Enum[i] + } + for i := range r.Enum { + rValues[r.Enum[i].Value] = r.Enum[i] + } + for k := range lValues { + if _, ok := rValues[k]; !ok { + CreateChange(&changes, ObjectRemoved, v3.EnumLabel, + lValues[k].ValueNode, nil, true, + lValues[k].Value, nil) + continue + } + } + for k := range rValues { + if _, ok := lValues[k]; !ok { + CreateChange(&changes, ObjectAdded, v3.EnumLabel, + lValues[k].ValueNode, rValues[k].ValueNode, false, + lValues[k].Value, rValues[k].Value) + } + } - // default - props = append(props, &PropertyCheck{ - LeftNode: l.Default.ValueNode, - RightNode: r.Default.ValueNode, - Label: v3.DefaultLabel, - Changes: &changes, - Breaking: true, - Original: l, - New: r, - }) + // default + props = append(props, &PropertyCheck{ + LeftNode: l.Default.ValueNode, + RightNode: r.Default.ValueNode, + Label: v3.DefaultLabel, + Changes: &changes, + Breaking: true, + Original: l, + New: r, + }) - // description - props = append(props, &PropertyCheck{ - LeftNode: l.Description.ValueNode, - RightNode: r.Description.ValueNode, - Label: v3.DescriptionLabel, - Changes: &changes, - Breaking: false, - Original: l, - New: r, - }) + // description + props = append(props, &PropertyCheck{ + LeftNode: l.Description.ValueNode, + RightNode: r.Description.ValueNode, + Label: v3.DescriptionLabel, + Changes: &changes, + Breaking: false, + Original: l, + New: r, + }) - // check everything. - CheckProperties(props) - sc := new(ServerVariableChanges) - sc.PropertyChanges = NewPropertyChanges(changes) - return sc + // check everything. + CheckProperties(props) + sc := new(ServerVariableChanges) + sc.PropertyChanges = NewPropertyChanges(changes) + return sc } diff --git a/what-changed/model/tags.go b/what-changed/model/tags.go index 106c9c3..81da441 100644 --- a/what-changed/model/tags.go +++ b/what-changed/model/tags.go @@ -4,46 +4,46 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/base" - "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/datamodel/low/v3" ) // TagChanges represents changes made to the Tags object of an OpenAPI document. type TagChanges struct { - *PropertyChanges - ExternalDocs *ExternalDocChanges `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + *PropertyChanges + ExternalDocs *ExternalDocChanges `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` } // GetAllChanges returns a slice of all changes made between Tag objects func (t *TagChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, t.Changes...) - if t.ExternalDocs != nil { - changes = append(changes, t.ExternalDocs.GetAllChanges()...) - } - if t.ExtensionChanges != nil { - changes = append(changes, t.ExtensionChanges.GetAllChanges()...) - } - return changes + var changes []*Change + changes = append(changes, t.Changes...) + if t.ExternalDocs != nil { + changes = append(changes, t.ExternalDocs.GetAllChanges()...) + } + if t.ExtensionChanges != nil { + changes = append(changes, t.ExtensionChanges.GetAllChanges()...) + } + return changes } // TotalChanges returns a count of everything that changed within tags. func (t *TagChanges) TotalChanges() int { - c := t.PropertyChanges.TotalChanges() - if t.ExternalDocs != nil { - c += t.ExternalDocs.TotalChanges() - } - if t.ExtensionChanges != nil { - c += t.ExtensionChanges.TotalChanges() - } - return c + c := t.PropertyChanges.TotalChanges() + if t.ExternalDocs != nil { + c += t.ExternalDocs.TotalChanges() + } + if t.ExtensionChanges != nil { + c += t.ExtensionChanges.TotalChanges() + } + return c } // TotalBreakingChanges returns the number of breaking changes made by Tags func (t *TagChanges) TotalBreakingChanges() int { - return t.PropertyChanges.TotalBreakingChanges() + return t.PropertyChanges.TotalBreakingChanges() } // CompareTags will compare a left (original) and a right (new) slice of ValueReference nodes for @@ -51,100 +51,100 @@ func (t *TagChanges) TotalBreakingChanges() int { // nil is returned instead. func CompareTags(l, r []low.ValueReference[*base.Tag]) []*TagChanges { - var tagResults []*TagChanges + var tagResults []*TagChanges - // look at the original and then look through the new. - seenLeft := make(map[string]*low.ValueReference[*base.Tag]) - seenRight := make(map[string]*low.ValueReference[*base.Tag]) - for i := range l { - h := l[i] - seenLeft[l[i].Value.Name.Value] = &h - } - for i := range r { - h := r[i] - seenRight[r[i].Value.Name.Value] = &h - } + // look at the original and then look through the new. + seenLeft := make(map[string]*low.ValueReference[*base.Tag]) + seenRight := make(map[string]*low.ValueReference[*base.Tag]) + for i := range l { + h := l[i] + seenLeft[l[i].Value.Name.Value] = &h + } + for i := range r { + h := r[i] + seenRight[r[i].Value.Name.Value] = &h + } - //var changes []*Change + //var changes []*Change - // check for removals, modifications and moves - for i := range seenLeft { - tc := new(TagChanges) - var changes []*Change + // check for removals, modifications and moves + for i := range seenLeft { + tc := new(TagChanges) + var changes []*Change - CheckForObjectAdditionOrRemoval[*base.Tag](seenLeft, seenRight, i, &changes, false, true) + CheckForObjectAdditionOrRemoval[*base.Tag](seenLeft, seenRight, i, &changes, false, true) - // if the existing tag exists, let's check it. - if seenRight[i] != nil { + // if the existing tag exists, let's check it. + if seenRight[i] != nil { - var props []*PropertyCheck + var props []*PropertyCheck - // Name - props = append(props, &PropertyCheck{ - LeftNode: seenLeft[i].Value.Name.ValueNode, - RightNode: seenRight[i].Value.Name.ValueNode, - Label: v3.NameLabel, - Changes: &changes, - Breaking: true, - Original: seenLeft[i].Value, - New: seenRight[i].Value, - }) + // Name + props = append(props, &PropertyCheck{ + LeftNode: seenLeft[i].Value.Name.ValueNode, + RightNode: seenRight[i].Value.Name.ValueNode, + Label: v3.NameLabel, + Changes: &changes, + Breaking: true, + Original: seenLeft[i].Value, + New: seenRight[i].Value, + }) - // Description - props = append(props, &PropertyCheck{ - LeftNode: seenLeft[i].Value.Description.ValueNode, - RightNode: seenRight[i].Value.Description.ValueNode, - Label: v3.DescriptionLabel, - Changes: &changes, - Breaking: false, - Original: seenLeft[i].Value, - New: seenRight[i].Value, - }) + // Description + props = append(props, &PropertyCheck{ + LeftNode: seenLeft[i].Value.Description.ValueNode, + RightNode: seenRight[i].Value.Description.ValueNode, + Label: v3.DescriptionLabel, + Changes: &changes, + Breaking: false, + Original: seenLeft[i].Value, + New: seenRight[i].Value, + }) - // check properties - CheckProperties(props) + // check properties + CheckProperties(props) - // compare external docs - if !seenLeft[i].Value.ExternalDocs.IsEmpty() && !seenRight[i].Value.ExternalDocs.IsEmpty() { - tc.ExternalDocs = CompareExternalDocs(seenLeft[i].Value.ExternalDocs.Value, - seenRight[i].Value.ExternalDocs.Value) - } - if seenLeft[i].Value.ExternalDocs.IsEmpty() && !seenRight[i].Value.ExternalDocs.IsEmpty() { - CreateChange(&changes, ObjectAdded, v3.ExternalDocsLabel, nil, seenRight[i].GetValueNode(), - false, nil, seenRight[i].Value.ExternalDocs.Value) - } - if !seenLeft[i].Value.ExternalDocs.IsEmpty() && seenRight[i].Value.ExternalDocs.IsEmpty() { - CreateChange(&changes, ObjectRemoved, v3.ExternalDocsLabel, seenLeft[i].GetValueNode(), nil, - false, seenLeft[i].Value.ExternalDocs.Value, nil) - } + // compare external docs + if !seenLeft[i].Value.ExternalDocs.IsEmpty() && !seenRight[i].Value.ExternalDocs.IsEmpty() { + tc.ExternalDocs = CompareExternalDocs(seenLeft[i].Value.ExternalDocs.Value, + seenRight[i].Value.ExternalDocs.Value) + } + if seenLeft[i].Value.ExternalDocs.IsEmpty() && !seenRight[i].Value.ExternalDocs.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.ExternalDocsLabel, nil, seenRight[i].GetValueNode(), + false, nil, seenRight[i].Value.ExternalDocs.Value) + } + if !seenLeft[i].Value.ExternalDocs.IsEmpty() && seenRight[i].Value.ExternalDocs.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.ExternalDocsLabel, seenLeft[i].GetValueNode(), nil, + false, seenLeft[i].Value.ExternalDocs.Value, nil) + } - // check extensions - tc.ExtensionChanges = CompareExtensions(seenLeft[i].Value.Extensions, seenRight[i].Value.Extensions) - tc.PropertyChanges = NewPropertyChanges(changes) - if tc.TotalChanges() > 0 { - tagResults = append(tagResults, tc) - } - continue - } + // check extensions + tc.ExtensionChanges = CompareExtensions(seenLeft[i].Value.Extensions, seenRight[i].Value.Extensions) + tc.PropertyChanges = NewPropertyChanges(changes) + if tc.TotalChanges() > 0 { + tagResults = append(tagResults, tc) + } + continue + } - if len(changes) > 0 { - tc.PropertyChanges = NewPropertyChanges(changes) - tagResults = append(tagResults, tc) - } + if len(changes) > 0 { + tc.PropertyChanges = NewPropertyChanges(changes) + tagResults = append(tagResults, tc) + } - } - for i := range seenRight { - if seenLeft[i] == nil { - tc := new(TagChanges) - var changes []*Change + } + for i := range seenRight { + if seenLeft[i] == nil { + tc := new(TagChanges) + var changes []*Change - CreateChange(&changes, ObjectAdded, i, nil, seenRight[i].GetValueNode(), - false, nil, seenRight[i].GetValue()) + CreateChange(&changes, ObjectAdded, i, nil, seenRight[i].GetValueNode(), + false, nil, seenRight[i].GetValue()) - tc.PropertyChanges = NewPropertyChanges(changes) - tagResults = append(tagResults, tc) + tc.PropertyChanges = NewPropertyChanges(changes) + tagResults = append(tagResults, tc) - } - } - return tagResults + } + } + return tagResults } diff --git a/what-changed/model/xml.go b/what-changed/model/xml.go index 624e930..a903c77 100644 --- a/what-changed/model/xml.go +++ b/what-changed/model/xml.go @@ -4,111 +4,111 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low/base" - v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/datamodel/low/base" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" ) // XMLChanges represents changes made to the XML object of an OpenAPI document. type XMLChanges struct { - *PropertyChanges - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + *PropertyChanges + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` } // GetAllChanges returns a slice of all changes made between XML objects func (x *XMLChanges) GetAllChanges() []*Change { - var changes []*Change - changes = append(changes, x.Changes...) - if x.ExtensionChanges != nil { - changes = append(changes, x.ExtensionChanges.GetAllChanges()...) - } - return changes + var changes []*Change + changes = append(changes, x.Changes...) + if x.ExtensionChanges != nil { + changes = append(changes, x.ExtensionChanges.GetAllChanges()...) + } + return changes } // TotalChanges returns a count of everything that was changed within an XML object. func (x *XMLChanges) TotalChanges() int { - c := x.PropertyChanges.TotalChanges() - if x.ExtensionChanges != nil { - c += x.ExtensionChanges.TotalChanges() - } - return c + c := x.PropertyChanges.TotalChanges() + if x.ExtensionChanges != nil { + c += x.ExtensionChanges.TotalChanges() + } + return c } // TotalBreakingChanges returns the number of breaking changes made by the XML object. func (x *XMLChanges) TotalBreakingChanges() int { - return x.PropertyChanges.TotalBreakingChanges() + return x.PropertyChanges.TotalBreakingChanges() } // CompareXML will compare a left (original) and a right (new) XML instance, and check for // any changes between them. If changes are found, the function returns a pointer to XMLChanges, // otherwise, if nothing changed - it will return nil func CompareXML(l, r *base.XML) *XMLChanges { - xc := new(XMLChanges) - var changes []*Change - var props []*PropertyCheck + xc := new(XMLChanges) + var changes []*Change + var props []*PropertyCheck - // Name (breaking change) - props = append(props, &PropertyCheck{ - LeftNode: l.Name.ValueNode, - RightNode: r.Name.ValueNode, - Label: v3.NameLabel, - Changes: &changes, - Breaking: true, - Original: l, - New: r, - }) + // Name (breaking change) + props = append(props, &PropertyCheck{ + LeftNode: l.Name.ValueNode, + RightNode: r.Name.ValueNode, + Label: v3.NameLabel, + Changes: &changes, + Breaking: true, + Original: l, + New: r, + }) - // Namespace (breaking change) - props = append(props, &PropertyCheck{ - LeftNode: l.Namespace.ValueNode, - RightNode: r.Namespace.ValueNode, - Label: v3.NamespaceLabel, - Changes: &changes, - Breaking: true, - Original: l, - New: r, - }) + // Namespace (breaking change) + props = append(props, &PropertyCheck{ + LeftNode: l.Namespace.ValueNode, + RightNode: r.Namespace.ValueNode, + Label: v3.NamespaceLabel, + Changes: &changes, + Breaking: true, + Original: l, + New: r, + }) - // Prefix (breaking change) - props = append(props, &PropertyCheck{ - LeftNode: l.Prefix.ValueNode, - RightNode: r.Prefix.ValueNode, - Label: v3.PrefixLabel, - Changes: &changes, - Breaking: true, - Original: l, - New: r, - }) + // Prefix (breaking change) + props = append(props, &PropertyCheck{ + LeftNode: l.Prefix.ValueNode, + RightNode: r.Prefix.ValueNode, + Label: v3.PrefixLabel, + Changes: &changes, + Breaking: true, + Original: l, + New: r, + }) - // Attribute (breaking change) - props = append(props, &PropertyCheck{ - LeftNode: l.Attribute.ValueNode, - RightNode: r.Attribute.ValueNode, - Label: v3.AttributeLabel, - Changes: &changes, - Breaking: true, - Original: l, - New: r, - }) + // Attribute (breaking change) + props = append(props, &PropertyCheck{ + LeftNode: l.Attribute.ValueNode, + RightNode: r.Attribute.ValueNode, + Label: v3.AttributeLabel, + Changes: &changes, + Breaking: true, + Original: l, + New: r, + }) - // Wrapped (breaking change) - props = append(props, &PropertyCheck{ - LeftNode: l.Wrapped.ValueNode, - RightNode: r.Wrapped.ValueNode, - Label: v3.WrappedLabel, - Changes: &changes, - Breaking: true, - Original: l, - New: r, - }) + // Wrapped (breaking change) + props = append(props, &PropertyCheck{ + LeftNode: l.Wrapped.ValueNode, + RightNode: r.Wrapped.ValueNode, + Label: v3.WrappedLabel, + Changes: &changes, + Breaking: true, + Original: l, + New: r, + }) - // check properties - CheckProperties(props) + // check properties + CheckProperties(props) - // check extensions - xc.ExtensionChanges = CheckExtensions(l, r) - xc.PropertyChanges = NewPropertyChanges(changes) - if xc.TotalChanges() <= 0 { - return nil - } - return xc + // check extensions + xc.ExtensionChanges = CheckExtensions(l, r) + xc.PropertyChanges = NewPropertyChanges(changes) + if xc.TotalChanges() <= 0 { + return nil + } + return xc } diff --git a/what-changed/model/xml_test.go b/what-changed/model/xml_test.go index 9a5e2e5..7f1390d 100644 --- a/what-changed/model/xml_test.go +++ b/what-changed/model/xml_test.go @@ -4,144 +4,144 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/base" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "testing" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "testing" ) func TestCompareXML_NameChanged(t *testing.T) { - left := `name: xml thing + left := `name: xml thing namespace: something prefix: another attribute: true wrapped: true` - right := `namespace: something + right := `namespace: something prefix: another name: changed xml thing attribute: true wrapped: true` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc base.XML - var rDoc base.XML - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc base.XML + var rDoc base.XML + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareXML(&lDoc, &rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, Modified, extChanges.Changes[0].ChangeType) + // compare. + extChanges := CompareXML(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, Modified, extChanges.Changes[0].ChangeType) } func TestCompareXML_NameRemoved(t *testing.T) { - left := `name: xml thing + left := `name: xml thing namespace: something prefix: another attribute: true wrapped: true` - right := `wrapped: true + right := `wrapped: true prefix: another attribute: true namespace: something` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc base.XML - var rDoc base.XML - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc base.XML + var rDoc base.XML + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareXML(&lDoc, &rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType) - assert.Equal(t, 1, extChanges.TotalBreakingChanges()) + // compare. + extChanges := CompareXML(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType) + assert.Equal(t, 1, extChanges.TotalBreakingChanges()) } func TestCompareXML_ExtensionAdded(t *testing.T) { - left := `name: xml thing + left := `name: xml thing namespace: something prefix: another attribute: true wrapped: true` - right := `name: xml thing + right := `name: xml thing namespace: something prefix: another attribute: true wrapped: true x-coffee: time` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc base.XML - var rDoc base.XML - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc base.XML + var rDoc base.XML + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareXML(&lDoc, &rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Len(t, extChanges.GetAllChanges(), 1) - assert.Equal(t, ObjectAdded, extChanges.ExtensionChanges.Changes[0].ChangeType) + // compare. + extChanges := CompareXML(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Len(t, extChanges.GetAllChanges(), 1) + assert.Equal(t, ObjectAdded, extChanges.ExtensionChanges.Changes[0].ChangeType) } func TestCompareXML_Identical(t *testing.T) { - left := `name: xml thing + left := `name: xml thing namespace: something prefix: another attribute: true wrapped: true` - right := `name: xml thing + right := `name: xml thing namespace: something prefix: another attribute: true wrapped: true` - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) - // create low level objects - var lDoc base.XML - var rDoc base.XML - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) + // create low level objects + var lDoc base.XML + var rDoc base.XML + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) - // compare. - extChanges := CompareXML(&lDoc, &rDoc) + // compare. + extChanges := CompareXML(&lDoc, &rDoc) assert.Nil(t, extChanges) } diff --git a/what-changed/reports/summary.go b/what-changed/reports/summary.go index 7d77162..c8f63df 100644 --- a/what-changed/reports/summary.go +++ b/what-changed/reports/summary.go @@ -4,85 +4,85 @@ package reports import ( - v3 "github.com/pb33f/libopenapi/datamodel/low/v3" - "github.com/pb33f/libopenapi/what-changed/model" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/what-changed/model" ) // Changed provides a simple wrapper for changed counts type Changed struct { - Total int `json:"totalChanges"` - Breaking int `json:"breakingChanges"` + Total int `json:"totalChanges"` + Breaking int `json:"breakingChanges"` } // OverallReport provides a Document level overview of all changes to an OpenAPI doc. type OverallReport struct { - ChangeReport map[string]*Changed `json:"overallSummaryReport"` + ChangeReport map[string]*Changed `json:"overallSummaryReport"` } // CreateOverallReport will create a high level report for all top level changes (but with deep counts) func CreateOverallReport(changes *model.DocumentChanges) *OverallReport { - changedReport := make(map[string]*Changed) - if changes.InfoChanges != nil { - changedReport[v3.InfoLabel] = createChangedModel(changes.InfoChanges) - } - if changes.PathsChanges != nil { - changedReport[v3.PathsLabel] = createChangedModel(changes.PathsChanges) - } - if changes.TagChanges != nil { - j := make([]HasChanges, len(changes.TagChanges)) - for k := range changes.TagChanges { - j[k] = HasChanges(changes.TagChanges[k]) - } - changedReport[v3.TagsLabel] = createChangedModelFromSlice(j) - } - if changes.ExternalDocChanges != nil { - changedReport[v3.ExternalDocsLabel] = createChangedModel(changes.ExternalDocChanges) - } - if changes.WebhookChanges != nil { - j := make([]HasChanges, len(changes.WebhookChanges)) - z := 0 - for k := range changes.WebhookChanges { - j[z] = HasChanges(changes.WebhookChanges[k]) - z++ - } - ch := createChangedModelFromSlice(j) - if ch.Total > 0 { - changedReport[v3.WebhooksLabel] = ch - } - } - if changes.ServerChanges != nil { - j := make([]HasChanges, len(changes.ServerChanges)) - for k := range changes.ServerChanges { - j[k] = HasChanges(changes.ServerChanges[k]) - } - changedReport[v3.ServersLabel] = createChangedModelFromSlice(j) - } - if changes.SecurityRequirementChanges != nil { - j := make([]HasChanges, len(changes.SecurityRequirementChanges)) - for k := range changes.SecurityRequirementChanges { - j[k] = HasChanges(changes.SecurityRequirementChanges[k]) - } - changedReport[v3.SecurityLabel] = createChangedModelFromSlice(j) - } - if changes.ComponentsChanges != nil { - changedReport[v3.ComponentsLabel] = createChangedModel(changes.ComponentsChanges) - } - return &OverallReport{ - ChangeReport: changedReport, - } + changedReport := make(map[string]*Changed) + if changes.InfoChanges != nil { + changedReport[v3.InfoLabel] = createChangedModel(changes.InfoChanges) + } + if changes.PathsChanges != nil { + changedReport[v3.PathsLabel] = createChangedModel(changes.PathsChanges) + } + if changes.TagChanges != nil { + j := make([]HasChanges, len(changes.TagChanges)) + for k := range changes.TagChanges { + j[k] = HasChanges(changes.TagChanges[k]) + } + changedReport[v3.TagsLabel] = createChangedModelFromSlice(j) + } + if changes.ExternalDocChanges != nil { + changedReport[v3.ExternalDocsLabel] = createChangedModel(changes.ExternalDocChanges) + } + if changes.WebhookChanges != nil { + j := make([]HasChanges, len(changes.WebhookChanges)) + z := 0 + for k := range changes.WebhookChanges { + j[z] = HasChanges(changes.WebhookChanges[k]) + z++ + } + ch := createChangedModelFromSlice(j) + if ch.Total > 0 { + changedReport[v3.WebhooksLabel] = ch + } + } + if changes.ServerChanges != nil { + j := make([]HasChanges, len(changes.ServerChanges)) + for k := range changes.ServerChanges { + j[k] = HasChanges(changes.ServerChanges[k]) + } + changedReport[v3.ServersLabel] = createChangedModelFromSlice(j) + } + if changes.SecurityRequirementChanges != nil { + j := make([]HasChanges, len(changes.SecurityRequirementChanges)) + for k := range changes.SecurityRequirementChanges { + j[k] = HasChanges(changes.SecurityRequirementChanges[k]) + } + changedReport[v3.SecurityLabel] = createChangedModelFromSlice(j) + } + if changes.ComponentsChanges != nil { + changedReport[v3.ComponentsLabel] = createChangedModel(changes.ComponentsChanges) + } + return &OverallReport{ + ChangeReport: changedReport, + } } func createChangedModel(ch HasChanges) *Changed { - return &Changed{ch.TotalChanges(), ch.TotalBreakingChanges()} + return &Changed{ch.TotalChanges(), ch.TotalBreakingChanges()} } func createChangedModelFromSlice(ch []HasChanges) *Changed { - t := 0 - b := 0 - for n := range ch { - t += ch[n].TotalChanges() - b += ch[n].TotalBreakingChanges() - } - return &Changed{t, b} + t := 0 + b := 0 + for n := range ch { + t += ch[n].TotalChanges() + b += ch[n].TotalBreakingChanges() + } + return &Changed{t, b} } diff --git a/what-changed/reports/summary_test.go b/what-changed/reports/summary_test.go index 233a160..4fb4020 100644 --- a/what-changed/reports/summary_test.go +++ b/what-changed/reports/summary_test.go @@ -4,35 +4,35 @@ package reports import ( - "github.com/pb33f/libopenapi" - v3 "github.com/pb33f/libopenapi/datamodel/low/v3" - "github.com/pb33f/libopenapi/what-changed/model" - "github.com/stretchr/testify/assert" - "io/ioutil" - "testing" + "github.com/pb33f/libopenapi" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/what-changed/model" + "github.com/stretchr/testify/assert" + "io/ioutil" + "testing" ) func createDiff() *model.DocumentChanges { - burgerShopOriginal, _ := ioutil.ReadFile("../../test_specs/burgershop.openapi.yaml") - burgerShopUpdated, _ := ioutil.ReadFile("../../test_specs/burgershop.openapi-modified.yaml") - originalDoc, _ := libopenapi.NewDocument(burgerShopOriginal) - updatedDoc, _ := libopenapi.NewDocument(burgerShopUpdated) - documentChanges, _ := libopenapi.CompareDocuments(originalDoc, updatedDoc) - return documentChanges + burgerShopOriginal, _ := ioutil.ReadFile("../../test_specs/burgershop.openapi.yaml") + burgerShopUpdated, _ := ioutil.ReadFile("../../test_specs/burgershop.openapi-modified.yaml") + originalDoc, _ := libopenapi.NewDocument(burgerShopOriginal) + updatedDoc, _ := libopenapi.NewDocument(burgerShopUpdated) + documentChanges, _ := libopenapi.CompareDocuments(originalDoc, updatedDoc) + return documentChanges } func TestCreateSummary_OverallReport(t *testing.T) { - changes := createDiff() - report := CreateOverallReport(changes) - assert.Equal(t, 1, report.ChangeReport[v3.InfoLabel].Total) - assert.Equal(t, 43, report.ChangeReport[v3.PathsLabel].Total) - assert.Equal(t, 9, report.ChangeReport[v3.PathsLabel].Breaking) - assert.Equal(t, 3, report.ChangeReport[v3.TagsLabel].Total) - assert.Equal(t, 1, report.ChangeReport[v3.ExternalDocsLabel].Total) - assert.Equal(t, 2, report.ChangeReport[v3.WebhooksLabel].Total) - assert.Equal(t, 2, report.ChangeReport[v3.ServersLabel].Total) - assert.Equal(t, 1, report.ChangeReport[v3.ServersLabel].Breaking) - assert.Equal(t, 1, report.ChangeReport[v3.SecurityLabel].Total) - assert.Equal(t, 17, report.ChangeReport[v3.ComponentsLabel].Total) - assert.Equal(t, 6, report.ChangeReport[v3.ComponentsLabel].Breaking) + changes := createDiff() + report := CreateOverallReport(changes) + assert.Equal(t, 1, report.ChangeReport[v3.InfoLabel].Total) + assert.Equal(t, 43, report.ChangeReport[v3.PathsLabel].Total) + assert.Equal(t, 9, report.ChangeReport[v3.PathsLabel].Breaking) + assert.Equal(t, 3, report.ChangeReport[v3.TagsLabel].Total) + assert.Equal(t, 1, report.ChangeReport[v3.ExternalDocsLabel].Total) + assert.Equal(t, 2, report.ChangeReport[v3.WebhooksLabel].Total) + assert.Equal(t, 2, report.ChangeReport[v3.ServersLabel].Total) + assert.Equal(t, 1, report.ChangeReport[v3.ServersLabel].Breaking) + assert.Equal(t, 1, report.ChangeReport[v3.SecurityLabel].Total) + assert.Equal(t, 17, report.ChangeReport[v3.ComponentsLabel].Total) + assert.Equal(t, 6, report.ChangeReport[v3.ComponentsLabel].Breaking) } diff --git a/what-changed/reports/types.go b/what-changed/reports/types.go index 45b0d56..68182d2 100644 --- a/what-changed/reports/types.go +++ b/what-changed/reports/types.go @@ -6,9 +6,9 @@ package reports // HasChanges represents a change model that provides a total change count and a breaking change count. type HasChanges interface { - // TotalChanges represents number of all changes found - TotalChanges() int + // TotalChanges represents number of all changes found + TotalChanges() int - // TotalBreakingChanges represents the number of contract breaking changes only. - TotalBreakingChanges() int + // TotalBreakingChanges represents the number of contract breaking changes only. + TotalBreakingChanges() int }