diff --git a/what-changed/model/comparison_functions.go b/what-changed/model/comparison_functions.go index 78d8b1f..6095d15 100644 --- a/what-changed/model/comparison_functions.go +++ b/what-changed/model/comparison_functions.go @@ -4,82 +4,83 @@ package model import ( - "fmt" - "github.com/pb33f/libopenapi/datamodel/low" - "gopkg.in/yaml.v3" - "strings" - "sync" + "fmt" + "strings" + "sync" + + "github.com/pb33f/libopenapi/datamodel/low" + "gopkg.in/yaml.v3" ) const ( - HashPh = "%x" - EMPTY_STR = "" + HashPh = "%x" + EMPTY_STR = "" ) // CreateChange is a generic function that will create a Change of type T, populate all properties if set, and then // add a pointer to Change[T] in the slice of Change pointers provided func CreateChange(changes *[]*Change, changeType int, property string, leftValueNode, rightValueNode *yaml.Node, - breaking bool, originalObject, newObject any) *[]*Change { + breaking bool, originalObject, newObject any) *[]*Change { - // create a new context for the left and right nodes. - ctx := CreateContext(leftValueNode, rightValueNode) - c := &Change{ - Context: ctx, - ChangeType: changeType, - Property: property, - Breaking: breaking, - } - // if the left is not nil, we have an original value - if leftValueNode != nil && leftValueNode.Value != "" { - c.Original = leftValueNode.Value - } - // if the right is not nil, then we have a new value - if rightValueNode != nil && rightValueNode.Value != "" { - c.New = rightValueNode.Value - } - // original and new objects - c.OriginalObject = originalObject - c.NewObject = newObject + // create a new context for the left and right nodes. + ctx := CreateContext(leftValueNode, rightValueNode) + c := &Change{ + Context: ctx, + ChangeType: changeType, + Property: property, + Breaking: breaking, + } + // if the left is not nil, we have an original value + if leftValueNode != nil && leftValueNode.Value != "" { + c.Original = leftValueNode.Value + } + // if the right is not nil, then we have a new value + if rightValueNode != nil && rightValueNode.Value != "" { + c.New = rightValueNode.Value + } + // original and new objects + c.OriginalObject = originalObject + c.NewObject = newObject - // add the change to supplied changes slice - *changes = append(*changes, c) - return changes + // add the change to supplied changes slice + *changes = append(*changes, c) + return changes } // CreateContext will return a pointer to a ChangeContext containing the original and new line and column numbers // of the left and right value nodes. func CreateContext(l, r *yaml.Node) *ChangeContext { - ctx := new(ChangeContext) - if l != nil { - ctx.OriginalLine = &l.Line - ctx.OriginalColumn = &l.Column - } - if r != nil { - ctx.NewLine = &r.Line - ctx.NewColumn = &r.Column - } - return ctx + ctx := new(ChangeContext) + if l != nil { + ctx.OriginalLine = &l.Line + ctx.OriginalColumn = &l.Column + } + if r != nil { + ctx.NewLine = &r.Line + ctx.NewColumn = &r.Column + } + return ctx } func FlattenLowLevelMap[T any]( - lowMap map[low.KeyReference[string]]low.ValueReference[T]) map[string]*low.ValueReference[T] { - flat := make(map[string]*low.ValueReference[T]) - for i := range lowMap { - l := lowMap[i] - flat[i.Value] = &l - } - return flat + lowMap map[low.KeyReference[string]]low.ValueReference[T]) map[string]*low.ValueReference[T] { + flat := make(map[string]*low.ValueReference[T]) + for i := range lowMap { + l := lowMap[i] + flat[i.Value] = &l + } + return flat } // CountBreakingChanges counts the number of changes in a slice that are breaking func CountBreakingChanges(changes []*Change) int { - b := 0 - for i := range changes { - if changes[i].Breaking { - b++ - } - } - return b + b := 0 + for i := range changes { + if changes[i].Breaking { + b++ + } + } + return b } // CheckForObjectAdditionOrRemoval will check for the addition or removal of an object from left and right maps. @@ -90,76 +91,77 @@ func CountBreakingChanges(changes []*Change) int { // properties like descriptions, summaries and other non-binding values, so a breakingRemove value can be tuned for // these circumstances. func CheckForObjectAdditionOrRemoval[T any](l, r map[string]*low.ValueReference[T], label string, changes *[]*Change, - breakingAdd, breakingRemove bool) { - var left, right T - if CheckSpecificObjectRemoved(l, r, label) { - left = l[label].GetValue() - CreateChange(changes, ObjectRemoved, label, l[label].GetValueNode(), nil, - breakingRemove, left, nil) - } - if CheckSpecificObjectAdded(l, r, label) { - right = r[label].GetValue() - CreateChange(changes, ObjectAdded, label, nil, r[label].GetValueNode(), - breakingAdd, nil, right) - } + breakingAdd, breakingRemove bool) { + var left, right T + if CheckSpecificObjectRemoved(l, r, label) { + left = l[label].GetValue() + CreateChange(changes, ObjectRemoved, label, l[label].GetValueNode(), nil, + breakingRemove, left, nil) + } + if CheckSpecificObjectAdded(l, r, label) { + right = r[label].GetValue() + CreateChange(changes, ObjectAdded, label, nil, r[label].GetValueNode(), + breakingAdd, nil, right) + } } // CheckSpecificObjectRemoved returns true if a specific value is not in both maps. func CheckSpecificObjectRemoved[T any](l, r map[string]*T, label string) bool { - return l[label] != nil && r[label] == nil + return l[label] != nil && r[label] == nil } // CheckSpecificObjectAdded returns true if a specific value is not in both maps. func CheckSpecificObjectAdded[T any](l, r map[string]*T, label string) bool { - return l[label] == nil && r[label] != nil + return l[label] == nil && r[label] != nil } // CheckProperties will iterate through a slice of PropertyCheck pointers of type T. The method is a convenience method // for running checks on the following methods in order: -// CheckPropertyAdditionOrRemoval -// CheckForModification +// +// CheckPropertyAdditionOrRemoval +// CheckForModification func CheckProperties(properties []*PropertyCheck) { - // todo: make this async to really speed things up. - for _, n := range properties { - CheckPropertyAdditionOrRemoval(n.LeftNode, n.RightNode, n.Label, n.Changes, n.Breaking, n.Original, n.New) - CheckForModification(n.LeftNode, n.RightNode, n.Label, n.Changes, n.Breaking, n.Original, n.New) - } + // todo: make this async to really speed things up. + for _, n := range properties { + CheckPropertyAdditionOrRemoval(n.LeftNode, n.RightNode, n.Label, n.Changes, n.Breaking, n.Original, n.New) + CheckForModification(n.LeftNode, n.RightNode, n.Label, n.Changes, n.Breaking, n.Original, n.New) + } } // CheckPropertyAdditionOrRemoval will run both CheckForRemoval (first) and CheckForAddition (second) func CheckPropertyAdditionOrRemoval[T any](l, r *yaml.Node, - label string, changes *[]*Change, breaking bool, orig, new T) { - CheckForRemoval[T](l, r, label, changes, breaking, orig, new) - CheckForAddition[T](l, r, label, changes, breaking, orig, new) + label string, changes *[]*Change, breaking bool, orig, new T) { + CheckForRemoval[T](l, r, label, changes, breaking, orig, new) + CheckForAddition[T](l, r, label, changes, breaking, orig, new) } // CheckForRemoval will check left and right yaml.Node instances for changes. Anything that is found missing on the // right, but present on the left, is considered a removal. A new Change[T] will be created with the type // -// PropertyRemoved +// PropertyRemoved // // The Change is then added to the slice of []Change[T] instances provided as a pointer. func CheckForRemoval[T any](l, r *yaml.Node, label string, changes *[]*Change, breaking bool, orig, new T) { - if l != nil && l.Value != "" && (r == nil || r.Value == "") { - CreateChange(changes, PropertyRemoved, label, l, r, breaking, orig, new) - return - } - if l != nil && r == nil { - CreateChange(changes, PropertyRemoved, label, l, nil, breaking, orig, nil) - } + if l != nil && l.Value != "" && (r == nil || r.Value == "") { + CreateChange(changes, PropertyRemoved, label, l, r, breaking, orig, new) + return + } + if l != nil && r == nil { + CreateChange(changes, PropertyRemoved, label, l, nil, breaking, orig, nil) + } } // CheckForAddition will check left and right yaml.Node instances for changes. Anything that is found missing on the // left, but present on the left, is considered an addition. A new Change[T] will be created with the type // -// PropertyAdded +// PropertyAdded // // The Change is then added to the slice of []Change[T] instances provided as a pointer. func CheckForAddition[T any](l, r *yaml.Node, label string, changes *[]*Change, breaking bool, orig, new T) { - if (l == nil || l.Value == "") && r != nil && r.Value != "" { - CreateChange(changes, PropertyAdded, label, l, r, breaking, orig, new) - } + if (l == nil || l.Value == "") && r != nil && r.Value != "" { + CreateChange(changes, PropertyAdded, label, l, r, breaking, orig, new) + } } // CheckForModification will check left and right yaml.Node instances for changes. Anything that is found in both @@ -169,31 +171,31 @@ func CheckForAddition[T any](l, r *yaml.Node, label string, changes *[]*Change, // // The Change is then added to the slice of []Change[T] instances provided as a pointer. func CheckForModification[T any](l, r *yaml.Node, label string, changes *[]*Change, breaking bool, orig, new T) { - if l != nil && l.Value != "" && r != nil && r.Value != "" && r.Value != l.Value && r.Tag == l.Tag { - CreateChange(changes, Modified, label, l, r, breaking, orig, new) - return - } - // the values may have not changed, but the tag (node type) type may have - if l != nil && l.Value != "" && r != nil && r.Value != "" && r.Value != l.Value && r.Tag != l.Tag { - CreateChange(changes, Modified, label, l, r, breaking, orig, new) - return - } + if l != nil && l.Value != "" && r != nil && r.Value != "" && r.Value != l.Value && r.Tag == l.Tag { + CreateChange(changes, Modified, label, l, r, breaking, orig, new) + return + } + // the values may have not changed, but the tag (node type) type may have + if l != nil && l.Value != "" && r != nil && r.Value != "" && r.Value != l.Value && r.Tag != l.Tag { + CreateChange(changes, Modified, label, l, r, breaking, orig, new) + return + } } // CheckMapForChanges checks a left and right low level map for any additions, subtractions or modifications to // values. The compareFunc argument should reference the correct comparison function for the generic type. func CheckMapForChanges[T any, R any](expLeft, expRight map[low.KeyReference[string]]low.ValueReference[T], - changes *[]*Change, label string, compareFunc func(l, r T) R) map[string]R { - return CheckMapForChangesWithComp(expLeft, expRight, changes, label, compareFunc, true) + changes *[]*Change, label string, compareFunc func(l, r T) R) map[string]R { + return CheckMapForChangesWithComp(expLeft, expRight, changes, label, compareFunc, true) } func CheckMapForAdditionRemoval[T any](expLeft, expRight map[low.KeyReference[string]]low.ValueReference[T], - changes *[]*Change, label string) any { - // do nothing - doNothing := func(l, r T) any { - return nil - } - return CheckMapForChangesWithComp(expLeft, expRight, changes, label, doNothing, false) + changes *[]*Change, label string) any { + // do nothing + doNothing := func(l, r T) any { + return nil + } + return CheckMapForChangesWithComp(expLeft, expRight, changes, label, doNothing, false) } //// CheckMapForAdditionRemoval checks a left and right low level map for any additions or subtractions, but not modifications @@ -211,168 +213,168 @@ func CheckMapForAdditionRemoval[T any](expLeft, expRight map[low.KeyReference[st // values. The compareFunc argument should reference the correct comparison function for the generic type. The compare // bit determines if the comparison should be run or not. func CheckMapForChangesWithComp[T any, R any](expLeft, expRight map[low.KeyReference[string]]low.ValueReference[T], - changes *[]*Change, label string, compareFunc func(l, r T) R, compare bool) map[string]R { + changes *[]*Change, label string, compareFunc func(l, r T) R, compare bool) map[string]R { - // stop concurrent threads screwing up changes. - var chLock sync.Mutex + // stop concurrent threads screwing up changes. + var chLock sync.Mutex - lHashes := make(map[string]string) - rHashes := make(map[string]string) - lValues := make(map[string]low.ValueReference[T]) - rValues := make(map[string]low.ValueReference[T]) + lHashes := make(map[string]string) + rHashes := make(map[string]string) + lValues := make(map[string]low.ValueReference[T]) + rValues := make(map[string]low.ValueReference[T]) - for k := range expLeft { - lHashes[k.Value] = low.GenerateHashString(expLeft[k].Value) - lValues[k.Value] = expLeft[k] - } + for k := range expLeft { + lHashes[k.Value] = low.GenerateHashString(expLeft[k].Value) + lValues[k.Value] = expLeft[k] + } - for k := range expRight { - rHashes[k.Value] = low.GenerateHashString(expRight[k].Value) - rValues[k.Value] = expRight[k] - } + for k := range expRight { + rHashes[k.Value] = low.GenerateHashString(expRight[k].Value) + rValues[k.Value] = expRight[k] + } - expChanges := make(map[string]R) + expChanges := make(map[string]R) - checkLeft := func(k string, doneChan chan bool, f, g map[string]string, p, h map[string]low.ValueReference[T]) { - rhash := g[k] - if rhash == "" { - if p[k].GetValueNode().Value == "" { - p[k].GetValueNode().Value = k - } - chLock.Lock() - CreateChange(changes, ObjectRemoved, label, - p[k].GetValueNode(), nil, true, - p[k].GetValue(), nil) - chLock.Unlock() - doneChan <- true - return - } - if f[k] == g[k] { - doneChan <- true - return - } - // run comparison. - if compare { - chLock.Lock() - expChanges[k] = compareFunc(p[k].Value, h[k].Value) - chLock.Unlock() - } - doneChan <- true - } + checkLeft := func(k string, doneChan chan bool, f, g map[string]string, p, h map[string]low.ValueReference[T]) { + rhash := g[k] + if rhash == "" { + if p[k].GetValueNode().Value == "" { + p[k].GetValueNode().Value = k + } + chLock.Lock() + CreateChange(changes, ObjectRemoved, label, + p[k].GetValueNode(), nil, true, + p[k].GetValue(), nil) + chLock.Unlock() + doneChan <- true + return + } + if f[k] == g[k] { + doneChan <- true + return + } + // run comparison. + if compare { + chLock.Lock() + expChanges[k] = compareFunc(p[k].Value, h[k].Value) + chLock.Unlock() + } + doneChan <- true + } - doneChan := make(chan bool) - count := 0 + doneChan := make(chan bool) + count := 0 - // check left example hashes - for k := range lHashes { - count++ - go checkLeft(k, doneChan, lHashes, rHashes, lValues, rValues) - } + // check left example hashes + for k := range lHashes { + count++ + go checkLeft(k, doneChan, lHashes, rHashes, lValues, rValues) + } - //check right example hashes - for k := range rHashes { - count++ - go checkRightValue(k, doneChan, lHashes, rValues, changes, label, &chLock) - } + //check right example hashes + for k := range rHashes { + count++ + go checkRightValue(k, doneChan, lHashes, rValues, changes, label, &chLock) + } - // wait for all done signals. - completed := 0 - for completed < count { - select { - case <-doneChan: - completed++ - } - } - return expChanges + // wait for all done signals. + completed := 0 + for completed < count { + select { + case <-doneChan: + completed++ + } + } + return expChanges } func checkRightValue[T any](k string, doneChan chan bool, f map[string]string, p map[string]low.ValueReference[T], - changes *[]*Change, label string, lock *sync.Mutex) { + changes *[]*Change, label string, lock *sync.Mutex) { - lhash := f[k] - if lhash == "" { - if p[k].GetValueNode().Value == "" { - p[k].GetValueNode().Value = k // this is kinda dirty, but I don't want to duplicate code so sue me. - } - lock.Lock() - CreateChange(changes, ObjectAdded, label, - nil, p[k].GetValueNode(), false, - nil, p[k].GetValue()) - lock.Unlock() - } - doneChan <- true + lhash := f[k] + if lhash == "" { + if p[k].GetValueNode().Value == "" { + p[k].GetValueNode().Value = k // this is kinda dirty, but I don't want to duplicate code so sue me. + } + lock.Lock() + CreateChange(changes, ObjectAdded, label, + nil, p[k].GetValueNode(), false, + nil, p[k].GetValue()) + lock.Unlock() + } + doneChan <- true } // ExtractStringValueSliceChanges will compare two low level string slices for changes. func ExtractStringValueSliceChanges(lParam, rParam []low.ValueReference[string], - changes *[]*Change, label string, breaking bool) { - lKeys := make([]string, len(lParam)) - rKeys := make([]string, len(rParam)) - lValues := make(map[string]low.ValueReference[string]) - rValues := make(map[string]low.ValueReference[string]) - for i := range lParam { - lKeys[i] = strings.ToLower(lParam[i].Value) - lValues[lKeys[i]] = lParam[i] - } - for i := range rParam { - rKeys[i] = strings.ToLower(rParam[i].Value) - rValues[rKeys[i]] = rParam[i] - } - for i := range lValues { - if _, ok := rValues[i]; !ok { - CreateChange(changes, PropertyRemoved, label, - lValues[i].ValueNode, - nil, - breaking, - lValues[i].Value, - nil) - } - } - for i := range rValues { - if _, ok := lValues[i]; !ok { - CreateChange(changes, PropertyAdded, label, - nil, - rValues[i].ValueNode, - false, - nil, - rValues[i].Value) - } - } + changes *[]*Change, label string, breaking bool) { + lKeys := make([]string, len(lParam)) + rKeys := make([]string, len(rParam)) + lValues := make(map[string]low.ValueReference[string]) + rValues := make(map[string]low.ValueReference[string]) + for i := range lParam { + lKeys[i] = strings.ToLower(lParam[i].Value) + lValues[lKeys[i]] = lParam[i] + } + for i := range rParam { + rKeys[i] = strings.ToLower(rParam[i].Value) + rValues[rKeys[i]] = rParam[i] + } + for i := range lValues { + if _, ok := rValues[i]; !ok { + CreateChange(changes, PropertyRemoved, label, + lValues[i].ValueNode, + nil, + breaking, + lValues[i].Value, + nil) + } + } + for i := range rValues { + if _, ok := lValues[i]; !ok { + CreateChange(changes, PropertyAdded, label, + nil, + rValues[i].ValueNode, + false, + nil, + rValues[i].Value) + } + } } // ExtractRawValueSliceChanges will compare two low level interface{} slices for changes. func ExtractRawValueSliceChanges(lParam, rParam []low.ValueReference[any], - changes *[]*Change, label string, breaking bool) { - lKeys := make([]string, len(lParam)) - rKeys := make([]string, len(rParam)) - lValues := make(map[string]low.ValueReference[any]) - rValues := make(map[string]low.ValueReference[any]) - for i := range lParam { - lKeys[i] = strings.ToLower(fmt.Sprint(lParam[i].Value)) - lValues[lKeys[i]] = lParam[i] - } - for i := range rParam { - rKeys[i] = strings.ToLower(fmt.Sprint(rParam[i].Value)) - rValues[rKeys[i]] = rParam[i] - } - for i := range lValues { - if _, ok := rValues[i]; !ok { - CreateChange(changes, PropertyRemoved, label, - lValues[i].ValueNode, - nil, - breaking, - lValues[i].Value, - nil) - } - } - for i := range rValues { - if _, ok := lValues[i]; !ok { - CreateChange(changes, PropertyAdded, label, - nil, - rValues[i].ValueNode, - false, - nil, - rValues[i].Value) - } - } + changes *[]*Change, label string, breaking bool) { + lKeys := make([]string, len(lParam)) + rKeys := make([]string, len(rParam)) + lValues := make(map[string]low.ValueReference[any]) + rValues := make(map[string]low.ValueReference[any]) + for i := range lParam { + lKeys[i] = strings.ToLower(fmt.Sprint(lParam[i].Value)) + lValues[lKeys[i]] = lParam[i] + } + for i := range rParam { + rKeys[i] = strings.ToLower(fmt.Sprint(rParam[i].Value)) + rValues[rKeys[i]] = rParam[i] + } + for i := range lValues { + if _, ok := rValues[i]; !ok { + CreateChange(changes, PropertyRemoved, label, + lValues[i].ValueNode, + nil, + breaking, + lValues[i].Value, + nil) + } + } + for i := range rValues { + if _, ok := lValues[i]; !ok { + CreateChange(changes, PropertyAdded, label, + nil, + rValues[i].ValueNode, + false, + nil, + rValues[i].Value) + } + } } diff --git a/what-changed/model/example.go b/what-changed/model/example.go index 478d4a7..42ec85e 100644 --- a/what-changed/model/example.go +++ b/what-changed/model/example.go @@ -4,179 +4,181 @@ package model import ( - "fmt" - "github.com/pb33f/libopenapi/datamodel/low/base" - v3 "github.com/pb33f/libopenapi/datamodel/low/v3" - "github.com/pb33f/libopenapi/utils" - "gopkg.in/yaml.v3" - "sort" + "fmt" + "sort" + + "gopkg.in/yaml.v3" + + "github.com/pb33f/libopenapi/datamodel/low/base" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/utils" ) // ExampleChanges represent changes to an Example object, part of an OpenAPI specification. type ExampleChanges struct { - *PropertyChanges - ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + *PropertyChanges + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` } // TotalChanges returns the total number of changes made to Example func (e *ExampleChanges) TotalChanges() int { - l := e.PropertyChanges.TotalChanges() - if e.ExtensionChanges != nil { - l += e.ExtensionChanges.PropertyChanges.TotalChanges() - } - return l + l := e.PropertyChanges.TotalChanges() + if e.ExtensionChanges != nil { + l += e.ExtensionChanges.PropertyChanges.TotalChanges() + } + return l } // TotalBreakingChanges returns the total number of breaking changes made to Example func (e *ExampleChanges) TotalBreakingChanges() int { - l := e.PropertyChanges.TotalBreakingChanges() - return l + l := e.PropertyChanges.TotalBreakingChanges() + return l } // CompareExamples returns a pointer to ExampleChanges that contains all changes made between // left and right Example instances. func CompareExamples(l, r *base.Example) *ExampleChanges { - ec := new(ExampleChanges) - var changes []*Change - var props []*PropertyCheck + ec := new(ExampleChanges) + var changes []*Change + var props []*PropertyCheck - // Summary - props = append(props, &PropertyCheck{ - LeftNode: l.Summary.ValueNode, - RightNode: r.Summary.ValueNode, - Label: v3.SummaryLabel, - Changes: &changes, - Breaking: false, - Original: l, - New: r, - }) + // Summary + props = append(props, &PropertyCheck{ + LeftNode: l.Summary.ValueNode, + RightNode: r.Summary.ValueNode, + Label: v3.SummaryLabel, + Changes: &changes, + Breaking: false, + Original: l, + New: r, + }) - // Description - props = append(props, &PropertyCheck{ - LeftNode: l.Description.ValueNode, - RightNode: r.Description.ValueNode, - Label: v3.DescriptionLabel, - Changes: &changes, - Breaking: false, - Original: l, - New: r, - }) + // Description + props = append(props, &PropertyCheck{ + LeftNode: l.Description.ValueNode, + RightNode: r.Description.ValueNode, + Label: v3.DescriptionLabel, + Changes: &changes, + Breaking: false, + Original: l, + New: r, + }) - // Value - if utils.IsNodeMap(l.Value.ValueNode) && utils.IsNodeMap(r.Value.ValueNode) { - lKeys := make([]string, len(l.Value.ValueNode.Content)/2) - rKeys := make([]string, len(r.Value.ValueNode.Content)/2) - z := 0 - for k := range l.Value.ValueNode.Content { - if k%2 == 0 { - lKeys[z] = fmt.Sprintf("%v-%v", l.Value.ValueNode.Content[k].Value, l.Value.ValueNode.Content[k+1].Value) - z++ - } else { - continue - } - } - z = 0 - for k := range r.Value.ValueNode.Content { - if k%2 == 0 { - rKeys[z] = fmt.Sprintf("%v-%v", r.Value.ValueNode.Content[k].Value, r.Value.ValueNode.Content[k+1].Value) - z++ - } else { - continue - } - } - sort.Strings(lKeys) - sort.Strings(rKeys) - //if (len(lKeys) > len(rKeys)) || (len(rKeys) > len(lKeys)) { - // CreateChange(&changes, Modified, v3.ValueLabel, - // l.Value.GetValueNode(), r.Value.GetValueNode(), false, l.Value.GetValue(), r.Value.GetValue()) - //} - for k := range lKeys { - if k < len(rKeys) && lKeys[k] != rKeys[k] { + // Value + if utils.IsNodeMap(l.Value.ValueNode) && utils.IsNodeMap(r.Value.ValueNode) { + lKeys := make([]string, len(l.Value.ValueNode.Content)/2) + rKeys := make([]string, len(r.Value.ValueNode.Content)/2) + z := 0 + for k := range l.Value.ValueNode.Content { + if k%2 == 0 { + lKeys[z] = fmt.Sprintf("%v-%v", l.Value.ValueNode.Content[k].Value, l.Value.ValueNode.Content[k+1].Value) + z++ + } else { + continue + } + } + z = 0 + for k := range r.Value.ValueNode.Content { + if k%2 == 0 { + rKeys[z] = fmt.Sprintf("%v-%v", r.Value.ValueNode.Content[k].Value, r.Value.ValueNode.Content[k+1].Value) + z++ + } else { + continue + } + } + sort.Strings(lKeys) + sort.Strings(rKeys) + //if (len(lKeys) > len(rKeys)) || (len(rKeys) > len(lKeys)) { + // CreateChange(&changes, Modified, v3.ValueLabel, + // l.Value.GetValueNode(), r.Value.GetValueNode(), false, l.Value.GetValue(), r.Value.GetValue()) + //} + for k := range lKeys { + if k < len(rKeys) && lKeys[k] != rKeys[k] { - if utils.IsNodeMap(l.Value.ValueNode) || utils.IsNodeArray(l.Value.ValueNode) { - // render down object - rendered, _ := yaml.Marshal(l.Value.ValueNode) - l.Value.ValueNode.Value = string(rendered) - } + if utils.IsNodeMap(l.Value.ValueNode) || utils.IsNodeArray(l.Value.ValueNode) { + // render down object + rendered, _ := yaml.Marshal(l.Value.ValueNode) + l.Value.ValueNode.Value = string(rendered) + } - if utils.IsNodeMap(r.Value.ValueNode) || utils.IsNodeArray(r.Value.ValueNode) { - // render down object - rendered, _ := yaml.Marshal(r.Value.ValueNode) - r.Value.ValueNode.Value = string(rendered) - } + if utils.IsNodeMap(r.Value.ValueNode) || utils.IsNodeArray(r.Value.ValueNode) { + // render down object + rendered, _ := yaml.Marshal(r.Value.ValueNode) + r.Value.ValueNode.Value = string(rendered) + } - CreateChange(&changes, Modified, v3.ValueLabel, - l.Value.GetValueNode(), r.Value.GetValueNode(), false, l.Value.GetValue(), r.Value.GetValue()) - continue - } - if k >= len(rKeys) { + CreateChange(&changes, Modified, v3.ValueLabel, + l.Value.GetValueNode(), r.Value.GetValueNode(), false, l.Value.GetValue(), r.Value.GetValue()) + continue + } + if k >= len(rKeys) { - if utils.IsNodeMap(l.Value.ValueNode) || utils.IsNodeArray(l.Value.ValueNode) { - // render down object - rendered, _ := yaml.Marshal(l.Value.ValueNode) - l.Value.ValueNode.Value = string(rendered) - } + if utils.IsNodeMap(l.Value.ValueNode) || utils.IsNodeArray(l.Value.ValueNode) { + // render down object + rendered, _ := yaml.Marshal(l.Value.ValueNode) + l.Value.ValueNode.Value = string(rendered) + } - if utils.IsNodeMap(r.Value.ValueNode) || utils.IsNodeArray(r.Value.ValueNode) { - // render down object - rendered, _ := yaml.Marshal(r.Value.ValueNode) - r.Value.ValueNode.Value = string(rendered) - } + if utils.IsNodeMap(r.Value.ValueNode) || utils.IsNodeArray(r.Value.ValueNode) { + // render down object + rendered, _ := yaml.Marshal(r.Value.ValueNode) + r.Value.ValueNode.Value = string(rendered) + } - CreateChange(&changes, PropertyRemoved, v3.ValueLabel, - l.Value.ValueNode, r.Value.ValueNode, false, l.Value.Value, r.Value.Value) - } - } - for k := range rKeys { - if k >= len(lKeys) { + CreateChange(&changes, PropertyRemoved, v3.ValueLabel, + l.Value.ValueNode, r.Value.ValueNode, false, l.Value.Value, r.Value.Value) + } + } + for k := range rKeys { + if k >= len(lKeys) { - if utils.IsNodeMap(l.Value.ValueNode) || utils.IsNodeArray(l.Value.ValueNode) { - // render down object - rendered, _ := yaml.Marshal(l.Value.ValueNode) - l.Value.ValueNode.Value = string(rendered) - } + if utils.IsNodeMap(l.Value.ValueNode) || utils.IsNodeArray(l.Value.ValueNode) { + // render down object + rendered, _ := yaml.Marshal(l.Value.ValueNode) + l.Value.ValueNode.Value = string(rendered) + } - if utils.IsNodeMap(r.Value.ValueNode) || utils.IsNodeArray(r.Value.ValueNode) { - // render down object - rendered, _ := yaml.Marshal(r.Value.ValueNode) - r.Value.ValueNode.Value = string(rendered) - } + if utils.IsNodeMap(r.Value.ValueNode) || utils.IsNodeArray(r.Value.ValueNode) { + // render down object + rendered, _ := yaml.Marshal(r.Value.ValueNode) + r.Value.ValueNode.Value = string(rendered) + } - CreateChange(&changes, PropertyAdded, v3.ValueLabel, - l.Value.ValueNode, r.Value.ValueNode, false, l.Value.Value, r.Value.Value) - } - } - } else { - props = append(props, &PropertyCheck{ - LeftNode: l.Value.ValueNode, - RightNode: r.Value.ValueNode, - Label: v3.ValueLabel, - Changes: &changes, - Breaking: false, - Original: l, - New: r, - }) - } - // ExternalValue - props = append(props, &PropertyCheck{ - LeftNode: l.ExternalValue.ValueNode, - RightNode: r.ExternalValue.ValueNode, - Label: v3.ExternalValue, - Changes: &changes, - Breaking: false, - Original: l, - New: r, - }) + CreateChange(&changes, PropertyAdded, v3.ValueLabel, + l.Value.ValueNode, r.Value.ValueNode, false, l.Value.Value, r.Value.Value) + } + } + } else { + props = append(props, &PropertyCheck{ + LeftNode: l.Value.ValueNode, + RightNode: r.Value.ValueNode, + Label: v3.ValueLabel, + Changes: &changes, + Breaking: false, + Original: l, + New: r, + }) + } + // ExternalValue + props = append(props, &PropertyCheck{ + LeftNode: l.ExternalValue.ValueNode, + RightNode: r.ExternalValue.ValueNode, + Label: v3.ExternalValue, + Changes: &changes, + Breaking: false, + Original: l, + New: r, + }) - // check properties - CheckProperties(props) + // check properties + CheckProperties(props) - // check extensions - ec.ExtensionChanges = CheckExtensions(l, r) - ec.PropertyChanges = NewPropertyChanges(changes) - if ec.TotalChanges() <= 0 { - return nil - } - return ec + // check extensions + ec.ExtensionChanges = CheckExtensions(l, r) + ec.PropertyChanges = NewPropertyChanges(changes) + if ec.TotalChanges() <= 0 { + return nil + } + return ec }