Change indentation to tabs

This commit is contained in:
Hugo Stijns
2022-12-29 11:38:43 +01:00
committed by Dave Shanley
parent d6031b5440
commit b395518a5e
4 changed files with 1352 additions and 1348 deletions

View File

@@ -4,11 +4,12 @@
package model package model
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "reflect"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/datamodel/low/v2" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v3" "github.com/pb33f/libopenapi/datamodel/low/base"
"reflect" 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 // 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 // modifications are not checked, these checks occur in-place by implementing objects as they are autp-resolved
// when the model is built. // when the model is built.
type ComponentsChanges struct { type ComponentsChanges struct {
*PropertyChanges *PropertyChanges
SchemaChanges map[string]*SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"` SchemaChanges map[string]*SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"`
SecuritySchemeChanges map[string]*SecuritySchemeChanges `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"` SecuritySchemeChanges map[string]*SecuritySchemeChanges `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
} }
// CompareComponents will compare OpenAPI components for any changes. Accepts Swagger Definition objects // CompareComponents will compare OpenAPI components for any changes. Accepts Swagger Definition objects
// like ParameterDefinitions or Definitions etc. // like ParameterDefinitions or Definitions etc.
func CompareComponents(l, r any) *ComponentsChanges { func CompareComponents(l, r any) *ComponentsChanges {
var changes []*Change var changes []*Change
cc := new(ComponentsChanges) cc := new(ComponentsChanges)
// Swagger Parameters // Swagger Parameters
if reflect.TypeOf(&v2.ParameterDefinitions{}) == reflect.TypeOf(l) && if reflect.TypeOf(&v2.ParameterDefinitions{}) == reflect.TypeOf(l) &&
reflect.TypeOf(&v2.ParameterDefinitions{}) == reflect.TypeOf(r) { reflect.TypeOf(&v2.ParameterDefinitions{}) == reflect.TypeOf(r) {
lDef := l.(*v2.ParameterDefinitions) lDef := l.(*v2.ParameterDefinitions)
rDef := r.(*v2.ParameterDefinitions) rDef := r.(*v2.ParameterDefinitions)
var a, b map[low.KeyReference[string]]low.ValueReference[*v2.Parameter] var a, b map[low.KeyReference[string]]low.ValueReference[*v2.Parameter]
if lDef != nil { if lDef != nil {
a = lDef.Definitions a = lDef.Definitions
} }
if rDef != nil { if rDef != nil {
b = rDef.Definitions b = rDef.Definitions
} }
CheckMapForAdditionRemoval(a, b, &changes, v3.ParametersLabel) CheckMapForAdditionRemoval(a, b, &changes, v3.ParametersLabel)
} }
// Swagger Responses // Swagger Responses
if reflect.TypeOf(&v2.ResponsesDefinitions{}) == reflect.TypeOf(l) && if reflect.TypeOf(&v2.ResponsesDefinitions{}) == reflect.TypeOf(l) &&
reflect.TypeOf(&v2.ResponsesDefinitions{}) == reflect.TypeOf(r) { reflect.TypeOf(&v2.ResponsesDefinitions{}) == reflect.TypeOf(r) {
lDef := l.(*v2.ResponsesDefinitions) lDef := l.(*v2.ResponsesDefinitions)
rDef := r.(*v2.ResponsesDefinitions) rDef := r.(*v2.ResponsesDefinitions)
var a, b map[low.KeyReference[string]]low.ValueReference[*v2.Response] var a, b map[low.KeyReference[string]]low.ValueReference[*v2.Response]
if lDef != nil { if lDef != nil {
a = lDef.Definitions a = lDef.Definitions
} }
if rDef != nil { if rDef != nil {
b = rDef.Definitions b = rDef.Definitions
} }
CheckMapForAdditionRemoval(a, b, &changes, v3.ResponsesLabel) CheckMapForAdditionRemoval(a, b, &changes, v3.ResponsesLabel)
} }
// Swagger Schemas // Swagger Schemas
if reflect.TypeOf(&v2.Definitions{}) == reflect.TypeOf(l) && if reflect.TypeOf(&v2.Definitions{}) == reflect.TypeOf(l) &&
reflect.TypeOf(&v2.Definitions{}) == reflect.TypeOf(r) { reflect.TypeOf(&v2.Definitions{}) == reflect.TypeOf(r) {
lDef := l.(*v2.Definitions) lDef := l.(*v2.Definitions)
rDef := r.(*v2.Definitions) rDef := r.(*v2.Definitions)
var a, b map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy] var a, b map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy]
if lDef != nil { if lDef != nil {
a = lDef.Schemas a = lDef.Schemas
} }
if rDef != nil { if rDef != nil {
b = rDef.Schemas b = rDef.Schemas
} }
cc.SchemaChanges = CheckMapForChanges(a, b, &changes, v2.DefinitionsLabel, CompareSchemas) cc.SchemaChanges = CheckMapForChanges(a, b, &changes, v2.DefinitionsLabel, CompareSchemas)
} }
// Swagger Security Definitions // Swagger Security Definitions
if reflect.TypeOf(&v2.SecurityDefinitions{}) == reflect.TypeOf(l) && if reflect.TypeOf(&v2.SecurityDefinitions{}) == reflect.TypeOf(l) &&
reflect.TypeOf(&v2.SecurityDefinitions{}) == reflect.TypeOf(r) { reflect.TypeOf(&v2.SecurityDefinitions{}) == reflect.TypeOf(r) {
lDef := l.(*v2.SecurityDefinitions) lDef := l.(*v2.SecurityDefinitions)
rDef := r.(*v2.SecurityDefinitions) rDef := r.(*v2.SecurityDefinitions)
var a, b map[low.KeyReference[string]]low.ValueReference[*v2.SecurityScheme] var a, b map[low.KeyReference[string]]low.ValueReference[*v2.SecurityScheme]
if lDef != nil { if lDef != nil {
a = lDef.Definitions a = lDef.Definitions
} }
if rDef != nil { if rDef != nil {
b = rDef.Definitions b = rDef.Definitions
} }
cc.SecuritySchemeChanges = CheckMapForChanges(a, b, &changes, cc.SecuritySchemeChanges = CheckMapForChanges(a, b, &changes,
v3.SecurityDefinitionLabel, CompareSecuritySchemesV2) v3.SecurityDefinitionLabel, CompareSecuritySchemesV2)
} }
// OpenAPI Components // OpenAPI Components
if reflect.TypeOf(&v3.Components{}) == reflect.TypeOf(l) && if reflect.TypeOf(&v3.Components{}) == reflect.TypeOf(l) &&
reflect.TypeOf(&v3.Components{}) == reflect.TypeOf(r) { reflect.TypeOf(&v3.Components{}) == reflect.TypeOf(r) {
lComponents := l.(*v3.Components) lComponents := l.(*v3.Components)
rComponents := r.(*v3.Components) rComponents := r.(*v3.Components)
//if low.AreEqual(lComponents, rComponents) { //if low.AreEqual(lComponents, rComponents) {
// return nil // return nil
//} //}
doneChan := make(chan componentComparison) doneChan := make(chan componentComparison)
comparisons := 0 comparisons := 0
// run as fast as we can, thread all the things. // run as fast as we can, thread all the things.
if !lComponents.Schemas.IsEmpty() || !rComponents.Schemas.IsEmpty() { if !lComponents.Schemas.IsEmpty() || !rComponents.Schemas.IsEmpty() {
comparisons++ comparisons++
go runComparison(lComponents.Schemas.Value, rComponents.Schemas.Value, go runComparison(lComponents.Schemas.Value, rComponents.Schemas.Value,
&changes, v3.SchemasLabel, CompareSchemas, doneChan) &changes, v3.SchemasLabel, CompareSchemas, doneChan)
} }
if !lComponents.Responses.IsEmpty() || !rComponents.Responses.IsEmpty() { if !lComponents.Responses.IsEmpty() || !rComponents.Responses.IsEmpty() {
comparisons++ comparisons++
go runComparison(lComponents.Responses.Value, rComponents.Responses.Value, go runComparison(lComponents.Responses.Value, rComponents.Responses.Value,
&changes, v3.ResponsesLabel, CompareResponseV3, doneChan) &changes, v3.ResponsesLabel, CompareResponseV3, doneChan)
} }
if !lComponents.Parameters.IsEmpty() || !rComponents.Parameters.IsEmpty() { if !lComponents.Parameters.IsEmpty() || !rComponents.Parameters.IsEmpty() {
comparisons++ comparisons++
go runComparison(lComponents.Parameters.Value, rComponents.Parameters.Value, go runComparison(lComponents.Parameters.Value, rComponents.Parameters.Value,
&changes, v3.ParametersLabel, CompareParametersV3, doneChan) &changes, v3.ParametersLabel, CompareParametersV3, doneChan)
} }
if !lComponents.Examples.IsEmpty() || !rComponents.Examples.IsEmpty() { if !lComponents.Examples.IsEmpty() || !rComponents.Examples.IsEmpty() {
comparisons++ comparisons++
go runComparison(lComponents.Examples.Value, rComponents.Examples.Value, go runComparison(lComponents.Examples.Value, rComponents.Examples.Value,
&changes, v3.ExamplesLabel, CompareExamples, doneChan) &changes, v3.ExamplesLabel, CompareExamples, doneChan)
} }
if !lComponents.RequestBodies.IsEmpty() || !rComponents.RequestBodies.IsEmpty() { if !lComponents.RequestBodies.IsEmpty() || !rComponents.RequestBodies.IsEmpty() {
comparisons++ comparisons++
go runComparison(lComponents.RequestBodies.Value, rComponents.RequestBodies.Value, go runComparison(lComponents.RequestBodies.Value, rComponents.RequestBodies.Value,
&changes, v3.RequestBodiesLabel, CompareRequestBodies, doneChan) &changes, v3.RequestBodiesLabel, CompareRequestBodies, doneChan)
} }
if !lComponents.Headers.IsEmpty() || !rComponents.Headers.IsEmpty() { if !lComponents.Headers.IsEmpty() || !rComponents.Headers.IsEmpty() {
comparisons++ comparisons++
go runComparison(lComponents.Headers.Value, rComponents.Headers.Value, go runComparison(lComponents.Headers.Value, rComponents.Headers.Value,
&changes, v3.HeadersLabel, CompareHeadersV3, doneChan) &changes, v3.HeadersLabel, CompareHeadersV3, doneChan)
} }
if !lComponents.SecuritySchemes.IsEmpty() || !rComponents.SecuritySchemes.IsEmpty() { if !lComponents.SecuritySchemes.IsEmpty() || !rComponents.SecuritySchemes.IsEmpty() {
comparisons++ comparisons++
go runComparison(lComponents.SecuritySchemes.Value, rComponents.SecuritySchemes.Value, go runComparison(lComponents.SecuritySchemes.Value, rComponents.SecuritySchemes.Value,
&changes, v3.SecuritySchemesLabel, CompareSecuritySchemesV3, doneChan) &changes, v3.SecuritySchemesLabel, CompareSecuritySchemesV3, doneChan)
} }
if !lComponents.Links.IsEmpty() || !rComponents.Links.IsEmpty() { if !lComponents.Links.IsEmpty() || !rComponents.Links.IsEmpty() {
comparisons++ comparisons++
go runComparison(lComponents.Links.Value, rComponents.Links.Value, go runComparison(lComponents.Links.Value, rComponents.Links.Value,
&changes, v3.LinksLabel, CompareLinks, doneChan) &changes, v3.LinksLabel, CompareLinks, doneChan)
} }
if !lComponents.Callbacks.IsEmpty() || !rComponents.Callbacks.IsEmpty() { if !lComponents.Callbacks.IsEmpty() || !rComponents.Callbacks.IsEmpty() {
comparisons++ comparisons++
go runComparison(lComponents.Callbacks.Value, rComponents.Callbacks.Value, go runComparison(lComponents.Callbacks.Value, rComponents.Callbacks.Value,
&changes, v3.CallbacksLabel, CompareCallback, doneChan) &changes, v3.CallbacksLabel, CompareCallback, doneChan)
} }
cc.ExtensionChanges = CompareExtensions(lComponents.Extensions, rComponents.Extensions) cc.ExtensionChanges = CompareExtensions(lComponents.Extensions, rComponents.Extensions)
completedComponents := 0 completedComponents := 0
for completedComponents < comparisons { for completedComponents < comparisons {
select { select {
case res := <-doneChan: case res := <-doneChan:
switch res.prop { switch res.prop {
case v3.SchemasLabel: case v3.SchemasLabel:
completedComponents++ completedComponents++
cc.SchemaChanges = res.result.(map[string]*SchemaChanges) cc.SchemaChanges = res.result.(map[string]*SchemaChanges)
break break
case v3.SecuritySchemesLabel: case v3.SecuritySchemesLabel:
completedComponents++ completedComponents++
cc.SecuritySchemeChanges = res.result.(map[string]*SecuritySchemeChanges) cc.SecuritySchemeChanges = res.result.(map[string]*SecuritySchemeChanges)
break break
case v3.ResponsesLabel, v3.ParametersLabel, v3.ExamplesLabel, v3.RequestBodiesLabel, v3.HeadersLabel, case v3.ResponsesLabel, v3.ParametersLabel, v3.ExamplesLabel, v3.RequestBodiesLabel, v3.HeadersLabel,
v3.LinksLabel, v3.CallbacksLabel: v3.LinksLabel, v3.CallbacksLabel:
completedComponents++ completedComponents++
break break
} }
} }
} }
} }
cc.PropertyChanges = NewPropertyChanges(changes) cc.PropertyChanges = NewPropertyChanges(changes)
if cc.TotalChanges() <= 0 { if cc.TotalChanges() <= 0 {
return nil return nil
} }
return cc return cc
} }
type componentComparison struct { type componentComparison struct {
prop string prop string
result any result any
} }
// run a generic comparison in a thread which in turn splits checks into further threads. // 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], 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 // for schemas
if label == v3.SchemasLabel || label == v2.DefinitionsLabel || label == v3.SecuritySchemesLabel { if label == v3.SchemasLabel || label == v2.DefinitionsLabel || label == v3.SecuritySchemesLabel {
doneChan <- componentComparison{ doneChan <- componentComparison{
prop: label, prop: label,
result: CheckMapForChanges(l, r, changes, label, compareFunc), result: CheckMapForChanges(l, r, changes, label, compareFunc),
} }
return return
} else { } else {
doneChan <- componentComparison{ doneChan <- componentComparison{
prop: label, prop: label,
result: CheckMapForAdditionRemoval(l, r, changes, label), result: CheckMapForAdditionRemoval(l, r, changes, label),
} }
} }
} }
// TotalChanges returns total changes for all Components and Definitions // TotalChanges returns total changes for all Components and Definitions
func (c *ComponentsChanges) TotalChanges() int { func (c *ComponentsChanges) TotalChanges() int {
v := c.PropertyChanges.TotalChanges() v := c.PropertyChanges.TotalChanges()
for k := range c.SchemaChanges { for k := range c.SchemaChanges {
v += c.SchemaChanges[k].TotalChanges() v += c.SchemaChanges[k].TotalChanges()
} }
for k := range c.SecuritySchemeChanges { for k := range c.SecuritySchemeChanges {
v += c.SecuritySchemeChanges[k].TotalChanges() v += c.SecuritySchemeChanges[k].TotalChanges()
} }
if c.ExtensionChanges != nil { if c.ExtensionChanges != nil {
v += c.ExtensionChanges.TotalChanges() v += c.ExtensionChanges.TotalChanges()
} }
return v return v
} }
// TotalBreakingChanges returns all breaking changes found for all Components and Definitions // TotalBreakingChanges returns all breaking changes found for all Components and Definitions
func (c *ComponentsChanges) TotalBreakingChanges() int { func (c *ComponentsChanges) TotalBreakingChanges() int {
v := c.PropertyChanges.TotalBreakingChanges() v := c.PropertyChanges.TotalBreakingChanges()
for k := range c.SchemaChanges { for k := range c.SchemaChanges {
v += c.SchemaChanges[k].TotalBreakingChanges() v += c.SchemaChanges[k].TotalBreakingChanges()
} }
for k := range c.SecuritySchemeChanges { for k := range c.SecuritySchemeChanges {
v += c.SecuritySchemeChanges[k].TotalBreakingChanges() v += c.SecuritySchemeChanges[k].TotalBreakingChanges()
} }
return v return v
} }

