what-changed operational

the code is working, the tests are passing. There is a lot more to check, but it's a good time to bring the feature into main.
This commit is contained in:
Dave Shanley
2022-11-18 10:47:39 -05:00
parent b510be3563
commit 9d8d6e36ea
8 changed files with 133 additions and 69 deletions

View File

@@ -246,17 +246,15 @@ func (s *Schema) Hash() [32]byte {
// hash polymorphic data // hash polymorphic data
if len(s.OneOf.Value) > 0 { if len(s.OneOf.Value) > 0 {
oneOfKeys := make([]string, len(s.OneOf.Value)) oneOfKeys := make([]string, len(s.OneOf.Value))
oneOfEntities := make(map[string]*Schema) oneOfEntities := make(map[string]*SchemaProxy)
z = 0 z = 0
for i := range s.OneOf.Value { for i := range s.OneOf.Value {
g := s.OneOf.Value[i].Value g := s.OneOf.Value[i].Value
if !g.IsSchemaReference() { r := low.GenerateHashString(g)
k := g.Schema() oneOfEntities[r] = g
r := low.GenerateHashString(k) oneOfKeys[z] = r
oneOfEntities[r] = k z++
oneOfKeys[z] = r
z++
}
} }
sort.Strings(oneOfKeys) sort.Strings(oneOfKeys)
for k := range oneOfKeys { for k := range oneOfKeys {
@@ -266,17 +264,15 @@ func (s *Schema) Hash() [32]byte {
if len(s.AllOf.Value) > 0 { if len(s.AllOf.Value) > 0 {
allOfKeys := make([]string, len(s.AllOf.Value)) allOfKeys := make([]string, len(s.AllOf.Value))
allOfEntities := make(map[string]*Schema) allOfEntities := make(map[string]*SchemaProxy)
z = 0 z = 0
for i := range s.AllOf.Value { for i := range s.AllOf.Value {
g := s.AllOf.Value[i].Value g := s.AllOf.Value[i].Value
if !g.IsSchemaReference() { r := low.GenerateHashString(g)
k := g.Schema() allOfEntities[r] = g
r := low.GenerateHashString(k) allOfKeys[z] = r
allOfEntities[r] = k z++
allOfKeys[z] = r
z++
}
} }
sort.Strings(allOfKeys) sort.Strings(allOfKeys)
for k := range allOfKeys { for k := range allOfKeys {
@@ -286,17 +282,15 @@ func (s *Schema) Hash() [32]byte {
if len(s.AnyOf.Value) > 0 { if len(s.AnyOf.Value) > 0 {
anyOfKeys := make([]string, len(s.AnyOf.Value)) anyOfKeys := make([]string, len(s.AnyOf.Value))
anyOfEntities := make(map[string]*Schema) anyOfEntities := make(map[string]*SchemaProxy)
z = 0 z = 0
for i := range s.AnyOf.Value { for i := range s.AnyOf.Value {
g := s.AnyOf.Value[i].Value g := s.AnyOf.Value[i].Value
if !g.IsSchemaReference() { r := low.GenerateHashString(g)
k := g.Schema() anyOfEntities[r] = g
r := low.GenerateHashString(k) anyOfKeys[z] = r
anyOfEntities[r] = k z++
anyOfKeys[z] = r
z++
}
} }
sort.Strings(anyOfKeys) sort.Strings(anyOfKeys)
for k := range anyOfKeys { for k := range anyOfKeys {
@@ -306,17 +300,15 @@ func (s *Schema) Hash() [32]byte {
if len(s.Not.Value) > 0 { if len(s.Not.Value) > 0 {
notKeys := make([]string, len(s.Not.Value)) notKeys := make([]string, len(s.Not.Value))
notEntities := make(map[string]*Schema) notEntities := make(map[string]*SchemaProxy)
z = 0 z = 0
for i := range s.Not.Value { for i := range s.Not.Value {
g := s.Not.Value[i].Value g := s.Not.Value[i].Value
if !g.IsSchemaReference() { r := low.GenerateHashString(g)
k := g.Schema() notEntities[r] = g
r := low.GenerateHashString(k) notKeys[z] = r
notEntities[r] = k z++
notKeys[z] = r
z++
}
} }
sort.Strings(notKeys) sort.Strings(notKeys)
for k := range notKeys { for k := range notKeys {
@@ -326,17 +318,14 @@ func (s *Schema) Hash() [32]byte {
if len(s.Items.Value) > 0 { if len(s.Items.Value) > 0 {
itemsKeys := make([]string, len(s.Items.Value)) itemsKeys := make([]string, len(s.Items.Value))
itemsEntities := make(map[string]*Schema) itemsEntities := make(map[string]*SchemaProxy)
z = 0 z = 0
for i := range s.Items.Value { for i := range s.Items.Value {
g := s.Items.Value[i].Value g := s.Items.Value[i].Value
if !g.IsSchemaReference() { r := low.GenerateHashString(g)
k := g.Schema() itemsEntities[r] = g
r := low.GenerateHashString(k) itemsKeys[z] = r
itemsEntities[r] = k z++
itemsKeys[z] = r
z++
}
} }
sort.Strings(itemsKeys) sort.Strings(itemsKeys)
for k := range itemsKeys { for k := range itemsKeys {

View File

@@ -62,7 +62,7 @@ func (h *Header) Hash() [32]byte {
f = append(f, fmt.Sprint(h.Explode.Value)) f = append(f, fmt.Sprint(h.Explode.Value))
f = append(f, fmt.Sprint(h.AllowReserved.Value)) f = append(f, fmt.Sprint(h.AllowReserved.Value))
if h.Schema.Value != nil { if h.Schema.Value != nil {
f = append(f, low.GenerateHashString(h.Schema.Value.Schema())) f = append(f, low.GenerateHashString(h.Schema.Value))
} }
if h.Example.Value != nil { if h.Example.Value != nil {
f = append(f, fmt.Sprint(h.Example.Value)) f = append(f, fmt.Sprint(h.Example.Value))

View File

@@ -98,7 +98,7 @@ func (mt *MediaType) Build(root *yaml.Node, idx *index.SpecIndex) error {
func (mt *MediaType) Hash() [32]byte { func (mt *MediaType) Hash() [32]byte {
var f []string var f []string
if mt.Schema.Value != nil { if mt.Schema.Value != nil {
f = append(f, low.GenerateHashString(mt.Schema.Value.Schema())) f = append(f, low.GenerateHashString(mt.Schema.Value))
} }
if mt.Example.Value != nil { if mt.Example.Value != nil {
f = append(f, fmt.Sprintf("%x", sha256.Sum256([]byte(fmt.Sprint(mt.Example.Value))))) f = append(f, fmt.Sprintf("%x", sha256.Sum256([]byte(fmt.Sprint(mt.Example.Value)))))

View File

@@ -9,6 +9,7 @@ import (
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"sort"
"strings" "strings"
) )
@@ -63,9 +64,14 @@ func (s *Server) Build(root *yaml.Node, idx *index.SpecIndex) error {
// Hash will return a consistent SHA256 Hash of the Server object // Hash will return a consistent SHA256 Hash of the Server object
func (s *Server) Hash() [32]byte { func (s *Server) Hash() [32]byte {
var f []string var f []string
keys := make([]string, len(s.Variables.Value))
z := 0
for k := range s.Variables.Value { for k := range s.Variables.Value {
f = append(f, low.GenerateHashString(s.Variables.Value[k].Value)) keys[z] = low.GenerateHashString(s.Variables.Value[k].Value)
z++
} }
sort.Strings(keys)
f = append(f, keys...)
if !s.URL.IsEmpty() { if !s.URL.IsEmpty() {
f = append(f, s.URL.Value) f = append(f, s.URL.Value)
} }

View File

@@ -102,7 +102,7 @@ paths:
unexpectedError: unexpectedError:
summary: oh my goodness summary: oh my goodness
value: value:
message: something went terribly wrong my friend, no new burger for you. message: something went terribly wrong my friend, no new burger for you. mate.
"422": "422":
description: Unprocessable entity description: Unprocessable entity
content: content:

View File

@@ -4,8 +4,11 @@
package model package model
import ( import (
"fmt"
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3" v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/utils"
"sort"
) )
// ExampleChanges represent changes to an Example object, part of an OpenAPI specification. // ExampleChanges represent changes to an Example object, part of an OpenAPI specification.
@@ -60,16 +63,56 @@ func CompareExamples(l, r *base.Example) *ExampleChanges {
}) })
// Value // Value
props = append(props, &PropertyCheck{ if utils.IsNodeMap(l.Value.ValueNode) && utils.IsNodeMap(r.Value.ValueNode) {
LeftNode: l.Value.ValueNode, lKeys := make([]string, len(l.Value.ValueNode.Content)/2)
RightNode: r.Value.ValueNode, rKeys := make([]string, len(r.Value.ValueNode.Content)/2)
Label: v3.ValueLabel, z := 0
Changes: &changes, for k := range l.Value.ValueNode.Content {
Breaking: false, if k%2 == 0 {
Original: l, lKeys[z] = fmt.Sprintf("%v-%v", l.Value.ValueNode.Content[k].Value, l.Value.ValueNode.Content[k+1].Value)
New: r, 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 lKeys[k] != rKeys[k] {
CreateChange(&changes, Modified, v3.ValueLabel,
l.Value.GetValueNode(), r.Value.GetValueNode(), false, l.Value.GetValue(), r.Value.GetValue())
}
}
for k := range rKeys {
if k >= len(lKeys) {
CreateChange(&changes, Modified, v3.ValueLabel,
nil, r.Value.GetValueNode(), false, nil, r.Value.GetValue())
}
}
} 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 // ExternalValue
props = append(props, &PropertyCheck{ props = append(props, &PropertyCheck{
LeftNode: l.ExternalValue.ValueNode, LeftNode: l.ExternalValue.ValueNode,

View File

@@ -411,6 +411,9 @@ func checkServers(lServers, rServers low.NodeReference[[]low.ValueReference[*v3.
nil, rServers.ValueNode, false, nil, nil, rServers.ValueNode, false, nil,
rServers.Value) rServers.Value)
} }
if len(serverChanges) <= 0 {
return nil
}
return serverChanges return serverChanges
} }

View File

@@ -3,18 +3,41 @@
package what_changed package what_changed
//func TestCompareOpenAPIDocuments(t *testing.T) { import (
// "github.com/pb33f/libopenapi/datamodel"
// original, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml") v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
// modified, _ := ioutil.ReadFile("../test_specs/burgershop.openapi-modified.yaml") "github.com/stretchr/testify/assert"
// infoOrig, _ := datamodel.ExtractSpecInfo(original) "io/ioutil"
// infoMod, _ := datamodel.ExtractSpecInfo(modified) "testing"
// )
// origDoc, _ := v3.CreateDocument(infoOrig)
// modDoc, _ := v3.CreateDocument(infoMod) func TestCompareOpenAPIDocuments(t *testing.T) {
//
// changes := CompareOpenAPIDocuments(origDoc, modDoc) original, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml")
// modified, _ := ioutil.ReadFile("../test_specs/burgershop.openapi-modified.yaml")
// assert.Nil(t, changes) infoOrig, _ := datamodel.ExtractSpecInfo(original)
// infoMod, _ := datamodel.ExtractSpecInfo(modified)
//}
origDoc, _ := v3.CreateDocument(infoOrig)
modDoc, _ := v3.CreateDocument(infoMod)
changes := CompareOpenAPIDocuments(origDoc, modDoc)
assert.Equal(t, 1, changes.TotalChanges())
assert.Equal(t, 0, changes.TotalBreakingChanges())
}
func Benchmark_CompareOpenAPIDocuments(b *testing.B) {
original, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml")
modified, _ := ioutil.ReadFile("../test_specs/burgershop.openapi-modified.yaml")
infoOrig, _ := datamodel.ExtractSpecInfo(original)
infoMod, _ := datamodel.ExtractSpecInfo(modified)
origDoc, _ := v3.CreateDocument(infoOrig)
modDoc, _ := v3.CreateDocument(infoMod)
for i := 0; i < b.N; i++ {
CompareOpenAPIDocuments(origDoc, modDoc)
}
}