diff --git a/what-changed/model/change_types.go b/what-changed/model/change_types.go index 7e77f98..f0b8743 100644 --- a/what-changed/model/change_types.go +++ b/what-changed/model/change_types.go @@ -113,14 +113,6 @@ func NewPropertyChanges(changes []*Change) *PropertyChanges { return &PropertyChanges{Changes: changes} } -// SortByChangeType will order changes by the types of change they represent, -// This is a destructive action and will permanently re-order Changes. -//func (p PropertyChanges[T]) SortByChangeType() { -// sort.SliceStable(p.Changes, func(i, j int) bool { -// return p.Changes[i].ChangeType < p.Changes[j].ChangeType -// }) -//} - // PropertyCheck is used by functions to check the state of left and right values. type PropertyCheck struct { diff --git a/what-changed/model/comparison_functions.go b/what-changed/model/comparison_functions.go index e1e323b..ccadd69 100644 --- a/what-changed/model/comparison_functions.go +++ b/what-changed/model/comparison_functions.go @@ -5,6 +5,7 @@ package model import ( "fmt" + "reflect" "strings" "sync" @@ -250,7 +251,12 @@ func CheckMapForChangesWithComp[T any, R any](expLeft, expRight map[low.KeyRefer // run comparison. if compare { chLock.Lock() - expChanges[k] = compareFunc(p[k].Value, h[k].Value) + ch := compareFunc(p[k].Value, h[k].Value) + // incorrect map results were being generated causing panics. + // https://github.com/pb33f/libopenapi/issues/61 + if !reflect.ValueOf(&ch).Elem().IsZero() { + expChanges[k] = ch + } chLock.Unlock() } doneChan <- true diff --git a/what-changed/model/document_test.go b/what-changed/model/document_test.go index 0804518..d0a7da2 100644 --- a/what-changed/model/document_test.go +++ b/what-changed/model/document_test.go @@ -1074,3 +1074,86 @@ webhooks: assert.Equal(t, 2, extChanges.TotalChanges()) assert.Equal(t, 0, extChanges.TotalBreakingChanges()) } + +func TestCompareDocuments_OpenAPIExampleMapChanges(t *testing.T) { + left := `openapi: 3.0.0 +paths: + /: + post: + requestBody: + content: + application/json: + examples: + test: + value: + map: + deep: + date: 2020-01-01` + + right := `openapi: 3.0.0 +paths: + /: + post: + requestBody: + content: + application/json: + examples: + test: + value: + map: + deep: + date: "2020-01-04"` + + // have to build docs fully to get access to objects + siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) + siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) + + lDoc, _ := v3.CreateDocument(siLeft) + rDoc, _ := v3.CreateDocument(siRight) + + // compare. + extChanges := CompareDocuments(lDoc, rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Equal(t, 0, extChanges.TotalBreakingChanges()) +} + +func TestCompareDocuments_OpenAPIExampleNoExampleMapChanges(t *testing.T) { + left := `openapi: 3.0.0 +paths: + /: + post: + requestBody: + content: + application/json: + examples: + test: + value: + map: + deep: + date: 2020-01-01` + + right := `openapi: 3.0.0 +paths: + /: + post: + requestBody: + content: + application/json: + examples: + test: + value: + map: + deep: + date: 2020-01-01` + + // have to build docs fully to get access to objects + siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) + siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) + + lDoc, _ := v3.CreateDocument(siLeft) + rDoc, _ := v3.CreateDocument(siRight) + + // compare. + extChanges := CompareDocuments(lDoc, rDoc) + assert.Nil(t, extChanges) +} diff --git a/what-changed/model/example.go b/what-changed/model/example.go index 14385c8..de4e20d 100644 --- a/what-changed/model/example.go +++ b/what-changed/model/example.go @@ -4,6 +4,7 @@ package model import ( + "crypto/sha256" "fmt" "sort" @@ -72,7 +73,17 @@ func CompareExamples(l, r *base.Example) *ExampleChanges { z := 0 for k := range l.Value.ValueNode.Content { if k%2 == 0 { - lKeys[z] = fmt.Sprintf("%v-%v-%v", l.Value.ValueNode.Content[k].Value, l.Value.ValueNode.Content[k+1].Tag, l.Value.ValueNode.Content[k+1].Value) + // if there is no value (value is another map or something else), render the node into yaml and hash it. + // https://github.com/pb33f/libopenapi/issues/61 + val := l.Value.ValueNode.Content[k+1].Value + if val == "" { + yaml, _ := yaml.Marshal(l.Value.ValueNode.Content[k+1].Content) + val = fmt.Sprint(sha256.Sum256(yaml)) + } + lKeys[z] = fmt.Sprintf("%v-%v-%v", + l.Value.ValueNode.Content[k].Value, + l.Value.ValueNode.Content[k+1].Tag, + fmt.Sprintf("%x", val)) z++ } else { continue @@ -81,7 +92,17 @@ func CompareExamples(l, r *base.Example) *ExampleChanges { z = 0 for k := range r.Value.ValueNode.Content { if k%2 == 0 { - rKeys[z] = fmt.Sprintf("%v-%v-%v", r.Value.ValueNode.Content[k].Value, r.Value.ValueNode.Content[k+1].Tag, r.Value.ValueNode.Content[k+1].Value) + // if there is no value (value is another map or something else), render the node into yaml and hash it. + // https://github.com/pb33f/libopenapi/issues/61 + val := r.Value.ValueNode.Content[k+1].Value + if val == "" { + yaml, _ := yaml.Marshal(r.Value.ValueNode.Content[k+1].Content) + val = fmt.Sprint(sha256.Sum256(yaml)) + } + rKeys[z] = fmt.Sprintf("%v-%v-%v", + r.Value.ValueNode.Content[k].Value, + r.Value.ValueNode.Content[k+1].Tag, + fmt.Sprintf("%x", val)) z++ } else { continue