diff --git a/datamodel/low/base/schema.go b/datamodel/low/base/schema.go index 4460a0b..408e788 100644 --- a/datamodel/low/base/schema.go +++ b/datamodel/low/base/schema.go @@ -246,17 +246,15 @@ func (s *Schema) Hash() [32]byte { // hash polymorphic data if len(s.OneOf.Value) > 0 { oneOfKeys := make([]string, len(s.OneOf.Value)) - oneOfEntities := make(map[string]*Schema) + oneOfEntities := make(map[string]*SchemaProxy) z = 0 for i := range s.OneOf.Value { g := s.OneOf.Value[i].Value - if !g.IsSchemaReference() { - k := g.Schema() - r := low.GenerateHashString(k) - oneOfEntities[r] = k - oneOfKeys[z] = r - z++ - } + r := low.GenerateHashString(g) + oneOfEntities[r] = g + oneOfKeys[z] = r + z++ + } sort.Strings(oneOfKeys) for k := range oneOfKeys { @@ -266,17 +264,15 @@ func (s *Schema) Hash() [32]byte { if len(s.AllOf.Value) > 0 { allOfKeys := make([]string, len(s.AllOf.Value)) - allOfEntities := make(map[string]*Schema) + allOfEntities := make(map[string]*SchemaProxy) z = 0 for i := range s.AllOf.Value { g := s.AllOf.Value[i].Value - if !g.IsSchemaReference() { - k := g.Schema() - r := low.GenerateHashString(k) - allOfEntities[r] = k - allOfKeys[z] = r - z++ - } + r := low.GenerateHashString(g) + allOfEntities[r] = g + allOfKeys[z] = r + z++ + } sort.Strings(allOfKeys) for k := range allOfKeys { @@ -286,17 +282,15 @@ func (s *Schema) Hash() [32]byte { if len(s.AnyOf.Value) > 0 { anyOfKeys := make([]string, len(s.AnyOf.Value)) - anyOfEntities := make(map[string]*Schema) + anyOfEntities := make(map[string]*SchemaProxy) z = 0 for i := range s.AnyOf.Value { g := s.AnyOf.Value[i].Value - if !g.IsSchemaReference() { - k := g.Schema() - r := low.GenerateHashString(k) - anyOfEntities[r] = k - anyOfKeys[z] = r - z++ - } + r := low.GenerateHashString(g) + anyOfEntities[r] = g + anyOfKeys[z] = r + z++ + } sort.Strings(anyOfKeys) for k := range anyOfKeys { @@ -306,17 +300,15 @@ func (s *Schema) Hash() [32]byte { if len(s.Not.Value) > 0 { notKeys := make([]string, len(s.Not.Value)) - notEntities := make(map[string]*Schema) + notEntities := make(map[string]*SchemaProxy) z = 0 for i := range s.Not.Value { g := s.Not.Value[i].Value - if !g.IsSchemaReference() { - k := g.Schema() - r := low.GenerateHashString(k) - notEntities[r] = k - notKeys[z] = r - z++ - } + r := low.GenerateHashString(g) + notEntities[r] = g + notKeys[z] = r + z++ + } sort.Strings(notKeys) for k := range notKeys { @@ -326,17 +318,14 @@ func (s *Schema) Hash() [32]byte { if len(s.Items.Value) > 0 { itemsKeys := make([]string, len(s.Items.Value)) - itemsEntities := make(map[string]*Schema) + itemsEntities := make(map[string]*SchemaProxy) z = 0 for i := range s.Items.Value { g := s.Items.Value[i].Value - if !g.IsSchemaReference() { - k := g.Schema() - r := low.GenerateHashString(k) - itemsEntities[r] = k - itemsKeys[z] = r - z++ - } + r := low.GenerateHashString(g) + itemsEntities[r] = g + itemsKeys[z] = r + z++ } sort.Strings(itemsKeys) for k := range itemsKeys { diff --git a/datamodel/low/v3/header.go b/datamodel/low/v3/header.go index 34a5fed..1d67647 100644 --- a/datamodel/low/v3/header.go +++ b/datamodel/low/v3/header.go @@ -62,7 +62,7 @@ func (h *Header) Hash() [32]byte { f = append(f, fmt.Sprint(h.Explode.Value)) f = append(f, fmt.Sprint(h.AllowReserved.Value)) 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 { f = append(f, fmt.Sprint(h.Example.Value)) diff --git a/datamodel/low/v3/media_type.go b/datamodel/low/v3/media_type.go index cedcae3..7334261 100644 --- a/datamodel/low/v3/media_type.go +++ b/datamodel/low/v3/media_type.go @@ -98,7 +98,7 @@ func (mt *MediaType) Build(root *yaml.Node, idx *index.SpecIndex) error { func (mt *MediaType) Hash() [32]byte { var f []string 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 { f = append(f, fmt.Sprintf("%x", sha256.Sum256([]byte(fmt.Sprint(mt.Example.Value))))) diff --git a/datamodel/low/v3/server.go b/datamodel/low/v3/server.go index 74776c1..2bffbb1 100644 --- a/datamodel/low/v3/server.go +++ b/datamodel/low/v3/server.go @@ -9,6 +9,7 @@ import ( "github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/utils" "gopkg.in/yaml.v3" + "sort" "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 func (s *Server) Hash() [32]byte { var f []string + keys := make([]string, len(s.Variables.Value)) + z := 0 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() { f = append(f, s.URL.Value) } diff --git a/test_specs/burgershop.openapi-modified.yaml b/test_specs/burgershop.openapi-modified.yaml index fcfdfae..7e03356 100644 --- a/test_specs/burgershop.openapi-modified.yaml +++ b/test_specs/burgershop.openapi-modified.yaml @@ -102,7 +102,7 @@ paths: unexpectedError: summary: oh my goodness 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": description: Unprocessable entity content: diff --git a/what-changed/model/example.go b/what-changed/model/example.go index 715e283..58217ea 100644 --- a/what-changed/model/example.go +++ b/what-changed/model/example.go @@ -4,8 +4,11 @@ package model import ( + "fmt" "github.com/pb33f/libopenapi/datamodel/low/base" 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. @@ -60,16 +63,56 @@ func CompareExamples(l, r *base.Example) *ExampleChanges { }) // Value - props = append(props, &PropertyCheck{ - LeftNode: l.Value.ValueNode, - RightNode: r.Value.ValueNode, - Label: v3.ValueLabel, - Changes: &changes, - Breaking: false, - Original: l, - New: r, - }) - + 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 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 props = append(props, &PropertyCheck{ LeftNode: l.ExternalValue.ValueNode, diff --git a/what-changed/model/operation.go b/what-changed/model/operation.go index 38cf7ce..ad32c4e 100644 --- a/what-changed/model/operation.go +++ b/what-changed/model/operation.go @@ -411,6 +411,9 @@ func checkServers(lServers, rServers low.NodeReference[[]low.ValueReference[*v3. nil, rServers.ValueNode, false, nil, rServers.Value) } + if len(serverChanges) <= 0 { + return nil + } return serverChanges } diff --git a/what-changed/what_changed_test.go b/what-changed/what_changed_test.go index 9726507..57bcd2c 100644 --- a/what-changed/what_changed_test.go +++ b/what-changed/what_changed_test.go @@ -3,18 +3,41 @@ package what_changed -//func TestCompareOpenAPIDocuments(t *testing.T) { -// -// 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) -// -// changes := CompareOpenAPIDocuments(origDoc, modDoc) -// -// assert.Nil(t, changes) -// -//} +import ( + "github.com/pb33f/libopenapi/datamodel" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/stretchr/testify/assert" + "io/ioutil" + "testing" +) + +func TestCompareOpenAPIDocuments(t *testing.T) { + + 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) + + 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) + } +}