fix: handle example comparison correctly. #61

examples can be anything, most commonly maps. previously `libopenapi` did not check maps correctly. Now it does. It was also creating a panic by inserting a nil pointer as the value to a map key. This has also been fixed.

Examples are the only element that use this pattern for comparison.
This commit is contained in:
Dave Shanley
2023-01-05 16:20:18 -05:00
parent e6a059750b
commit f269259fcf
4 changed files with 113 additions and 11 deletions

View File

@@ -113,14 +113,6 @@ func NewPropertyChanges(changes []*Change) *PropertyChanges {
return &PropertyChanges{Changes: changes} 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. // PropertyCheck is used by functions to check the state of left and right values.
type PropertyCheck struct { type PropertyCheck struct {

View File

@@ -5,6 +5,7 @@ package model
import ( import (
"fmt" "fmt"
"reflect"
"strings" "strings"
"sync" "sync"
@@ -250,7 +251,12 @@ func CheckMapForChangesWithComp[T any, R any](expLeft, expRight map[low.KeyRefer
// run comparison. // run comparison.
if compare { if compare {
chLock.Lock() 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() chLock.Unlock()
} }
doneChan <- true doneChan <- true

View File

@@ -1074,3 +1074,86 @@ webhooks:
assert.Equal(t, 2, extChanges.TotalChanges()) assert.Equal(t, 2, extChanges.TotalChanges())
assert.Equal(t, 0, extChanges.TotalBreakingChanges()) 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)
}

View File

@@ -4,6 +4,7 @@
package model package model
import ( import (
"crypto/sha256"
"fmt" "fmt"
"sort" "sort"
@@ -72,7 +73,17 @@ func CompareExamples(l, r *base.Example) *ExampleChanges {
z := 0 z := 0
for k := range l.Value.ValueNode.Content { for k := range l.Value.ValueNode.Content {
if k%2 == 0 { 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++ z++
} else { } else {
continue continue
@@ -81,7 +92,17 @@ func CompareExamples(l, r *base.Example) *ExampleChanges {
z = 0 z = 0
for k := range r.Value.ValueNode.Content { for k := range r.Value.ValueNode.Content {
if k%2 == 0 { 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++ z++
} else { } else {
continue continue