mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 12:37:49 +00:00
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:
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)))))
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user