View File

@@ -11,279 +11,280 @@
package model package model
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "reflect"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/datamodel/low/v2" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v3" "github.com/pb33f/libopenapi/datamodel/low/base"
"reflect" 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. // DocumentChanges represents all the changes made to an OpenAPI document.
type DocumentChanges struct { type DocumentChanges struct {
*PropertyChanges *PropertyChanges
InfoChanges *InfoChanges `json:"info,omitempty" yaml:"info,omitempty"` InfoChanges *InfoChanges `json:"info,omitempty" yaml:"info,omitempty"`
PathsChanges *PathsChanges `json:"paths,omitempty" yaml:"paths,omitempty"` PathsChanges *PathsChanges `json:"paths,omitempty" yaml:"paths,omitempty"`
TagChanges []*TagChanges `json:"tags,omitempty" yaml:"tags,omitempty"` TagChanges []*TagChanges `json:"tags,omitempty" yaml:"tags,omitempty"`
ExternalDocChanges *ExternalDocChanges `json:"externalDoc,omitempty" yaml:"externalDoc,omitempty"` ExternalDocChanges *ExternalDocChanges `json:"externalDoc,omitempty" yaml:"externalDoc,omitempty"`
WebhookChanges map[string]*PathItemChanges `json:"webhooks,omitempty" yaml:"webhooks,omitempty"` WebhookChanges map[string]*PathItemChanges `json:"webhooks,omitempty" yaml:"webhooks,omitempty"`
ServerChanges []*ServerChanges `json:"servers,omitempty" yaml:"servers,omitempty"` ServerChanges []*ServerChanges `json:"servers,omitempty" yaml:"servers,omitempty"`
SecurityRequirementChanges []*SecurityRequirementChanges `json:"securityRequirements,omitempty" yaml:"securityRequirements,omitempty"` SecurityRequirementChanges []*SecurityRequirementChanges `json:"securityRequirements,omitempty" yaml:"securityRequirements,omitempty"`
ComponentsChanges *ComponentsChanges `json:"components,omitempty" yaml:"components,omitempty"` ComponentsChanges *ComponentsChanges `json:"components,omitempty" yaml:"components,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
} }
// TotalChanges returns a total count of all changes made in the Document // TotalChanges returns a total count of all changes made in the Document
func (d *DocumentChanges) TotalChanges() int { func (d *DocumentChanges) TotalChanges() int {
c := d.PropertyChanges.TotalChanges() c := d.PropertyChanges.TotalChanges()
if d.InfoChanges != nil { if d.InfoChanges != nil {
c += d.InfoChanges.TotalChanges() c += d.InfoChanges.TotalChanges()
} }
if d.PathsChanges != nil { if d.PathsChanges != nil {
c += d.PathsChanges.TotalChanges() c += d.PathsChanges.TotalChanges()
} }
for k := range d.TagChanges { for k := range d.TagChanges {
c += d.TagChanges[k].TotalChanges() c += d.TagChanges[k].TotalChanges()
} }
if d.ExternalDocChanges != nil { if d.ExternalDocChanges != nil {
c += d.ExternalDocChanges.TotalChanges() c += d.ExternalDocChanges.TotalChanges()
} }
for k := range d.WebhookChanges { for k := range d.WebhookChanges {
c += d.WebhookChanges[k].TotalChanges() c += d.WebhookChanges[k].TotalChanges()
} }
for k := range d.ServerChanges { for k := range d.ServerChanges {
c += d.ServerChanges[k].TotalChanges() c += d.ServerChanges[k].TotalChanges()
} }
for k := range d.SecurityRequirementChanges { for k := range d.SecurityRequirementChanges {
c += d.SecurityRequirementChanges[k].TotalChanges() c += d.SecurityRequirementChanges[k].TotalChanges()
} }
if d.ComponentsChanges != nil { if d.ComponentsChanges != nil {
c += d.ComponentsChanges.TotalChanges() c += d.ComponentsChanges.TotalChanges()
} }
if d.ExtensionChanges != nil { if d.ExtensionChanges != nil {
c += d.ExtensionChanges.TotalChanges() c += d.ExtensionChanges.TotalChanges()
} }
return c return c
} }
// TotalBreakingChanges returns a total count of all breaking changes made in the Document // TotalBreakingChanges returns a total count of all breaking changes made in the Document
func (d *DocumentChanges) TotalBreakingChanges() int { func (d *DocumentChanges) TotalBreakingChanges() int {
c := d.PropertyChanges.TotalBreakingChanges() c := d.PropertyChanges.TotalBreakingChanges()
if d.InfoChanges != nil { if d.InfoChanges != nil {
c += d.InfoChanges.TotalBreakingChanges() c += d.InfoChanges.TotalBreakingChanges()
} }
if d.PathsChanges != nil { if d.PathsChanges != nil {
c += d.PathsChanges.TotalBreakingChanges() c += d.PathsChanges.TotalBreakingChanges()
} }
for k := range d.TagChanges { for k := range d.TagChanges {
c += d.TagChanges[k].TotalBreakingChanges() c += d.TagChanges[k].TotalBreakingChanges()
} }
if d.ExternalDocChanges != nil { if d.ExternalDocChanges != nil {
c += d.ExternalDocChanges.TotalBreakingChanges() c += d.ExternalDocChanges.TotalBreakingChanges()
} }
for k := range d.WebhookChanges { for k := range d.WebhookChanges {
c += d.WebhookChanges[k].TotalBreakingChanges() c += d.WebhookChanges[k].TotalBreakingChanges()
} }
for k := range d.ServerChanges { for k := range d.ServerChanges {
c += d.ServerChanges[k].TotalBreakingChanges() c += d.ServerChanges[k].TotalBreakingChanges()
} }
for k := range d.SecurityRequirementChanges { for k := range d.SecurityRequirementChanges {
c += d.SecurityRequirementChanges[k].TotalBreakingChanges() c += d.SecurityRequirementChanges[k].TotalBreakingChanges()
} }
if d.ComponentsChanges != nil { if d.ComponentsChanges != nil {
c += d.ComponentsChanges.TotalBreakingChanges() c += d.ComponentsChanges.TotalBreakingChanges()
} }
return c return c
} }
// CompareDocuments will compare any two OpenAPI documents (either Swagger or OpenAPI) and return a pointer to // 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. // DocumentChanges that outlines everything that was found to have changed.
func CompareDocuments(l, r any) *DocumentChanges { func CompareDocuments(l, r any) *DocumentChanges {
var changes []*Change var changes []*Change
var props []*PropertyCheck var props []*PropertyCheck
dc := new(DocumentChanges) dc := new(DocumentChanges)
if reflect.TypeOf(&v2.Swagger{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Swagger{}) == reflect.TypeOf(r) { if reflect.TypeOf(&v2.Swagger{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Swagger{}) == reflect.TypeOf(r) {
lDoc := l.(*v2.Swagger) lDoc := l.(*v2.Swagger)
rDoc := r.(*v2.Swagger) rDoc := r.(*v2.Swagger)
// version // version
addPropertyCheck(&props, lDoc.Swagger.ValueNode, rDoc.Swagger.ValueNode, addPropertyCheck(&props, lDoc.Swagger.ValueNode, rDoc.Swagger.ValueNode,
lDoc.Swagger.Value, rDoc.Swagger.Value, &changes, v3.SwaggerLabel, true) lDoc.Swagger.Value, rDoc.Swagger.Value, &changes, v3.SwaggerLabel, true)
// host // host
addPropertyCheck(&props, lDoc.Host.ValueNode, rDoc.Host.ValueNode, addPropertyCheck(&props, lDoc.Host.ValueNode, rDoc.Host.ValueNode,
lDoc.Host.Value, rDoc.Host.Value, &changes, v3.HostLabel, true) lDoc.Host.Value, rDoc.Host.Value, &changes, v3.HostLabel, true)
// base path // base path
addPropertyCheck(&props, lDoc.BasePath.ValueNode, rDoc.BasePath.ValueNode, addPropertyCheck(&props, lDoc.BasePath.ValueNode, rDoc.BasePath.ValueNode,
lDoc.BasePath.Value, rDoc.BasePath.Value, &changes, v3.BasePathLabel, true) lDoc.BasePath.Value, rDoc.BasePath.Value, &changes, v3.BasePathLabel, true)
// schemes // schemes
if len(lDoc.Schemes.Value) > 0 || len(lDoc.Schemes.Value) > 0 { if len(lDoc.Schemes.Value) > 0 || len(lDoc.Schemes.Value) > 0 {
ExtractStringValueSliceChanges(lDoc.Schemes.Value, rDoc.Schemes.Value, ExtractStringValueSliceChanges(lDoc.Schemes.Value, rDoc.Schemes.Value,
&changes, v3.SchemesLabel, true) &changes, v3.SchemesLabel, true)
} }
// consumes // consumes
if len(lDoc.Consumes.Value) > 0 || len(lDoc.Consumes.Value) > 0 { if len(lDoc.Consumes.Value) > 0 || len(lDoc.Consumes.Value) > 0 {
ExtractStringValueSliceChanges(lDoc.Consumes.Value, rDoc.Consumes.Value, ExtractStringValueSliceChanges(lDoc.Consumes.Value, rDoc.Consumes.Value,
&changes, v3.ConsumesLabel, true) &changes, v3.ConsumesLabel, true)
} }
// produces // produces
if len(lDoc.Produces.Value) > 0 || len(lDoc.Produces.Value) > 0 { if len(lDoc.Produces.Value) > 0 || len(lDoc.Produces.Value) > 0 {
ExtractStringValueSliceChanges(lDoc.Produces.Value, rDoc.Produces.Value, ExtractStringValueSliceChanges(lDoc.Produces.Value, rDoc.Produces.Value,
&changes, v3.ProducesLabel, true) &changes, v3.ProducesLabel, true)
} }
// tags // tags
dc.TagChanges = CompareTags(lDoc.Tags.Value, rDoc.Tags.Value) dc.TagChanges = CompareTags(lDoc.Tags.Value, rDoc.Tags.Value)
// paths // paths
if !lDoc.Paths.IsEmpty() || !rDoc.Paths.IsEmpty() { if !lDoc.Paths.IsEmpty() || !rDoc.Paths.IsEmpty() {
dc.PathsChanges = ComparePaths(lDoc.Paths.Value, rDoc.Paths.Value) dc.PathsChanges = ComparePaths(lDoc.Paths.Value, rDoc.Paths.Value)
} }
// external docs // external docs
compareDocumentExternalDocs(lDoc, rDoc, dc, &changes) compareDocumentExternalDocs(lDoc, rDoc, dc, &changes)
// info // info
compareDocumentInfo(&lDoc.Info, &rDoc.Info, dc, &changes) compareDocumentInfo(&lDoc.Info, &rDoc.Info, dc, &changes)
// security // security
if !lDoc.Security.IsEmpty() || !rDoc.Security.IsEmpty() { if !lDoc.Security.IsEmpty() || !rDoc.Security.IsEmpty() {
checkSecurity(lDoc.Security, rDoc.Security, &changes, dc) checkSecurity(lDoc.Security, rDoc.Security, &changes, dc)
} }
// components / definitions // components / definitions
// swagger (damn you) decided to put all this stuff at the document root, rather than cleanly // 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 // 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. // creating a new set of changes and then morphing them into a single changes object.
cc := new(ComponentsChanges) cc := new(ComponentsChanges)
cc.PropertyChanges = new(PropertyChanges) cc.PropertyChanges = new(PropertyChanges)
if n := CompareComponents(lDoc.Definitions.Value, rDoc.Definitions.Value); n != nil { if n := CompareComponents(lDoc.Definitions.Value, rDoc.Definitions.Value); n != nil {
cc.SchemaChanges = n.SchemaChanges cc.SchemaChanges = n.SchemaChanges
} }
if n := CompareComponents(lDoc.SecurityDefinitions.Value, rDoc.SecurityDefinitions.Value); n != nil { if n := CompareComponents(lDoc.SecurityDefinitions.Value, rDoc.SecurityDefinitions.Value); n != nil {
cc.SecuritySchemeChanges = n.SecuritySchemeChanges cc.SecuritySchemeChanges = n.SecuritySchemeChanges
} }
if n := CompareComponents(lDoc.Parameters.Value, rDoc.Parameters.Value); n != nil { if n := CompareComponents(lDoc.Parameters.Value, rDoc.Parameters.Value); n != nil {
cc.PropertyChanges.Changes = append(cc.PropertyChanges.Changes, n.Changes...) cc.PropertyChanges.Changes = append(cc.PropertyChanges.Changes, n.Changes...)
} }
if n := CompareComponents(lDoc.Responses.Value, rDoc.Responses.Value); n != nil { if n := CompareComponents(lDoc.Responses.Value, rDoc.Responses.Value); n != nil {
cc.Changes = append(cc.Changes, n.Changes...) cc.Changes = append(cc.Changes, n.Changes...)
} }
dc.ExtensionChanges = CompareExtensions(lDoc.Extensions, rDoc.Extensions) dc.ExtensionChanges = CompareExtensions(lDoc.Extensions, rDoc.Extensions)
if cc.TotalChanges() > 0 { if cc.TotalChanges() > 0 {
dc.ComponentsChanges = cc dc.ComponentsChanges = cc
} }
} }
if reflect.TypeOf(&v3.Document{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Document{}) == reflect.TypeOf(r) { if reflect.TypeOf(&v3.Document{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Document{}) == reflect.TypeOf(r) {
lDoc := l.(*v3.Document) lDoc := l.(*v3.Document)
rDoc := r.(*v3.Document) rDoc := r.(*v3.Document)
// version // version
addPropertyCheck(&props, lDoc.Version.ValueNode, rDoc.Version.ValueNode, addPropertyCheck(&props, lDoc.Version.ValueNode, rDoc.Version.ValueNode,
lDoc.Version.Value, rDoc.Version.Value, &changes, v3.OpenAPILabel, true) lDoc.Version.Value, rDoc.Version.Value, &changes, v3.OpenAPILabel, true)
// schema dialect // schema dialect
addPropertyCheck(&props, lDoc.JsonSchemaDialect.ValueNode, rDoc.JsonSchemaDialect.ValueNode, addPropertyCheck(&props, lDoc.JsonSchemaDialect.ValueNode, rDoc.JsonSchemaDialect.ValueNode,
lDoc.JsonSchemaDialect.Value, rDoc.JsonSchemaDialect.Value, &changes, v3.JSONSchemaDialectLabel, true) lDoc.JsonSchemaDialect.Value, rDoc.JsonSchemaDialect.Value, &changes, v3.JSONSchemaDialectLabel, true)
// tags // tags
dc.TagChanges = CompareTags(lDoc.Tags.Value, rDoc.Tags.Value) dc.TagChanges = CompareTags(lDoc.Tags.Value, rDoc.Tags.Value)
// paths // paths
if !lDoc.Paths.IsEmpty() || !rDoc.Paths.IsEmpty() { if !lDoc.Paths.IsEmpty() || !rDoc.Paths.IsEmpty() {
dc.PathsChanges = ComparePaths(lDoc.Paths.Value, rDoc.Paths.Value) dc.PathsChanges = ComparePaths(lDoc.Paths.Value, rDoc.Paths.Value)
} }
// external docs // external docs
compareDocumentExternalDocs(lDoc, rDoc, dc, &changes) compareDocumentExternalDocs(lDoc, rDoc, dc, &changes)
// info // info
compareDocumentInfo(&lDoc.Info, &rDoc.Info, dc, &changes) compareDocumentInfo(&lDoc.Info, &rDoc.Info, dc, &changes)
// security // security
if !lDoc.Security.IsEmpty() || !rDoc.Security.IsEmpty() { if !lDoc.Security.IsEmpty() || !rDoc.Security.IsEmpty() {
checkSecurity(lDoc.Security, rDoc.Security, &changes, dc) checkSecurity(lDoc.Security, rDoc.Security, &changes, dc)
} }
// compare components. // compare components.
if !lDoc.Components.IsEmpty() && !rDoc.Components.IsEmpty() { if !lDoc.Components.IsEmpty() && !rDoc.Components.IsEmpty() {
if n := CompareComponents(lDoc.Components.Value, rDoc.Components.Value); n != nil { if n := CompareComponents(lDoc.Components.Value, rDoc.Components.Value); n != nil {
dc.ComponentsChanges = n dc.ComponentsChanges = n
} }
} }
if !lDoc.Components.IsEmpty() && rDoc.Components.IsEmpty() { if !lDoc.Components.IsEmpty() && rDoc.Components.IsEmpty() {
CreateChange(&changes, PropertyRemoved, v3.ComponentsLabel, CreateChange(&changes, PropertyRemoved, v3.ComponentsLabel,
lDoc.Components.ValueNode, nil, true, lDoc.Components.Value, nil) lDoc.Components.ValueNode, nil, true, lDoc.Components.Value, nil)
} }
if lDoc.Components.IsEmpty() && !rDoc.Components.IsEmpty() { if lDoc.Components.IsEmpty() && !rDoc.Components.IsEmpty() {
CreateChange(&changes, PropertyAdded, v3.ComponentsLabel, CreateChange(&changes, PropertyAdded, v3.ComponentsLabel,
rDoc.Components.ValueNode, nil, false, nil, lDoc.Components.Value) rDoc.Components.ValueNode, nil, false, nil, lDoc.Components.Value)
} }
// compare servers // compare servers
if n := checkServers(lDoc.Servers, rDoc.Servers); n != nil { if n := checkServers(lDoc.Servers, rDoc.Servers); n != nil {
dc.ServerChanges = n dc.ServerChanges = n
} }
// compare webhooks // compare webhooks
dc.WebhookChanges = CheckMapForChanges(lDoc.Webhooks.Value, rDoc.Webhooks.Value, &changes, dc.WebhookChanges = CheckMapForChanges(lDoc.Webhooks.Value, rDoc.Webhooks.Value, &changes,
v3.WebhooksLabel, ComparePathItemsV3) v3.WebhooksLabel, ComparePathItemsV3)
// extensions // extensions
dc.ExtensionChanges = CompareExtensions(lDoc.Extensions, rDoc.Extensions) dc.ExtensionChanges = CompareExtensions(lDoc.Extensions, rDoc.Extensions)
} }
CheckProperties(props) CheckProperties(props)
dc.PropertyChanges = NewPropertyChanges(changes) dc.PropertyChanges = NewPropertyChanges(changes)
if dc.TotalChanges() <= 0 { if dc.TotalChanges() <= 0 {
return nil return nil
} }
return dc return dc
} }
func compareDocumentExternalDocs(l, r low.HasExternalDocs, dc *DocumentChanges, changes *[]*Change) { func compareDocumentExternalDocs(l, r low.HasExternalDocs, dc *DocumentChanges, changes *[]*Change) {
// external docs // external docs
if !l.GetExternalDocs().IsEmpty() && !r.GetExternalDocs().IsEmpty() { if !l.GetExternalDocs().IsEmpty() && !r.GetExternalDocs().IsEmpty() {
lExtDoc := l.GetExternalDocs().Value.(*base.ExternalDoc) lExtDoc := l.GetExternalDocs().Value.(*base.ExternalDoc)
rExtDoc := r.GetExternalDocs().Value.(*base.ExternalDoc) rExtDoc := r.GetExternalDocs().Value.(*base.ExternalDoc)
if !low.AreEqual(lExtDoc, rExtDoc) { if !low.AreEqual(lExtDoc, rExtDoc) {
dc.ExternalDocChanges = CompareExternalDocs(lExtDoc, rExtDoc) dc.ExternalDocChanges = CompareExternalDocs(lExtDoc, rExtDoc)
} }
} }
if l.GetExternalDocs().IsEmpty() && !r.GetExternalDocs().IsEmpty() { if l.GetExternalDocs().IsEmpty() && !r.GetExternalDocs().IsEmpty() {
CreateChange(changes, PropertyAdded, v3.ExternalDocsLabel, CreateChange(changes, PropertyAdded, v3.ExternalDocsLabel,
nil, r.GetExternalDocs().ValueNode, false, nil, nil, r.GetExternalDocs().ValueNode, false, nil,
r.GetExternalDocs().Value) r.GetExternalDocs().Value)
} }
if !l.GetExternalDocs().IsEmpty() && r.GetExternalDocs().IsEmpty() { if !l.GetExternalDocs().IsEmpty() && r.GetExternalDocs().IsEmpty() {
CreateChange(changes, PropertyRemoved, v3.ExternalDocsLabel, CreateChange(changes, PropertyRemoved, v3.ExternalDocsLabel,
l.GetExternalDocs().ValueNode, nil, false, l.GetExternalDocs().Value, l.GetExternalDocs().ValueNode, nil, false, l.GetExternalDocs().Value,
nil) nil)
} }
} }
func compareDocumentInfo(l, r *low.NodeReference[*base.Info], dc *DocumentChanges, changes *[]*Change) { func compareDocumentInfo(l, r *low.NodeReference[*base.Info], dc *DocumentChanges, changes *[]*Change) {
// info // info
if !l.IsEmpty() && !r.IsEmpty() { if !l.IsEmpty() && !r.IsEmpty() {
lInfo := l.Value lInfo := l.Value
rInfo := r.Value rInfo := r.Value
if !low.AreEqual(lInfo, rInfo) { if !low.AreEqual(lInfo, rInfo) {
dc.InfoChanges = CompareInfo(lInfo, rInfo) dc.InfoChanges = CompareInfo(lInfo, rInfo)
} }
} }
if l.IsEmpty() && !r.IsEmpty() { if l.IsEmpty() && !r.IsEmpty() {
CreateChange(changes, PropertyAdded, v3.InfoLabel, CreateChange(changes, PropertyAdded, v3.InfoLabel,
nil, r.ValueNode, false, nil, nil, r.ValueNode, false, nil,
r.Value) r.Value)
} }
if !l.IsEmpty() && r.IsEmpty() { if !l.IsEmpty() && r.IsEmpty() {
CreateChange(changes, PropertyRemoved, v3.InfoLabel, CreateChange(changes, PropertyRemoved, v3.InfoLabel,
l.ValueNode, nil, false, l.Value, l.ValueNode, nil, false, l.Value,
nil) nil)
} }
} }

View File

@@ -4,154 +4,155 @@
package model package model
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "reflect"
"github.com/pb33f/libopenapi/datamodel/low/base" "sort"
"github.com/pb33f/libopenapi/datamodel/low/v2" "strings"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"gopkg.in/yaml.v3" "github.com/pb33f/libopenapi/datamodel/low"
"reflect" "github.com/pb33f/libopenapi/datamodel/low/base"
"sort" v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
"strings" v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"gopkg.in/yaml.v3"
) )
// OperationChanges represent changes made between two Swagger or OpenAPI Operation objects. // OperationChanges represent changes made between two Swagger or OpenAPI Operation objects.
type OperationChanges struct { type OperationChanges struct {
*PropertyChanges *PropertyChanges
ExternalDocChanges *ExternalDocChanges `json:"externalDoc,omitempty" yaml:"externalDoc,omitempty"` ExternalDocChanges *ExternalDocChanges `json:"externalDoc,omitempty" yaml:"externalDoc,omitempty"`
ParameterChanges []*ParameterChanges `json:"parameters,omitempty" yaml:"parameters,omitempty"` ParameterChanges []*ParameterChanges `json:"parameters,omitempty" yaml:"parameters,omitempty"`
ResponsesChanges *ResponsesChanges `json:"responses,omitempty" yaml:"responses,omitempty"` ResponsesChanges *ResponsesChanges `json:"responses,omitempty" yaml:"responses,omitempty"`
SecurityRequirementChanges []*SecurityRequirementChanges `json:"securityRequirements,omitempty" yaml:"securityRequirements,omitempty"` SecurityRequirementChanges []*SecurityRequirementChanges `json:"securityRequirements,omitempty" yaml:"securityRequirements,omitempty"`
// OpenAPI 3+ only changes // OpenAPI 3+ only changes
RequestBodyChanges *RequestBodyChanges `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"` RequestBodyChanges *RequestBodyChanges `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"`
ServerChanges []*ServerChanges `json:"servers,omitempty" yaml:"servers,omitempty"` ServerChanges []*ServerChanges `json:"servers,omitempty" yaml:"servers,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
CallbackChanges map[string]*CallbackChanges `json:"callbacks,omitempty" yaml:"callbacks,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. // TotalChanges returns the total number of changes made between two Swagger or OpenAPI Operation objects.
func (o *OperationChanges) TotalChanges() int { func (o *OperationChanges) TotalChanges() int {
c := o.PropertyChanges.TotalChanges() c := o.PropertyChanges.TotalChanges()
if o.ExternalDocChanges != nil { if o.ExternalDocChanges != nil {
c += o.ExternalDocChanges.TotalChanges() c += o.ExternalDocChanges.TotalChanges()
} }
for k := range o.ParameterChanges { for k := range o.ParameterChanges {
c += o.ParameterChanges[k].TotalChanges() c += o.ParameterChanges[k].TotalChanges()
} }
if o.ResponsesChanges != nil { if o.ResponsesChanges != nil {
c += o.ResponsesChanges.TotalChanges() c += o.ResponsesChanges.TotalChanges()
} }
for k := range o.SecurityRequirementChanges { for k := range o.SecurityRequirementChanges {
c += o.SecurityRequirementChanges[k].TotalChanges() c += o.SecurityRequirementChanges[k].TotalChanges()
} }
if o.RequestBodyChanges != nil { if o.RequestBodyChanges != nil {
c += o.RequestBodyChanges.TotalChanges() c += o.RequestBodyChanges.TotalChanges()
} }
for k := range o.ServerChanges { for k := range o.ServerChanges {
c += o.ServerChanges[k].TotalChanges() c += o.ServerChanges[k].TotalChanges()
} }
for k := range o.CallbackChanges { for k := range o.CallbackChanges {
c += o.CallbackChanges[k].TotalChanges() c += o.CallbackChanges[k].TotalChanges()
} }
if o.ExtensionChanges != nil { if o.ExtensionChanges != nil {
c += o.ExtensionChanges.TotalChanges() c += o.ExtensionChanges.TotalChanges()
} }
return c return c
} }
// TotalBreakingChanges returns the total number of breaking changes made between two Swagger // TotalBreakingChanges returns the total number of breaking changes made between two Swagger
// or OpenAPI Operation objects. // or OpenAPI Operation objects.
func (o *OperationChanges) TotalBreakingChanges() int { func (o *OperationChanges) TotalBreakingChanges() int {
c := o.PropertyChanges.TotalBreakingChanges() c := o.PropertyChanges.TotalBreakingChanges()
if o.ExternalDocChanges != nil { if o.ExternalDocChanges != nil {
c += o.ExternalDocChanges.TotalBreakingChanges() c += o.ExternalDocChanges.TotalBreakingChanges()
} }
for k := range o.ParameterChanges { for k := range o.ParameterChanges {
c += o.ParameterChanges[k].TotalBreakingChanges() c += o.ParameterChanges[k].TotalBreakingChanges()
} }
if o.ResponsesChanges != nil { if o.ResponsesChanges != nil {
c += o.ResponsesChanges.TotalBreakingChanges() c += o.ResponsesChanges.TotalBreakingChanges()
} }
for k := range o.SecurityRequirementChanges { for k := range o.SecurityRequirementChanges {
c += o.SecurityRequirementChanges[k].TotalBreakingChanges() c += o.SecurityRequirementChanges[k].TotalBreakingChanges()
} }
for k := range o.CallbackChanges { for k := range o.CallbackChanges {
c += o.CallbackChanges[k].TotalBreakingChanges() c += o.CallbackChanges[k].TotalBreakingChanges()
} }
if o.RequestBodyChanges != nil { if o.RequestBodyChanges != nil {
c += o.RequestBodyChanges.TotalBreakingChanges() c += o.RequestBodyChanges.TotalBreakingChanges()
} }
for k := range o.ServerChanges { for k := range o.ServerChanges {
c += o.ServerChanges[k].TotalBreakingChanges() c += o.ServerChanges[k].TotalBreakingChanges()
} }
return c return c
} }
// check for properties shared between operations objects. // check for properties shared between operations objects.
func addSharedOperationProperties(left, right low.SharedOperations, changes *[]*Change) []*PropertyCheck { func addSharedOperationProperties(left, right low.SharedOperations, changes *[]*Change) []*PropertyCheck {
var props []*PropertyCheck var props []*PropertyCheck
// tags // tags
if len(left.GetTags().Value) > 0 || len(right.GetTags().Value) > 0 { if len(left.GetTags().Value) > 0 || len(right.GetTags().Value) > 0 {
ExtractStringValueSliceChanges(left.GetTags().Value, right.GetTags().Value, ExtractStringValueSliceChanges(left.GetTags().Value, right.GetTags().Value,
changes, v3.TagsLabel, false) changes, v3.TagsLabel, false)
} }
// summary // summary
addPropertyCheck(&props, left.GetSummary().ValueNode, right.GetSummary().ValueNode, addPropertyCheck(&props, left.GetSummary().ValueNode, right.GetSummary().ValueNode,
left.GetSummary(), right.GetSummary(), changes, v3.SummaryLabel, false) left.GetSummary(), right.GetSummary(), changes, v3.SummaryLabel, false)
// description // description
addPropertyCheck(&props, left.GetDescription().ValueNode, right.GetDescription().ValueNode, addPropertyCheck(&props, left.GetDescription().ValueNode, right.GetDescription().ValueNode,
left.GetDescription(), right.GetDescription(), changes, v3.DescriptionLabel, false) left.GetDescription(), right.GetDescription(), changes, v3.DescriptionLabel, false)
// deprecated // deprecated
addPropertyCheck(&props, left.GetDeprecated().ValueNode, right.GetDeprecated().ValueNode, addPropertyCheck(&props, left.GetDeprecated().ValueNode, right.GetDeprecated().ValueNode,
left.GetDeprecated(), right.GetDeprecated(), changes, v3.DeprecatedLabel, false) left.GetDeprecated(), right.GetDeprecated(), changes, v3.DeprecatedLabel, false)
// operation id // operation id
addPropertyCheck(&props, left.GetOperationId().ValueNode, right.GetOperationId().ValueNode, addPropertyCheck(&props, left.GetOperationId().ValueNode, right.GetOperationId().ValueNode,
left.GetOperationId(), right.GetOperationId(), changes, v3.OperationIdLabel, true) left.GetOperationId(), right.GetOperationId(), changes, v3.OperationIdLabel, true)
return props return props
} }
// check shared objects // check shared objects
func compareSharedOperationObjects(l, r low.SharedOperations, changes *[]*Change, opChanges *OperationChanges) { func compareSharedOperationObjects(l, r low.SharedOperations, changes *[]*Change, opChanges *OperationChanges) {
// external docs // external docs
if !l.GetExternalDocs().IsEmpty() && !r.GetExternalDocs().IsEmpty() { if !l.GetExternalDocs().IsEmpty() && !r.GetExternalDocs().IsEmpty() {
lExtDoc := l.GetExternalDocs().Value.(*base.ExternalDoc) lExtDoc := l.GetExternalDocs().Value.(*base.ExternalDoc)
rExtDoc := r.GetExternalDocs().Value.(*base.ExternalDoc) rExtDoc := r.GetExternalDocs().Value.(*base.ExternalDoc)
if !low.AreEqual(lExtDoc, rExtDoc) { if !low.AreEqual(lExtDoc, rExtDoc) {
opChanges.ExternalDocChanges = CompareExternalDocs(lExtDoc, rExtDoc) opChanges.ExternalDocChanges = CompareExternalDocs(lExtDoc, rExtDoc)
} }
} }
if l.GetExternalDocs().IsEmpty() && !r.GetExternalDocs().IsEmpty() { if l.GetExternalDocs().IsEmpty() && !r.GetExternalDocs().IsEmpty() {
CreateChange(changes, PropertyAdded, v3.ExternalDocsLabel, CreateChange(changes, PropertyAdded, v3.ExternalDocsLabel,
nil, r.GetExternalDocs().ValueNode, false, nil, nil, r.GetExternalDocs().ValueNode, false, nil,
r.GetExternalDocs().Value) r.GetExternalDocs().Value)
} }
if !l.GetExternalDocs().IsEmpty() && r.GetExternalDocs().IsEmpty() { if !l.GetExternalDocs().IsEmpty() && r.GetExternalDocs().IsEmpty() {
CreateChange(changes, PropertyRemoved, v3.ExternalDocsLabel, CreateChange(changes, PropertyRemoved, v3.ExternalDocsLabel,
l.GetExternalDocs().ValueNode, nil, false, l.GetExternalDocs().Value, l.GetExternalDocs().ValueNode, nil, false, l.GetExternalDocs().Value,
nil) nil)
} }
// responses // responses
if !l.GetResponses().IsEmpty() && !r.GetResponses().IsEmpty() { if !l.GetResponses().IsEmpty() && !r.GetResponses().IsEmpty() {
opChanges.ResponsesChanges = CompareResponses(l.GetResponses().Value, r.GetResponses().Value) opChanges.ResponsesChanges = CompareResponses(l.GetResponses().Value, r.GetResponses().Value)
} }
if l.GetResponses().IsEmpty() && !r.GetResponses().IsEmpty() { if l.GetResponses().IsEmpty() && !r.GetResponses().IsEmpty() {
CreateChange(changes, PropertyAdded, v3.ResponsesLabel, CreateChange(changes, PropertyAdded, v3.ResponsesLabel,
nil, r.GetResponses().ValueNode, false, nil, nil, r.GetResponses().ValueNode, false, nil,
r.GetResponses().Value) r.GetResponses().Value)
} }
if !l.GetResponses().IsEmpty() && r.GetResponses().IsEmpty() { if !l.GetResponses().IsEmpty() && r.GetResponses().IsEmpty() {
CreateChange(changes, PropertyRemoved, v3.ResponsesLabel, CreateChange(changes, PropertyRemoved, v3.ResponsesLabel,
l.GetResponses().ValueNode, nil, true, l.GetResponses().Value, l.GetResponses().ValueNode, nil, true, l.GetResponses().Value,
nil) 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. // a pointer to an OperationChanges instance, or nil if nothing is found.
func CompareOperations(l, r any) *OperationChanges { func CompareOperations(l, r any) *OperationChanges {
var changes []*Change var changes []*Change
var props []*PropertyCheck var props []*PropertyCheck
oc := new(OperationChanges) oc := new(OperationChanges)
// Swagger // Swagger
if reflect.TypeOf(&v2.Operation{}) == reflect.TypeOf(l) && if reflect.TypeOf(&v2.Operation{}) == reflect.TypeOf(l) &&
reflect.TypeOf(&v2.Operation{}) == reflect.TypeOf(r) { reflect.TypeOf(&v2.Operation{}) == reflect.TypeOf(r) {
lOperation := l.(*v2.Operation) lOperation := l.(*v2.Operation)
rOperation := r.(*v2.Operation) rOperation := r.(*v2.Operation)
// perform hash check to avoid further processing // perform hash check to avoid further processing
if low.AreEqual(lOperation, rOperation) { if low.AreEqual(lOperation, rOperation) {
return nil 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 // parameters
lParamsUntyped := lOperation.GetParameters() lParamsUntyped := lOperation.GetParameters()
rParamsUntyped := rOperation.GetParameters() rParamsUntyped := rOperation.GetParameters()
if !lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() { if !lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() {
lParams := lParamsUntyped.Value.([]low.ValueReference[*v2.Parameter]) lParams := lParamsUntyped.Value.([]low.ValueReference[*v2.Parameter])
rParams := rParamsUntyped.Value.([]low.ValueReference[*v2.Parameter]) rParams := rParamsUntyped.Value.([]low.ValueReference[*v2.Parameter])
lv := make(map[string]*v2.Parameter, len(lParams)) lv := make(map[string]*v2.Parameter, len(lParams))
rv := make(map[string]*v2.Parameter, len(rParams)) rv := make(map[string]*v2.Parameter, len(rParams))
for i := range lParams { for i := range lParams {
s := lParams[i].Value.Name.Value s := lParams[i].Value.Name.Value
lv[s] = lParams[i].Value lv[s] = lParams[i].Value
} }
for i := range rParams { for i := range rParams {
s := rParams[i].Value.Name.Value s := rParams[i].Value.Name.Value
rv[s] = rParams[i].Value rv[s] = rParams[i].Value
} }
var paramChanges []*ParameterChanges var paramChanges []*ParameterChanges
for n := range lv { for n := range lv {
if _, ok := rv[n]; ok { if _, ok := rv[n]; ok {
if !low.AreEqual(lv[n], rv[n]) { if !low.AreEqual(lv[n], rv[n]) {
ch := CompareParameters(lv[n], rv[n]) ch := CompareParameters(lv[n], rv[n])
if ch != nil { if ch != nil {
paramChanges = append(paramChanges, ch) paramChanges = append(paramChanges, ch)
} }
} }
continue continue
} }
CreateChange(&changes, ObjectRemoved, v3.ParametersLabel, CreateChange(&changes, ObjectRemoved, v3.ParametersLabel,
lv[n].Name.ValueNode, nil, true, lv[n].Name.Value, lv[n].Name.ValueNode, nil, true, lv[n].Name.Value,
nil) nil)
} }
for n := range rv { for n := range rv {
if _, ok := lv[n]; !ok { if _, ok := lv[n]; !ok {
CreateChange(&changes, ObjectAdded, v3.ParametersLabel, CreateChange(&changes, ObjectAdded, v3.ParametersLabel,
nil, rv[n].Name.ValueNode, true, nil, nil, rv[n].Name.ValueNode, true, nil,
rv[n].Name.Value) rv[n].Name.Value)
} }
} }
oc.ParameterChanges = paramChanges oc.ParameterChanges = paramChanges
} }
if !lParamsUntyped.IsEmpty() && rParamsUntyped.IsEmpty() { if !lParamsUntyped.IsEmpty() && rParamsUntyped.IsEmpty() {
CreateChange(&changes, PropertyRemoved, v3.ParametersLabel, CreateChange(&changes, PropertyRemoved, v3.ParametersLabel,
lParamsUntyped.ValueNode, nil, true, lParamsUntyped.Value, lParamsUntyped.ValueNode, nil, true, lParamsUntyped.Value,
nil) nil)
} }
if lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() { if lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() {
CreateChange(&changes, PropertyAdded, v3.ParametersLabel, CreateChange(&changes, PropertyAdded, v3.ParametersLabel,
nil, rParamsUntyped.ValueNode, true, nil, nil, rParamsUntyped.ValueNode, true, nil,
rParamsUntyped.Value) rParamsUntyped.Value)
} }
// security // security
if !lOperation.Security.IsEmpty() || !rOperation.Security.IsEmpty() { if !lOperation.Security.IsEmpty() || !rOperation.Security.IsEmpty() {
checkSecurity(lOperation.Security, rOperation.Security, &changes, oc) checkSecurity(lOperation.Security, rOperation.Security, &changes, oc)
} }
// produces // produces
if len(lOperation.Produces.Value) > 0 || len(rOperation.Produces.Value) > 0 { if len(lOperation.Produces.Value) > 0 || len(rOperation.Produces.Value) > 0 {
ExtractStringValueSliceChanges(lOperation.Produces.Value, rOperation.Produces.Value, ExtractStringValueSliceChanges(lOperation.Produces.Value, rOperation.Produces.Value,
&changes, v3.ProducesLabel, true) &changes, v3.ProducesLabel, true)
} }
// consumes // consumes
if len(lOperation.Consumes.Value) > 0 || len(rOperation.Consumes.Value) > 0 { if len(lOperation.Consumes.Value) > 0 || len(rOperation.Consumes.Value) > 0 {
ExtractStringValueSliceChanges(lOperation.Consumes.Value, rOperation.Consumes.Value, ExtractStringValueSliceChanges(lOperation.Consumes.Value, rOperation.Consumes.Value,
&changes, v3.ConsumesLabel, true) &changes, v3.ConsumesLabel, true)
} }
// schemes // schemes
if len(lOperation.Schemes.Value) > 0 || len(rOperation.Schemes.Value) > 0 { if len(lOperation.Schemes.Value) > 0 || len(rOperation.Schemes.Value) > 0 {
ExtractStringValueSliceChanges(lOperation.Schemes.Value, rOperation.Schemes.Value, ExtractStringValueSliceChanges(lOperation.Schemes.Value, rOperation.Schemes.Value,
&changes, v3.SchemesLabel, true) &changes, v3.SchemesLabel, true)
} }
oc.ExtensionChanges = CompareExtensions(lOperation.Extensions, rOperation.Extensions) oc.ExtensionChanges = CompareExtensions(lOperation.Extensions, rOperation.Extensions)
} }
// OpenAPI // OpenAPI
if reflect.TypeOf(&v3.Operation{}) == reflect.TypeOf(l) && if reflect.TypeOf(&v3.Operation{}) == reflect.TypeOf(l) &&
reflect.TypeOf(&v3.Operation{}) == reflect.TypeOf(r) { reflect.TypeOf(&v3.Operation{}) == reflect.TypeOf(r) {
lOperation := l.(*v3.Operation) lOperation := l.(*v3.Operation)
rOperation := r.(*v3.Operation) rOperation := r.(*v3.Operation)
// perform hash check to avoid further processing // perform hash check to avoid further processing
if low.AreEqual(lOperation, rOperation) { if low.AreEqual(lOperation, rOperation) {
return nil 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 // parameters
lParamsUntyped := lOperation.GetParameters() lParamsUntyped := lOperation.GetParameters()
rParamsUntyped := rOperation.GetParameters() rParamsUntyped := rOperation.GetParameters()
if !lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() { if !lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() {
lParams := lParamsUntyped.Value.([]low.ValueReference[*v3.Parameter]) lParams := lParamsUntyped.Value.([]low.ValueReference[*v3.Parameter])
rParams := rParamsUntyped.Value.([]low.ValueReference[*v3.Parameter]) rParams := rParamsUntyped.Value.([]low.ValueReference[*v3.Parameter])
lv := make(map[string]*v3.Parameter, len(lParams)) lv := make(map[string]*v3.Parameter, len(lParams))
rv := make(map[string]*v3.Parameter, len(rParams)) rv := make(map[string]*v3.Parameter, len(rParams))
for i := range lParams { for i := range lParams {
s := lParams[i].Value.Name.Value s := lParams[i].Value.Name.Value
lv[s] = lParams[i].Value lv[s] = lParams[i].Value
} }
for i := range rParams { for i := range rParams {
s := rParams[i].Value.Name.Value s := rParams[i].Value.Name.Value
rv[s] = rParams[i].Value rv[s] = rParams[i].Value
} }
var paramChanges []*ParameterChanges var paramChanges []*ParameterChanges
for n := range lv { for n := range lv {
if _, ok := rv[n]; ok { if _, ok := rv[n]; ok {
if !low.AreEqual(lv[n], rv[n]) { if !low.AreEqual(lv[n], rv[n]) {
ch := CompareParameters(lv[n], rv[n]) ch := CompareParameters(lv[n], rv[n])
if ch != nil { if ch != nil {
paramChanges = append(paramChanges, ch) paramChanges = append(paramChanges, ch)
} }
} }
continue continue
} }
CreateChange(&changes, ObjectRemoved, v3.ParametersLabel, CreateChange(&changes, ObjectRemoved, v3.ParametersLabel,
lv[n].Name.ValueNode, nil, true, lv[n].Name.Value, lv[n].Name.ValueNode, nil, true, lv[n].Name.Value,
nil) nil)
} }
for n := range rv { for n := range rv {
if _, ok := lv[n]; !ok { if _, ok := lv[n]; !ok {
CreateChange(&changes, ObjectAdded, v3.ParametersLabel, CreateChange(&changes, ObjectAdded, v3.ParametersLabel,
nil, rv[n].Name.ValueNode, true, nil, nil, rv[n].Name.ValueNode, true, nil,
rv[n].Name.Value) rv[n].Name.Value)
} }
} }
oc.ParameterChanges = paramChanges oc.ParameterChanges = paramChanges
} }
if !lParamsUntyped.IsEmpty() && rParamsUntyped.IsEmpty() { if !lParamsUntyped.IsEmpty() && rParamsUntyped.IsEmpty() {
CreateChange(&changes, PropertyRemoved, v3.ParametersLabel, CreateChange(&changes, PropertyRemoved, v3.ParametersLabel,
lParamsUntyped.ValueNode, nil, true, lParamsUntyped.Value, lParamsUntyped.ValueNode, nil, true, lParamsUntyped.Value,
nil) nil)
} }
if lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() { if lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() {
CreateChange(&changes, PropertyAdded, v3.ParametersLabel, CreateChange(&changes, PropertyAdded, v3.ParametersLabel,
nil, rParamsUntyped.ValueNode, true, nil, nil, rParamsUntyped.ValueNode, true, nil,
rParamsUntyped.Value) rParamsUntyped.Value)
} }
// security // security
if !lOperation.Security.IsEmpty() && !lOperation.Security.IsEmpty() { if !lOperation.Security.IsEmpty() && !lOperation.Security.IsEmpty() {
checkSecurity(lOperation.Security, rOperation.Security, &changes, oc) checkSecurity(lOperation.Security, rOperation.Security, &changes, oc)
} }
// request body // request body
if !lOperation.RequestBody.IsEmpty() && !rOperation.RequestBody.IsEmpty() { if !lOperation.RequestBody.IsEmpty() && !rOperation.RequestBody.IsEmpty() {
if !low.AreEqual(lOperation.RequestBody.Value, rOperation.RequestBody.Value) { if !low.AreEqual(lOperation.RequestBody.Value, rOperation.RequestBody.Value) {
oc.RequestBodyChanges = CompareRequestBodies(lOperation.RequestBody.Value, rOperation.RequestBody.Value) oc.RequestBodyChanges = CompareRequestBodies(lOperation.RequestBody.Value, rOperation.RequestBody.Value)
} }
} }
if !lOperation.RequestBody.IsEmpty() && rOperation.RequestBody.IsEmpty() { if !lOperation.RequestBody.IsEmpty() && rOperation.RequestBody.IsEmpty() {
CreateChange(&changes, PropertyRemoved, v3.RequestBodyLabel, CreateChange(&changes, PropertyRemoved, v3.RequestBodyLabel,
lOperation.RequestBody.ValueNode, nil, true, lOperation.RequestBody.Value, lOperation.RequestBody.ValueNode, nil, true, lOperation.RequestBody.Value,
nil) nil)
} }
if lOperation.RequestBody.IsEmpty() && !rOperation.RequestBody.IsEmpty() { if lOperation.RequestBody.IsEmpty() && !rOperation.RequestBody.IsEmpty() {
CreateChange(&changes, PropertyAdded, v3.RequestBodyLabel, CreateChange(&changes, PropertyAdded, v3.RequestBodyLabel,
nil, rOperation.RequestBody.ValueNode, true, nil, nil, rOperation.RequestBody.ValueNode, true, nil,
rOperation.RequestBody.Value) rOperation.RequestBody.Value)
} }
// callbacks // callbacks
if !lOperation.GetCallbacks().IsEmpty() && !rOperation.GetCallbacks().IsEmpty() { if !lOperation.GetCallbacks().IsEmpty() && !rOperation.GetCallbacks().IsEmpty() {
oc.CallbackChanges = CheckMapForChanges(lOperation.Callbacks.Value, rOperation.Callbacks.Value, &changes, oc.CallbackChanges = CheckMapForChanges(lOperation.Callbacks.Value, rOperation.Callbacks.Value, &changes,
v3.CallbacksLabel, CompareCallback) v3.CallbacksLabel, CompareCallback)
} }
if !lOperation.GetCallbacks().IsEmpty() && rOperation.GetCallbacks().IsEmpty() { if !lOperation.GetCallbacks().IsEmpty() && rOperation.GetCallbacks().IsEmpty() {
CreateChange(&changes, PropertyRemoved, v3.CallbacksLabel, CreateChange(&changes, PropertyRemoved, v3.CallbacksLabel,
lOperation.Callbacks.ValueNode, nil, true, lOperation.Callbacks.Value, lOperation.Callbacks.ValueNode, nil, true, lOperation.Callbacks.Value,
nil) nil)
} }
if lOperation.Callbacks.IsEmpty() && !rOperation.Callbacks.IsEmpty() { if lOperation.Callbacks.IsEmpty() && !rOperation.Callbacks.IsEmpty() {
CreateChange(&changes, PropertyAdded, v3.CallbacksLabel, CreateChange(&changes, PropertyAdded, v3.CallbacksLabel,
nil, rOperation.Callbacks.ValueNode, false, nil, nil, rOperation.Callbacks.ValueNode, false, nil,
rOperation.Callbacks.Value) rOperation.Callbacks.Value)
} }
// servers // servers
oc.ServerChanges = checkServers(lOperation.Servers, rOperation.Servers) oc.ServerChanges = checkServers(lOperation.Servers, rOperation.Servers)
oc.ExtensionChanges = CompareExtensions(lOperation.Extensions, rOperation.Extensions) oc.ExtensionChanges = CompareExtensions(lOperation.Extensions, rOperation.Extensions)
// todo: callbacks // todo: callbacks
} }
CheckProperties(props) CheckProperties(props)
oc.PropertyChanges = NewPropertyChanges(changes) oc.PropertyChanges = NewPropertyChanges(changes)
return oc return oc
} }
// check servers property // check servers property
func checkServers(lServers, rServers low.NodeReference[[]low.ValueReference[*v3.Server]]) []*ServerChanges { 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)) lv := make(map[string]low.ValueReference[*v3.Server], len(lServers.Value))
rv := make(map[string]low.ValueReference[*v3.Server], len(rServers.Value)) rv := make(map[string]low.ValueReference[*v3.Server], len(rServers.Value))
for i := range lServers.Value { for i := range lServers.Value {
var s string var s string
if !lServers.Value[i].Value.URL.IsEmpty() { if !lServers.Value[i].Value.URL.IsEmpty() {
s = lServers.Value[i].Value.URL.Value s = lServers.Value[i].Value.URL.Value
} else { } else {
s = low.GenerateHashString(lServers.Value[i].Value) s = low.GenerateHashString(lServers.Value[i].Value)
} }
lv[s] = lServers.Value[i] lv[s] = lServers.Value[i]
} }
for i := range rServers.Value { for i := range rServers.Value {
var s string var s string
if !rServers.Value[i].Value.URL.IsEmpty() { if !rServers.Value[i].Value.URL.IsEmpty() {
s = rServers.Value[i].Value.URL.Value s = rServers.Value[i].Value.URL.Value
} else { } else {
s = low.GenerateHashString(rServers.Value[i].Value) s = low.GenerateHashString(rServers.Value[i].Value)
} }
rv[s] = rServers.Value[i] 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 _, ok := rv[k]; ok {
if !low.AreEqual(lv[k].Value, rv[k].Value) { if !low.AreEqual(lv[k].Value, rv[k].Value) {
serverChanges = append(serverChanges, CompareServers(lv[k].Value, rv[k].Value)) serverChanges = append(serverChanges, CompareServers(lv[k].Value, rv[k].Value))
} }
continue continue
} }
lv[k].ValueNode.Value = lv[k].Value.URL.Value lv[k].ValueNode.Value = lv[k].Value.URL.Value
CreateChange(&changes, ObjectRemoved, v3.ServersLabel, CreateChange(&changes, ObjectRemoved, v3.ServersLabel,
lv[k].ValueNode, nil, true, lv[k].Value.URL.Value, lv[k].ValueNode, nil, true, lv[k].Value.URL.Value,
nil) nil)
sc := new(ServerChanges) sc := new(ServerChanges)
sc.PropertyChanges = NewPropertyChanges(changes) sc.PropertyChanges = NewPropertyChanges(changes)
serverChanges = append(serverChanges, sc) 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 var changes []*Change
rv[k].ValueNode.Value = rv[k].Value.URL.Value rv[k].ValueNode.Value = rv[k].Value.URL.Value
CreateChange(&changes, ObjectAdded, v3.ServersLabel, CreateChange(&changes, ObjectAdded, v3.ServersLabel,
nil, rv[k].ValueNode, false, nil, nil, rv[k].ValueNode, false, nil,
rv[k].Value.URL.Value) rv[k].Value.URL.Value)
sc := new(ServerChanges) sc := new(ServerChanges)
sc.PropertyChanges = NewPropertyChanges(changes) sc.PropertyChanges = NewPropertyChanges(changes)
serverChanges = append(serverChanges, sc) serverChanges = append(serverChanges, sc)
} }
} }
} }
var changes []*Change var changes []*Change
sc := new(ServerChanges) sc := new(ServerChanges)
if !lServers.IsEmpty() && rServers.IsEmpty() { if !lServers.IsEmpty() && rServers.IsEmpty() {
CreateChange(&changes, PropertyRemoved, v3.ServersLabel, CreateChange(&changes, PropertyRemoved, v3.ServersLabel,
lServers.ValueNode, nil, true, lServers.Value, lServers.ValueNode, nil, true, lServers.Value,
nil) nil)
} }
if lServers.IsEmpty() && !rServers.IsEmpty() { if lServers.IsEmpty() && !rServers.IsEmpty() {
CreateChange(&changes, PropertyAdded, v3.ServersLabel, CreateChange(&changes, PropertyAdded, v3.ServersLabel,
nil, rServers.ValueNode, false, nil, nil, rServers.ValueNode, false, nil,
rServers.Value) rServers.Value)
} }
sc.PropertyChanges = NewPropertyChanges(changes) sc.PropertyChanges = NewPropertyChanges(changes)
if len(changes) > 0 { if len(changes) > 0 {
serverChanges = append(serverChanges, sc) serverChanges = append(serverChanges, sc)
} }
if len(serverChanges) <= 0 { if len(serverChanges) <= 0 {
return nil return nil
} }
return serverChanges return serverChanges
} }
// check security property. // check security property.
func checkSecurity(lSecurity, rSecurity low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]], 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)) lv := make(map[string]*base.SecurityRequirement, len(lSecurity.Value))
rv := make(map[string]*base.SecurityRequirement, len(rSecurity.Value)) rv := make(map[string]*base.SecurityRequirement, len(rSecurity.Value))
lvn := make(map[string]*yaml.Node, len(lSecurity.Value)) lvn := make(map[string]*yaml.Node, len(lSecurity.Value))
rvn := make(map[string]*yaml.Node, len(rSecurity.Value)) rvn := make(map[string]*yaml.Node, len(rSecurity.Value))
for i := range lSecurity.Value { for i := range lSecurity.Value {
keys := lSecurity.Value[i].Value.GetKeys() keys := lSecurity.Value[i].Value.GetKeys()
sort.Strings(keys) sort.Strings(keys)
s := strings.Join(keys, "|") s := strings.Join(keys, "|")
lv[s] = lSecurity.Value[i].Value lv[s] = lSecurity.Value[i].Value
lvn[s] = lSecurity.Value[i].ValueNode lvn[s] = lSecurity.Value[i].ValueNode
} }
for i := range rSecurity.Value { for i := range rSecurity.Value {
keys := rSecurity.Value[i].Value.GetKeys() keys := rSecurity.Value[i].Value.GetKeys()
sort.Strings(keys) sort.Strings(keys)
s := strings.Join(keys, "|") s := strings.Join(keys, "|")
rv[s] = rSecurity.Value[i].Value rv[s] = rSecurity.Value[i].Value
rvn[s] = rSecurity.Value[i].ValueNode rvn[s] = rSecurity.Value[i].ValueNode
} }
var secChanges []*SecurityRequirementChanges var secChanges []*SecurityRequirementChanges
for n := range lv { for n := range lv {
if _, ok := rv[n]; ok { if _, ok := rv[n]; ok {
if !low.AreEqual(lv[n], rv[n]) { if !low.AreEqual(lv[n], rv[n]) {
ch := CompareSecurityRequirement(lv[n], rv[n]) ch := CompareSecurityRequirement(lv[n], rv[n])
if ch != nil { if ch != nil {
secChanges = append(secChanges, ch) secChanges = append(secChanges, ch)
} }
} }
continue continue
} }
lvn[n].Value = strings.Join(lv[n].GetKeys(), ", ") lvn[n].Value = strings.Join(lv[n].GetKeys(), ", ")
CreateChange(changes, ObjectRemoved, v3.SecurityLabel, CreateChange(changes, ObjectRemoved, v3.SecurityLabel,
lvn[n], nil, true, lv[n], lvn[n], nil, true, lv[n],
nil) nil)
} }
for n := range rv { for n := range rv {
if _, ok := lv[n]; !ok { if _, ok := lv[n]; !ok {
rvn[n].Value = strings.Join(rv[n].GetKeys(), ", ") rvn[n].Value = strings.Join(rv[n].GetKeys(), ", ")
CreateChange(changes, ObjectAdded, v3.SecurityLabel, CreateChange(changes, ObjectAdded, v3.SecurityLabel,
nil, rvn[n], false, nil, nil, rvn[n], false, nil,
rv[n]) rv[n])
} }
} }
// handle different change types. // handle different change types.
if reflect.TypeOf(&OperationChanges{}) == reflect.TypeOf(oc) { if reflect.TypeOf(&OperationChanges{}) == reflect.TypeOf(oc) {
oc.(*OperationChanges).SecurityRequirementChanges = secChanges oc.(*OperationChanges).SecurityRequirementChanges = secChanges
} }
if reflect.TypeOf(&DocumentChanges{}) == reflect.TypeOf(oc) { if reflect.TypeOf(&DocumentChanges{}) == reflect.TypeOf(oc) {
oc.(*DocumentChanges).SecurityRequirementChanges = secChanges oc.(*DocumentChanges).SecurityRequirementChanges = secChanges
} }
} }

File diff suppressed because it is too large Load Diff