diff --git a/what-changed/model/components.go b/what-changed/model/components.go index 454096e..b240219 100644 --- a/what-changed/model/components.go +++ b/what-changed/model/components.go @@ -4,11 +4,12 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/base" - "github.com/pb33f/libopenapi/datamodel/low/v2" - "github.com/pb33f/libopenapi/datamodel/low/v3" - "reflect" + "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" ) // ComponentsChanges represents changes made to both OpenAPI and Swagger documents. This model is based on OpenAPI 3 @@ -35,228 +36,228 @@ import ( // modifications are not checked, these checks occur in-place by implementing objects as they are autp-resolved // when the model is built. type ComponentsChanges struct { - *PropertyChanges - SchemaChanges map[string]*SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"` - SecuritySchemeChanges map[string]*SecuritySchemeChanges `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"` - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + *PropertyChanges + SchemaChanges map[string]*SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"` + SecuritySchemeChanges map[string]*SecuritySchemeChanges `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` } // CompareComponents will compare OpenAPI components for any changes. Accepts Swagger Definition objects // like ParameterDefinitions or Definitions etc. func CompareComponents(l, r any) *ComponentsChanges { - var changes []*Change + var changes []*Change - cc := new(ComponentsChanges) + cc := new(ComponentsChanges) - // Swagger Parameters - if reflect.TypeOf(&v2.ParameterDefinitions{}) == reflect.TypeOf(l) && - reflect.TypeOf(&v2.ParameterDefinitions{}) == reflect.TypeOf(r) { - lDef := l.(*v2.ParameterDefinitions) - rDef := r.(*v2.ParameterDefinitions) - var a, b map[low.KeyReference[string]]low.ValueReference[*v2.Parameter] - if lDef != nil { - a = lDef.Definitions - } - if rDef != nil { - b = rDef.Definitions - } - CheckMapForAdditionRemoval(a, b, &changes, v3.ParametersLabel) - } + // Swagger Parameters + if reflect.TypeOf(&v2.ParameterDefinitions{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v2.ParameterDefinitions{}) == reflect.TypeOf(r) { + lDef := l.(*v2.ParameterDefinitions) + rDef := r.(*v2.ParameterDefinitions) + var a, b map[low.KeyReference[string]]low.ValueReference[*v2.Parameter] + if lDef != nil { + a = lDef.Definitions + } + if rDef != nil { + b = rDef.Definitions + } + CheckMapForAdditionRemoval(a, b, &changes, v3.ParametersLabel) + } - // Swagger Responses - if reflect.TypeOf(&v2.ResponsesDefinitions{}) == reflect.TypeOf(l) && - reflect.TypeOf(&v2.ResponsesDefinitions{}) == reflect.TypeOf(r) { - lDef := l.(*v2.ResponsesDefinitions) - rDef := r.(*v2.ResponsesDefinitions) - var a, b map[low.KeyReference[string]]low.ValueReference[*v2.Response] - if lDef != nil { - a = lDef.Definitions - } - if rDef != nil { - b = rDef.Definitions - } - CheckMapForAdditionRemoval(a, b, &changes, v3.ResponsesLabel) - } + // Swagger Responses + if reflect.TypeOf(&v2.ResponsesDefinitions{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v2.ResponsesDefinitions{}) == reflect.TypeOf(r) { + lDef := l.(*v2.ResponsesDefinitions) + rDef := r.(*v2.ResponsesDefinitions) + var a, b map[low.KeyReference[string]]low.ValueReference[*v2.Response] + if lDef != nil { + a = lDef.Definitions + } + if rDef != nil { + b = rDef.Definitions + } + CheckMapForAdditionRemoval(a, b, &changes, v3.ResponsesLabel) + } - // Swagger Schemas - if reflect.TypeOf(&v2.Definitions{}) == reflect.TypeOf(l) && - reflect.TypeOf(&v2.Definitions{}) == reflect.TypeOf(r) { - lDef := l.(*v2.Definitions) - rDef := r.(*v2.Definitions) - var a, b map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy] - if lDef != nil { - a = lDef.Schemas - } - if rDef != nil { - b = rDef.Schemas - } - cc.SchemaChanges = CheckMapForChanges(a, b, &changes, v2.DefinitionsLabel, CompareSchemas) - } + // Swagger Schemas + if reflect.TypeOf(&v2.Definitions{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v2.Definitions{}) == reflect.TypeOf(r) { + lDef := l.(*v2.Definitions) + rDef := r.(*v2.Definitions) + var a, b map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy] + if lDef != nil { + a = lDef.Schemas + } + if rDef != nil { + b = rDef.Schemas + } + cc.SchemaChanges = CheckMapForChanges(a, b, &changes, v2.DefinitionsLabel, CompareSchemas) + } - // Swagger Security Definitions - if reflect.TypeOf(&v2.SecurityDefinitions{}) == reflect.TypeOf(l) && - reflect.TypeOf(&v2.SecurityDefinitions{}) == reflect.TypeOf(r) { - lDef := l.(*v2.SecurityDefinitions) - rDef := r.(*v2.SecurityDefinitions) - var a, b map[low.KeyReference[string]]low.ValueReference[*v2.SecurityScheme] - if lDef != nil { - a = lDef.Definitions - } - if rDef != nil { - b = rDef.Definitions - } - cc.SecuritySchemeChanges = CheckMapForChanges(a, b, &changes, - v3.SecurityDefinitionLabel, CompareSecuritySchemesV2) - } + // Swagger Security Definitions + if reflect.TypeOf(&v2.SecurityDefinitions{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v2.SecurityDefinitions{}) == reflect.TypeOf(r) { + lDef := l.(*v2.SecurityDefinitions) + rDef := r.(*v2.SecurityDefinitions) + var a, b map[low.KeyReference[string]]low.ValueReference[*v2.SecurityScheme] + if lDef != nil { + a = lDef.Definitions + } + if rDef != nil { + b = rDef.Definitions + } + cc.SecuritySchemeChanges = CheckMapForChanges(a, b, &changes, + v3.SecurityDefinitionLabel, CompareSecuritySchemesV2) + } - // OpenAPI Components - if reflect.TypeOf(&v3.Components{}) == reflect.TypeOf(l) && - reflect.TypeOf(&v3.Components{}) == reflect.TypeOf(r) { + // OpenAPI Components + if reflect.TypeOf(&v3.Components{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v3.Components{}) == reflect.TypeOf(r) { - lComponents := l.(*v3.Components) - rComponents := r.(*v3.Components) + lComponents := l.(*v3.Components) + rComponents := r.(*v3.Components) - //if low.AreEqual(lComponents, rComponents) { - // return nil - //} + //if low.AreEqual(lComponents, rComponents) { + // return nil + //} - doneChan := make(chan componentComparison) - comparisons := 0 + doneChan := make(chan componentComparison) + comparisons := 0 - // run as fast as we can, thread all the things. - if !lComponents.Schemas.IsEmpty() || !rComponents.Schemas.IsEmpty() { - comparisons++ - go runComparison(lComponents.Schemas.Value, rComponents.Schemas.Value, - &changes, v3.SchemasLabel, CompareSchemas, doneChan) - } + // run as fast as we can, thread all the things. + if !lComponents.Schemas.IsEmpty() || !rComponents.Schemas.IsEmpty() { + comparisons++ + go runComparison(lComponents.Schemas.Value, rComponents.Schemas.Value, + &changes, v3.SchemasLabel, CompareSchemas, doneChan) + } - if !lComponents.Responses.IsEmpty() || !rComponents.Responses.IsEmpty() { - comparisons++ - go runComparison(lComponents.Responses.Value, rComponents.Responses.Value, - &changes, v3.ResponsesLabel, CompareResponseV3, doneChan) - } + if !lComponents.Responses.IsEmpty() || !rComponents.Responses.IsEmpty() { + comparisons++ + go runComparison(lComponents.Responses.Value, rComponents.Responses.Value, + &changes, v3.ResponsesLabel, CompareResponseV3, doneChan) + } - if !lComponents.Parameters.IsEmpty() || !rComponents.Parameters.IsEmpty() { - comparisons++ - go runComparison(lComponents.Parameters.Value, rComponents.Parameters.Value, - &changes, v3.ParametersLabel, CompareParametersV3, doneChan) - } + if !lComponents.Parameters.IsEmpty() || !rComponents.Parameters.IsEmpty() { + comparisons++ + go runComparison(lComponents.Parameters.Value, rComponents.Parameters.Value, + &changes, v3.ParametersLabel, CompareParametersV3, doneChan) + } - if !lComponents.Examples.IsEmpty() || !rComponents.Examples.IsEmpty() { - comparisons++ - go runComparison(lComponents.Examples.Value, rComponents.Examples.Value, - &changes, v3.ExamplesLabel, CompareExamples, doneChan) - } + if !lComponents.Examples.IsEmpty() || !rComponents.Examples.IsEmpty() { + comparisons++ + go runComparison(lComponents.Examples.Value, rComponents.Examples.Value, + &changes, v3.ExamplesLabel, CompareExamples, doneChan) + } - if !lComponents.RequestBodies.IsEmpty() || !rComponents.RequestBodies.IsEmpty() { - comparisons++ - go runComparison(lComponents.RequestBodies.Value, rComponents.RequestBodies.Value, - &changes, v3.RequestBodiesLabel, CompareRequestBodies, doneChan) - } + if !lComponents.RequestBodies.IsEmpty() || !rComponents.RequestBodies.IsEmpty() { + comparisons++ + go runComparison(lComponents.RequestBodies.Value, rComponents.RequestBodies.Value, + &changes, v3.RequestBodiesLabel, CompareRequestBodies, doneChan) + } - if !lComponents.Headers.IsEmpty() || !rComponents.Headers.IsEmpty() { - comparisons++ - go runComparison(lComponents.Headers.Value, rComponents.Headers.Value, - &changes, v3.HeadersLabel, CompareHeadersV3, doneChan) - } + if !lComponents.Headers.IsEmpty() || !rComponents.Headers.IsEmpty() { + comparisons++ + go runComparison(lComponents.Headers.Value, rComponents.Headers.Value, + &changes, v3.HeadersLabel, CompareHeadersV3, doneChan) + } - if !lComponents.SecuritySchemes.IsEmpty() || !rComponents.SecuritySchemes.IsEmpty() { - comparisons++ - go runComparison(lComponents.SecuritySchemes.Value, rComponents.SecuritySchemes.Value, - &changes, v3.SecuritySchemesLabel, CompareSecuritySchemesV3, doneChan) - } + if !lComponents.SecuritySchemes.IsEmpty() || !rComponents.SecuritySchemes.IsEmpty() { + comparisons++ + go runComparison(lComponents.SecuritySchemes.Value, rComponents.SecuritySchemes.Value, + &changes, v3.SecuritySchemesLabel, CompareSecuritySchemesV3, doneChan) + } - if !lComponents.Links.IsEmpty() || !rComponents.Links.IsEmpty() { - comparisons++ - go runComparison(lComponents.Links.Value, rComponents.Links.Value, - &changes, v3.LinksLabel, CompareLinks, doneChan) - } + if !lComponents.Links.IsEmpty() || !rComponents.Links.IsEmpty() { + comparisons++ + go runComparison(lComponents.Links.Value, rComponents.Links.Value, + &changes, v3.LinksLabel, CompareLinks, doneChan) + } - if !lComponents.Callbacks.IsEmpty() || !rComponents.Callbacks.IsEmpty() { - comparisons++ - go runComparison(lComponents.Callbacks.Value, rComponents.Callbacks.Value, - &changes, v3.CallbacksLabel, CompareCallback, doneChan) - } + if !lComponents.Callbacks.IsEmpty() || !rComponents.Callbacks.IsEmpty() { + comparisons++ + go runComparison(lComponents.Callbacks.Value, rComponents.Callbacks.Value, + &changes, v3.CallbacksLabel, CompareCallback, doneChan) + } - cc.ExtensionChanges = CompareExtensions(lComponents.Extensions, rComponents.Extensions) + cc.ExtensionChanges = CompareExtensions(lComponents.Extensions, rComponents.Extensions) - completedComponents := 0 - for completedComponents < comparisons { - select { - case res := <-doneChan: - switch res.prop { - case v3.SchemasLabel: - completedComponents++ - cc.SchemaChanges = res.result.(map[string]*SchemaChanges) - break - case v3.SecuritySchemesLabel: - completedComponents++ - cc.SecuritySchemeChanges = res.result.(map[string]*SecuritySchemeChanges) - break - case v3.ResponsesLabel, v3.ParametersLabel, v3.ExamplesLabel, v3.RequestBodiesLabel, v3.HeadersLabel, - v3.LinksLabel, v3.CallbacksLabel: - completedComponents++ - break - } - } - } - } + completedComponents := 0 + for completedComponents < comparisons { + select { + case res := <-doneChan: + switch res.prop { + case v3.SchemasLabel: + completedComponents++ + cc.SchemaChanges = res.result.(map[string]*SchemaChanges) + break + case v3.SecuritySchemesLabel: + completedComponents++ + cc.SecuritySchemeChanges = res.result.(map[string]*SecuritySchemeChanges) + break + case v3.ResponsesLabel, v3.ParametersLabel, v3.ExamplesLabel, v3.RequestBodiesLabel, v3.HeadersLabel, + v3.LinksLabel, v3.CallbacksLabel: + completedComponents++ + break + } + } + } + } - cc.PropertyChanges = NewPropertyChanges(changes) - if cc.TotalChanges() <= 0 { - return nil - } - return cc + cc.PropertyChanges = NewPropertyChanges(changes) + if cc.TotalChanges() <= 0 { + return nil + } + return cc } type componentComparison struct { - prop string - result any + prop string + result any } // run a generic comparison in a thread which in turn splits checks into further threads. func runComparison[T any, R any](l, r map[low.KeyReference[string]]low.ValueReference[T], - changes *[]*Change, label string, compareFunc func(l, r T) R, doneChan chan componentComparison) { + changes *[]*Change, label string, compareFunc func(l, r T) R, doneChan chan componentComparison) { - // for schemas - if label == v3.SchemasLabel || label == v2.DefinitionsLabel || label == v3.SecuritySchemesLabel { - doneChan <- componentComparison{ - prop: label, - result: CheckMapForChanges(l, r, changes, label, compareFunc), - } - return - } else { - doneChan <- componentComparison{ - prop: label, - result: CheckMapForAdditionRemoval(l, r, changes, label), - } - } + // for schemas + if label == v3.SchemasLabel || label == v2.DefinitionsLabel || label == v3.SecuritySchemesLabel { + doneChan <- componentComparison{ + prop: label, + result: CheckMapForChanges(l, r, changes, label, compareFunc), + } + return + } else { + doneChan <- componentComparison{ + prop: label, + result: CheckMapForAdditionRemoval(l, r, changes, label), + } + } } // TotalChanges returns total changes for all Components and Definitions func (c *ComponentsChanges) TotalChanges() int { - v := c.PropertyChanges.TotalChanges() - for k := range c.SchemaChanges { - v += c.SchemaChanges[k].TotalChanges() - } - for k := range c.SecuritySchemeChanges { - v += c.SecuritySchemeChanges[k].TotalChanges() - } - if c.ExtensionChanges != nil { - v += c.ExtensionChanges.TotalChanges() - } - return v + v := c.PropertyChanges.TotalChanges() + for k := range c.SchemaChanges { + v += c.SchemaChanges[k].TotalChanges() + } + for k := range c.SecuritySchemeChanges { + v += c.SecuritySchemeChanges[k].TotalChanges() + } + if c.ExtensionChanges != nil { + v += c.ExtensionChanges.TotalChanges() + } + return v } // TotalBreakingChanges returns all breaking changes found for all Components and Definitions func (c *ComponentsChanges) TotalBreakingChanges() int { - v := c.PropertyChanges.TotalBreakingChanges() - for k := range c.SchemaChanges { - v += c.SchemaChanges[k].TotalBreakingChanges() - } - for k := range c.SecuritySchemeChanges { - v += c.SecuritySchemeChanges[k].TotalBreakingChanges() - } - return v + v := c.PropertyChanges.TotalBreakingChanges() + for k := range c.SchemaChanges { + v += c.SchemaChanges[k].TotalBreakingChanges() + } + for k := range c.SecuritySchemeChanges { + v += c.SecuritySchemeChanges[k].TotalBreakingChanges() + } + return v } diff --git a/what-changed/model/document.go b/what-changed/model/document.go index e611f9d..20c16e7 100644 --- a/what-changed/model/document.go +++ b/what-changed/model/document.go @@ -11,279 +11,280 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/base" - "github.com/pb33f/libopenapi/datamodel/low/v2" - "github.com/pb33f/libopenapi/datamodel/low/v3" - "reflect" + "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" ) // DocumentChanges represents all the changes made to an OpenAPI document. type DocumentChanges struct { - *PropertyChanges - InfoChanges *InfoChanges `json:"info,omitempty" yaml:"info,omitempty"` - PathsChanges *PathsChanges `json:"paths,omitempty" yaml:"paths,omitempty"` - TagChanges []*TagChanges `json:"tags,omitempty" yaml:"tags,omitempty"` - ExternalDocChanges *ExternalDocChanges `json:"externalDoc,omitempty" yaml:"externalDoc,omitempty"` - WebhookChanges map[string]*PathItemChanges `json:"webhooks,omitempty" yaml:"webhooks,omitempty"` - ServerChanges []*ServerChanges `json:"servers,omitempty" yaml:"servers,omitempty"` - SecurityRequirementChanges []*SecurityRequirementChanges `json:"securityRequirements,omitempty" yaml:"securityRequirements,omitempty"` - ComponentsChanges *ComponentsChanges `json:"components,omitempty" yaml:"components,omitempty"` - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + *PropertyChanges + InfoChanges *InfoChanges `json:"info,omitempty" yaml:"info,omitempty"` + PathsChanges *PathsChanges `json:"paths,omitempty" yaml:"paths,omitempty"` + TagChanges []*TagChanges `json:"tags,omitempty" yaml:"tags,omitempty"` + ExternalDocChanges *ExternalDocChanges `json:"externalDoc,omitempty" yaml:"externalDoc,omitempty"` + WebhookChanges map[string]*PathItemChanges `json:"webhooks,omitempty" yaml:"webhooks,omitempty"` + ServerChanges []*ServerChanges `json:"servers,omitempty" yaml:"servers,omitempty"` + SecurityRequirementChanges []*SecurityRequirementChanges `json:"securityRequirements,omitempty" yaml:"securityRequirements,omitempty"` + ComponentsChanges *ComponentsChanges `json:"components,omitempty" yaml:"components,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` } // TotalChanges returns a total count of all changes made in the Document func (d *DocumentChanges) TotalChanges() int { - c := d.PropertyChanges.TotalChanges() - if d.InfoChanges != nil { - c += d.InfoChanges.TotalChanges() - } - if d.PathsChanges != nil { - c += d.PathsChanges.TotalChanges() - } - for k := range d.TagChanges { - c += d.TagChanges[k].TotalChanges() - } - if d.ExternalDocChanges != nil { - c += d.ExternalDocChanges.TotalChanges() - } - for k := range d.WebhookChanges { - c += d.WebhookChanges[k].TotalChanges() - } - for k := range d.ServerChanges { - c += d.ServerChanges[k].TotalChanges() - } - for k := range d.SecurityRequirementChanges { - c += d.SecurityRequirementChanges[k].TotalChanges() - } - if d.ComponentsChanges != nil { - c += d.ComponentsChanges.TotalChanges() - } - if d.ExtensionChanges != nil { - c += d.ExtensionChanges.TotalChanges() - } - return c + c := d.PropertyChanges.TotalChanges() + if d.InfoChanges != nil { + c += d.InfoChanges.TotalChanges() + } + if d.PathsChanges != nil { + c += d.PathsChanges.TotalChanges() + } + for k := range d.TagChanges { + c += d.TagChanges[k].TotalChanges() + } + if d.ExternalDocChanges != nil { + c += d.ExternalDocChanges.TotalChanges() + } + for k := range d.WebhookChanges { + c += d.WebhookChanges[k].TotalChanges() + } + for k := range d.ServerChanges { + c += d.ServerChanges[k].TotalChanges() + } + for k := range d.SecurityRequirementChanges { + c += d.SecurityRequirementChanges[k].TotalChanges() + } + if d.ComponentsChanges != nil { + c += d.ComponentsChanges.TotalChanges() + } + if d.ExtensionChanges != nil { + c += d.ExtensionChanges.TotalChanges() + } + return c } // TotalBreakingChanges returns a total count of all breaking changes made in the Document func (d *DocumentChanges) TotalBreakingChanges() int { - c := d.PropertyChanges.TotalBreakingChanges() - if d.InfoChanges != nil { - c += d.InfoChanges.TotalBreakingChanges() - } - if d.PathsChanges != nil { - c += d.PathsChanges.TotalBreakingChanges() - } - for k := range d.TagChanges { - c += d.TagChanges[k].TotalBreakingChanges() - } - if d.ExternalDocChanges != nil { - c += d.ExternalDocChanges.TotalBreakingChanges() - } - for k := range d.WebhookChanges { - c += d.WebhookChanges[k].TotalBreakingChanges() - } - for k := range d.ServerChanges { - c += d.ServerChanges[k].TotalBreakingChanges() - } - for k := range d.SecurityRequirementChanges { - c += d.SecurityRequirementChanges[k].TotalBreakingChanges() - } - if d.ComponentsChanges != nil { - c += d.ComponentsChanges.TotalBreakingChanges() - } - return c + c := d.PropertyChanges.TotalBreakingChanges() + if d.InfoChanges != nil { + c += d.InfoChanges.TotalBreakingChanges() + } + if d.PathsChanges != nil { + c += d.PathsChanges.TotalBreakingChanges() + } + for k := range d.TagChanges { + c += d.TagChanges[k].TotalBreakingChanges() + } + if d.ExternalDocChanges != nil { + c += d.ExternalDocChanges.TotalBreakingChanges() + } + for k := range d.WebhookChanges { + c += d.WebhookChanges[k].TotalBreakingChanges() + } + for k := range d.ServerChanges { + c += d.ServerChanges[k].TotalBreakingChanges() + } + for k := range d.SecurityRequirementChanges { + c += d.SecurityRequirementChanges[k].TotalBreakingChanges() + } + if d.ComponentsChanges != nil { + c += d.ComponentsChanges.TotalBreakingChanges() + } + return c } // CompareDocuments will compare any two OpenAPI documents (either Swagger or OpenAPI) and return a pointer to // DocumentChanges that outlines everything that was found to have changed. func CompareDocuments(l, r any) *DocumentChanges { - var changes []*Change - var props []*PropertyCheck + var changes []*Change + var props []*PropertyCheck - dc := new(DocumentChanges) + dc := new(DocumentChanges) - if reflect.TypeOf(&v2.Swagger{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Swagger{}) == reflect.TypeOf(r) { - lDoc := l.(*v2.Swagger) - rDoc := r.(*v2.Swagger) + if reflect.TypeOf(&v2.Swagger{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Swagger{}) == reflect.TypeOf(r) { + lDoc := l.(*v2.Swagger) + rDoc := r.(*v2.Swagger) - // version - addPropertyCheck(&props, lDoc.Swagger.ValueNode, rDoc.Swagger.ValueNode, - lDoc.Swagger.Value, rDoc.Swagger.Value, &changes, v3.SwaggerLabel, true) + // version + addPropertyCheck(&props, lDoc.Swagger.ValueNode, rDoc.Swagger.ValueNode, + lDoc.Swagger.Value, rDoc.Swagger.Value, &changes, v3.SwaggerLabel, true) - // host - addPropertyCheck(&props, lDoc.Host.ValueNode, rDoc.Host.ValueNode, - lDoc.Host.Value, rDoc.Host.Value, &changes, v3.HostLabel, true) + // host + addPropertyCheck(&props, lDoc.Host.ValueNode, rDoc.Host.ValueNode, + lDoc.Host.Value, rDoc.Host.Value, &changes, v3.HostLabel, true) - // base path - addPropertyCheck(&props, lDoc.BasePath.ValueNode, rDoc.BasePath.ValueNode, - lDoc.BasePath.Value, rDoc.BasePath.Value, &changes, v3.BasePathLabel, true) + // base path + addPropertyCheck(&props, lDoc.BasePath.ValueNode, rDoc.BasePath.ValueNode, + lDoc.BasePath.Value, rDoc.BasePath.Value, &changes, v3.BasePathLabel, true) - // schemes - if len(lDoc.Schemes.Value) > 0 || len(lDoc.Schemes.Value) > 0 { - ExtractStringValueSliceChanges(lDoc.Schemes.Value, rDoc.Schemes.Value, - &changes, v3.SchemesLabel, true) - } - // consumes - if len(lDoc.Consumes.Value) > 0 || len(lDoc.Consumes.Value) > 0 { - ExtractStringValueSliceChanges(lDoc.Consumes.Value, rDoc.Consumes.Value, - &changes, v3.ConsumesLabel, true) - } - // produces - if len(lDoc.Produces.Value) > 0 || len(lDoc.Produces.Value) > 0 { - ExtractStringValueSliceChanges(lDoc.Produces.Value, rDoc.Produces.Value, - &changes, v3.ProducesLabel, true) - } + // schemes + if len(lDoc.Schemes.Value) > 0 || len(lDoc.Schemes.Value) > 0 { + ExtractStringValueSliceChanges(lDoc.Schemes.Value, rDoc.Schemes.Value, + &changes, v3.SchemesLabel, true) + } + // consumes + if len(lDoc.Consumes.Value) > 0 || len(lDoc.Consumes.Value) > 0 { + ExtractStringValueSliceChanges(lDoc.Consumes.Value, rDoc.Consumes.Value, + &changes, v3.ConsumesLabel, true) + } + // produces + if len(lDoc.Produces.Value) > 0 || len(lDoc.Produces.Value) > 0 { + ExtractStringValueSliceChanges(lDoc.Produces.Value, rDoc.Produces.Value, + &changes, v3.ProducesLabel, true) + } - // tags - dc.TagChanges = CompareTags(lDoc.Tags.Value, rDoc.Tags.Value) + // tags + dc.TagChanges = CompareTags(lDoc.Tags.Value, rDoc.Tags.Value) - // paths - if !lDoc.Paths.IsEmpty() || !rDoc.Paths.IsEmpty() { - dc.PathsChanges = ComparePaths(lDoc.Paths.Value, rDoc.Paths.Value) - } + // paths + if !lDoc.Paths.IsEmpty() || !rDoc.Paths.IsEmpty() { + dc.PathsChanges = ComparePaths(lDoc.Paths.Value, rDoc.Paths.Value) + } - // external docs - compareDocumentExternalDocs(lDoc, rDoc, dc, &changes) + // external docs + compareDocumentExternalDocs(lDoc, rDoc, dc, &changes) - // info - compareDocumentInfo(&lDoc.Info, &rDoc.Info, dc, &changes) + // info + compareDocumentInfo(&lDoc.Info, &rDoc.Info, dc, &changes) - // security - if !lDoc.Security.IsEmpty() || !rDoc.Security.IsEmpty() { - checkSecurity(lDoc.Security, rDoc.Security, &changes, dc) - } + // security + if !lDoc.Security.IsEmpty() || !rDoc.Security.IsEmpty() { + checkSecurity(lDoc.Security, rDoc.Security, &changes, dc) + } - // components / definitions - // swagger (damn you) decided to put all this stuff at the document root, rather than cleanly - // placing it under a parent, like they did with OpenAPI. This means picking through each definition - // creating a new set of changes and then morphing them into a single changes object. - cc := new(ComponentsChanges) - cc.PropertyChanges = new(PropertyChanges) - if n := CompareComponents(lDoc.Definitions.Value, rDoc.Definitions.Value); n != nil { - cc.SchemaChanges = n.SchemaChanges - } - if n := CompareComponents(lDoc.SecurityDefinitions.Value, rDoc.SecurityDefinitions.Value); n != nil { - cc.SecuritySchemeChanges = n.SecuritySchemeChanges - } - if n := CompareComponents(lDoc.Parameters.Value, rDoc.Parameters.Value); n != nil { - cc.PropertyChanges.Changes = append(cc.PropertyChanges.Changes, n.Changes...) - } - if n := CompareComponents(lDoc.Responses.Value, rDoc.Responses.Value); n != nil { - cc.Changes = append(cc.Changes, n.Changes...) - } - dc.ExtensionChanges = CompareExtensions(lDoc.Extensions, rDoc.Extensions) - if cc.TotalChanges() > 0 { - dc.ComponentsChanges = cc - } - } + // components / definitions + // swagger (damn you) decided to put all this stuff at the document root, rather than cleanly + // placing it under a parent, like they did with OpenAPI. This means picking through each definition + // creating a new set of changes and then morphing them into a single changes object. + cc := new(ComponentsChanges) + cc.PropertyChanges = new(PropertyChanges) + if n := CompareComponents(lDoc.Definitions.Value, rDoc.Definitions.Value); n != nil { + cc.SchemaChanges = n.SchemaChanges + } + if n := CompareComponents(lDoc.SecurityDefinitions.Value, rDoc.SecurityDefinitions.Value); n != nil { + cc.SecuritySchemeChanges = n.SecuritySchemeChanges + } + if n := CompareComponents(lDoc.Parameters.Value, rDoc.Parameters.Value); n != nil { + cc.PropertyChanges.Changes = append(cc.PropertyChanges.Changes, n.Changes...) + } + if n := CompareComponents(lDoc.Responses.Value, rDoc.Responses.Value); n != nil { + cc.Changes = append(cc.Changes, n.Changes...) + } + dc.ExtensionChanges = CompareExtensions(lDoc.Extensions, rDoc.Extensions) + if cc.TotalChanges() > 0 { + dc.ComponentsChanges = cc + } + } - if reflect.TypeOf(&v3.Document{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Document{}) == reflect.TypeOf(r) { - lDoc := l.(*v3.Document) - rDoc := r.(*v3.Document) + if reflect.TypeOf(&v3.Document{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Document{}) == reflect.TypeOf(r) { + lDoc := l.(*v3.Document) + rDoc := r.(*v3.Document) - // version - addPropertyCheck(&props, lDoc.Version.ValueNode, rDoc.Version.ValueNode, - lDoc.Version.Value, rDoc.Version.Value, &changes, v3.OpenAPILabel, true) + // version + addPropertyCheck(&props, lDoc.Version.ValueNode, rDoc.Version.ValueNode, + lDoc.Version.Value, rDoc.Version.Value, &changes, v3.OpenAPILabel, true) - // schema dialect - addPropertyCheck(&props, lDoc.JsonSchemaDialect.ValueNode, rDoc.JsonSchemaDialect.ValueNode, - lDoc.JsonSchemaDialect.Value, rDoc.JsonSchemaDialect.Value, &changes, v3.JSONSchemaDialectLabel, true) + // schema dialect + addPropertyCheck(&props, lDoc.JsonSchemaDialect.ValueNode, rDoc.JsonSchemaDialect.ValueNode, + lDoc.JsonSchemaDialect.Value, rDoc.JsonSchemaDialect.Value, &changes, v3.JSONSchemaDialectLabel, true) - // tags - dc.TagChanges = CompareTags(lDoc.Tags.Value, rDoc.Tags.Value) + // tags + dc.TagChanges = CompareTags(lDoc.Tags.Value, rDoc.Tags.Value) - // paths - if !lDoc.Paths.IsEmpty() || !rDoc.Paths.IsEmpty() { - dc.PathsChanges = ComparePaths(lDoc.Paths.Value, rDoc.Paths.Value) - } + // paths + if !lDoc.Paths.IsEmpty() || !rDoc.Paths.IsEmpty() { + dc.PathsChanges = ComparePaths(lDoc.Paths.Value, rDoc.Paths.Value) + } - // external docs - compareDocumentExternalDocs(lDoc, rDoc, dc, &changes) + // external docs + compareDocumentExternalDocs(lDoc, rDoc, dc, &changes) - // info - compareDocumentInfo(&lDoc.Info, &rDoc.Info, dc, &changes) + // info + compareDocumentInfo(&lDoc.Info, &rDoc.Info, dc, &changes) - // security - if !lDoc.Security.IsEmpty() || !rDoc.Security.IsEmpty() { - checkSecurity(lDoc.Security, rDoc.Security, &changes, dc) - } + // security + if !lDoc.Security.IsEmpty() || !rDoc.Security.IsEmpty() { + checkSecurity(lDoc.Security, rDoc.Security, &changes, dc) + } - // compare components. - if !lDoc.Components.IsEmpty() && !rDoc.Components.IsEmpty() { - if n := CompareComponents(lDoc.Components.Value, rDoc.Components.Value); n != nil { - dc.ComponentsChanges = n - } - } - if !lDoc.Components.IsEmpty() && rDoc.Components.IsEmpty() { - CreateChange(&changes, PropertyRemoved, v3.ComponentsLabel, - lDoc.Components.ValueNode, nil, true, lDoc.Components.Value, nil) - } - if lDoc.Components.IsEmpty() && !rDoc.Components.IsEmpty() { - CreateChange(&changes, PropertyAdded, v3.ComponentsLabel, - rDoc.Components.ValueNode, nil, false, nil, lDoc.Components.Value) - } + // compare components. + if !lDoc.Components.IsEmpty() && !rDoc.Components.IsEmpty() { + if n := CompareComponents(lDoc.Components.Value, rDoc.Components.Value); n != nil { + dc.ComponentsChanges = n + } + } + if !lDoc.Components.IsEmpty() && rDoc.Components.IsEmpty() { + CreateChange(&changes, PropertyRemoved, v3.ComponentsLabel, + lDoc.Components.ValueNode, nil, true, lDoc.Components.Value, nil) + } + if lDoc.Components.IsEmpty() && !rDoc.Components.IsEmpty() { + CreateChange(&changes, PropertyAdded, v3.ComponentsLabel, + rDoc.Components.ValueNode, nil, false, nil, lDoc.Components.Value) + } - // compare servers - if n := checkServers(lDoc.Servers, rDoc.Servers); n != nil { - dc.ServerChanges = n - } + // compare servers + if n := checkServers(lDoc.Servers, rDoc.Servers); n != nil { + dc.ServerChanges = n + } - // compare webhooks - dc.WebhookChanges = CheckMapForChanges(lDoc.Webhooks.Value, rDoc.Webhooks.Value, &changes, - v3.WebhooksLabel, ComparePathItemsV3) + // compare webhooks + dc.WebhookChanges = CheckMapForChanges(lDoc.Webhooks.Value, rDoc.Webhooks.Value, &changes, + v3.WebhooksLabel, ComparePathItemsV3) - // extensions - dc.ExtensionChanges = CompareExtensions(lDoc.Extensions, rDoc.Extensions) - } + // extensions + dc.ExtensionChanges = CompareExtensions(lDoc.Extensions, rDoc.Extensions) + } - CheckProperties(props) - dc.PropertyChanges = NewPropertyChanges(changes) - if dc.TotalChanges() <= 0 { - return nil - } - return dc + CheckProperties(props) + dc.PropertyChanges = NewPropertyChanges(changes) + if dc.TotalChanges() <= 0 { + return nil + } + return dc } func compareDocumentExternalDocs(l, r low.HasExternalDocs, dc *DocumentChanges, changes *[]*Change) { - // external docs - if !l.GetExternalDocs().IsEmpty() && !r.GetExternalDocs().IsEmpty() { - lExtDoc := l.GetExternalDocs().Value.(*base.ExternalDoc) - rExtDoc := r.GetExternalDocs().Value.(*base.ExternalDoc) - if !low.AreEqual(lExtDoc, rExtDoc) { - dc.ExternalDocChanges = CompareExternalDocs(lExtDoc, rExtDoc) - } - } - if l.GetExternalDocs().IsEmpty() && !r.GetExternalDocs().IsEmpty() { - CreateChange(changes, PropertyAdded, v3.ExternalDocsLabel, - nil, r.GetExternalDocs().ValueNode, false, nil, - r.GetExternalDocs().Value) - } - if !l.GetExternalDocs().IsEmpty() && r.GetExternalDocs().IsEmpty() { - CreateChange(changes, PropertyRemoved, v3.ExternalDocsLabel, - l.GetExternalDocs().ValueNode, nil, false, l.GetExternalDocs().Value, - nil) - } + // external docs + if !l.GetExternalDocs().IsEmpty() && !r.GetExternalDocs().IsEmpty() { + lExtDoc := l.GetExternalDocs().Value.(*base.ExternalDoc) + rExtDoc := r.GetExternalDocs().Value.(*base.ExternalDoc) + if !low.AreEqual(lExtDoc, rExtDoc) { + dc.ExternalDocChanges = CompareExternalDocs(lExtDoc, rExtDoc) + } + } + if l.GetExternalDocs().IsEmpty() && !r.GetExternalDocs().IsEmpty() { + CreateChange(changes, PropertyAdded, v3.ExternalDocsLabel, + nil, r.GetExternalDocs().ValueNode, false, nil, + r.GetExternalDocs().Value) + } + if !l.GetExternalDocs().IsEmpty() && r.GetExternalDocs().IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.ExternalDocsLabel, + l.GetExternalDocs().ValueNode, nil, false, l.GetExternalDocs().Value, + nil) + } } func compareDocumentInfo(l, r *low.NodeReference[*base.Info], dc *DocumentChanges, changes *[]*Change) { - // info - if !l.IsEmpty() && !r.IsEmpty() { - lInfo := l.Value - rInfo := r.Value - if !low.AreEqual(lInfo, rInfo) { - dc.InfoChanges = CompareInfo(lInfo, rInfo) - } - } - if l.IsEmpty() && !r.IsEmpty() { - CreateChange(changes, PropertyAdded, v3.InfoLabel, - nil, r.ValueNode, false, nil, - r.Value) - } - if !l.IsEmpty() && r.IsEmpty() { - CreateChange(changes, PropertyRemoved, v3.InfoLabel, - l.ValueNode, nil, false, l.Value, - nil) - } + // info + if !l.IsEmpty() && !r.IsEmpty() { + lInfo := l.Value + rInfo := r.Value + if !low.AreEqual(lInfo, rInfo) { + dc.InfoChanges = CompareInfo(lInfo, rInfo) + } + } + if l.IsEmpty() && !r.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.InfoLabel, + nil, r.ValueNode, false, nil, + r.Value) + } + if !l.IsEmpty() && r.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.InfoLabel, + l.ValueNode, nil, false, l.Value, + nil) + } } diff --git a/what-changed/model/operation.go b/what-changed/model/operation.go index 4cd2915..ccd39ba 100644 --- a/what-changed/model/operation.go +++ b/what-changed/model/operation.go @@ -4,154 +4,155 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/base" - "github.com/pb33f/libopenapi/datamodel/low/v2" - "github.com/pb33f/libopenapi/datamodel/low/v3" - "gopkg.in/yaml.v3" - "reflect" - "sort" - "strings" + "reflect" + "sort" + "strings" + + "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" ) // OperationChanges represent changes made between two Swagger or OpenAPI Operation objects. type OperationChanges struct { - *PropertyChanges - ExternalDocChanges *ExternalDocChanges `json:"externalDoc,omitempty" yaml:"externalDoc,omitempty"` - ParameterChanges []*ParameterChanges `json:"parameters,omitempty" yaml:"parameters,omitempty"` - ResponsesChanges *ResponsesChanges `json:"responses,omitempty" yaml:"responses,omitempty"` - SecurityRequirementChanges []*SecurityRequirementChanges `json:"securityRequirements,omitempty" yaml:"securityRequirements,omitempty"` + *PropertyChanges + ExternalDocChanges *ExternalDocChanges `json:"externalDoc,omitempty" yaml:"externalDoc,omitempty"` + ParameterChanges []*ParameterChanges `json:"parameters,omitempty" yaml:"parameters,omitempty"` + ResponsesChanges *ResponsesChanges `json:"responses,omitempty" yaml:"responses,omitempty"` + SecurityRequirementChanges []*SecurityRequirementChanges `json:"securityRequirements,omitempty" yaml:"securityRequirements,omitempty"` - // OpenAPI 3+ only changes - RequestBodyChanges *RequestBodyChanges `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"` - ServerChanges []*ServerChanges `json:"servers,omitempty" yaml:"servers,omitempty"` - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` - CallbackChanges map[string]*CallbackChanges `json:"callbacks,omitempty" yaml:"callbacks,omitempty"` + // OpenAPI 3+ only changes + RequestBodyChanges *RequestBodyChanges `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"` + ServerChanges []*ServerChanges `json:"servers,omitempty" yaml:"servers,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + CallbackChanges map[string]*CallbackChanges `json:"callbacks,omitempty" yaml:"callbacks,omitempty"` } // TotalChanges returns the total number of changes made between two Swagger or OpenAPI Operation objects. func (o *OperationChanges) TotalChanges() int { - c := o.PropertyChanges.TotalChanges() - if o.ExternalDocChanges != nil { - c += o.ExternalDocChanges.TotalChanges() - } - for k := range o.ParameterChanges { - c += o.ParameterChanges[k].TotalChanges() - } - if o.ResponsesChanges != nil { - c += o.ResponsesChanges.TotalChanges() - } - for k := range o.SecurityRequirementChanges { - c += o.SecurityRequirementChanges[k].TotalChanges() - } - if o.RequestBodyChanges != nil { - c += o.RequestBodyChanges.TotalChanges() - } - for k := range o.ServerChanges { - c += o.ServerChanges[k].TotalChanges() - } - for k := range o.CallbackChanges { - c += o.CallbackChanges[k].TotalChanges() - } - if o.ExtensionChanges != nil { - c += o.ExtensionChanges.TotalChanges() - } - return c + c := o.PropertyChanges.TotalChanges() + if o.ExternalDocChanges != nil { + c += o.ExternalDocChanges.TotalChanges() + } + for k := range o.ParameterChanges { + c += o.ParameterChanges[k].TotalChanges() + } + if o.ResponsesChanges != nil { + c += o.ResponsesChanges.TotalChanges() + } + for k := range o.SecurityRequirementChanges { + c += o.SecurityRequirementChanges[k].TotalChanges() + } + if o.RequestBodyChanges != nil { + c += o.RequestBodyChanges.TotalChanges() + } + for k := range o.ServerChanges { + c += o.ServerChanges[k].TotalChanges() + } + for k := range o.CallbackChanges { + c += o.CallbackChanges[k].TotalChanges() + } + if o.ExtensionChanges != nil { + c += o.ExtensionChanges.TotalChanges() + } + return c } // TotalBreakingChanges returns the total number of breaking changes made between two Swagger // or OpenAPI Operation objects. func (o *OperationChanges) TotalBreakingChanges() int { - c := o.PropertyChanges.TotalBreakingChanges() - if o.ExternalDocChanges != nil { - c += o.ExternalDocChanges.TotalBreakingChanges() - } - for k := range o.ParameterChanges { - c += o.ParameterChanges[k].TotalBreakingChanges() - } - if o.ResponsesChanges != nil { - c += o.ResponsesChanges.TotalBreakingChanges() - } - for k := range o.SecurityRequirementChanges { - c += o.SecurityRequirementChanges[k].TotalBreakingChanges() - } - for k := range o.CallbackChanges { - c += o.CallbackChanges[k].TotalBreakingChanges() - } - if o.RequestBodyChanges != nil { - c += o.RequestBodyChanges.TotalBreakingChanges() - } - for k := range o.ServerChanges { - c += o.ServerChanges[k].TotalBreakingChanges() - } - return c + c := o.PropertyChanges.TotalBreakingChanges() + if o.ExternalDocChanges != nil { + c += o.ExternalDocChanges.TotalBreakingChanges() + } + for k := range o.ParameterChanges { + c += o.ParameterChanges[k].TotalBreakingChanges() + } + if o.ResponsesChanges != nil { + c += o.ResponsesChanges.TotalBreakingChanges() + } + for k := range o.SecurityRequirementChanges { + c += o.SecurityRequirementChanges[k].TotalBreakingChanges() + } + for k := range o.CallbackChanges { + c += o.CallbackChanges[k].TotalBreakingChanges() + } + if o.RequestBodyChanges != nil { + c += o.RequestBodyChanges.TotalBreakingChanges() + } + for k := range o.ServerChanges { + c += o.ServerChanges[k].TotalBreakingChanges() + } + return c } // check for properties shared between operations objects. func addSharedOperationProperties(left, right low.SharedOperations, changes *[]*Change) []*PropertyCheck { - var props []*PropertyCheck + var props []*PropertyCheck - // tags - if len(left.GetTags().Value) > 0 || len(right.GetTags().Value) > 0 { - ExtractStringValueSliceChanges(left.GetTags().Value, right.GetTags().Value, - changes, v3.TagsLabel, false) - } + // tags + if len(left.GetTags().Value) > 0 || len(right.GetTags().Value) > 0 { + ExtractStringValueSliceChanges(left.GetTags().Value, right.GetTags().Value, + changes, v3.TagsLabel, false) + } - // summary - addPropertyCheck(&props, left.GetSummary().ValueNode, right.GetSummary().ValueNode, - left.GetSummary(), right.GetSummary(), changes, v3.SummaryLabel, false) + // summary + addPropertyCheck(&props, left.GetSummary().ValueNode, right.GetSummary().ValueNode, + left.GetSummary(), right.GetSummary(), changes, v3.SummaryLabel, false) - // 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) - // 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) - // operation id - addPropertyCheck(&props, left.GetOperationId().ValueNode, right.GetOperationId().ValueNode, - left.GetOperationId(), right.GetOperationId(), changes, v3.OperationIdLabel, true) + // operation id + addPropertyCheck(&props, left.GetOperationId().ValueNode, right.GetOperationId().ValueNode, + left.GetOperationId(), right.GetOperationId(), changes, v3.OperationIdLabel, true) - return props + return props } // check shared objects func compareSharedOperationObjects(l, r low.SharedOperations, changes *[]*Change, opChanges *OperationChanges) { - // external docs - if !l.GetExternalDocs().IsEmpty() && !r.GetExternalDocs().IsEmpty() { - lExtDoc := l.GetExternalDocs().Value.(*base.ExternalDoc) - rExtDoc := r.GetExternalDocs().Value.(*base.ExternalDoc) - if !low.AreEqual(lExtDoc, rExtDoc) { - opChanges.ExternalDocChanges = CompareExternalDocs(lExtDoc, rExtDoc) - } - } - if l.GetExternalDocs().IsEmpty() && !r.GetExternalDocs().IsEmpty() { - CreateChange(changes, PropertyAdded, v3.ExternalDocsLabel, - nil, r.GetExternalDocs().ValueNode, false, nil, - r.GetExternalDocs().Value) - } - if !l.GetExternalDocs().IsEmpty() && r.GetExternalDocs().IsEmpty() { - CreateChange(changes, PropertyRemoved, v3.ExternalDocsLabel, - l.GetExternalDocs().ValueNode, nil, false, l.GetExternalDocs().Value, - nil) - } + // external docs + if !l.GetExternalDocs().IsEmpty() && !r.GetExternalDocs().IsEmpty() { + lExtDoc := l.GetExternalDocs().Value.(*base.ExternalDoc) + rExtDoc := r.GetExternalDocs().Value.(*base.ExternalDoc) + if !low.AreEqual(lExtDoc, rExtDoc) { + opChanges.ExternalDocChanges = CompareExternalDocs(lExtDoc, rExtDoc) + } + } + if l.GetExternalDocs().IsEmpty() && !r.GetExternalDocs().IsEmpty() { + CreateChange(changes, PropertyAdded, v3.ExternalDocsLabel, + nil, r.GetExternalDocs().ValueNode, false, nil, + r.GetExternalDocs().Value) + } + if !l.GetExternalDocs().IsEmpty() && r.GetExternalDocs().IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.ExternalDocsLabel, + l.GetExternalDocs().ValueNode, nil, false, l.GetExternalDocs().Value, + nil) + } - // responses - if !l.GetResponses().IsEmpty() && !r.GetResponses().IsEmpty() { - opChanges.ResponsesChanges = CompareResponses(l.GetResponses().Value, r.GetResponses().Value) - } - if l.GetResponses().IsEmpty() && !r.GetResponses().IsEmpty() { - CreateChange(changes, PropertyAdded, v3.ResponsesLabel, - nil, r.GetResponses().ValueNode, false, nil, - r.GetResponses().Value) - } - if !l.GetResponses().IsEmpty() && r.GetResponses().IsEmpty() { - CreateChange(changes, PropertyRemoved, v3.ResponsesLabel, - l.GetResponses().ValueNode, nil, true, l.GetResponses().Value, - nil) - } + // responses + if !l.GetResponses().IsEmpty() && !r.GetResponses().IsEmpty() { + opChanges.ResponsesChanges = CompareResponses(l.GetResponses().Value, r.GetResponses().Value) + } + if l.GetResponses().IsEmpty() && !r.GetResponses().IsEmpty() { + CreateChange(changes, PropertyAdded, v3.ResponsesLabel, + nil, r.GetResponses().ValueNode, false, nil, + r.GetResponses().Value) + } + if !l.GetResponses().IsEmpty() && r.GetResponses().IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.ResponsesLabel, + l.GetResponses().ValueNode, nil, true, l.GetResponses().Value, + nil) + } } @@ -159,371 +160,371 @@ func compareSharedOperationObjects(l, r low.SharedOperations, changes *[]*Change // a pointer to an OperationChanges instance, or nil if nothing is found. func CompareOperations(l, r any) *OperationChanges { - var changes []*Change - var props []*PropertyCheck + var changes []*Change + var props []*PropertyCheck - oc := new(OperationChanges) + oc := new(OperationChanges) - // Swagger - if reflect.TypeOf(&v2.Operation{}) == reflect.TypeOf(l) && - reflect.TypeOf(&v2.Operation{}) == reflect.TypeOf(r) { + // Swagger + if reflect.TypeOf(&v2.Operation{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v2.Operation{}) == reflect.TypeOf(r) { - lOperation := l.(*v2.Operation) - rOperation := r.(*v2.Operation) + lOperation := l.(*v2.Operation) + rOperation := r.(*v2.Operation) - // perform hash check to avoid further processing - if low.AreEqual(lOperation, rOperation) { - return nil - } + // perform hash check to avoid further processing + if low.AreEqual(lOperation, rOperation) { + return nil + } - props = append(props, addSharedOperationProperties(lOperation, rOperation, &changes)...) + props = append(props, addSharedOperationProperties(lOperation, rOperation, &changes)...) - compareSharedOperationObjects(lOperation, rOperation, &changes, oc) + compareSharedOperationObjects(lOperation, rOperation, &changes, oc) - // parameters - lParamsUntyped := lOperation.GetParameters() - rParamsUntyped := rOperation.GetParameters() - if !lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() { - lParams := lParamsUntyped.Value.([]low.ValueReference[*v2.Parameter]) - rParams := rParamsUntyped.Value.([]low.ValueReference[*v2.Parameter]) + // parameters + lParamsUntyped := lOperation.GetParameters() + rParamsUntyped := rOperation.GetParameters() + if !lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() { + lParams := lParamsUntyped.Value.([]low.ValueReference[*v2.Parameter]) + rParams := rParamsUntyped.Value.([]low.ValueReference[*v2.Parameter]) - lv := make(map[string]*v2.Parameter, len(lParams)) - rv := make(map[string]*v2.Parameter, len(rParams)) + lv := make(map[string]*v2.Parameter, len(lParams)) + rv := make(map[string]*v2.Parameter, len(rParams)) - for i := range lParams { - s := lParams[i].Value.Name.Value - lv[s] = lParams[i].Value - } - for i := range rParams { - s := rParams[i].Value.Name.Value - rv[s] = rParams[i].Value - } + for i := range lParams { + s := lParams[i].Value.Name.Value + lv[s] = lParams[i].Value + } + for i := range rParams { + s := rParams[i].Value.Name.Value + rv[s] = rParams[i].Value + } - var paramChanges []*ParameterChanges - for n := range lv { - if _, ok := rv[n]; ok { - if !low.AreEqual(lv[n], rv[n]) { - ch := CompareParameters(lv[n], rv[n]) - if ch != nil { - paramChanges = append(paramChanges, ch) - } - } - continue - } - CreateChange(&changes, ObjectRemoved, v3.ParametersLabel, - lv[n].Name.ValueNode, nil, true, lv[n].Name.Value, - nil) + var paramChanges []*ParameterChanges + for n := range lv { + if _, ok := rv[n]; ok { + if !low.AreEqual(lv[n], rv[n]) { + ch := CompareParameters(lv[n], rv[n]) + if ch != nil { + paramChanges = append(paramChanges, ch) + } + } + continue + } + CreateChange(&changes, ObjectRemoved, v3.ParametersLabel, + lv[n].Name.ValueNode, nil, true, lv[n].Name.Value, + nil) - } - for n := range rv { - if _, ok := lv[n]; !ok { - CreateChange(&changes, ObjectAdded, v3.ParametersLabel, - nil, rv[n].Name.ValueNode, true, nil, - rv[n].Name.Value) - } - } - oc.ParameterChanges = paramChanges - } - if !lParamsUntyped.IsEmpty() && rParamsUntyped.IsEmpty() { - CreateChange(&changes, PropertyRemoved, v3.ParametersLabel, - lParamsUntyped.ValueNode, nil, true, lParamsUntyped.Value, - nil) - } - if lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() { - CreateChange(&changes, PropertyAdded, v3.ParametersLabel, - nil, rParamsUntyped.ValueNode, true, nil, - rParamsUntyped.Value) - } + } + for n := range rv { + if _, ok := lv[n]; !ok { + CreateChange(&changes, ObjectAdded, v3.ParametersLabel, + nil, rv[n].Name.ValueNode, true, nil, + rv[n].Name.Value) + } + } + oc.ParameterChanges = paramChanges + } + if !lParamsUntyped.IsEmpty() && rParamsUntyped.IsEmpty() { + CreateChange(&changes, PropertyRemoved, v3.ParametersLabel, + lParamsUntyped.ValueNode, nil, true, lParamsUntyped.Value, + nil) + } + if lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() { + CreateChange(&changes, PropertyAdded, v3.ParametersLabel, + nil, rParamsUntyped.ValueNode, true, nil, + rParamsUntyped.Value) + } - // security - if !lOperation.Security.IsEmpty() || !rOperation.Security.IsEmpty() { - checkSecurity(lOperation.Security, rOperation.Security, &changes, oc) - } + // security + if !lOperation.Security.IsEmpty() || !rOperation.Security.IsEmpty() { + checkSecurity(lOperation.Security, rOperation.Security, &changes, oc) + } - // produces - if len(lOperation.Produces.Value) > 0 || len(rOperation.Produces.Value) > 0 { - ExtractStringValueSliceChanges(lOperation.Produces.Value, rOperation.Produces.Value, - &changes, v3.ProducesLabel, true) - } + // produces + if len(lOperation.Produces.Value) > 0 || len(rOperation.Produces.Value) > 0 { + ExtractStringValueSliceChanges(lOperation.Produces.Value, rOperation.Produces.Value, + &changes, v3.ProducesLabel, true) + } - // consumes - if len(lOperation.Consumes.Value) > 0 || len(rOperation.Consumes.Value) > 0 { - ExtractStringValueSliceChanges(lOperation.Consumes.Value, rOperation.Consumes.Value, - &changes, v3.ConsumesLabel, true) - } + // consumes + if len(lOperation.Consumes.Value) > 0 || len(rOperation.Consumes.Value) > 0 { + ExtractStringValueSliceChanges(lOperation.Consumes.Value, rOperation.Consumes.Value, + &changes, v3.ConsumesLabel, true) + } - // schemes - if len(lOperation.Schemes.Value) > 0 || len(rOperation.Schemes.Value) > 0 { - ExtractStringValueSliceChanges(lOperation.Schemes.Value, rOperation.Schemes.Value, - &changes, v3.SchemesLabel, true) - } + // schemes + if len(lOperation.Schemes.Value) > 0 || len(rOperation.Schemes.Value) > 0 { + ExtractStringValueSliceChanges(lOperation.Schemes.Value, rOperation.Schemes.Value, + &changes, v3.SchemesLabel, true) + } - oc.ExtensionChanges = CompareExtensions(lOperation.Extensions, rOperation.Extensions) - } + oc.ExtensionChanges = CompareExtensions(lOperation.Extensions, rOperation.Extensions) + } - // OpenAPI - if reflect.TypeOf(&v3.Operation{}) == reflect.TypeOf(l) && - reflect.TypeOf(&v3.Operation{}) == reflect.TypeOf(r) { + // OpenAPI + if reflect.TypeOf(&v3.Operation{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v3.Operation{}) == reflect.TypeOf(r) { - lOperation := l.(*v3.Operation) - rOperation := r.(*v3.Operation) + lOperation := l.(*v3.Operation) + rOperation := r.(*v3.Operation) - // perform hash check to avoid further processing - if low.AreEqual(lOperation, rOperation) { - return nil - } + // perform hash check to avoid further processing + if low.AreEqual(lOperation, rOperation) { + return nil + } - props = append(props, addSharedOperationProperties(lOperation, rOperation, &changes)...) - compareSharedOperationObjects(lOperation, rOperation, &changes, oc) + props = append(props, addSharedOperationProperties(lOperation, rOperation, &changes)...) + compareSharedOperationObjects(lOperation, rOperation, &changes, oc) - // parameters - lParamsUntyped := lOperation.GetParameters() - rParamsUntyped := rOperation.GetParameters() - if !lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() { - lParams := lParamsUntyped.Value.([]low.ValueReference[*v3.Parameter]) - rParams := rParamsUntyped.Value.([]low.ValueReference[*v3.Parameter]) + // parameters + lParamsUntyped := lOperation.GetParameters() + rParamsUntyped := rOperation.GetParameters() + if !lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() { + lParams := lParamsUntyped.Value.([]low.ValueReference[*v3.Parameter]) + rParams := rParamsUntyped.Value.([]low.ValueReference[*v3.Parameter]) - lv := make(map[string]*v3.Parameter, len(lParams)) - rv := make(map[string]*v3.Parameter, len(rParams)) + lv := make(map[string]*v3.Parameter, len(lParams)) + rv := make(map[string]*v3.Parameter, len(rParams)) - for i := range lParams { - s := lParams[i].Value.Name.Value - lv[s] = lParams[i].Value - } - for i := range rParams { - s := rParams[i].Value.Name.Value - rv[s] = rParams[i].Value - } + for i := range lParams { + s := lParams[i].Value.Name.Value + lv[s] = lParams[i].Value + } + for i := range rParams { + s := rParams[i].Value.Name.Value + rv[s] = rParams[i].Value + } - var paramChanges []*ParameterChanges - for n := range lv { - if _, ok := rv[n]; ok { - if !low.AreEqual(lv[n], rv[n]) { - ch := CompareParameters(lv[n], rv[n]) - if ch != nil { - paramChanges = append(paramChanges, ch) - } - } - continue - } - CreateChange(&changes, ObjectRemoved, v3.ParametersLabel, - lv[n].Name.ValueNode, nil, true, lv[n].Name.Value, - nil) + var paramChanges []*ParameterChanges + for n := range lv { + if _, ok := rv[n]; ok { + if !low.AreEqual(lv[n], rv[n]) { + ch := CompareParameters(lv[n], rv[n]) + if ch != nil { + paramChanges = append(paramChanges, ch) + } + } + continue + } + CreateChange(&changes, ObjectRemoved, v3.ParametersLabel, + lv[n].Name.ValueNode, nil, true, lv[n].Name.Value, + nil) - } - for n := range rv { - if _, ok := lv[n]; !ok { - CreateChange(&changes, ObjectAdded, v3.ParametersLabel, - nil, rv[n].Name.ValueNode, true, nil, - rv[n].Name.Value) - } - } - oc.ParameterChanges = paramChanges - } - if !lParamsUntyped.IsEmpty() && rParamsUntyped.IsEmpty() { - CreateChange(&changes, PropertyRemoved, v3.ParametersLabel, - lParamsUntyped.ValueNode, nil, true, lParamsUntyped.Value, - nil) - } - if lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() { - CreateChange(&changes, PropertyAdded, v3.ParametersLabel, - nil, rParamsUntyped.ValueNode, true, nil, - rParamsUntyped.Value) - } + } + for n := range rv { + if _, ok := lv[n]; !ok { + CreateChange(&changes, ObjectAdded, v3.ParametersLabel, + nil, rv[n].Name.ValueNode, true, nil, + rv[n].Name.Value) + } + } + oc.ParameterChanges = paramChanges + } + if !lParamsUntyped.IsEmpty() && rParamsUntyped.IsEmpty() { + CreateChange(&changes, PropertyRemoved, v3.ParametersLabel, + lParamsUntyped.ValueNode, nil, true, lParamsUntyped.Value, + nil) + } + if lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() { + CreateChange(&changes, PropertyAdded, v3.ParametersLabel, + nil, rParamsUntyped.ValueNode, true, nil, + rParamsUntyped.Value) + } - // security - if !lOperation.Security.IsEmpty() && !lOperation.Security.IsEmpty() { - checkSecurity(lOperation.Security, rOperation.Security, &changes, oc) - } + // security + if !lOperation.Security.IsEmpty() && !lOperation.Security.IsEmpty() { + checkSecurity(lOperation.Security, rOperation.Security, &changes, oc) + } - // request body - if !lOperation.RequestBody.IsEmpty() && !rOperation.RequestBody.IsEmpty() { - if !low.AreEqual(lOperation.RequestBody.Value, rOperation.RequestBody.Value) { - oc.RequestBodyChanges = CompareRequestBodies(lOperation.RequestBody.Value, rOperation.RequestBody.Value) - } - } - if !lOperation.RequestBody.IsEmpty() && rOperation.RequestBody.IsEmpty() { - CreateChange(&changes, PropertyRemoved, v3.RequestBodyLabel, - lOperation.RequestBody.ValueNode, nil, true, lOperation.RequestBody.Value, - nil) - } - if lOperation.RequestBody.IsEmpty() && !rOperation.RequestBody.IsEmpty() { - CreateChange(&changes, PropertyAdded, v3.RequestBodyLabel, - nil, rOperation.RequestBody.ValueNode, true, nil, - rOperation.RequestBody.Value) - } + // request body + if !lOperation.RequestBody.IsEmpty() && !rOperation.RequestBody.IsEmpty() { + if !low.AreEqual(lOperation.RequestBody.Value, rOperation.RequestBody.Value) { + oc.RequestBodyChanges = CompareRequestBodies(lOperation.RequestBody.Value, rOperation.RequestBody.Value) + } + } + if !lOperation.RequestBody.IsEmpty() && rOperation.RequestBody.IsEmpty() { + CreateChange(&changes, PropertyRemoved, v3.RequestBodyLabel, + lOperation.RequestBody.ValueNode, nil, true, lOperation.RequestBody.Value, + nil) + } + if lOperation.RequestBody.IsEmpty() && !rOperation.RequestBody.IsEmpty() { + CreateChange(&changes, PropertyAdded, v3.RequestBodyLabel, + nil, rOperation.RequestBody.ValueNode, true, nil, + rOperation.RequestBody.Value) + } - // callbacks - if !lOperation.GetCallbacks().IsEmpty() && !rOperation.GetCallbacks().IsEmpty() { - oc.CallbackChanges = CheckMapForChanges(lOperation.Callbacks.Value, rOperation.Callbacks.Value, &changes, - v3.CallbacksLabel, CompareCallback) - } - if !lOperation.GetCallbacks().IsEmpty() && rOperation.GetCallbacks().IsEmpty() { - CreateChange(&changes, PropertyRemoved, v3.CallbacksLabel, - lOperation.Callbacks.ValueNode, nil, true, lOperation.Callbacks.Value, - nil) - } - if lOperation.Callbacks.IsEmpty() && !rOperation.Callbacks.IsEmpty() { - CreateChange(&changes, PropertyAdded, v3.CallbacksLabel, - nil, rOperation.Callbacks.ValueNode, false, nil, - rOperation.Callbacks.Value) - } + // callbacks + if !lOperation.GetCallbacks().IsEmpty() && !rOperation.GetCallbacks().IsEmpty() { + oc.CallbackChanges = CheckMapForChanges(lOperation.Callbacks.Value, rOperation.Callbacks.Value, &changes, + v3.CallbacksLabel, CompareCallback) + } + if !lOperation.GetCallbacks().IsEmpty() && rOperation.GetCallbacks().IsEmpty() { + CreateChange(&changes, PropertyRemoved, v3.CallbacksLabel, + lOperation.Callbacks.ValueNode, nil, true, lOperation.Callbacks.Value, + nil) + } + if lOperation.Callbacks.IsEmpty() && !rOperation.Callbacks.IsEmpty() { + CreateChange(&changes, PropertyAdded, v3.CallbacksLabel, + nil, rOperation.Callbacks.ValueNode, false, nil, + rOperation.Callbacks.Value) + } - // servers - oc.ServerChanges = checkServers(lOperation.Servers, rOperation.Servers) - oc.ExtensionChanges = CompareExtensions(lOperation.Extensions, rOperation.Extensions) + // servers + oc.ServerChanges = checkServers(lOperation.Servers, rOperation.Servers) + oc.ExtensionChanges = CompareExtensions(lOperation.Extensions, rOperation.Extensions) - // todo: callbacks - } - CheckProperties(props) - oc.PropertyChanges = NewPropertyChanges(changes) - return oc + // todo: callbacks + } + CheckProperties(props) + oc.PropertyChanges = NewPropertyChanges(changes) + return oc } // check servers property func checkServers(lServers, rServers low.NodeReference[[]low.ValueReference[*v3.Server]]) []*ServerChanges { - var serverChanges []*ServerChanges + var serverChanges []*ServerChanges - if !lServers.IsEmpty() && !rServers.IsEmpty() { + if !lServers.IsEmpty() && !rServers.IsEmpty() { - lv := make(map[string]low.ValueReference[*v3.Server], len(lServers.Value)) - rv := make(map[string]low.ValueReference[*v3.Server], len(rServers.Value)) + lv := make(map[string]low.ValueReference[*v3.Server], len(lServers.Value)) + rv := make(map[string]low.ValueReference[*v3.Server], len(rServers.Value)) - for i := range lServers.Value { - var s string - if !lServers.Value[i].Value.URL.IsEmpty() { - s = lServers.Value[i].Value.URL.Value - } else { - s = low.GenerateHashString(lServers.Value[i].Value) - } - lv[s] = lServers.Value[i] - } - for i := range rServers.Value { - var s string - if !rServers.Value[i].Value.URL.IsEmpty() { - s = rServers.Value[i].Value.URL.Value - } else { - s = low.GenerateHashString(rServers.Value[i].Value) - } - rv[s] = rServers.Value[i] - } + for i := range lServers.Value { + var s string + if !lServers.Value[i].Value.URL.IsEmpty() { + s = lServers.Value[i].Value.URL.Value + } else { + s = low.GenerateHashString(lServers.Value[i].Value) + } + lv[s] = lServers.Value[i] + } + for i := range rServers.Value { + var s string + if !rServers.Value[i].Value.URL.IsEmpty() { + s = rServers.Value[i].Value.URL.Value + } else { + s = low.GenerateHashString(rServers.Value[i].Value) + } + rv[s] = rServers.Value[i] + } - for k := range lv { + for k := range lv { - var changes []*Change + var changes []*Change - if _, ok := rv[k]; ok { - if !low.AreEqual(lv[k].Value, rv[k].Value) { - serverChanges = append(serverChanges, CompareServers(lv[k].Value, rv[k].Value)) - } - continue - } - lv[k].ValueNode.Value = lv[k].Value.URL.Value - CreateChange(&changes, ObjectRemoved, v3.ServersLabel, - lv[k].ValueNode, nil, true, lv[k].Value.URL.Value, - nil) - sc := new(ServerChanges) - sc.PropertyChanges = NewPropertyChanges(changes) - serverChanges = append(serverChanges, sc) + if _, ok := rv[k]; ok { + if !low.AreEqual(lv[k].Value, rv[k].Value) { + serverChanges = append(serverChanges, CompareServers(lv[k].Value, rv[k].Value)) + } + continue + } + lv[k].ValueNode.Value = lv[k].Value.URL.Value + CreateChange(&changes, ObjectRemoved, v3.ServersLabel, + lv[k].ValueNode, nil, true, lv[k].Value.URL.Value, + nil) + sc := new(ServerChanges) + sc.PropertyChanges = NewPropertyChanges(changes) + serverChanges = append(serverChanges, sc) - } + } - for k := range rv { + for k := range rv { - if _, ok := lv[k]; !ok { + if _, ok := lv[k]; !ok { - var changes []*Change - rv[k].ValueNode.Value = rv[k].Value.URL.Value - CreateChange(&changes, ObjectAdded, v3.ServersLabel, - nil, rv[k].ValueNode, false, nil, - rv[k].Value.URL.Value) + var changes []*Change + rv[k].ValueNode.Value = rv[k].Value.URL.Value + CreateChange(&changes, ObjectAdded, v3.ServersLabel, + nil, rv[k].ValueNode, false, nil, + rv[k].Value.URL.Value) - sc := new(ServerChanges) - sc.PropertyChanges = NewPropertyChanges(changes) - serverChanges = append(serverChanges, sc) - } + sc := new(ServerChanges) + sc.PropertyChanges = NewPropertyChanges(changes) + serverChanges = append(serverChanges, sc) + } - } - } - var changes []*Change - sc := new(ServerChanges) - if !lServers.IsEmpty() && rServers.IsEmpty() { - CreateChange(&changes, PropertyRemoved, v3.ServersLabel, - lServers.ValueNode, nil, true, lServers.Value, - nil) - } - if lServers.IsEmpty() && !rServers.IsEmpty() { - CreateChange(&changes, PropertyAdded, v3.ServersLabel, - nil, rServers.ValueNode, false, nil, - rServers.Value) - } - sc.PropertyChanges = NewPropertyChanges(changes) - if len(changes) > 0 { - serverChanges = append(serverChanges, sc) - } - if len(serverChanges) <= 0 { - return nil - } - return serverChanges + } + } + var changes []*Change + sc := new(ServerChanges) + if !lServers.IsEmpty() && rServers.IsEmpty() { + CreateChange(&changes, PropertyRemoved, v3.ServersLabel, + lServers.ValueNode, nil, true, lServers.Value, + nil) + } + if lServers.IsEmpty() && !rServers.IsEmpty() { + CreateChange(&changes, PropertyAdded, v3.ServersLabel, + nil, rServers.ValueNode, false, nil, + rServers.Value) + } + sc.PropertyChanges = NewPropertyChanges(changes) + if len(changes) > 0 { + serverChanges = append(serverChanges, sc) + } + if len(serverChanges) <= 0 { + return nil + } + return serverChanges } // check security property. func checkSecurity(lSecurity, rSecurity low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]], - changes *[]*Change, oc any) { + changes *[]*Change, oc any) { - lv := make(map[string]*base.SecurityRequirement, len(lSecurity.Value)) - rv := make(map[string]*base.SecurityRequirement, len(rSecurity.Value)) - lvn := make(map[string]*yaml.Node, len(lSecurity.Value)) - rvn := make(map[string]*yaml.Node, len(rSecurity.Value)) + lv := make(map[string]*base.SecurityRequirement, len(lSecurity.Value)) + rv := make(map[string]*base.SecurityRequirement, len(rSecurity.Value)) + lvn := make(map[string]*yaml.Node, len(lSecurity.Value)) + rvn := make(map[string]*yaml.Node, len(rSecurity.Value)) - for i := range lSecurity.Value { - keys := lSecurity.Value[i].Value.GetKeys() - sort.Strings(keys) - s := strings.Join(keys, "|") - lv[s] = lSecurity.Value[i].Value - lvn[s] = lSecurity.Value[i].ValueNode + for i := range lSecurity.Value { + keys := lSecurity.Value[i].Value.GetKeys() + sort.Strings(keys) + s := strings.Join(keys, "|") + lv[s] = lSecurity.Value[i].Value + lvn[s] = lSecurity.Value[i].ValueNode - } - for i := range rSecurity.Value { - keys := rSecurity.Value[i].Value.GetKeys() - sort.Strings(keys) - s := strings.Join(keys, "|") - rv[s] = rSecurity.Value[i].Value - rvn[s] = rSecurity.Value[i].ValueNode - } + } + for i := range rSecurity.Value { + keys := rSecurity.Value[i].Value.GetKeys() + sort.Strings(keys) + s := strings.Join(keys, "|") + rv[s] = rSecurity.Value[i].Value + rvn[s] = rSecurity.Value[i].ValueNode + } - var secChanges []*SecurityRequirementChanges - for n := range lv { - if _, ok := rv[n]; ok { - if !low.AreEqual(lv[n], rv[n]) { - ch := CompareSecurityRequirement(lv[n], rv[n]) - if ch != nil { - secChanges = append(secChanges, ch) - } - } - continue - } - lvn[n].Value = strings.Join(lv[n].GetKeys(), ", ") - CreateChange(changes, ObjectRemoved, v3.SecurityLabel, - lvn[n], nil, true, lv[n], - nil) + var secChanges []*SecurityRequirementChanges + for n := range lv { + if _, ok := rv[n]; ok { + if !low.AreEqual(lv[n], rv[n]) { + ch := CompareSecurityRequirement(lv[n], rv[n]) + if ch != nil { + secChanges = append(secChanges, ch) + } + } + continue + } + lvn[n].Value = strings.Join(lv[n].GetKeys(), ", ") + CreateChange(changes, ObjectRemoved, v3.SecurityLabel, + lvn[n], nil, true, lv[n], + nil) - } - for n := range rv { - if _, ok := lv[n]; !ok { - rvn[n].Value = strings.Join(rv[n].GetKeys(), ", ") - CreateChange(changes, ObjectAdded, v3.SecurityLabel, - nil, rvn[n], false, nil, - rv[n]) - } - } + } + for n := range rv { + if _, ok := lv[n]; !ok { + rvn[n].Value = strings.Join(rv[n].GetKeys(), ", ") + CreateChange(changes, ObjectAdded, v3.SecurityLabel, + nil, rvn[n], false, nil, + rv[n]) + } + } - // handle different change types. - if reflect.TypeOf(&OperationChanges{}) == reflect.TypeOf(oc) { - oc.(*OperationChanges).SecurityRequirementChanges = secChanges - } - if reflect.TypeOf(&DocumentChanges{}) == reflect.TypeOf(oc) { - oc.(*DocumentChanges).SecurityRequirementChanges = secChanges - } + // handle different change types. + if reflect.TypeOf(&OperationChanges{}) == reflect.TypeOf(oc) { + oc.(*OperationChanges).SecurityRequirementChanges = secChanges + } + if reflect.TypeOf(&DocumentChanges{}) == reflect.TypeOf(oc) { + oc.(*DocumentChanges).SecurityRequirementChanges = secChanges + } } diff --git a/what-changed/model/path_item.go b/what-changed/model/path_item.go index cbc56f2..7fd1833 100644 --- a/what-changed/model/path_item.go +++ b/what-changed/model/path_item.go @@ -4,595 +4,596 @@ package model import ( - "github.com/pb33f/libopenapi/datamodel/low" - v2 "github.com/pb33f/libopenapi/datamodel/low/v2" - v3 "github.com/pb33f/libopenapi/datamodel/low/v3" - "reflect" + "reflect" + + "github.com/pb33f/libopenapi/datamodel/low" + v2 "github.com/pb33f/libopenapi/datamodel/low/v2" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" ) // PathItemChanges represents changes found between to Swagger or OpenAPI PathItem object. type PathItemChanges struct { - *PropertyChanges - GetChanges *OperationChanges `json:"get,omitempty" yaml:"get,omitempty"` - PutChanges *OperationChanges `json:"put,omitempty" yaml:"put,omitempty"` - PostChanges *OperationChanges `json:"post,omitempty" yaml:"post,omitempty"` - DeleteChanges *OperationChanges `json:"delete,omitempty" yaml:"delete,omitempty"` - OptionsChanges *OperationChanges `json:"options,omitempty" yaml:"options,omitempty"` - HeadChanges *OperationChanges `json:"head,omitempty" yaml:"head,omitempty"` - PatchChanges *OperationChanges `json:"patch,omitempty" yaml:"patch,omitempty"` - TraceChanges *OperationChanges `json:"trace,omitempty" yaml:"trace,omitempty"` - ServerChanges []*ServerChanges `json:"servers,omitempty" yaml:"servers,omitempty"` - ParameterChanges []*ParameterChanges `json:"parameters,omitempty" yaml:"parameters,omitempty"` - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + *PropertyChanges + GetChanges *OperationChanges `json:"get,omitempty" yaml:"get,omitempty"` + PutChanges *OperationChanges `json:"put,omitempty" yaml:"put,omitempty"` + PostChanges *OperationChanges `json:"post,omitempty" yaml:"post,omitempty"` + DeleteChanges *OperationChanges `json:"delete,omitempty" yaml:"delete,omitempty"` + OptionsChanges *OperationChanges `json:"options,omitempty" yaml:"options,omitempty"` + HeadChanges *OperationChanges `json:"head,omitempty" yaml:"head,omitempty"` + PatchChanges *OperationChanges `json:"patch,omitempty" yaml:"patch,omitempty"` + TraceChanges *OperationChanges `json:"trace,omitempty" yaml:"trace,omitempty"` + ServerChanges []*ServerChanges `json:"servers,omitempty" yaml:"servers,omitempty"` + ParameterChanges []*ParameterChanges `json:"parameters,omitempty" yaml:"parameters,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` } // TotalChanges returns the total number of changes found between two Swagger or OpenAPI PathItems func (p *PathItemChanges) TotalChanges() int { - c := p.PropertyChanges.TotalChanges() - if p.GetChanges != nil { - c += p.GetChanges.TotalChanges() - } - if p.PutChanges != nil { - c += p.PutChanges.TotalChanges() - } - if p.PostChanges != nil { - c += p.PostChanges.TotalChanges() - } - if p.DeleteChanges != nil { - c += p.DeleteChanges.TotalChanges() - } - if p.OptionsChanges != nil { - c += p.OptionsChanges.TotalChanges() - } - if p.HeadChanges != nil { - c += p.HeadChanges.TotalChanges() - } - if p.PatchChanges != nil { - c += p.PatchChanges.TotalChanges() - } - if p.TraceChanges != nil { - c += p.TraceChanges.TotalChanges() - } - for i := range p.ServerChanges { - c += p.ServerChanges[i].TotalChanges() - } - for i := range p.ParameterChanges { - c += p.ParameterChanges[i].TotalChanges() - } - if p.ExtensionChanges != nil { - c += p.ExtensionChanges.TotalChanges() - } - return c + c := p.PropertyChanges.TotalChanges() + if p.GetChanges != nil { + c += p.GetChanges.TotalChanges() + } + if p.PutChanges != nil { + c += p.PutChanges.TotalChanges() + } + if p.PostChanges != nil { + c += p.PostChanges.TotalChanges() + } + if p.DeleteChanges != nil { + c += p.DeleteChanges.TotalChanges() + } + if p.OptionsChanges != nil { + c += p.OptionsChanges.TotalChanges() + } + if p.HeadChanges != nil { + c += p.HeadChanges.TotalChanges() + } + if p.PatchChanges != nil { + c += p.PatchChanges.TotalChanges() + } + if p.TraceChanges != nil { + c += p.TraceChanges.TotalChanges() + } + for i := range p.ServerChanges { + c += p.ServerChanges[i].TotalChanges() + } + for i := range p.ParameterChanges { + c += p.ParameterChanges[i].TotalChanges() + } + if p.ExtensionChanges != nil { + c += p.ExtensionChanges.TotalChanges() + } + return c } // TotalBreakingChanges returns the total number of breaking changes found between two Swagger or OpenAPI PathItems func (p *PathItemChanges) TotalBreakingChanges() int { - c := p.PropertyChanges.TotalBreakingChanges() - if p.GetChanges != nil { - c += p.GetChanges.TotalBreakingChanges() - } - if p.PutChanges != nil { - c += p.PutChanges.TotalBreakingChanges() - } - if p.PostChanges != nil { - c += p.PostChanges.TotalBreakingChanges() - } - if p.DeleteChanges != nil { - c += p.DeleteChanges.TotalBreakingChanges() - } - if p.OptionsChanges != nil { - c += p.OptionsChanges.TotalBreakingChanges() - } - if p.HeadChanges != nil { - c += p.HeadChanges.TotalBreakingChanges() - } - if p.PatchChanges != nil { - c += p.PatchChanges.TotalBreakingChanges() - } - if p.TraceChanges != nil { - c += p.TraceChanges.TotalBreakingChanges() - } - for i := range p.ServerChanges { - c += p.ServerChanges[i].TotalBreakingChanges() - } - for i := range p.ParameterChanges { - c += p.ParameterChanges[i].TotalBreakingChanges() - } - return c + c := p.PropertyChanges.TotalBreakingChanges() + if p.GetChanges != nil { + c += p.GetChanges.TotalBreakingChanges() + } + if p.PutChanges != nil { + c += p.PutChanges.TotalBreakingChanges() + } + if p.PostChanges != nil { + c += p.PostChanges.TotalBreakingChanges() + } + if p.DeleteChanges != nil { + c += p.DeleteChanges.TotalBreakingChanges() + } + if p.OptionsChanges != nil { + c += p.OptionsChanges.TotalBreakingChanges() + } + if p.HeadChanges != nil { + c += p.HeadChanges.TotalBreakingChanges() + } + if p.PatchChanges != nil { + c += p.PatchChanges.TotalBreakingChanges() + } + if p.TraceChanges != nil { + c += p.TraceChanges.TotalBreakingChanges() + } + for i := range p.ServerChanges { + c += p.ServerChanges[i].TotalBreakingChanges() + } + for i := range p.ParameterChanges { + c += p.ParameterChanges[i].TotalBreakingChanges() + } + return c } type opCheck struct { - label string - changes *OperationChanges + label string + changes *OperationChanges } // ComparePathItemsV3 is an OpenAPI typesafe proxy method for ComparePathItems func ComparePathItemsV3(l, r *v3.PathItem) *PathItemChanges { - return ComparePathItems(l, r) + return ComparePathItems(l, r) } // ComparePathItems compare a left and right Swagger or OpenAPI PathItem object for changes. If found, returns // a pointer to PathItemChanges, or returns nil if nothing is found. func ComparePathItems(l, r any) *PathItemChanges { - var changes []*Change - var props []*PropertyCheck + var changes []*Change + var props []*PropertyCheck - pc := new(PathItemChanges) + pc := new(PathItemChanges) - // Swagger - if reflect.TypeOf(&v2.PathItem{}) == reflect.TypeOf(l) && - reflect.TypeOf(&v2.PathItem{}) == reflect.TypeOf(r) { + // Swagger + if reflect.TypeOf(&v2.PathItem{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v2.PathItem{}) == reflect.TypeOf(r) { - lPath := l.(*v2.PathItem) - rPath := r.(*v2.PathItem) + lPath := l.(*v2.PathItem) + rPath := r.(*v2.PathItem) - // 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 + } - props = append(props, compareSwaggerPathItem(lPath, rPath, &changes, pc)...) - } + props = append(props, compareSwaggerPathItem(lPath, rPath, &changes, pc)...) + } - // OpenAPI - if reflect.TypeOf(&v3.PathItem{}) == reflect.TypeOf(l) && - reflect.TypeOf(&v3.PathItem{}) == reflect.TypeOf(r) { + // OpenAPI + if reflect.TypeOf(&v3.PathItem{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v3.PathItem{}) == reflect.TypeOf(r) { - lPath := l.(*v3.PathItem) - rPath := r.(*v3.PathItem) + lPath := l.(*v3.PathItem) + rPath := r.(*v3.PathItem) - // 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 + } - // description - props = append(props, &PropertyCheck{ - LeftNode: lPath.Description.ValueNode, - RightNode: rPath.Description.ValueNode, - Label: v3.DescriptionLabel, - Changes: &changes, - Breaking: false, - Original: lPath, - New: lPath, - }) + // description + props = append(props, &PropertyCheck{ + LeftNode: lPath.Description.ValueNode, + RightNode: rPath.Description.ValueNode, + Label: v3.DescriptionLabel, + Changes: &changes, + Breaking: false, + Original: lPath, + New: lPath, + }) - // summary - props = append(props, &PropertyCheck{ - LeftNode: lPath.Summary.ValueNode, - RightNode: rPath.Summary.ValueNode, - Label: v3.SummaryLabel, - Changes: &changes, - Breaking: false, - Original: lPath, - New: lPath, - }) + // summary + props = append(props, &PropertyCheck{ + LeftNode: lPath.Summary.ValueNode, + RightNode: rPath.Summary.ValueNode, + Label: v3.SummaryLabel, + Changes: &changes, + Breaking: false, + Original: lPath, + New: lPath, + }) - compareOpenAPIPathItem(lPath, rPath, &changes, pc) - } + compareOpenAPIPathItem(lPath, rPath, &changes, pc) + } - CheckProperties(props) - pc.PropertyChanges = NewPropertyChanges(changes) - return pc + CheckProperties(props) + pc.PropertyChanges = NewPropertyChanges(changes) + return pc } func compareSwaggerPathItem(lPath, rPath *v2.PathItem, changes *[]*Change, pc *PathItemChanges) []*PropertyCheck { - var props []*PropertyCheck + var props []*PropertyCheck - totalOps := 0 - opChan := make(chan opCheck) - // get - if !lPath.Get.IsEmpty() && !rPath.Get.IsEmpty() { - totalOps++ - go checkOperation(lPath.Get.Value, rPath.Get.Value, opChan, v3.GetLabel) - } - if !lPath.Get.IsEmpty() && rPath.Get.IsEmpty() { - CreateChange(changes, PropertyRemoved, v3.GetLabel, - lPath.Get.ValueNode, nil, true, lPath.Get.Value, nil) - } - if lPath.Get.IsEmpty() && !rPath.Get.IsEmpty() { - CreateChange(changes, PropertyAdded, v3.GetLabel, - nil, rPath.Get.ValueNode, false, nil, lPath.Get.Value) - } + totalOps := 0 + opChan := make(chan opCheck) + // get + if !lPath.Get.IsEmpty() && !rPath.Get.IsEmpty() { + totalOps++ + go checkOperation(lPath.Get.Value, rPath.Get.Value, opChan, v3.GetLabel) + } + if !lPath.Get.IsEmpty() && rPath.Get.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.GetLabel, + lPath.Get.ValueNode, nil, true, lPath.Get.Value, nil) + } + if lPath.Get.IsEmpty() && !rPath.Get.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.GetLabel, + nil, rPath.Get.ValueNode, false, nil, lPath.Get.Value) + } - // put - if !lPath.Put.IsEmpty() && !rPath.Put.IsEmpty() { - totalOps++ - go checkOperation(lPath.Put.Value, rPath.Put.Value, opChan, v3.PutLabel) - } - if !lPath.Put.IsEmpty() && rPath.Put.IsEmpty() { - CreateChange(changes, PropertyRemoved, v3.PutLabel, - lPath.Put.ValueNode, nil, true, lPath.Put.Value, nil) - } - if lPath.Put.IsEmpty() && !rPath.Put.IsEmpty() { - CreateChange(changes, PropertyAdded, v3.PutLabel, - nil, rPath.Put.ValueNode, false, nil, lPath.Put.Value) - } + // put + if !lPath.Put.IsEmpty() && !rPath.Put.IsEmpty() { + totalOps++ + go checkOperation(lPath.Put.Value, rPath.Put.Value, opChan, v3.PutLabel) + } + if !lPath.Put.IsEmpty() && rPath.Put.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.PutLabel, + lPath.Put.ValueNode, nil, true, lPath.Put.Value, nil) + } + if lPath.Put.IsEmpty() && !rPath.Put.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.PutLabel, + nil, rPath.Put.ValueNode, false, nil, lPath.Put.Value) + } - // post - if !lPath.Post.IsEmpty() && !rPath.Post.IsEmpty() { - totalOps++ - go checkOperation(lPath.Post.Value, rPath.Post.Value, opChan, v3.PostLabel) - } - if !lPath.Post.IsEmpty() && rPath.Post.IsEmpty() { - CreateChange(changes, PropertyRemoved, v3.PostLabel, - lPath.Post.ValueNode, nil, true, lPath.Post.Value, nil) - } - if lPath.Post.IsEmpty() && !rPath.Post.IsEmpty() { - CreateChange(changes, PropertyAdded, v3.PostLabel, - nil, rPath.Post.ValueNode, false, nil, lPath.Post.Value) - } + // post + if !lPath.Post.IsEmpty() && !rPath.Post.IsEmpty() { + totalOps++ + go checkOperation(lPath.Post.Value, rPath.Post.Value, opChan, v3.PostLabel) + } + if !lPath.Post.IsEmpty() && rPath.Post.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.PostLabel, + lPath.Post.ValueNode, nil, true, lPath.Post.Value, nil) + } + if lPath.Post.IsEmpty() && !rPath.Post.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.PostLabel, + nil, rPath.Post.ValueNode, false, nil, lPath.Post.Value) + } - // delete - if !lPath.Delete.IsEmpty() && !rPath.Delete.IsEmpty() { - totalOps++ - go checkOperation(lPath.Delete.Value, rPath.Delete.Value, opChan, v3.DeleteLabel) - } - if !lPath.Delete.IsEmpty() && rPath.Delete.IsEmpty() { - CreateChange(changes, PropertyRemoved, v3.DeleteLabel, - lPath.Delete.ValueNode, nil, true, lPath.Delete.Value, nil) - } - if lPath.Delete.IsEmpty() && !rPath.Delete.IsEmpty() { - CreateChange(changes, PropertyAdded, v3.DeleteLabel, - nil, rPath.Delete.ValueNode, false, nil, lPath.Delete.Value) - } + // delete + if !lPath.Delete.IsEmpty() && !rPath.Delete.IsEmpty() { + totalOps++ + go checkOperation(lPath.Delete.Value, rPath.Delete.Value, opChan, v3.DeleteLabel) + } + if !lPath.Delete.IsEmpty() && rPath.Delete.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.DeleteLabel, + lPath.Delete.ValueNode, nil, true, lPath.Delete.Value, nil) + } + if lPath.Delete.IsEmpty() && !rPath.Delete.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.DeleteLabel, + nil, rPath.Delete.ValueNode, false, nil, lPath.Delete.Value) + } - // options - if !lPath.Options.IsEmpty() && !rPath.Options.IsEmpty() { - totalOps++ - go checkOperation(lPath.Options.Value, rPath.Options.Value, opChan, v3.OptionsLabel) - } - if !lPath.Options.IsEmpty() && rPath.Options.IsEmpty() { - CreateChange(changes, PropertyRemoved, v3.OptionsLabel, - lPath.Options.ValueNode, nil, true, lPath.Options.Value, nil) - } - if lPath.Options.IsEmpty() && !rPath.Options.IsEmpty() { - CreateChange(changes, PropertyAdded, v3.OptionsLabel, - nil, rPath.Options.ValueNode, false, nil, lPath.Options.Value) - } + // options + if !lPath.Options.IsEmpty() && !rPath.Options.IsEmpty() { + totalOps++ + go checkOperation(lPath.Options.Value, rPath.Options.Value, opChan, v3.OptionsLabel) + } + if !lPath.Options.IsEmpty() && rPath.Options.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.OptionsLabel, + lPath.Options.ValueNode, nil, true, lPath.Options.Value, nil) + } + if lPath.Options.IsEmpty() && !rPath.Options.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.OptionsLabel, + nil, rPath.Options.ValueNode, false, nil, lPath.Options.Value) + } - // head - if !lPath.Head.IsEmpty() && !rPath.Head.IsEmpty() { - totalOps++ - go checkOperation(lPath.Head.Value, rPath.Head.Value, opChan, v3.HeadLabel) - } - if !lPath.Head.IsEmpty() && rPath.Head.IsEmpty() { - CreateChange(changes, PropertyRemoved, v3.HeadLabel, - lPath.Head.ValueNode, nil, true, lPath.Head.Value, nil) - } - if lPath.Head.IsEmpty() && !rPath.Head.IsEmpty() { - CreateChange(changes, PropertyAdded, v3.HeadLabel, - nil, rPath.Head.ValueNode, false, nil, lPath.Head.Value) - } + // head + if !lPath.Head.IsEmpty() && !rPath.Head.IsEmpty() { + totalOps++ + go checkOperation(lPath.Head.Value, rPath.Head.Value, opChan, v3.HeadLabel) + } + if !lPath.Head.IsEmpty() && rPath.Head.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.HeadLabel, + lPath.Head.ValueNode, nil, true, lPath.Head.Value, nil) + } + if lPath.Head.IsEmpty() && !rPath.Head.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.HeadLabel, + nil, rPath.Head.ValueNode, false, nil, lPath.Head.Value) + } - // patch - if !lPath.Patch.IsEmpty() && !rPath.Patch.IsEmpty() { - totalOps++ - go checkOperation(lPath.Patch.Value, rPath.Patch.Value, opChan, v3.PatchLabel) - } - if !lPath.Patch.IsEmpty() && rPath.Patch.IsEmpty() { - CreateChange(changes, PropertyRemoved, v3.PatchLabel, - lPath.Patch.ValueNode, nil, true, lPath.Patch.Value, nil) - } - if lPath.Patch.IsEmpty() && !rPath.Patch.IsEmpty() { - CreateChange(changes, PropertyAdded, v3.PatchLabel, - nil, rPath.Patch.ValueNode, false, nil, lPath.Patch.Value) - } + // patch + if !lPath.Patch.IsEmpty() && !rPath.Patch.IsEmpty() { + totalOps++ + go checkOperation(lPath.Patch.Value, rPath.Patch.Value, opChan, v3.PatchLabel) + } + if !lPath.Patch.IsEmpty() && rPath.Patch.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.PatchLabel, + lPath.Patch.ValueNode, nil, true, lPath.Patch.Value, nil) + } + if lPath.Patch.IsEmpty() && !rPath.Patch.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.PatchLabel, + nil, rPath.Patch.ValueNode, false, nil, lPath.Patch.Value) + } - // parameters - if !lPath.Parameters.IsEmpty() && !lPath.Parameters.IsEmpty() { - lParams := lPath.Parameters.Value - rParams := rPath.Parameters.Value - lp, rp := extractV2ParametersIntoInterface(lParams, rParams) - checkParameters(lp, rp, changes, pc) - } - if !lPath.Parameters.IsEmpty() && rPath.Parameters.IsEmpty() { - CreateChange(changes, PropertyRemoved, v3.ParametersLabel, - lPath.Parameters.ValueNode, nil, true, lPath.Parameters.Value, - nil) - } - if lPath.Parameters.IsEmpty() && !rPath.Parameters.IsEmpty() { - CreateChange(changes, PropertyAdded, v3.ParametersLabel, - nil, rPath.Parameters.ValueNode, true, nil, - rPath.Parameters.Value) - } + // parameters + if !lPath.Parameters.IsEmpty() && !lPath.Parameters.IsEmpty() { + lParams := lPath.Parameters.Value + rParams := rPath.Parameters.Value + lp, rp := extractV2ParametersIntoInterface(lParams, rParams) + checkParameters(lp, rp, changes, pc) + } + if !lPath.Parameters.IsEmpty() && rPath.Parameters.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.ParametersLabel, + lPath.Parameters.ValueNode, nil, true, lPath.Parameters.Value, + nil) + } + if lPath.Parameters.IsEmpty() && !rPath.Parameters.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.ParametersLabel, + nil, rPath.Parameters.ValueNode, true, nil, + rPath.Parameters.Value) + } - // collect up operations changes. - completedOperations := 0 - for completedOperations < totalOps { - select { - case n := <-opChan: - switch n.label { - case v3.GetLabel: - pc.GetChanges = n.changes - break - case v3.PutLabel: - pc.PutChanges = n.changes - break - case v3.PostLabel: - pc.PostChanges = n.changes - break - case v3.DeleteLabel: - pc.DeleteChanges = n.changes - break - case v3.OptionsLabel: - pc.OptionsChanges = n.changes - break - case v2.HeadLabel: - pc.HeadChanges = n.changes - break - case v2.PatchLabel: - pc.PatchChanges = n.changes - break - } - completedOperations++ - } - } - pc.ExtensionChanges = CompareExtensions(lPath.Extensions, rPath.Extensions) - return props + // collect up operations changes. + completedOperations := 0 + for completedOperations < totalOps { + select { + case n := <-opChan: + switch n.label { + case v3.GetLabel: + pc.GetChanges = n.changes + break + case v3.PutLabel: + pc.PutChanges = n.changes + break + case v3.PostLabel: + pc.PostChanges = n.changes + break + case v3.DeleteLabel: + pc.DeleteChanges = n.changes + break + case v3.OptionsLabel: + pc.OptionsChanges = n.changes + break + case v2.HeadLabel: + pc.HeadChanges = n.changes + break + case v2.PatchLabel: + pc.PatchChanges = n.changes + break + } + completedOperations++ + } + } + pc.ExtensionChanges = CompareExtensions(lPath.Extensions, rPath.Extensions) + return props } func extractV2ParametersIntoInterface(l, r []low.ValueReference[*v2.Parameter]) ([]low.ValueReference[low.SharedParameters], - []low.ValueReference[low.SharedParameters]) { - lp := make([]low.ValueReference[low.SharedParameters], len(l)) - rp := make([]low.ValueReference[low.SharedParameters], len(r)) - for i := range l { - lp[i] = low.ValueReference[low.SharedParameters]{ - Value: l[i].Value, - ValueNode: l[i].ValueNode, - } - } - for i := range r { - rp[i] = low.ValueReference[low.SharedParameters]{ - Value: r[i].Value, - ValueNode: r[i].ValueNode, - } - } - return lp, rp + []low.ValueReference[low.SharedParameters]) { + lp := make([]low.ValueReference[low.SharedParameters], len(l)) + rp := make([]low.ValueReference[low.SharedParameters], len(r)) + for i := range l { + lp[i] = low.ValueReference[low.SharedParameters]{ + Value: l[i].Value, + ValueNode: l[i].ValueNode, + } + } + for i := range r { + rp[i] = low.ValueReference[low.SharedParameters]{ + Value: r[i].Value, + ValueNode: r[i].ValueNode, + } + } + return lp, rp } func extractV3ParametersIntoInterface(l, r []low.ValueReference[*v3.Parameter]) ([]low.ValueReference[low.SharedParameters], - []low.ValueReference[low.SharedParameters]) { - lp := make([]low.ValueReference[low.SharedParameters], len(l)) - rp := make([]low.ValueReference[low.SharedParameters], len(r)) - for i := range l { - lp[i] = low.ValueReference[low.SharedParameters]{ - Value: l[i].Value, - ValueNode: l[i].ValueNode, - } - } - for i := range r { - rp[i] = low.ValueReference[low.SharedParameters]{ - Value: r[i].Value, - ValueNode: r[i].ValueNode, - } - } - return lp, rp + []low.ValueReference[low.SharedParameters]) { + lp := make([]low.ValueReference[low.SharedParameters], len(l)) + rp := make([]low.ValueReference[low.SharedParameters], len(r)) + for i := range l { + lp[i] = low.ValueReference[low.SharedParameters]{ + Value: l[i].Value, + ValueNode: l[i].ValueNode, + } + } + for i := range r { + rp[i] = low.ValueReference[low.SharedParameters]{ + Value: r[i].Value, + ValueNode: r[i].ValueNode, + } + } + return lp, rp } func checkParameters(lParams, rParams []low.ValueReference[low.SharedParameters], changes *[]*Change, pc *PathItemChanges) { - lv := make(map[string]low.SharedParameters, len(lParams)) - rv := make(map[string]low.SharedParameters, len(rParams)) + lv := make(map[string]low.SharedParameters, len(lParams)) + rv := make(map[string]low.SharedParameters, len(rParams)) - for i := range lParams { - s := lParams[i].Value.GetName().Value - lv[s] = lParams[i].Value - } - for i := range rParams { - s := rParams[i].Value.GetName().Value - rv[s] = rParams[i].Value - } + for i := range lParams { + s := lParams[i].Value.GetName().Value + lv[s] = lParams[i].Value + } + for i := range rParams { + s := rParams[i].Value.GetName().Value + rv[s] = rParams[i].Value + } - var paramChanges []*ParameterChanges - for n := range lv { - if _, ok := rv[n]; ok { - if !low.AreEqual(lv[n], rv[n]) { - ch := CompareParameters(lv[n], rv[n]) - if ch != nil { - paramChanges = append(paramChanges, ch) - } - } - continue - } - CreateChange(changes, ObjectRemoved, v3.ParametersLabel, - lv[n].GetName().ValueNode, nil, true, lv[n].GetName().Value, - nil) + var paramChanges []*ParameterChanges + for n := range lv { + if _, ok := rv[n]; ok { + if !low.AreEqual(lv[n], rv[n]) { + ch := CompareParameters(lv[n], rv[n]) + if ch != nil { + paramChanges = append(paramChanges, ch) + } + } + continue + } + CreateChange(changes, ObjectRemoved, v3.ParametersLabel, + lv[n].GetName().ValueNode, nil, true, lv[n].GetName().Value, + nil) - } - for n := range rv { - if _, ok := lv[n]; !ok { - CreateChange(changes, ObjectAdded, v3.ParametersLabel, - nil, rv[n].GetName().ValueNode, true, nil, - rv[n].GetName().Value) - } - } - pc.ParameterChanges = paramChanges + } + for n := range rv { + if _, ok := lv[n]; !ok { + CreateChange(changes, ObjectAdded, v3.ParametersLabel, + nil, rv[n].GetName().ValueNode, true, nil, + rv[n].GetName().Value) + } + } + pc.ParameterChanges = paramChanges } func compareOpenAPIPathItem(lPath, rPath *v3.PathItem, changes *[]*Change, pc *PathItemChanges) { - //var props []*PropertyCheck + //var props []*PropertyCheck - totalOps := 0 - opChan := make(chan opCheck) + totalOps := 0 + opChan := make(chan opCheck) - // get - if !lPath.Get.IsEmpty() && !rPath.Get.IsEmpty() { - totalOps++ - go checkOperation(lPath.Get.Value, rPath.Get.Value, opChan, v3.GetLabel) - } - if !lPath.Get.IsEmpty() && rPath.Get.IsEmpty() { - CreateChange(changes, PropertyRemoved, v3.GetLabel, - lPath.Get.ValueNode, nil, true, lPath.Get.Value, nil) - } - if lPath.Get.IsEmpty() && !rPath.Get.IsEmpty() { - CreateChange(changes, PropertyAdded, v3.GetLabel, - nil, rPath.Get.ValueNode, false, nil, lPath.Get.Value) - } + // get + if !lPath.Get.IsEmpty() && !rPath.Get.IsEmpty() { + totalOps++ + go checkOperation(lPath.Get.Value, rPath.Get.Value, opChan, v3.GetLabel) + } + if !lPath.Get.IsEmpty() && rPath.Get.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.GetLabel, + lPath.Get.ValueNode, nil, true, lPath.Get.Value, nil) + } + if lPath.Get.IsEmpty() && !rPath.Get.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.GetLabel, + nil, rPath.Get.ValueNode, false, nil, lPath.Get.Value) + } - // put - if !lPath.Put.IsEmpty() && !rPath.Put.IsEmpty() { - totalOps++ - go checkOperation(lPath.Put.Value, rPath.Put.Value, opChan, v3.PutLabel) - } - if !lPath.Put.IsEmpty() && rPath.Put.IsEmpty() { - CreateChange(changes, PropertyRemoved, v3.PutLabel, - lPath.Put.ValueNode, nil, true, lPath.Put.Value, nil) - } - if lPath.Put.IsEmpty() && !rPath.Put.IsEmpty() { - CreateChange(changes, PropertyAdded, v3.PutLabel, - nil, rPath.Put.ValueNode, false, nil, lPath.Put.Value) - } + // put + if !lPath.Put.IsEmpty() && !rPath.Put.IsEmpty() { + totalOps++ + go checkOperation(lPath.Put.Value, rPath.Put.Value, opChan, v3.PutLabel) + } + if !lPath.Put.IsEmpty() && rPath.Put.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.PutLabel, + lPath.Put.ValueNode, nil, true, lPath.Put.Value, nil) + } + if lPath.Put.IsEmpty() && !rPath.Put.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.PutLabel, + nil, rPath.Put.ValueNode, false, nil, lPath.Put.Value) + } - // post - if !lPath.Post.IsEmpty() && !rPath.Post.IsEmpty() { - totalOps++ - go checkOperation(lPath.Post.Value, rPath.Post.Value, opChan, v3.PostLabel) - } - if !lPath.Post.IsEmpty() && rPath.Post.IsEmpty() { - CreateChange(changes, PropertyRemoved, v3.PostLabel, - lPath.Post.ValueNode, nil, true, lPath.Post.Value, nil) - } - if lPath.Post.IsEmpty() && !rPath.Post.IsEmpty() { - CreateChange(changes, PropertyAdded, v3.PostLabel, - nil, rPath.Post.ValueNode, false, nil, lPath.Post.Value) - } + // post + if !lPath.Post.IsEmpty() && !rPath.Post.IsEmpty() { + totalOps++ + go checkOperation(lPath.Post.Value, rPath.Post.Value, opChan, v3.PostLabel) + } + if !lPath.Post.IsEmpty() && rPath.Post.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.PostLabel, + lPath.Post.ValueNode, nil, true, lPath.Post.Value, nil) + } + if lPath.Post.IsEmpty() && !rPath.Post.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.PostLabel, + nil, rPath.Post.ValueNode, false, nil, lPath.Post.Value) + } - // delete - if !lPath.Delete.IsEmpty() && !rPath.Delete.IsEmpty() { - totalOps++ - go checkOperation(lPath.Delete.Value, rPath.Delete.Value, opChan, v3.DeleteLabel) - } - if !lPath.Delete.IsEmpty() && rPath.Delete.IsEmpty() { - CreateChange(changes, PropertyRemoved, v3.DeleteLabel, - lPath.Delete.ValueNode, nil, true, lPath.Delete.Value, nil) - } - if lPath.Delete.IsEmpty() && !rPath.Delete.IsEmpty() { - CreateChange(changes, PropertyAdded, v3.DeleteLabel, - nil, rPath.Delete.ValueNode, false, nil, lPath.Delete.Value) - } + // delete + if !lPath.Delete.IsEmpty() && !rPath.Delete.IsEmpty() { + totalOps++ + go checkOperation(lPath.Delete.Value, rPath.Delete.Value, opChan, v3.DeleteLabel) + } + if !lPath.Delete.IsEmpty() && rPath.Delete.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.DeleteLabel, + lPath.Delete.ValueNode, nil, true, lPath.Delete.Value, nil) + } + if lPath.Delete.IsEmpty() && !rPath.Delete.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.DeleteLabel, + nil, rPath.Delete.ValueNode, false, nil, lPath.Delete.Value) + } - // options - if !lPath.Options.IsEmpty() && !rPath.Options.IsEmpty() { - totalOps++ - go checkOperation(lPath.Options.Value, rPath.Options.Value, opChan, v3.OptionsLabel) - } - if !lPath.Options.IsEmpty() && rPath.Options.IsEmpty() { - CreateChange(changes, PropertyRemoved, v3.OptionsLabel, - lPath.Options.ValueNode, nil, true, lPath.Options.Value, nil) - } - if lPath.Options.IsEmpty() && !rPath.Options.IsEmpty() { - CreateChange(changes, PropertyAdded, v3.OptionsLabel, - nil, rPath.Options.ValueNode, false, nil, lPath.Options.Value) - } + // options + if !lPath.Options.IsEmpty() && !rPath.Options.IsEmpty() { + totalOps++ + go checkOperation(lPath.Options.Value, rPath.Options.Value, opChan, v3.OptionsLabel) + } + if !lPath.Options.IsEmpty() && rPath.Options.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.OptionsLabel, + lPath.Options.ValueNode, nil, true, lPath.Options.Value, nil) + } + if lPath.Options.IsEmpty() && !rPath.Options.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.OptionsLabel, + nil, rPath.Options.ValueNode, false, nil, lPath.Options.Value) + } - // head - if !lPath.Head.IsEmpty() && !rPath.Head.IsEmpty() { - totalOps++ - go checkOperation(lPath.Head.Value, rPath.Head.Value, opChan, v3.HeadLabel) - } - if !lPath.Head.IsEmpty() && rPath.Head.IsEmpty() { - CreateChange(changes, PropertyRemoved, v3.HeadLabel, - lPath.Head.ValueNode, nil, true, lPath.Head.Value, nil) - } - if lPath.Head.IsEmpty() && !rPath.Head.IsEmpty() { - CreateChange(changes, PropertyAdded, v3.HeadLabel, - nil, rPath.Head.ValueNode, false, nil, lPath.Head.Value) - } + // head + if !lPath.Head.IsEmpty() && !rPath.Head.IsEmpty() { + totalOps++ + go checkOperation(lPath.Head.Value, rPath.Head.Value, opChan, v3.HeadLabel) + } + if !lPath.Head.IsEmpty() && rPath.Head.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.HeadLabel, + lPath.Head.ValueNode, nil, true, lPath.Head.Value, nil) + } + if lPath.Head.IsEmpty() && !rPath.Head.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.HeadLabel, + nil, rPath.Head.ValueNode, false, nil, lPath.Head.Value) + } - // patch - if !lPath.Patch.IsEmpty() && !rPath.Patch.IsEmpty() { - totalOps++ - go checkOperation(lPath.Patch.Value, rPath.Patch.Value, opChan, v3.PatchLabel) - } - if !lPath.Patch.IsEmpty() && rPath.Patch.IsEmpty() { - CreateChange(changes, PropertyRemoved, v3.PatchLabel, - lPath.Patch.ValueNode, nil, true, lPath.Patch.Value, nil) - } - if lPath.Patch.IsEmpty() && !rPath.Patch.IsEmpty() { - CreateChange(changes, PropertyAdded, v3.PatchLabel, - nil, rPath.Patch.ValueNode, false, nil, lPath.Patch.Value) - } + // patch + if !lPath.Patch.IsEmpty() && !rPath.Patch.IsEmpty() { + totalOps++ + go checkOperation(lPath.Patch.Value, rPath.Patch.Value, opChan, v3.PatchLabel) + } + if !lPath.Patch.IsEmpty() && rPath.Patch.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.PatchLabel, + lPath.Patch.ValueNode, nil, true, lPath.Patch.Value, nil) + } + if lPath.Patch.IsEmpty() && !rPath.Patch.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.PatchLabel, + nil, rPath.Patch.ValueNode, false, nil, lPath.Patch.Value) + } - // trace - if !lPath.Trace.IsEmpty() && !rPath.Trace.IsEmpty() { - totalOps++ - go checkOperation(lPath.Trace.Value, rPath.Trace.Value, opChan, v3.TraceLabel) - } - if !lPath.Trace.IsEmpty() && rPath.Trace.IsEmpty() { - CreateChange(changes, PropertyRemoved, v3.TraceLabel, - lPath.Trace.ValueNode, nil, true, lPath.Trace.Value, nil) - } - if lPath.Trace.IsEmpty() && !rPath.Trace.IsEmpty() { - CreateChange(changes, PropertyAdded, v3.TraceLabel, - nil, rPath.Trace.ValueNode, false, nil, lPath.Trace.Value) - } + // trace + if !lPath.Trace.IsEmpty() && !rPath.Trace.IsEmpty() { + totalOps++ + go checkOperation(lPath.Trace.Value, rPath.Trace.Value, opChan, v3.TraceLabel) + } + if !lPath.Trace.IsEmpty() && rPath.Trace.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.TraceLabel, + lPath.Trace.ValueNode, nil, true, lPath.Trace.Value, nil) + } + if lPath.Trace.IsEmpty() && !rPath.Trace.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.TraceLabel, + nil, rPath.Trace.ValueNode, false, nil, lPath.Trace.Value) + } - // servers - pc.ServerChanges = checkServers(lPath.Servers, rPath.Servers) + // servers + pc.ServerChanges = checkServers(lPath.Servers, rPath.Servers) - // parameters - if !lPath.Parameters.IsEmpty() && !rPath.Parameters.IsEmpty() { - lParams := lPath.Parameters.Value - rParams := rPath.Parameters.Value - lp, rp := extractV3ParametersIntoInterface(lParams, rParams) - checkParameters(lp, rp, changes, pc) - } + // parameters + if !lPath.Parameters.IsEmpty() && !rPath.Parameters.IsEmpty() { + lParams := lPath.Parameters.Value + rParams := rPath.Parameters.Value + lp, rp := extractV3ParametersIntoInterface(lParams, rParams) + checkParameters(lp, rp, changes, pc) + } - if !lPath.Parameters.IsEmpty() && rPath.Parameters.IsEmpty() { - CreateChange(changes, PropertyRemoved, v3.ParametersLabel, - lPath.Parameters.ValueNode, nil, true, lPath.Parameters.Value, - nil) - } - if lPath.Parameters.IsEmpty() && !rPath.Parameters.IsEmpty() { - CreateChange(changes, PropertyAdded, v3.ParametersLabel, - nil, rPath.Parameters.ValueNode, true, nil, - rPath.Parameters.Value) - } + if !lPath.Parameters.IsEmpty() && rPath.Parameters.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.ParametersLabel, + lPath.Parameters.ValueNode, nil, true, lPath.Parameters.Value, + nil) + } + if lPath.Parameters.IsEmpty() && !rPath.Parameters.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.ParametersLabel, + nil, rPath.Parameters.ValueNode, true, nil, + rPath.Parameters.Value) + } - // collect up operations changes. - completedOperations := 0 - for completedOperations < totalOps { - select { - case n := <-opChan: - switch n.label { - case v3.GetLabel: - pc.GetChanges = n.changes - break - case v3.PutLabel: - pc.PutChanges = n.changes - break - case v3.PostLabel: - pc.PostChanges = n.changes - break - case v3.DeleteLabel: - pc.DeleteChanges = n.changes - break - case v3.OptionsLabel: - pc.OptionsChanges = n.changes - break - case v3.HeadLabel: - pc.HeadChanges = n.changes - break - case v3.PatchLabel: - pc.PatchChanges = n.changes - break - case v3.TraceLabel: - pc.TraceChanges = n.changes - break - } - completedOperations++ - } - } - pc.ExtensionChanges = CompareExtensions(lPath.Extensions, rPath.Extensions) + // collect up operations changes. + completedOperations := 0 + for completedOperations < totalOps { + select { + case n := <-opChan: + switch n.label { + case v3.GetLabel: + pc.GetChanges = n.changes + break + case v3.PutLabel: + pc.PutChanges = n.changes + break + case v3.PostLabel: + pc.PostChanges = n.changes + break + case v3.DeleteLabel: + pc.DeleteChanges = n.changes + break + case v3.OptionsLabel: + pc.OptionsChanges = n.changes + break + case v3.HeadLabel: + pc.HeadChanges = n.changes + break + case v3.PatchLabel: + pc.PatchChanges = n.changes + break + case v3.TraceLabel: + pc.TraceChanges = n.changes + break + } + completedOperations++ + } + } + pc.ExtensionChanges = CompareExtensions(lPath.Extensions, rPath.Extensions) } func checkOperation(l, r any, done chan opCheck, method string) { - done <- opCheck{ - label: method, - changes: CompareOperations(l, r), - } + done <- opCheck{ + label: method, + changes: CompareOperations(l, r), + } }