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