Added support for unevaluatedProperties as Schema and bool #118

Also ran `gofmt` across the entire project. Things need cleaning up.

Signed-off-by: Dave Shanley <dave@quobix.com>
This commit is contained in:
Dave Shanley
2023-06-15 08:58:20 -04:00
committed by quobix
parent 148822fa2a
commit c3cf5f1e38
142 changed files with 11751 additions and 11577 deletions

View File

@@ -4,110 +4,110 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v3"
)
// CallbackChanges represents all changes made between two Callback OpenAPI objects.
type CallbackChanges struct {
*PropertyChanges
ExpressionChanges map[string]*PathItemChanges `json:"expressions,omitempty" yaml:"expressions,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
*PropertyChanges
ExpressionChanges map[string]*PathItemChanges `json:"expressions,omitempty" yaml:"expressions,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
// TotalChanges returns a total count of all changes made between Callback objects
func (c *CallbackChanges) TotalChanges() int {
d := c.PropertyChanges.TotalChanges()
for k := range c.ExpressionChanges {
d += c.ExpressionChanges[k].TotalChanges()
}
if c.ExtensionChanges != nil {
d += c.ExtensionChanges.TotalChanges()
}
return d
d := c.PropertyChanges.TotalChanges()
for k := range c.ExpressionChanges {
d += c.ExpressionChanges[k].TotalChanges()
}
if c.ExtensionChanges != nil {
d += c.ExtensionChanges.TotalChanges()
}
return d
}
// GetAllChanges returns a slice of all changes made between Callback objects
func (c *CallbackChanges) GetAllChanges() []*Change {
var changes []*Change
changes = append(changes, c.Changes...)
for k := range c.ExpressionChanges {
changes = append(changes, c.ExpressionChanges[k].GetAllChanges()...)
}
if c.ExtensionChanges != nil {
changes = append(changes, c.ExtensionChanges.GetAllChanges()...)
}
return changes
var changes []*Change
changes = append(changes, c.Changes...)
for k := range c.ExpressionChanges {
changes = append(changes, c.ExpressionChanges[k].GetAllChanges()...)
}
if c.ExtensionChanges != nil {
changes = append(changes, c.ExtensionChanges.GetAllChanges()...)
}
return changes
}
// TotalBreakingChanges returns a total count of all changes made between Callback objects
func (c *CallbackChanges) TotalBreakingChanges() int {
d := c.PropertyChanges.TotalBreakingChanges()
for k := range c.ExpressionChanges {
d += c.ExpressionChanges[k].TotalBreakingChanges()
}
if c.ExtensionChanges != nil {
d += c.ExtensionChanges.TotalBreakingChanges()
}
return d
d := c.PropertyChanges.TotalBreakingChanges()
for k := range c.ExpressionChanges {
d += c.ExpressionChanges[k].TotalBreakingChanges()
}
if c.ExtensionChanges != nil {
d += c.ExtensionChanges.TotalBreakingChanges()
}
return d
}
// CompareCallback will compare two Callback objects and return a pointer to CallbackChanges with all the things
// that have changed between them.
func CompareCallback(l, r *v3.Callback) *CallbackChanges {
cc := new(CallbackChanges)
var changes []*Change
cc := new(CallbackChanges)
var changes []*Change
lHashes := make(map[string]string)
rHashes := make(map[string]string)
lHashes := make(map[string]string)
rHashes := make(map[string]string)
lValues := make(map[string]low.ValueReference[*v3.PathItem])
rValues := make(map[string]low.ValueReference[*v3.PathItem])
lValues := make(map[string]low.ValueReference[*v3.PathItem])
rValues := make(map[string]low.ValueReference[*v3.PathItem])
for k := range l.Expression.Value {
lHashes[k.Value] = low.GenerateHashString(l.Expression.Value[k].Value)
lValues[k.Value] = l.Expression.Value[k]
}
for k := range l.Expression.Value {
lHashes[k.Value] = low.GenerateHashString(l.Expression.Value[k].Value)
lValues[k.Value] = l.Expression.Value[k]
}
for k := range r.Expression.Value {
rHashes[k.Value] = low.GenerateHashString(r.Expression.Value[k].Value)
rValues[k.Value] = r.Expression.Value[k]
}
for k := range r.Expression.Value {
rHashes[k.Value] = low.GenerateHashString(r.Expression.Value[k].Value)
rValues[k.Value] = r.Expression.Value[k]
}
expChanges := make(map[string]*PathItemChanges)
expChanges := make(map[string]*PathItemChanges)
// check left path item hashes
for k := range lHashes {
rhash := rHashes[k]
if rhash == "" {
CreateChange(&changes, ObjectRemoved, k,
lValues[k].GetValueNode(), nil, true,
lValues[k].GetValue(), nil)
continue
}
if lHashes[k] == rHashes[k] {
continue
}
// run comparison.
expChanges[k] = ComparePathItems(lValues[k].Value, rValues[k].Value)
}
// check left path item hashes
for k := range lHashes {
rhash := rHashes[k]
if rhash == "" {
CreateChange(&changes, ObjectRemoved, k,
lValues[k].GetValueNode(), nil, true,
lValues[k].GetValue(), nil)
continue
}
if lHashes[k] == rHashes[k] {
continue
}
// run comparison.
expChanges[k] = ComparePathItems(lValues[k].Value, rValues[k].Value)
}
//check right path item hashes
for k := range rHashes {
lhash := lHashes[k]
if lhash == "" {
CreateChange(&changes, ObjectAdded, k,
nil, rValues[k].GetValueNode(), false,
nil, rValues[k].GetValue())
continue
}
}
cc.ExpressionChanges = expChanges
cc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions)
cc.PropertyChanges = NewPropertyChanges(changes)
if cc.TotalChanges() <= 0 {
return nil
}
return cc
//check right path item hashes
for k := range rHashes {
lhash := lHashes[k]
if lhash == "" {
CreateChange(&changes, ObjectAdded, k,
nil, rValues[k].GetValueNode(), false,
nil, rValues[k].GetValue())
continue
}
}
cc.ExpressionChanges = expChanges
cc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions)
cc.PropertyChanges = NewPropertyChanges(changes)
if cc.TotalChanges() <= 0 {
return nil
}
return cc
}

View File

@@ -4,42 +4,42 @@
package model
import (
"gopkg.in/yaml.v3"
"gopkg.in/yaml.v3"
)
// Definitions of the possible changes between two items
const (
// Modified means that was a modification of a value was made
Modified = iota + 1
// Modified means that was a modification of a value was made
Modified = iota + 1
// PropertyAdded means that a new property to an object was added
PropertyAdded
// PropertyAdded means that a new property to an object was added
PropertyAdded
// ObjectAdded means that a new object was added to a parent object
ObjectAdded
// ObjectAdded means that a new object was added to a parent object
ObjectAdded
// ObjectRemoved means that an object was removed from a parent object
ObjectRemoved
// ObjectRemoved means that an object was removed from a parent object
ObjectRemoved
// PropertyRemoved means that a property of an object was removed
PropertyRemoved
// PropertyRemoved means that a property of an object was removed
PropertyRemoved
)
// WhatChanged is a summary object that contains a high level summary of everything changed.
type WhatChanged struct {
Added int `json:"added,omitempty" yaml:"added,omitempty"`
Removed int `json:"removed,omitempty" yaml:"removed,omitempty"`
Modified int `json:"modified,omitempty" yaml:"modified,omitempty"`
TotalChanges int `json:"total,omitempty" yaml:"total,omitempty"`
Added int `json:"added,omitempty" yaml:"added,omitempty"`
Removed int `json:"removed,omitempty" yaml:"removed,omitempty"`
Modified int `json:"modified,omitempty" yaml:"modified,omitempty"`
TotalChanges int `json:"total,omitempty" yaml:"total,omitempty"`
}
// ChangeContext holds a reference to the line and column positions of original and new change.
type ChangeContext struct {
OriginalLine *int `json:"originalLine,omitempty" yaml:"originalLine,omitempty"`
OriginalColumn *int `json:"originalColumn,omitempty" yaml:"originalColumn,omitempty"`
NewLine *int `json:"newLine,omitempty" yaml:"newLine,omitempty"`
NewColumn *int `json:"newColumn,omitempty" yaml:"newColumn,omitempty"`
OriginalLine *int `json:"originalLine,omitempty" yaml:"originalLine,omitempty"`
OriginalColumn *int `json:"originalColumn,omitempty" yaml:"originalColumn,omitempty"`
NewLine *int `json:"newLine,omitempty" yaml:"newLine,omitempty"`
NewColumn *int `json:"newColumn,omitempty" yaml:"newColumn,omitempty"`
}
// HasChanged determines if the line and column numbers of the original and new values have changed.
@@ -47,93 +47,93 @@ type ChangeContext struct {
// It's worth noting that there is no guarantee to the positions of anything in either left or right, so
// considering these values as 'changes' is going to add a considerable amount of noise to results.
func (c *ChangeContext) HasChanged() bool {
if c.NewLine != nil && c.OriginalLine != nil && *c.NewLine != *c.OriginalLine {
return true
}
//if c.NewColumn != nil && c.OriginalColumn != nil && *c.NewColumn != *c.OriginalColumn {
// return true
//}
if (c.NewLine == nil && c.OriginalLine != nil) || (c.NewLine != nil && c.OriginalLine == nil) {
return true
}
//if (c.NewColumn == nil && c.OriginalColumn != nil) || (c.NewColumn != nil && c.OriginalColumn == nil) {
// return true
//}
return false
if c.NewLine != nil && c.OriginalLine != nil && *c.NewLine != *c.OriginalLine {
return true
}
//if c.NewColumn != nil && c.OriginalColumn != nil && *c.NewColumn != *c.OriginalColumn {
// return true
//}
if (c.NewLine == nil && c.OriginalLine != nil) || (c.NewLine != nil && c.OriginalLine == nil) {
return true
}
//if (c.NewColumn == nil && c.OriginalColumn != nil) || (c.NewColumn != nil && c.OriginalColumn == nil) {
// return true
//}
return false
}
// Change represents a change between two different elements inside an OpenAPI specification.
type Change struct {
// Context represents the lines and column numbers of the original and new values
// It's worth noting that these values may frequently be different and are not used to calculate
// a change. If the positions change, but values do not, then no change is recorded.
Context *ChangeContext `json:"context,omitempty" yaml:"context,omitempty"`
// Context represents the lines and column numbers of the original and new values
// It's worth noting that these values may frequently be different and are not used to calculate
// a change. If the positions change, but values do not, then no change is recorded.
Context *ChangeContext `json:"context,omitempty" yaml:"context,omitempty"`
// ChangeType represents the type of change that occurred. stored as an integer, defined by constants above.
ChangeType int `json:"change,omitempty" yaml:"change,omitempty"`
// ChangeType represents the type of change that occurred. stored as an integer, defined by constants above.
ChangeType int `json:"change,omitempty" yaml:"change,omitempty"`
// Property is the property name key being changed.
Property string `json:"property,omitempty" yaml:"property,omitempty"`
// Property is the property name key being changed.
Property string `json:"property,omitempty" yaml:"property,omitempty"`
// Original is the original value represented as a string.
Original string `json:"original,omitempty" yaml:"original,omitempty"`
// Original is the original value represented as a string.
Original string `json:"original,omitempty" yaml:"original,omitempty"`
// New is the new value represented as a string.
New string `json:"new,omitempty" yaml:"new,omitempty"`
// New is the new value represented as a string.
New string `json:"new,omitempty" yaml:"new,omitempty"`
// Breaking determines if the change is a breaking one or not.
Breaking bool `json:"breaking" yaml:"breaking"`
// Breaking determines if the change is a breaking one or not.
Breaking bool `json:"breaking" yaml:"breaking"`
// OriginalObject represents the original object that was changed.
OriginalObject any `json:"-" yaml:"-"`
// OriginalObject represents the original object that was changed.
OriginalObject any `json:"-" yaml:"-"`
// NewObject represents the new object that has been modified.
NewObject any `json:"-" yaml:"-"`
// NewObject represents the new object that has been modified.
NewObject any `json:"-" yaml:"-"`
}
// PropertyChanges holds a slice of Change pointers
type PropertyChanges struct {
//Total *int `json:"total,omitempty" yaml:"total,omitempty"`
//Breaking *int `json:"breaking,omitempty" yaml:"breaking,omitempty"`
Changes []*Change `json:"changes,omitempty" yaml:"changes,omitempty"`
//Total *int `json:"total,omitempty" yaml:"total,omitempty"`
//Breaking *int `json:"breaking,omitempty" yaml:"breaking,omitempty"`
Changes []*Change `json:"changes,omitempty" yaml:"changes,omitempty"`
}
// TotalChanges returns the total number of property changes made.
func (p PropertyChanges) TotalChanges() int {
return len(p.Changes)
return len(p.Changes)
}
// TotalBreakingChanges returns the total number of property breaking changes made.
func (p PropertyChanges) TotalBreakingChanges() int {
return CountBreakingChanges(p.Changes)
return CountBreakingChanges(p.Changes)
}
func NewPropertyChanges(changes []*Change) *PropertyChanges {
return &PropertyChanges{Changes: changes}
return &PropertyChanges{Changes: changes}
}
// PropertyCheck is used by functions to check the state of left and right values.
type PropertyCheck struct {
// Original is the property we're checking on the left
Original any
// Original is the property we're checking on the left
Original any
// New is s the property we're checking on the right
New any
// New is s the property we're checking on the right
New any
// Label is the identifier we're looking for on the left and right hand sides
Label string
// Label is the identifier we're looking for on the left and right hand sides
Label string
// LeftNode is the yaml.Node pointer that holds the original node structure of the value
LeftNode *yaml.Node
// LeftNode is the yaml.Node pointer that holds the original node structure of the value
LeftNode *yaml.Node
// RightNode is the yaml.Node pointer that holds the new node structure of the value
RightNode *yaml.Node
// RightNode is the yaml.Node pointer that holds the new node structure of the value
RightNode *yaml.Node
// Breaking determines if the check is a breaking change (modifications or removals etc.)
Breaking bool
// Breaking determines if the check is a breaking change (modifications or removals etc.)
Breaking bool
// Changes represents a pointer to the slice to contain all changes found.
Changes *[]*Change
// Changes represents a pointer to the slice to contain all changes found.
Changes *[]*Change
}

View File

@@ -4,28 +4,28 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/datamodel/low/v3"
)
// ContactChanges Represent changes to a Contact object that is a child of Info, part of an OpenAPI document.
type ContactChanges struct {
*PropertyChanges
*PropertyChanges
}
// GetAllChanges returns a slice of all changes made between Callback objects
func (c *ContactChanges) GetAllChanges() []*Change {
return c.Changes
return c.Changes
}
// TotalChanges represents the total number of changes that have occurred to a Contact object
func (c *ContactChanges) TotalChanges() int {
return c.PropertyChanges.TotalChanges()
return c.PropertyChanges.TotalChanges()
}
// TotalBreakingChanges always returns 0 for Contact objects, they are non-binding.
func (c *ContactChanges) TotalBreakingChanges() int {
return 0
return 0
}
// CompareContact will check a left (original) and right (new) Contact object for any changes. If there
@@ -33,49 +33,49 @@ func (c *ContactChanges) TotalBreakingChanges() int {
// returns nil.
func CompareContact(l, r *base.Contact) *ContactChanges {
var changes []*Change
var props []*PropertyCheck
var changes []*Change
var props []*PropertyCheck
// check URL
props = append(props, &PropertyCheck{
LeftNode: l.URL.ValueNode,
RightNode: r.URL.ValueNode,
Label: v3.URLLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// check URL
props = append(props, &PropertyCheck{
LeftNode: l.URL.ValueNode,
RightNode: r.URL.ValueNode,
Label: v3.URLLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// check name
props = append(props, &PropertyCheck{
LeftNode: l.Name.ValueNode,
RightNode: r.Name.ValueNode,
Label: v3.NameLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// check name
props = append(props, &PropertyCheck{
LeftNode: l.Name.ValueNode,
RightNode: r.Name.ValueNode,
Label: v3.NameLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// check email
props = append(props, &PropertyCheck{
LeftNode: l.Email.ValueNode,
RightNode: r.Email.ValueNode,
Label: v3.EmailLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// check email
props = append(props, &PropertyCheck{
LeftNode: l.Email.ValueNode,
RightNode: r.Email.ValueNode,
Label: v3.EmailLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// check everything.
CheckProperties(props)
// check everything.
CheckProperties(props)
dc := new(ContactChanges)
dc.PropertyChanges = NewPropertyChanges(changes)
if dc.TotalChanges() <= 0 {
return nil
}
return dc
dc := new(ContactChanges)
dc.PropertyChanges = NewPropertyChanges(changes)
if dc.TotalChanges() <= 0 {
return nil
}
return dc
}

View File

@@ -4,93 +4,93 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low/base"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/datamodel/low/base"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
)
// DiscriminatorChanges represents changes made to a Discriminator OpenAPI object
type DiscriminatorChanges struct {
*PropertyChanges
MappingChanges []*Change `json:"mappings,omitempty" yaml:"mappings,omitempty"`
*PropertyChanges
MappingChanges []*Change `json:"mappings,omitempty" yaml:"mappings,omitempty"`
}
// TotalChanges returns a count of everything changed within the Discriminator object
func (d *DiscriminatorChanges) TotalChanges() int {
l := 0
if k := d.PropertyChanges.TotalChanges(); k > 0 {
l += k
}
if k := len(d.MappingChanges); k > 0 {
l += k
}
return l
l := 0
if k := d.PropertyChanges.TotalChanges(); k > 0 {
l += k
}
if k := len(d.MappingChanges); k > 0 {
l += k
}
return l
}
// GetAllChanges returns a slice of all changes made between Callback objects
func (c *DiscriminatorChanges) GetAllChanges() []*Change {
var changes []*Change
changes = append(changes, c.Changes...)
if c.MappingChanges != nil {
changes = append(changes, c.MappingChanges...)
}
return changes
var changes []*Change
changes = append(changes, c.Changes...)
if c.MappingChanges != nil {
changes = append(changes, c.MappingChanges...)
}
return changes
}
// TotalBreakingChanges returns the number of breaking changes made by the Discriminator
func (d *DiscriminatorChanges) TotalBreakingChanges() int {
return d.PropertyChanges.TotalBreakingChanges() + CountBreakingChanges(d.MappingChanges)
return d.PropertyChanges.TotalBreakingChanges() + CountBreakingChanges(d.MappingChanges)
}
// CompareDiscriminator will check a left (original) and right (new) Discriminator object for changes
// and will return a pointer to DiscriminatorChanges
func CompareDiscriminator(l, r *base.Discriminator) *DiscriminatorChanges {
dc := new(DiscriminatorChanges)
var changes []*Change
var props []*PropertyCheck
var mappingChanges []*Change
dc := new(DiscriminatorChanges)
var changes []*Change
var props []*PropertyCheck
var mappingChanges []*Change
// Name (breaking change)
props = append(props, &PropertyCheck{
LeftNode: l.PropertyName.ValueNode,
RightNode: r.PropertyName.ValueNode,
Label: v3.PropertyNameLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// Name (breaking change)
props = append(props, &PropertyCheck{
LeftNode: l.PropertyName.ValueNode,
RightNode: r.PropertyName.ValueNode,
Label: v3.PropertyNameLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// check properties
CheckProperties(props)
// check properties
CheckProperties(props)
// flatten maps
lMap := FlattenLowLevelMap[string](l.Mapping.Value)
rMap := FlattenLowLevelMap[string](r.Mapping.Value)
// flatten maps
lMap := FlattenLowLevelMap[string](l.Mapping.Value)
rMap := FlattenLowLevelMap[string](r.Mapping.Value)
// check for removals, modifications and moves
for i := range lMap {
CheckForObjectAdditionOrRemoval[string](lMap, rMap, i, &mappingChanges, false, true)
// if the existing tag exists, let's check it.
if rMap[i] != nil {
if lMap[i].Value != rMap[i].Value {
CreateChange(&mappingChanges, Modified, i, lMap[i].GetValueNode(),
rMap[i].GetValueNode(), true, lMap[i].GetValue(), rMap[i].GetValue())
}
}
}
// check for removals, modifications and moves
for i := range lMap {
CheckForObjectAdditionOrRemoval[string](lMap, rMap, i, &mappingChanges, false, true)
// if the existing tag exists, let's check it.
if rMap[i] != nil {
if lMap[i].Value != rMap[i].Value {
CreateChange(&mappingChanges, Modified, i, lMap[i].GetValueNode(),
rMap[i].GetValueNode(), true, lMap[i].GetValue(), rMap[i].GetValue())
}
}
}
for i := range rMap {
if lMap[i] == nil {
CreateChange(&mappingChanges, ObjectAdded, i, nil,
rMap[i].GetValueNode(), false, nil, rMap[i].GetValue())
}
}
for i := range rMap {
if lMap[i] == nil {
CreateChange(&mappingChanges, ObjectAdded, i, nil,
rMap[i].GetValueNode(), false, nil, rMap[i].GetValue())
}
}
dc.PropertyChanges = NewPropertyChanges(changes)
dc.MappingChanges = mappingChanges
if dc.TotalChanges() <= 0 {
return nil
}
return dc
dc.PropertyChanges = NewPropertyChanges(changes)
dc.MappingChanges = mappingChanges
if dc.TotalChanges() <= 0 {
return nil
}
return dc
}

View File

@@ -4,96 +4,96 @@
package model
import (
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
)
// EncodingChanges represent all the changes made to an Encoding object
type EncodingChanges struct {
*PropertyChanges
HeaderChanges map[string]*HeaderChanges `json:"headers,omitempty" yaml:"headers,omitempty"`
*PropertyChanges
HeaderChanges map[string]*HeaderChanges `json:"headers,omitempty" yaml:"headers,omitempty"`
}
// GetAllChanges returns a slice of all changes made between Encoding objects
func (e *EncodingChanges) GetAllChanges() []*Change {
var changes []*Change
changes = append(changes, e.Changes...)
for k := range e.HeaderChanges {
changes = append(changes, e.HeaderChanges[k].GetAllChanges()...)
}
return changes
var changes []*Change
changes = append(changes, e.Changes...)
for k := range e.HeaderChanges {
changes = append(changes, e.HeaderChanges[k].GetAllChanges()...)
}
return changes
}
// TotalChanges returns the total number of changes made between two Encoding objects
func (e *EncodingChanges) TotalChanges() int {
c := e.PropertyChanges.TotalChanges()
if e.HeaderChanges != nil {
for i := range e.HeaderChanges {
c += e.HeaderChanges[i].TotalChanges()
}
}
return c
c := e.PropertyChanges.TotalChanges()
if e.HeaderChanges != nil {
for i := range e.HeaderChanges {
c += e.HeaderChanges[i].TotalChanges()
}
}
return c
}
// TotalBreakingChanges returns the number of changes made between two Encoding objects that were breaking.
func (e *EncodingChanges) TotalBreakingChanges() int {
c := e.PropertyChanges.TotalBreakingChanges()
if e.HeaderChanges != nil {
for i := range e.HeaderChanges {
c += e.HeaderChanges[i].TotalBreakingChanges()
}
}
return c
c := e.PropertyChanges.TotalBreakingChanges()
if e.HeaderChanges != nil {
for i := range e.HeaderChanges {
c += e.HeaderChanges[i].TotalBreakingChanges()
}
}
return c
}
// CompareEncoding returns a pointer to *EncodingChanges that contain all changes made between a left and right
// set of Encoding objects.
func CompareEncoding(l, r *v3.Encoding) *EncodingChanges {
var changes []*Change
var props []*PropertyCheck
var changes []*Change
var props []*PropertyCheck
// ContentType
props = append(props, &PropertyCheck{
LeftNode: l.ContentType.ValueNode,
RightNode: r.ContentType.ValueNode,
Label: v3.ContentTypeLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// ContentType
props = append(props, &PropertyCheck{
LeftNode: l.ContentType.ValueNode,
RightNode: r.ContentType.ValueNode,
Label: v3.ContentTypeLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// Explode
props = append(props, &PropertyCheck{
LeftNode: l.Explode.ValueNode,
RightNode: r.Explode.ValueNode,
Label: v3.ExplodeLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// Explode
props = append(props, &PropertyCheck{
LeftNode: l.Explode.ValueNode,
RightNode: r.Explode.ValueNode,
Label: v3.ExplodeLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// AllowReserved
props = append(props, &PropertyCheck{
LeftNode: l.AllowReserved.ValueNode,
RightNode: r.AllowReserved.ValueNode,
Label: v3.AllowReservedLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// AllowReserved
props = append(props, &PropertyCheck{
LeftNode: l.AllowReserved.ValueNode,
RightNode: r.AllowReserved.ValueNode,
Label: v3.AllowReservedLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// check everything.
CheckProperties(props)
ec := new(EncodingChanges)
// check everything.
CheckProperties(props)
ec := new(EncodingChanges)
// headers
ec.HeaderChanges = CheckMapForChanges(l.Headers.Value, r.Headers.Value, &changes, v3.HeadersLabel, CompareHeadersV3)
ec.PropertyChanges = NewPropertyChanges(changes)
if ec.TotalChanges() <= 0 {
return nil
}
return ec
// headers
ec.HeaderChanges = CheckMapForChanges(l.Headers.Value, r.Headers.Value, &changes, v3.HeadersLabel, CompareHeadersV3)
ec.PropertyChanges = NewPropertyChanges(changes)
if ec.TotalChanges() <= 0 {
return nil
}
return ec
}

View File

@@ -4,83 +4,83 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/datamodel/low"
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
)
// ExamplesChanges represents changes made between Swagger Examples objects (Not OpenAPI 3).
type ExamplesChanges struct {
*PropertyChanges
*PropertyChanges
}
// GetAllChanges returns a slice of all changes made between Examples objects
func (a *ExamplesChanges) GetAllChanges() []*Change {
return a.Changes
return a.Changes
}
// TotalChanges represents the total number of changes made between Example instances.
func (a *ExamplesChanges) TotalChanges() int {
return a.PropertyChanges.TotalChanges()
return a.PropertyChanges.TotalChanges()
}
// TotalBreakingChanges will always return 0. Examples cannot break a contract.
func (a *ExamplesChanges) TotalBreakingChanges() int {
return 0 // not supported.
return 0 // not supported.
}
// CompareExamplesV2 compares two Swagger Examples objects, returning a pointer to
//ExamplesChanges if anything was found.
// ExamplesChanges if anything was found.
func CompareExamplesV2(l, r *v2.Examples) *ExamplesChanges {
lHashes := make(map[string]string)
rHashes := make(map[string]string)
lValues := make(map[string]low.ValueReference[any])
rValues := make(map[string]low.ValueReference[any])
lHashes := make(map[string]string)
rHashes := make(map[string]string)
lValues := make(map[string]low.ValueReference[any])
rValues := make(map[string]low.ValueReference[any])
for k := range l.Values {
lHashes[k.Value] = low.GenerateHashString(l.Values[k].Value)
lValues[k.Value] = l.Values[k]
}
for k := range l.Values {
lHashes[k.Value] = low.GenerateHashString(l.Values[k].Value)
lValues[k.Value] = l.Values[k]
}
for k := range r.Values {
rHashes[k.Value] = low.GenerateHashString(r.Values[k].Value)
rValues[k.Value] = r.Values[k]
}
var changes []*Change
for k := range r.Values {
rHashes[k.Value] = low.GenerateHashString(r.Values[k].Value)
rValues[k.Value] = r.Values[k]
}
var changes []*Change
// check left example hashes
for k := range lHashes {
rhash := rHashes[k]
if rhash == "" {
CreateChange(&changes, ObjectRemoved, k,
lValues[k].GetValueNode(), nil, false,
lValues[k].GetValue(), nil)
continue
}
if lHashes[k] == rHashes[k] {
continue
}
CreateChange(&changes, Modified, k,
lValues[k].GetValueNode(), rValues[k].GetValueNode(), false,
lValues[k].GetValue(), lValues[k].GetValue())
// check left example hashes
for k := range lHashes {
rhash := rHashes[k]
if rhash == "" {
CreateChange(&changes, ObjectRemoved, k,
lValues[k].GetValueNode(), nil, false,
lValues[k].GetValue(), nil)
continue
}
if lHashes[k] == rHashes[k] {
continue
}
CreateChange(&changes, Modified, k,
lValues[k].GetValueNode(), rValues[k].GetValueNode(), false,
lValues[k].GetValue(), lValues[k].GetValue())
}
}
//check right example hashes
for k := range rHashes {
lhash := lHashes[k]
if lhash == "" {
CreateChange(&changes, ObjectAdded, k,
nil, lValues[k].GetValueNode(), false,
nil, lValues[k].GetValue())
continue
}
}
//check right example hashes
for k := range rHashes {
lhash := lHashes[k]
if lhash == "" {
CreateChange(&changes, ObjectAdded, k,
nil, lValues[k].GetValueNode(), false,
nil, lValues[k].GetValue())
continue
}
}
ex := new(ExamplesChanges)
ex.PropertyChanges = NewPropertyChanges(changes)
if ex.TotalChanges() <= 0 {
return nil
}
return ex
ex := new(ExamplesChanges)
ex.PropertyChanges = NewPropertyChanges(changes)
if ex.TotalChanges() <= 0 {
return nil
}
return ex
}

View File

@@ -4,108 +4,108 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
)
func TestCompareExamplesV2(t *testing.T) {
left := `summary: magic herbs`
right := `summary: cure all`
left := `summary: magic herbs`
right := `summary: cure all`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.Examples
var rDoc v2.Examples
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v2.Examples
var rDoc v2.Examples
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
extChanges := CompareExamplesV2(&lDoc, &rDoc)
assert.Equal(t, extChanges.TotalChanges(), 1)
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, Modified, extChanges.Changes[0].ChangeType)
assert.Equal(t, v3.SummaryLabel, extChanges.Changes[0].Property)
assert.Equal(t, "magic herbs", extChanges.Changes[0].Original)
assert.Equal(t, "cure all", extChanges.Changes[0].New)
extChanges := CompareExamplesV2(&lDoc, &rDoc)
assert.Equal(t, extChanges.TotalChanges(), 1)
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, Modified, extChanges.Changes[0].ChangeType)
assert.Equal(t, v3.SummaryLabel, extChanges.Changes[0].Property)
assert.Equal(t, "magic herbs", extChanges.Changes[0].Original)
assert.Equal(t, "cure all", extChanges.Changes[0].New)
}
func TestCompareExamplesV2_Add(t *testing.T) {
left := `summary: magic herbs`
right := `summary: magic herbs
left := `summary: magic herbs`
right := `summary: magic herbs
yummy: coffee`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.Examples
var rDoc v2.Examples
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v2.Examples
var rDoc v2.Examples
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
extChanges := CompareExamplesV2(&lDoc, &rDoc)
assert.Equal(t, extChanges.TotalChanges(), 1)
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType)
extChanges := CompareExamplesV2(&lDoc, &rDoc)
assert.Equal(t, extChanges.TotalChanges(), 1)
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType)
}
func TestCompareExamplesV2_Remove(t *testing.T) {
left := `summary: magic herbs`
right := `summary: magic herbs
left := `summary: magic herbs`
right := `summary: magic herbs
yummy: coffee`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.Examples
var rDoc v2.Examples
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v2.Examples
var rDoc v2.Examples
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
extChanges := CompareExamplesV2(&rDoc, &lDoc)
assert.Equal(t, extChanges.TotalChanges(), 1)
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
extChanges := CompareExamplesV2(&rDoc, &lDoc)
assert.Equal(t, extChanges.TotalChanges(), 1)
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
}
func TestCompareExamplesV2_Identical(t *testing.T) {
left := `summary: magic herbs`
right := left
left := `summary: magic herbs`
right := left
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.Examples
var rDoc v2.Examples
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v2.Examples
var rDoc v2.Examples
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
extChanges := CompareExamplesV2(&rDoc, &lDoc)
extChanges := CompareExamplesV2(&rDoc, &lDoc)
assert.Nil(t, extChanges)
}

View File

@@ -4,28 +4,28 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"strings"
"github.com/pb33f/libopenapi/datamodel/low"
"strings"
)
// ExtensionChanges represents any changes to custom extensions defined for an OpenAPI object.
type ExtensionChanges struct {
*PropertyChanges
*PropertyChanges
}
// GetAllChanges returns a slice of all changes made between Extension objects
func (e *ExtensionChanges) GetAllChanges() []*Change {
return e.Changes
return e.Changes
}
// TotalChanges returns the total number of object extensions that were made.
func (e *ExtensionChanges) TotalChanges() int {
return e.PropertyChanges.TotalChanges()
return e.PropertyChanges.TotalChanges()
}
// TotalBreakingChanges always returns 0 for Extension objects, they are non-binding.
func (e *ExtensionChanges) TotalBreakingChanges() int {
return 0
return 0
}
// CompareExtensions will compare a left and right map of Tag/ValueReference models for any changes to
@@ -36,62 +36,62 @@ func (e *ExtensionChanges) TotalBreakingChanges() int {
// there is currently no support for knowing anything changed - so it is ignored.
func CompareExtensions(l, r map[low.KeyReference[string]]low.ValueReference[any]) *ExtensionChanges {
// look at the original and then look through the new.
seenLeft := make(map[string]*low.ValueReference[any])
seenRight := make(map[string]*low.ValueReference[any])
for i := range l {
h := l[i]
seenLeft[strings.ToLower(i.Value)] = &h
}
for i := range r {
h := r[i]
seenRight[strings.ToLower(i.Value)] = &h
}
// look at the original and then look through the new.
seenLeft := make(map[string]*low.ValueReference[any])
seenRight := make(map[string]*low.ValueReference[any])
for i := range l {
h := l[i]
seenLeft[strings.ToLower(i.Value)] = &h
}
for i := range r {
h := r[i]
seenRight[strings.ToLower(i.Value)] = &h
}
var changes []*Change
for i := range seenLeft {
var changes []*Change
for i := range seenLeft {
CheckForObjectAdditionOrRemoval[any](seenLeft, seenRight, i, &changes, false, true)
CheckForObjectAdditionOrRemoval[any](seenLeft, seenRight, i, &changes, false, true)
if seenRight[i] != nil {
var props []*PropertyCheck
if seenRight[i] != nil {
var props []*PropertyCheck
props = append(props, &PropertyCheck{
LeftNode: seenLeft[i].ValueNode,
RightNode: seenRight[i].ValueNode,
Label: i,
Changes: &changes,
Breaking: false,
Original: seenLeft[i].Value,
New: seenRight[i].Value,
})
props = append(props, &PropertyCheck{
LeftNode: seenLeft[i].ValueNode,
RightNode: seenRight[i].ValueNode,
Label: i,
Changes: &changes,
Breaking: false,
Original: seenLeft[i].Value,
New: seenRight[i].Value,
})
// check properties
CheckProperties(props)
}
}
for i := range seenRight {
if seenLeft[i] == nil {
CheckForObjectAdditionOrRemoval[any](seenLeft, seenRight, i, &changes, false, true)
}
}
ex := new(ExtensionChanges)
ex.PropertyChanges = NewPropertyChanges(changes)
if ex.TotalChanges() <= 0 {
return nil
}
return ex
// check properties
CheckProperties(props)
}
}
for i := range seenRight {
if seenLeft[i] == nil {
CheckForObjectAdditionOrRemoval[any](seenLeft, seenRight, i, &changes, false, true)
}
}
ex := new(ExtensionChanges)
ex.PropertyChanges = NewPropertyChanges(changes)
if ex.TotalChanges() <= 0 {
return nil
}
return ex
}
// CheckExtensions is a helper method to un-pack a left and right model that contains extensions. Once unpacked
// the extensions are compared and returns a pointer to ExtensionChanges. If nothing changed, nil is returned.
func CheckExtensions[T low.HasExtensions[T]](l, r T) *ExtensionChanges {
var lExt, rExt map[low.KeyReference[string]]low.ValueReference[any]
if len(l.GetExtensions()) > 0 {
lExt = l.GetExtensions()
}
if len(r.GetExtensions()) > 0 {
rExt = r.GetExtensions()
}
return CompareExtensions(lExt, rExt)
var lExt, rExt map[low.KeyReference[string]]low.ValueReference[any]
if len(l.GetExtensions()) > 0 {
lExt = l.GetExtensions()
}
if len(r.GetExtensions()) > 0 {
rExt = r.GetExtensions()
}
return CompareExtensions(lExt, rExt)
}

View File

@@ -4,79 +4,79 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/datamodel/low/v3"
)
// ExternalDocChanges represents changes made to any ExternalDoc object from an OpenAPI document.
type ExternalDocChanges struct {
*PropertyChanges
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
*PropertyChanges
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
// GetAllChanges returns a slice of all changes made between Example objects
func (e *ExternalDocChanges) GetAllChanges() []*Change {
var changes []*Change
changes = append(changes, e.Changes...)
if e.ExtensionChanges != nil {
changes = append(changes, e.ExtensionChanges.GetAllChanges()...)
}
return changes
var changes []*Change
changes = append(changes, e.Changes...)
if e.ExtensionChanges != nil {
changes = append(changes, e.ExtensionChanges.GetAllChanges()...)
}
return changes
}
// TotalChanges returns a count of everything that changed
func (e *ExternalDocChanges) TotalChanges() int {
c := e.PropertyChanges.TotalChanges()
if e.ExtensionChanges != nil {
c += e.ExtensionChanges.TotalChanges()
}
return c
c := e.PropertyChanges.TotalChanges()
if e.ExtensionChanges != nil {
c += e.ExtensionChanges.TotalChanges()
}
return c
}
// TotalBreakingChanges always returns 0 for ExternalDoc objects, they are non-binding.
func (e *ExternalDocChanges) TotalBreakingChanges() int {
return 0
return 0
}
// CompareExternalDocs will compare a left (original) and a right (new) slice of ValueReference
// nodes for any changes between them. If there are changes, then a pointer to ExternalDocChanges
// is returned, otherwise if nothing changed - then nil is returned.
func CompareExternalDocs(l, r *base.ExternalDoc) *ExternalDocChanges {
var changes []*Change
var props []*PropertyCheck
var changes []*Change
var props []*PropertyCheck
// URL
props = append(props, &PropertyCheck{
LeftNode: l.URL.ValueNode,
RightNode: r.URL.ValueNode,
Label: v3.URLLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// URL
props = append(props, &PropertyCheck{
LeftNode: l.URL.ValueNode,
RightNode: r.URL.ValueNode,
Label: v3.URLLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// description.
props = append(props, &PropertyCheck{
LeftNode: l.Description.ValueNode,
RightNode: r.Description.ValueNode,
Label: v3.DescriptionLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// description.
props = append(props, &PropertyCheck{
LeftNode: l.Description.ValueNode,
RightNode: r.Description.ValueNode,
Label: v3.DescriptionLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// check everything.
CheckProperties(props)
// check everything.
CheckProperties(props)
dc := new(ExternalDocChanges)
dc.PropertyChanges = NewPropertyChanges(changes)
dc := new(ExternalDocChanges)
dc.PropertyChanges = NewPropertyChanges(changes)
// check extensions
dc.ExtensionChanges = CheckExtensions(l, r)
if dc.TotalChanges() <= 0 {
return nil
}
return dc
// check extensions
dc.ExtensionChanges = CheckExtensions(l, r)
if dc.TotalChanges() <= 0 {
return nil
}
return dc
}

View File

@@ -4,277 +4,277 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"reflect"
"github.com/pb33f/libopenapi/datamodel/low"
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"reflect"
)
// HeaderChanges represents changes made between two Header objects. Supports both Swagger and OpenAPI header
// objects, V2 only property Items is broken out into its own.
type HeaderChanges struct {
*PropertyChanges
SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"`
ExamplesChanges map[string]*ExampleChanges `json:"examples,omitempty" yaml:"examples,omitempty"`
ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
*PropertyChanges
SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"`
ExamplesChanges map[string]*ExampleChanges `json:"examples,omitempty" yaml:"examples,omitempty"`
ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
// Items only supported by Swagger (V2)
ItemsChanges *ItemsChanges `json:"items,omitempty" yaml:"items,omitempty"`
// Items only supported by Swagger (V2)
ItemsChanges *ItemsChanges `json:"items,omitempty" yaml:"items,omitempty"`
}
// GetAllChanges returns a slice of all changes made between Header objects
func (h *HeaderChanges) GetAllChanges() []*Change {
var changes []*Change
changes = append(changes, h.Changes...)
for k := range h.ExamplesChanges {
changes = append(changes, h.ExamplesChanges[k].GetAllChanges()...)
}
for k := range h.ContentChanges {
changes = append(changes, h.ContentChanges[k].GetAllChanges()...)
}
if h.ExtensionChanges != nil {
changes = append(changes, h.ExtensionChanges.GetAllChanges()...)
}
if h.SchemaChanges != nil {
changes = append(changes, h.SchemaChanges.GetAllChanges()...)
}
if h.ItemsChanges != nil {
changes = append(changes, h.ItemsChanges.GetAllChanges()...)
}
return changes
var changes []*Change
changes = append(changes, h.Changes...)
for k := range h.ExamplesChanges {
changes = append(changes, h.ExamplesChanges[k].GetAllChanges()...)
}
for k := range h.ContentChanges {
changes = append(changes, h.ContentChanges[k].GetAllChanges()...)
}
if h.ExtensionChanges != nil {
changes = append(changes, h.ExtensionChanges.GetAllChanges()...)
}
if h.SchemaChanges != nil {
changes = append(changes, h.SchemaChanges.GetAllChanges()...)
}
if h.ItemsChanges != nil {
changes = append(changes, h.ItemsChanges.GetAllChanges()...)
}
return changes
}
// TotalChanges returns the total number of changes made between two Header objects.
func (h *HeaderChanges) TotalChanges() int {
c := h.PropertyChanges.TotalChanges()
for k := range h.ExamplesChanges {
c += h.ExamplesChanges[k].TotalChanges()
}
for k := range h.ContentChanges {
c += h.ContentChanges[k].TotalChanges()
}
if h.ExtensionChanges != nil {
c += h.ExtensionChanges.TotalChanges()
}
if h.SchemaChanges != nil {
c += h.SchemaChanges.TotalChanges()
}
if h.ItemsChanges != nil {
c += h.ItemsChanges.TotalChanges()
}
return c
c := h.PropertyChanges.TotalChanges()
for k := range h.ExamplesChanges {
c += h.ExamplesChanges[k].TotalChanges()
}
for k := range h.ContentChanges {
c += h.ContentChanges[k].TotalChanges()
}
if h.ExtensionChanges != nil {
c += h.ExtensionChanges.TotalChanges()
}
if h.SchemaChanges != nil {
c += h.SchemaChanges.TotalChanges()
}
if h.ItemsChanges != nil {
c += h.ItemsChanges.TotalChanges()
}
return c
}
// TotalBreakingChanges returns the total number of breaking changes made between two Header instances.
func (h *HeaderChanges) TotalBreakingChanges() int {
c := h.PropertyChanges.TotalBreakingChanges()
for k := range h.ContentChanges {
c += h.ContentChanges[k].TotalBreakingChanges()
}
if h.ItemsChanges != nil {
c += h.ItemsChanges.TotalBreakingChanges()
}
if h.SchemaChanges != nil {
c += h.SchemaChanges.TotalBreakingChanges()
}
return c
c := h.PropertyChanges.TotalBreakingChanges()
for k := range h.ContentChanges {
c += h.ContentChanges[k].TotalBreakingChanges()
}
if h.ItemsChanges != nil {
c += h.ItemsChanges.TotalBreakingChanges()
}
if h.SchemaChanges != nil {
c += h.SchemaChanges.TotalBreakingChanges()
}
return c
}
// shared header properties
func addOpenAPIHeaderProperties(left, right low.OpenAPIHeader, changes *[]*Change) []*PropertyCheck {
var props []*PropertyCheck
var props []*PropertyCheck
// style
addPropertyCheck(&props, left.GetStyle().ValueNode, right.GetStyle().ValueNode,
left.GetStyle(), right.GetStyle(), changes, v3.StyleLabel, false)
// style
addPropertyCheck(&props, left.GetStyle().ValueNode, right.GetStyle().ValueNode,
left.GetStyle(), right.GetStyle(), changes, v3.StyleLabel, false)
// allow reserved
addPropertyCheck(&props, left.GetAllowReserved().ValueNode, right.GetAllowReserved().ValueNode,
left.GetAllowReserved(), right.GetAllowReserved(), changes, v3.AllowReservedLabel, false)
// allow reserved
addPropertyCheck(&props, left.GetAllowReserved().ValueNode, right.GetAllowReserved().ValueNode,
left.GetAllowReserved(), right.GetAllowReserved(), changes, v3.AllowReservedLabel, false)
// allow empty value
addPropertyCheck(&props, left.GetAllowEmptyValue().ValueNode, right.GetAllowEmptyValue().ValueNode,
left.GetAllowEmptyValue(), right.GetAllowEmptyValue(), changes, v3.AllowEmptyValueLabel, true)
// allow empty value
addPropertyCheck(&props, left.GetAllowEmptyValue().ValueNode, right.GetAllowEmptyValue().ValueNode,
left.GetAllowEmptyValue(), right.GetAllowEmptyValue(), changes, v3.AllowEmptyValueLabel, true)
// explode
addPropertyCheck(&props, left.GetExplode().ValueNode, right.GetExplode().ValueNode,
left.GetExplode(), right.GetExplode(), changes, v3.ExplodeLabel, false)
// explode
addPropertyCheck(&props, left.GetExplode().ValueNode, right.GetExplode().ValueNode,
left.GetExplode(), right.GetExplode(), changes, v3.ExplodeLabel, false)
// example
addPropertyCheck(&props, left.GetExample().ValueNode, right.GetExample().ValueNode,
left.GetExample(), right.GetExample(), changes, v3.ExampleLabel, false)
// example
addPropertyCheck(&props, left.GetExample().ValueNode, right.GetExample().ValueNode,
left.GetExample(), right.GetExample(), changes, v3.ExampleLabel, false)
// deprecated
addPropertyCheck(&props, left.GetDeprecated().ValueNode, right.GetDeprecated().ValueNode,
left.GetDeprecated(), right.GetDeprecated(), changes, v3.DeprecatedLabel, false)
// deprecated
addPropertyCheck(&props, left.GetDeprecated().ValueNode, right.GetDeprecated().ValueNode,
left.GetDeprecated(), right.GetDeprecated(), changes, v3.DeprecatedLabel, false)
// required
addPropertyCheck(&props, left.GetRequired().ValueNode, right.GetRequired().ValueNode,
left.GetRequired(), right.GetRequired(), changes, v3.RequiredLabel, true)
// required
addPropertyCheck(&props, left.GetRequired().ValueNode, right.GetRequired().ValueNode,
left.GetRequired(), right.GetRequired(), changes, v3.RequiredLabel, true)
return props
return props
}
// swagger only properties
func addSwaggerHeaderProperties(left, right low.SwaggerHeader, changes *[]*Change) []*PropertyCheck {
var props []*PropertyCheck
var props []*PropertyCheck
// type
addPropertyCheck(&props, left.GetType().ValueNode, right.GetType().ValueNode,
left.GetType(), right.GetType(), changes, v3.TypeLabel, true)
// type
addPropertyCheck(&props, left.GetType().ValueNode, right.GetType().ValueNode,
left.GetType(), right.GetType(), changes, v3.TypeLabel, true)
// format
addPropertyCheck(&props, left.GetFormat().ValueNode, right.GetFormat().ValueNode,
left.GetFormat(), right.GetFormat(), changes, v3.FormatLabel, true)
// format
addPropertyCheck(&props, left.GetFormat().ValueNode, right.GetFormat().ValueNode,
left.GetFormat(), right.GetFormat(), changes, v3.FormatLabel, true)
// collection format
addPropertyCheck(&props, left.GetCollectionFormat().ValueNode, right.GetCollectionFormat().ValueNode,
left.GetCollectionFormat(), right.GetCollectionFormat(), changes, v3.CollectionFormatLabel, true)
// collection format
addPropertyCheck(&props, left.GetCollectionFormat().ValueNode, right.GetCollectionFormat().ValueNode,
left.GetCollectionFormat(), right.GetCollectionFormat(), changes, v3.CollectionFormatLabel, true)
// maximum
addPropertyCheck(&props, left.GetMaximum().ValueNode, right.GetMaximum().ValueNode,
left.GetMaximum(), right.GetMaximum(), changes, v3.MaximumLabel, true)
// maximum
addPropertyCheck(&props, left.GetMaximum().ValueNode, right.GetMaximum().ValueNode,
left.GetMaximum(), right.GetMaximum(), changes, v3.MaximumLabel, true)
// minimum
addPropertyCheck(&props, left.GetMinimum().ValueNode, right.GetMinimum().ValueNode,
left.GetMinimum(), right.GetMinimum(), changes, v3.MinimumLabel, true)
// minimum
addPropertyCheck(&props, left.GetMinimum().ValueNode, right.GetMinimum().ValueNode,
left.GetMinimum(), right.GetMinimum(), changes, v3.MinimumLabel, true)
// exclusive maximum
addPropertyCheck(&props, left.GetExclusiveMaximum().ValueNode, right.GetExclusiveMaximum().ValueNode,
left.GetExclusiveMaximum(), right.GetExclusiveMaximum(), changes, v3.ExclusiveMaximumLabel, true)
// exclusive maximum
addPropertyCheck(&props, left.GetExclusiveMaximum().ValueNode, right.GetExclusiveMaximum().ValueNode,
left.GetExclusiveMaximum(), right.GetExclusiveMaximum(), changes, v3.ExclusiveMaximumLabel, true)
// exclusive minimum
addPropertyCheck(&props, left.GetExclusiveMinimum().ValueNode, right.GetExclusiveMinimum().ValueNode,
left.GetExclusiveMinimum(), right.GetExclusiveMinimum(), changes, v3.ExclusiveMinimumLabel, true)
// exclusive minimum
addPropertyCheck(&props, left.GetExclusiveMinimum().ValueNode, right.GetExclusiveMinimum().ValueNode,
left.GetExclusiveMinimum(), right.GetExclusiveMinimum(), changes, v3.ExclusiveMinimumLabel, true)
// max length
addPropertyCheck(&props, left.GetMaxLength().ValueNode, right.GetMaxLength().ValueNode,
left.GetMaxLength(), right.GetMaxLength(), changes, v3.MaxLengthLabel, true)
// max length
addPropertyCheck(&props, left.GetMaxLength().ValueNode, right.GetMaxLength().ValueNode,
left.GetMaxLength(), right.GetMaxLength(), changes, v3.MaxLengthLabel, true)
// min length
addPropertyCheck(&props, left.GetMinLength().ValueNode, right.GetMinLength().ValueNode,
left.GetMinLength(), right.GetMinLength(), changes, v3.MinLengthLabel, true)
// min length
addPropertyCheck(&props, left.GetMinLength().ValueNode, right.GetMinLength().ValueNode,
left.GetMinLength(), right.GetMinLength(), changes, v3.MinLengthLabel, true)
// pattern
addPropertyCheck(&props, left.GetPattern().ValueNode, right.GetPattern().ValueNode,
left.GetPattern(), right.GetPattern(), changes, v3.PatternLabel, true)
// pattern
addPropertyCheck(&props, left.GetPattern().ValueNode, right.GetPattern().ValueNode,
left.GetPattern(), right.GetPattern(), changes, v3.PatternLabel, true)
// max items
addPropertyCheck(&props, left.GetMaxItems().ValueNode, right.GetMaxItems().ValueNode,
left.GetMaxItems(), right.GetMaxItems(), changes, v3.MaxItemsLabel, true)
// max items
addPropertyCheck(&props, left.GetMaxItems().ValueNode, right.GetMaxItems().ValueNode,
left.GetMaxItems(), right.GetMaxItems(), changes, v3.MaxItemsLabel, true)
// min items
addPropertyCheck(&props, left.GetMinItems().ValueNode, right.GetMinItems().ValueNode,
left.GetMinItems(), right.GetMinItems(), changes, v3.MinItemsLabel, true)
// min items
addPropertyCheck(&props, left.GetMinItems().ValueNode, right.GetMinItems().ValueNode,
left.GetMinItems(), right.GetMinItems(), changes, v3.MinItemsLabel, true)
// unique items
addPropertyCheck(&props, left.GetUniqueItems().ValueNode, right.GetUniqueItems().ValueNode,
left.GetUniqueItems(), right.GetUniqueItems(), changes, v3.UniqueItemsLabel, true)
// unique items
addPropertyCheck(&props, left.GetUniqueItems().ValueNode, right.GetUniqueItems().ValueNode,
left.GetUniqueItems(), right.GetUniqueItems(), changes, v3.UniqueItemsLabel, true)
// multiple of
addPropertyCheck(&props, left.GetMultipleOf().ValueNode, right.GetMultipleOf().ValueNode,
left.GetMultipleOf(), right.GetMultipleOf(), changes, v3.MultipleOfLabel, true)
// multiple of
addPropertyCheck(&props, left.GetMultipleOf().ValueNode, right.GetMultipleOf().ValueNode,
left.GetMultipleOf(), right.GetMultipleOf(), changes, v3.MultipleOfLabel, true)
return props
return props
}
// common header properties
func addCommonHeaderProperties(left, right low.HasDescription, changes *[]*Change) []*PropertyCheck {
var props []*PropertyCheck
var props []*PropertyCheck
// description
addPropertyCheck(&props, left.GetDescription().ValueNode, right.GetDescription().ValueNode,
left.GetDescription(), right.GetDescription(), changes, v3.DescriptionLabel, false)
// description
addPropertyCheck(&props, left.GetDescription().ValueNode, right.GetDescription().ValueNode,
left.GetDescription(), right.GetDescription(), changes, v3.DescriptionLabel, false)
return props
return props
}
// CompareHeadersV2 is a Swagger compatible, typed signature used for other generic functions. It simply
// wraps CompareHeaders and provides nothing other that a typed interface.
func CompareHeadersV2(l, r *v2.Header) *HeaderChanges {
return CompareHeaders(l, r)
return CompareHeaders(l, r)
}
// CompareHeadersV3 is an OpenAPI 3+ compatible, typed signature used for other generic functions. It simply
// wraps CompareHeaders and provides nothing other that a typed interface.
func CompareHeadersV3(l, r *v3.Header) *HeaderChanges {
return CompareHeaders(l, r)
return CompareHeaders(l, r)
}
// CompareHeaders will compare left and right Header objects (any version of Swagger or OpenAPI) and return
// a pointer to HeaderChanges with anything that has changed, or nil if nothing changed.
func CompareHeaders(l, r any) *HeaderChanges {
var changes []*Change
var props []*PropertyCheck
hc := new(HeaderChanges)
var changes []*Change
var props []*PropertyCheck
hc := new(HeaderChanges)
// handle swagger.
if reflect.TypeOf(&v2.Header{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Header{}) == reflect.TypeOf(r) {
lHeader := l.(*v2.Header)
rHeader := r.(*v2.Header)
// handle swagger.
if reflect.TypeOf(&v2.Header{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Header{}) == reflect.TypeOf(r) {
lHeader := l.(*v2.Header)
rHeader := r.(*v2.Header)
// perform hash check to avoid further processing
if low.AreEqual(lHeader, rHeader) {
return nil
}
// perform hash check to avoid further processing
if low.AreEqual(lHeader, rHeader) {
return nil
}
props = append(props, addCommonHeaderProperties(lHeader, rHeader, &changes)...)
props = append(props, addSwaggerHeaderProperties(lHeader, rHeader, &changes)...)
props = append(props, addCommonHeaderProperties(lHeader, rHeader, &changes)...)
props = append(props, addSwaggerHeaderProperties(lHeader, rHeader, &changes)...)
// enum
if len(lHeader.Enum.Value) > 0 || len(rHeader.Enum.Value) > 0 {
ExtractRawValueSliceChanges(lHeader.Enum.Value, rHeader.Enum.Value, &changes, v3.EnumLabel, true)
}
// enum
if len(lHeader.Enum.Value) > 0 || len(rHeader.Enum.Value) > 0 {
ExtractRawValueSliceChanges(lHeader.Enum.Value, rHeader.Enum.Value, &changes, v3.EnumLabel, true)
}
// items
if !lHeader.Items.IsEmpty() && !rHeader.Items.IsEmpty() {
if !low.AreEqual(lHeader.Items.Value, rHeader.Items.Value) {
hc.ItemsChanges = CompareItems(lHeader.Items.Value, rHeader.Items.Value)
}
}
if lHeader.Items.IsEmpty() && !rHeader.Items.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.ItemsLabel, nil,
rHeader.Items.ValueNode, true, nil, rHeader.Items.Value)
}
if !lHeader.Items.IsEmpty() && rHeader.Items.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.SchemaLabel, lHeader.Items.ValueNode,
nil, true, lHeader.Items.Value, nil)
}
hc.ExtensionChanges = CompareExtensions(lHeader.Extensions, rHeader.Extensions)
}
// items
if !lHeader.Items.IsEmpty() && !rHeader.Items.IsEmpty() {
if !low.AreEqual(lHeader.Items.Value, rHeader.Items.Value) {
hc.ItemsChanges = CompareItems(lHeader.Items.Value, rHeader.Items.Value)
}
}
if lHeader.Items.IsEmpty() && !rHeader.Items.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.ItemsLabel, nil,
rHeader.Items.ValueNode, true, nil, rHeader.Items.Value)
}
if !lHeader.Items.IsEmpty() && rHeader.Items.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.SchemaLabel, lHeader.Items.ValueNode,
nil, true, lHeader.Items.Value, nil)
}
hc.ExtensionChanges = CompareExtensions(lHeader.Extensions, rHeader.Extensions)
}
// handle OpenAPI
if reflect.TypeOf(&v3.Header{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Header{}) == reflect.TypeOf(r) {
lHeader := l.(*v3.Header)
rHeader := r.(*v3.Header)
// handle OpenAPI
if reflect.TypeOf(&v3.Header{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Header{}) == reflect.TypeOf(r) {
lHeader := l.(*v3.Header)
rHeader := r.(*v3.Header)
// perform hash check to avoid further processing
if low.AreEqual(lHeader, rHeader) {
return nil
}
// perform hash check to avoid further processing
if low.AreEqual(lHeader, rHeader) {
return nil
}
props = append(props, addCommonHeaderProperties(lHeader, rHeader, &changes)...)
props = append(props, addOpenAPIHeaderProperties(lHeader, rHeader, &changes)...)
props = append(props, addCommonHeaderProperties(lHeader, rHeader, &changes)...)
props = append(props, addOpenAPIHeaderProperties(lHeader, rHeader, &changes)...)
// header
if !lHeader.Schema.IsEmpty() || !rHeader.Schema.IsEmpty() {
hc.SchemaChanges = CompareSchemas(lHeader.Schema.Value, rHeader.Schema.Value)
}
// header
if !lHeader.Schema.IsEmpty() || !rHeader.Schema.IsEmpty() {
hc.SchemaChanges = CompareSchemas(lHeader.Schema.Value, rHeader.Schema.Value)
}
// examples
hc.ExamplesChanges = CheckMapForChanges(lHeader.Examples.Value, rHeader.Examples.Value,
&changes, v3.ExamplesLabel, CompareExamples)
// examples
hc.ExamplesChanges = CheckMapForChanges(lHeader.Examples.Value, rHeader.Examples.Value,
&changes, v3.ExamplesLabel, CompareExamples)
// content
hc.ContentChanges = CheckMapForChanges(lHeader.Content.Value, rHeader.Content.Value,
&changes, v3.ContentLabel, CompareMediaTypes)
// content
hc.ContentChanges = CheckMapForChanges(lHeader.Content.Value, rHeader.Content.Value,
&changes, v3.ContentLabel, CompareMediaTypes)
hc.ExtensionChanges = CompareExtensions(lHeader.Extensions, rHeader.Extensions)
hc.ExtensionChanges = CompareExtensions(lHeader.Extensions, rHeader.Extensions)
}
CheckProperties(props)
hc.PropertyChanges = NewPropertyChanges(changes)
return hc
}
CheckProperties(props)
hc.PropertyChanges = NewPropertyChanges(changes)
return hc
}

View File

@@ -17,15 +17,15 @@ type InfoChanges struct {
// GetAllChanges returns a slice of all changes made between Info objects
func (i *InfoChanges) GetAllChanges() []*Change {
var changes []*Change
changes = append(changes, i.Changes...)
if i.ContactChanges != nil {
changes = append(changes, i.ContactChanges.GetAllChanges()...)
}
if i.LicenseChanges != nil {
changes = append(changes, i.LicenseChanges.GetAllChanges()...)
}
return changes
var changes []*Change
changes = append(changes, i.Changes...)
if i.ContactChanges != nil {
changes = append(changes, i.ContactChanges.GetAllChanges()...)
}
if i.LicenseChanges != nil {
changes = append(changes, i.LicenseChanges.GetAllChanges()...)
}
return changes
}
// TotalChanges represents the total number of changes made to an Info object.

View File

@@ -4,17 +4,17 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
)
func TestCompareInfo_DescriptionAdded(t *testing.T) {
left := `title: a nice spec
left := `title: a nice spec
termsOfService: https://pb33f.io/terms
version: '1.2.3'
contact:
@@ -23,7 +23,7 @@ contact:
license:
name: MIT`
right := `title: a nice spec
right := `title: a nice spec
termsOfService: https://pb33f.io/terms
version: '1.2.3'
description: this is a description
@@ -33,29 +33,29 @@ contact:
license:
name: MIT`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc base.Info
var rDoc base.Info
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc base.Info
var rDoc base.Info
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareInfo(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType)
assert.Equal(t, v3.DescriptionLabel, extChanges.Changes[0].Property)
// compare.
extChanges := CompareInfo(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType)
assert.Equal(t, v3.DescriptionLabel, extChanges.Changes[0].Property)
}
func TestCompareInfo_TitleRemoved(t *testing.T) {
left := `title: a nice spec
left := `title: a nice spec
termsOfService: https://pb33f.io/terms
version: '1.2.3'
description: this is a description
@@ -65,7 +65,7 @@ contact:
license:
name: MIT`
right := `termsOfService: https://pb33f.io/terms
right := `termsOfService: https://pb33f.io/terms
version: '1.2.3'
description: this is a description
contact:
@@ -74,29 +74,29 @@ contact:
license:
name: MIT`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc base.Info
var rDoc base.Info
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc base.Info
var rDoc base.Info
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareInfo(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType)
assert.Equal(t, v3.TitleLabel, extChanges.Changes[0].Property)
// compare.
extChanges := CompareInfo(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType)
assert.Equal(t, v3.TitleLabel, extChanges.Changes[0].Property)
}
func TestCompareInfo_VersionModified(t *testing.T) {
left := `title: a nice spec
left := `title: a nice spec
termsOfService: https://pb33f.io/terms
version: '1.2.3'
contact:
@@ -105,7 +105,7 @@ contact:
license:
name: MIT`
right := `title: a nice spec
right := `title: a nice spec
termsOfService: https://pb33f.io/terms
version: '99.99'
contact:
@@ -114,29 +114,29 @@ contact:
license:
name: MIT`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc base.Info
var rDoc base.Info
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc base.Info
var rDoc base.Info
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareInfo(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, Modified, extChanges.Changes[0].ChangeType)
assert.Equal(t, v3.VersionLabel, extChanges.Changes[0].Property)
// compare.
extChanges := CompareInfo(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, Modified, extChanges.Changes[0].ChangeType)
assert.Equal(t, v3.VersionLabel, extChanges.Changes[0].Property)
}
func TestCompareInfo_RemoveLicense(t *testing.T) {
left := `title: a nice spec
left := `title: a nice spec
termsOfService: https://pb33f.io/terms
version: '1.2.3'
contact:
@@ -145,43 +145,43 @@ contact:
license:
name: MIT`
right := `title: a nice spec
right := `title: a nice spec
termsOfService: https://pb33f.io/terms
version: '1.2.3'
contact:
name: buckaroo
email: buckaroo@pb33f.io`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc base.Info
var rDoc base.Info
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc base.Info
var rDoc base.Info
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareInfo(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
assert.Equal(t, v3.LicenseLabel, extChanges.Changes[0].Property)
// compare.
extChanges := CompareInfo(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
assert.Equal(t, v3.LicenseLabel, extChanges.Changes[0].Property)
}
func TestCompareInfo_AddLicense(t *testing.T) {
left := `title: a nice spec
left := `title: a nice spec
termsOfService: https://pb33f.io/terms
version: '1.2.3'
contact:
name: buckaroo
email: buckaroo@pb33f.io`
right := `title: a nice spec
right := `title: a nice spec
termsOfService: https://pb33f.io/terms
version: '1.2.3'
contact:
@@ -190,29 +190,29 @@ contact:
license:
name: MIT`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc base.Info
var rDoc base.Info
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc base.Info
var rDoc base.Info
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareInfo(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType)
assert.Equal(t, v3.LicenseLabel, extChanges.Changes[0].Property)
// compare.
extChanges := CompareInfo(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType)
assert.Equal(t, v3.LicenseLabel, extChanges.Changes[0].Property)
}
func TestCompareInfo_LicenseChanged(t *testing.T) {
left := `title: a nice spec
left := `title: a nice spec
termsOfService: https://pb33f.io/terms
version: '1.2.3'
contact:
@@ -221,7 +221,7 @@ contact:
license:
name: MIT`
right := `title: a nice spec
right := `title: a nice spec
termsOfService: https://pb33f.io/terms
version: '1.2.3'
contact:
@@ -230,35 +230,35 @@ contact:
license:
name: Apache`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc base.Info
var rDoc base.Info
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc base.Info
var rDoc base.Info
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareInfo(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, Modified, extChanges.LicenseChanges.Changes[0].ChangeType)
assert.Equal(t, v3.NameLabel, extChanges.LicenseChanges.Changes[0].Property)
// compare.
extChanges := CompareInfo(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, Modified, extChanges.LicenseChanges.Changes[0].ChangeType)
assert.Equal(t, v3.NameLabel, extChanges.LicenseChanges.Changes[0].Property)
}
func TestCompareInfo_AddContact(t *testing.T) {
left := `title: a nice spec
left := `title: a nice spec
termsOfService: https://pb33f.io/terms
version: '1.2.3'
license:
name: MIT`
right := `title: a nice spec
right := `title: a nice spec
termsOfService: https://pb33f.io/terms
version: '1.2.3'
contact:
@@ -267,29 +267,29 @@ contact:
license:
name: MIT`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc base.Info
var rDoc base.Info
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc base.Info
var rDoc base.Info
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareInfo(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType)
assert.Equal(t, v3.ContactLabel, extChanges.Changes[0].Property)
// compare.
extChanges := CompareInfo(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType)
assert.Equal(t, v3.ContactLabel, extChanges.Changes[0].Property)
}
func TestCompareInfo_RemoveContact(t *testing.T) {
left := `title: a nice spec
left := `title: a nice spec
termsOfService: https://pb33f.io/terms
version: '1.2.3'
contact:
@@ -298,35 +298,35 @@ contact:
license:
name: MIT`
right := `title: a nice spec
right := `title: a nice spec
termsOfService: https://pb33f.io/terms
version: '1.2.3'
license:
name: MIT`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc base.Info
var rDoc base.Info
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc base.Info
var rDoc base.Info
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareInfo(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
assert.Equal(t, v3.ContactLabel, extChanges.Changes[0].Property)
// compare.
extChanges := CompareInfo(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
assert.Equal(t, v3.ContactLabel, extChanges.Changes[0].Property)
}
func TestCompareInfo_ContactModified(t *testing.T) {
left := `title: a nice spec
left := `title: a nice spec
termsOfService: https://pb33f.io/terms
version: '1.2.3'
contact:
@@ -335,7 +335,7 @@ contact:
license:
name: MIT`
right := `title: a nice spec
right := `title: a nice spec
termsOfService: https://pb33f.io/terms
version: '1.2.3'
contact:
@@ -344,30 +344,30 @@ contact:
license:
name: MIT`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc base.Info
var rDoc base.Info
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc base.Info
var rDoc base.Info
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareInfo(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, Modified, extChanges.ContactChanges.Changes[0].ChangeType)
assert.Equal(t, v3.NameLabel, extChanges.ContactChanges.Changes[0].Property)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
// compare.
extChanges := CompareInfo(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, Modified, extChanges.ContactChanges.Changes[0].ChangeType)
assert.Equal(t, v3.NameLabel, extChanges.ContactChanges.Changes[0].Property)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}
func TestCompareInfo_Equal(t *testing.T) {
left := `title: a nice spec
left := `title: a nice spec
termsOfService: https://pb33f.io/terms
version: '1.2.3'
contact:
@@ -376,7 +376,7 @@ contact:
license:
name: MIT`
right := `title: a nice spec
right := `title: a nice spec
termsOfService: https://pb33f.io/terms
version: '1.2.3'
contact:
@@ -385,17 +385,17 @@ contact:
license:
name: MIT`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc base.Info
var rDoc base.Info
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc base.Info
var rDoc base.Info
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareInfo(&lDoc, &rDoc)

View File

@@ -4,45 +4,45 @@
package model
import (
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
)
// ItemsChanges represent changes found between a left (original) and right (modified) object. Items is only
// used by Swagger documents.
type ItemsChanges struct {
*PropertyChanges
ItemsChanges *ItemsChanges `json:"items,omitempty" yaml:"items,omitempty"`
*PropertyChanges
ItemsChanges *ItemsChanges `json:"items,omitempty" yaml:"items,omitempty"`
}
// GetAllChanges returns a slice of all changes made between Items objects
func (i *ItemsChanges) GetAllChanges() []*Change {
var changes []*Change
changes = append(changes, i.Changes...)
if i.ItemsChanges != nil {
changes = append(changes, i.ItemsChanges.GetAllChanges()...)
}
return changes
var changes []*Change
changes = append(changes, i.Changes...)
if i.ItemsChanges != nil {
changes = append(changes, i.ItemsChanges.GetAllChanges()...)
}
return changes
}
// TotalChanges returns the total number of changes found between two Items objects
// This is a recursive function because Items can contain Items. Be careful!
func (i *ItemsChanges) TotalChanges() int {
c := i.PropertyChanges.TotalChanges()
if i.ItemsChanges != nil {
c += i.ItemsChanges.TotalChanges()
}
return c
c := i.PropertyChanges.TotalChanges()
if i.ItemsChanges != nil {
c += i.ItemsChanges.TotalChanges()
}
return c
}
// TotalBreakingChanges returns the total number of breaking changes found between two Swagger Items objects
// This is a recursive method, Items are recursive, be careful!
func (i *ItemsChanges) TotalBreakingChanges() int {
c := i.PropertyChanges.TotalBreakingChanges()
if i.ItemsChanges != nil {
c += i.ItemsChanges.TotalBreakingChanges()
}
return c
c := i.PropertyChanges.TotalBreakingChanges()
if i.ItemsChanges != nil {
c += i.ItemsChanges.TotalBreakingChanges()
}
return c
}
// CompareItems compares two sets of Swagger Item objects. If there are any changes found then a pointer to
@@ -52,37 +52,37 @@ func (i *ItemsChanges) TotalBreakingChanges() int {
// runaway code if not using the resolver's circular reference checking.
func CompareItems(l, r *v2.Items) *ItemsChanges {
var changes []*Change
var props []*PropertyCheck
var changes []*Change
var props []*PropertyCheck
ic := new(ItemsChanges)
ic := new(ItemsChanges)
// header is identical to items, except for a description.
props = append(props, addSwaggerHeaderProperties(l, r, &changes)...)
CheckProperties(props)
// header is identical to items, except for a description.
props = append(props, addSwaggerHeaderProperties(l, r, &changes)...)
CheckProperties(props)
if !l.Items.IsEmpty() && !r.Items.IsEmpty() {
// inline, check hashes, if they don't match, compare.
if l.Items.Value.Hash() != r.Items.Value.Hash() {
// compare.
ic.ItemsChanges = CompareItems(l.Items.Value, r.Items.Value)
}
if !l.Items.IsEmpty() && !r.Items.IsEmpty() {
// inline, check hashes, if they don't match, compare.
if l.Items.Value.Hash() != r.Items.Value.Hash() {
// compare.
ic.ItemsChanges = CompareItems(l.Items.Value, r.Items.Value)
}
}
if l.Items.IsEmpty() && !r.Items.IsEmpty() {
// added items
CreateChange(&changes, PropertyAdded, v3.ItemsLabel,
nil, r.Items.GetValueNode(), true, nil, r.Items.GetValue())
}
if !l.Items.IsEmpty() && r.Items.IsEmpty() {
// removed items
CreateChange(&changes, PropertyRemoved, v3.ItemsLabel,
l.Items.GetValueNode(), nil, true, l.Items.GetValue(),
nil)
}
ic.PropertyChanges = NewPropertyChanges(changes)
if ic.TotalChanges() <= 0 {
return nil
}
return ic
}
if l.Items.IsEmpty() && !r.Items.IsEmpty() {
// added items
CreateChange(&changes, PropertyAdded, v3.ItemsLabel,
nil, r.Items.GetValueNode(), true, nil, r.Items.GetValue())
}
if !l.Items.IsEmpty() && r.Items.IsEmpty() {
// removed items
CreateChange(&changes, PropertyRemoved, v3.ItemsLabel,
l.Items.GetValueNode(), nil, true, l.Items.GetValue(),
nil)
}
ic.PropertyChanges = NewPropertyChanges(changes)
if ic.TotalChanges() <= 0 {
return nil
}
return ic
}

View File

@@ -4,136 +4,136 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
)
func TestCompareItems(t *testing.T) {
left := `type: string`
left := `type: string`
right := `type: int`
right := `type: int`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.Items
var rDoc v2.Items
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v2.Items
var rDoc v2.Items
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
changes := CompareItems(&lDoc, &rDoc)
assert.NotNil(t, changes)
assert.Equal(t, 1, changes.TotalChanges())
assert.Len(t, changes.GetAllChanges(), 1)
assert.Equal(t, 1, changes.TotalBreakingChanges())
assert.Equal(t, v3.TypeLabel, changes.Changes[0].Property)
// compare.
changes := CompareItems(&lDoc, &rDoc)
assert.NotNil(t, changes)
assert.Equal(t, 1, changes.TotalChanges())
assert.Len(t, changes.GetAllChanges(), 1)
assert.Equal(t, 1, changes.TotalBreakingChanges())
assert.Equal(t, v3.TypeLabel, changes.Changes[0].Property)
}
func TestCompareItems_RecursiveCheck(t *testing.T) {
left := `type: string
left := `type: string
items:
type: string`
right := `type: int
right := `type: int
items:
type: int`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.Items
var rDoc v2.Items
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v2.Items
var rDoc v2.Items
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
changes := CompareItems(&lDoc, &rDoc)
assert.NotNil(t, changes)
assert.Equal(t, 2, changes.TotalChanges())
assert.Len(t, changes.GetAllChanges(), 2)
assert.Equal(t, 2, changes.TotalBreakingChanges())
assert.Equal(t, 1, changes.ItemsChanges.TotalChanges())
assert.Equal(t, v3.TypeLabel, changes.Changes[0].Property)
// compare.
changes := CompareItems(&lDoc, &rDoc)
assert.NotNil(t, changes)
assert.Equal(t, 2, changes.TotalChanges())
assert.Len(t, changes.GetAllChanges(), 2)
assert.Equal(t, 2, changes.TotalBreakingChanges())
assert.Equal(t, 1, changes.ItemsChanges.TotalChanges())
assert.Equal(t, v3.TypeLabel, changes.Changes[0].Property)
}
func TestCompareItems_AddItems(t *testing.T) {
left := `type: int`
left := `type: int`
right := `type: int
right := `type: int
items:
type: int`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.Items
var rDoc v2.Items
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v2.Items
var rDoc v2.Items
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
changes := CompareItems(&lDoc, &rDoc)
assert.NotNil(t, changes)
assert.Equal(t, 1, changes.TotalChanges())
assert.Len(t, changes.GetAllChanges(), 1)
assert.Equal(t, 1, changes.TotalBreakingChanges())
assert.Equal(t, v3.ItemsLabel, changes.Changes[0].Property)
assert.Equal(t, PropertyAdded, changes.Changes[0].ChangeType)
// compare.
changes := CompareItems(&lDoc, &rDoc)
assert.NotNil(t, changes)
assert.Equal(t, 1, changes.TotalChanges())
assert.Len(t, changes.GetAllChanges(), 1)
assert.Equal(t, 1, changes.TotalBreakingChanges())
assert.Equal(t, v3.ItemsLabel, changes.Changes[0].Property)
assert.Equal(t, PropertyAdded, changes.Changes[0].ChangeType)
}
func TestCompareItems_RemoveItems(t *testing.T) {
left := `type: int`
left := `type: int`
right := `type: int
right := `type: int
items:
type: int`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.Items
var rDoc v2.Items
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v2.Items
var rDoc v2.Items
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
changes := CompareItems(&rDoc, &lDoc)
assert.NotNil(t, changes)
assert.Equal(t, 1, changes.TotalChanges())
assert.Len(t, changes.GetAllChanges(), 1)
assert.Equal(t, 1, changes.TotalBreakingChanges())
assert.Equal(t, v3.ItemsLabel, changes.Changes[0].Property)
assert.Equal(t, PropertyRemoved, changes.Changes[0].ChangeType)
// compare.
changes := CompareItems(&rDoc, &lDoc)
assert.NotNil(t, changes)
assert.Equal(t, 1, changes.TotalChanges())
assert.Len(t, changes.GetAllChanges(), 1)
assert.Equal(t, 1, changes.TotalBreakingChanges())
assert.Equal(t, v3.ItemsLabel, changes.Changes[0].Property)
assert.Equal(t, PropertyRemoved, changes.Changes[0].ChangeType)
}
func TestCompareItems_RefVsInlineIdentical(t *testing.T) {
left := `swagger: 2.0
left := `swagger: 2.0
definitions:
thing:
type: string
@@ -149,7 +149,7 @@ paths:
items:
$ref: '#/definitions/thing'`
right := `swagger: 2.0
right := `swagger: 2.0
definitions:
thing:
type: string
@@ -165,15 +165,15 @@ paths:
items:
type: bool`
leftDoc, rightDoc := test_BuildDocv2(left, right)
leftDoc, rightDoc := test_BuildDocv2(left, right)
// extract left reference schema and non reference schema.
lItems := leftDoc.Paths.Value.FindPath("/a/path").Value.Get.Value.Parameters.
Value[0].Value.Items.Value
rItems := rightDoc.Paths.Value.FindPath("/a/path").Value.Get.Value.Parameters.
Value[0].Value.Items.Value
// extract left reference schema and non reference schema.
lItems := leftDoc.Paths.Value.FindPath("/a/path").Value.Get.Value.Parameters.
Value[0].Value.Items.Value
rItems := rightDoc.Paths.Value.FindPath("/a/path").Value.Get.Value.Parameters.
Value[0].Value.Items.Value
// compare.
changes := CompareItems(lItems, rItems)
// compare.
changes := CompareItems(lItems, rItems)
assert.Nil(t, changes)
}

View File

@@ -4,28 +4,28 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/datamodel/low/v3"
)
// LicenseChanges represent changes to a License object that is a child of Info object. Part of an OpenAPI document
type LicenseChanges struct {
*PropertyChanges
*PropertyChanges
}
// GetAllChanges returns a slice of all changes made between License objects
func (l *LicenseChanges) GetAllChanges() []*Change {
return l.Changes
return l.Changes
}
// TotalChanges represents the total number of changes made to a License instance.
func (l *LicenseChanges) TotalChanges() int {
return l.PropertyChanges.TotalChanges()
return l.PropertyChanges.TotalChanges()
}
// TotalBreakingChanges always returns 0 for License objects, they are non-binding.
func (l *LicenseChanges) TotalBreakingChanges() int {
return 0
return 0
}
// CompareLicense will check a left (original) and right (new) License object for any changes. If there
@@ -33,38 +33,38 @@ func (l *LicenseChanges) TotalBreakingChanges() int {
// returns nil.
func CompareLicense(l, r *base.License) *LicenseChanges {
var changes []*Change
var props []*PropertyCheck
var changes []*Change
var props []*PropertyCheck
// check URL
props = append(props, &PropertyCheck{
LeftNode: l.URL.ValueNode,
RightNode: r.URL.ValueNode,
Label: v3.URLLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// check URL
props = append(props, &PropertyCheck{
LeftNode: l.URL.ValueNode,
RightNode: r.URL.ValueNode,
Label: v3.URLLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// check name
props = append(props, &PropertyCheck{
LeftNode: l.Name.ValueNode,
RightNode: r.Name.ValueNode,
Label: v3.NameLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// check name
props = append(props, &PropertyCheck{
LeftNode: l.Name.ValueNode,
RightNode: r.Name.ValueNode,
Label: v3.NameLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// check everything.
CheckProperties(props)
// check everything.
CheckProperties(props)
lc := new(LicenseChanges)
lc.PropertyChanges = NewPropertyChanges(changes)
if lc.TotalChanges() <= 0 {
return nil
}
return lc
lc := new(LicenseChanges)
lc.PropertyChanges = NewPropertyChanges(changes)
if lc.TotalChanges() <= 0 {
return nil
}
return lc
}

View File

@@ -4,157 +4,157 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
)
// LinkChanges represent changes made between two OpenAPI Link Objects.
type LinkChanges struct {
*PropertyChanges
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
ServerChanges *ServerChanges `json:"server,omitempty" yaml:"server,omitempty"`
*PropertyChanges
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
ServerChanges *ServerChanges `json:"server,omitempty" yaml:"server,omitempty"`
}
// GetAllChanges returns a slice of all changes made between Link objects
func (l *LinkChanges) GetAllChanges() []*Change {
var changes []*Change
changes = append(changes, l.Changes...)
if l.ServerChanges != nil {
changes = append(changes, l.ServerChanges.GetAllChanges()...)
}
if l.ExtensionChanges != nil {
changes = append(changes, l.ExtensionChanges.GetAllChanges()...)
}
return changes
var changes []*Change
changes = append(changes, l.Changes...)
if l.ServerChanges != nil {
changes = append(changes, l.ServerChanges.GetAllChanges()...)
}
if l.ExtensionChanges != nil {
changes = append(changes, l.ExtensionChanges.GetAllChanges()...)
}
return changes
}
// TotalChanges returns the total changes made between OpenAPI Link objects
func (l *LinkChanges) TotalChanges() int {
c := l.PropertyChanges.TotalChanges()
if l.ExtensionChanges != nil {
c += l.ExtensionChanges.TotalChanges()
}
if l.ServerChanges != nil {
c += l.ServerChanges.TotalChanges()
}
return c
c := l.PropertyChanges.TotalChanges()
if l.ExtensionChanges != nil {
c += l.ExtensionChanges.TotalChanges()
}
if l.ServerChanges != nil {
c += l.ServerChanges.TotalChanges()
}
return c
}
// TotalBreakingChanges returns the number of breaking changes made between two OpenAPI Link Objects
func (l *LinkChanges) TotalBreakingChanges() int {
c := l.PropertyChanges.TotalBreakingChanges()
if l.ServerChanges != nil {
c += l.ServerChanges.TotalBreakingChanges()
}
return c
c := l.PropertyChanges.TotalBreakingChanges()
if l.ServerChanges != nil {
c += l.ServerChanges.TotalBreakingChanges()
}
return c
}
// CompareLinks checks a left and right OpenAPI Link for any changes. If they are found, returns a pointer to
// LinkChanges, and returns nil if nothing is found.
func CompareLinks(l, r *v3.Link) *LinkChanges {
if low.AreEqual(l, r) {
return nil
}
if low.AreEqual(l, r) {
return nil
}
var props []*PropertyCheck
var changes []*Change
var props []*PropertyCheck
var changes []*Change
// operation ref
props = append(props, &PropertyCheck{
LeftNode: l.OperationRef.ValueNode,
RightNode: r.OperationRef.ValueNode,
Label: v3.OperationRefLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// operation ref
props = append(props, &PropertyCheck{
LeftNode: l.OperationRef.ValueNode,
RightNode: r.OperationRef.ValueNode,
Label: v3.OperationRefLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// operation id
props = append(props, &PropertyCheck{
LeftNode: l.OperationId.ValueNode,
RightNode: r.OperationId.ValueNode,
Label: v3.OperationIdLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// operation id
props = append(props, &PropertyCheck{
LeftNode: l.OperationId.ValueNode,
RightNode: r.OperationId.ValueNode,
Label: v3.OperationIdLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// request body
props = append(props, &PropertyCheck{
LeftNode: l.RequestBody.ValueNode,
RightNode: r.RequestBody.ValueNode,
Label: v3.RequestBodyLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// request body
props = append(props, &PropertyCheck{
LeftNode: l.RequestBody.ValueNode,
RightNode: r.RequestBody.ValueNode,
Label: v3.RequestBodyLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// description
props = append(props, &PropertyCheck{
LeftNode: l.Description.ValueNode,
RightNode: r.Description.ValueNode,
Label: v3.DescriptionLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// description
props = append(props, &PropertyCheck{
LeftNode: l.Description.ValueNode,
RightNode: r.Description.ValueNode,
Label: v3.DescriptionLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
CheckProperties(props)
lc := new(LinkChanges)
lc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions)
CheckProperties(props)
lc := new(LinkChanges)
lc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions)
// server
if !l.Server.IsEmpty() && !r.Server.IsEmpty() {
if !low.AreEqual(l.Server.Value, r.Server.Value) {
lc.ServerChanges = CompareServers(l.Server.Value, r.Server.Value)
}
}
if !l.Server.IsEmpty() && r.Server.IsEmpty() {
CreateChange(&changes, PropertyRemoved, v3.ServerLabel,
l.Server.ValueNode, nil, true,
l.Server.Value, nil)
}
if l.Server.IsEmpty() && !r.Server.IsEmpty() {
CreateChange(&changes, PropertyAdded, v3.ServerLabel,
nil, r.Server.ValueNode, true,
nil, r.Server.Value)
}
// server
if !l.Server.IsEmpty() && !r.Server.IsEmpty() {
if !low.AreEqual(l.Server.Value, r.Server.Value) {
lc.ServerChanges = CompareServers(l.Server.Value, r.Server.Value)
}
}
if !l.Server.IsEmpty() && r.Server.IsEmpty() {
CreateChange(&changes, PropertyRemoved, v3.ServerLabel,
l.Server.ValueNode, nil, true,
l.Server.Value, nil)
}
if l.Server.IsEmpty() && !r.Server.IsEmpty() {
CreateChange(&changes, PropertyAdded, v3.ServerLabel,
nil, r.Server.ValueNode, true,
nil, r.Server.Value)
}
// parameters
lValues := make(map[string]low.ValueReference[string])
rValues := make(map[string]low.ValueReference[string])
for i := range l.Parameters.Value {
lValues[i.Value] = l.Parameters.Value[i]
}
for i := range r.Parameters.Value {
rValues[i.Value] = r.Parameters.Value[i]
}
for k := range lValues {
if _, ok := rValues[k]; !ok {
CreateChange(&changes, ObjectRemoved, v3.ParametersLabel,
lValues[k].ValueNode, nil, true,
k, nil)
continue
}
if lValues[k].Value != rValues[k].Value {
CreateChange(&changes, Modified, v3.ParametersLabel,
lValues[k].ValueNode, rValues[k].ValueNode, true,
k, k)
}
// parameters
lValues := make(map[string]low.ValueReference[string])
rValues := make(map[string]low.ValueReference[string])
for i := range l.Parameters.Value {
lValues[i.Value] = l.Parameters.Value[i]
}
for i := range r.Parameters.Value {
rValues[i.Value] = r.Parameters.Value[i]
}
for k := range lValues {
if _, ok := rValues[k]; !ok {
CreateChange(&changes, ObjectRemoved, v3.ParametersLabel,
lValues[k].ValueNode, nil, true,
k, nil)
continue
}
if lValues[k].Value != rValues[k].Value {
CreateChange(&changes, Modified, v3.ParametersLabel,
lValues[k].ValueNode, rValues[k].ValueNode, true,
k, k)
}
}
for k := range rValues {
if _, ok := lValues[k]; !ok {
CreateChange(&changes, ObjectAdded, v3.ParametersLabel,
nil, rValues[k].ValueNode, true,
nil, k)
}
}
}
for k := range rValues {
if _, ok := lValues[k]; !ok {
CreateChange(&changes, ObjectAdded, v3.ParametersLabel,
nil, rValues[k].ValueNode, true,
nil, k)
}
}
lc.PropertyChanges = NewPropertyChanges(changes)
return lc
lc.PropertyChanges = NewPropertyChanges(changes)
return lc
}

View File

@@ -4,16 +4,16 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
)
func TestCompareLinks(t *testing.T) {
left := `operationId: someOperation
left := `operationId: someOperation
requestBody: expression-says-what
description: a nice link
server:
@@ -21,29 +21,29 @@ server:
parameters:
nice: rice`
right := left
right := left
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Link
var rDoc v3.Link
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.Link
var rDoc v3.Link
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareLinks(&lDoc, &rDoc)
assert.Nil(t, extChanges)
// compare.
extChanges := CompareLinks(&lDoc, &rDoc)
assert.Nil(t, extChanges)
}
func TestCompareLinks_ModifyExtension(t *testing.T) {
left := `operationId: someOperation
left := `operationId: someOperation
requestBody: expression-says-what
description: a nice link
server:
@@ -52,7 +52,7 @@ parameters:
nice: rice
x-cake: tasty`
right := `operationId: someOperation
right := `operationId: someOperation
requestBody: expression-says-what
description: a nice link
server:
@@ -61,30 +61,30 @@ parameters:
nice: rice
x-cake: very tasty`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Link
var rDoc v3.Link
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.Link
var rDoc v3.Link
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareLinks(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, Modified, extChanges.ExtensionChanges.Changes[0].ChangeType)
// compare.
extChanges := CompareLinks(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, Modified, extChanges.ExtensionChanges.Changes[0].ChangeType)
}
func TestCompareLinks_ModifyServer(t *testing.T) {
left := `operationId: someOperation
left := `operationId: someOperation
requestBody: expression-says-what
description: a nice link
server:
@@ -92,7 +92,7 @@ server:
parameters:
nice: rice`
right := `operationId: someOperation
right := `operationId: someOperation
requestBody: expression-says-what
description: a nice link
server:
@@ -100,35 +100,35 @@ server:
parameters:
nice: rice`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Link
var rDoc v3.Link
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.Link
var rDoc v3.Link
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareLinks(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, Modified, extChanges.ServerChanges.Changes[0].ChangeType)
// compare.
extChanges := CompareLinks(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, Modified, extChanges.ServerChanges.Changes[0].ChangeType)
}
func TestCompareLinks_AddServer(t *testing.T) {
left := `operationId: someOperation
left := `operationId: someOperation
requestBody: expression-says-what
description: a nice link
parameters:
nice: rice`
right := `operationId: someOperation
right := `operationId: someOperation
requestBody: expression-says-what
description: a nice link
server:
@@ -136,35 +136,35 @@ server:
parameters:
nice: rice`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Link
var rDoc v3.Link
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.Link
var rDoc v3.Link
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareLinks(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType)
// compare.
extChanges := CompareLinks(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType)
}
func TestCompareLinks_RemoveServer(t *testing.T) {
left := `operationId: someOperation
left := `operationId: someOperation
requestBody: expression-says-what
description: a nice link
parameters:
nice: rice`
right := `operationId: someOperation
right := `operationId: someOperation
requestBody: expression-says-what
description: a nice link
server:
@@ -172,29 +172,29 @@ server:
parameters:
nice: rice`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Link
var rDoc v3.Link
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.Link
var rDoc v3.Link
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareLinks(&rDoc, &lDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType)
// compare.
extChanges := CompareLinks(&rDoc, &lDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType)
}
func TestCompareLinks_ModifyParam(t *testing.T) {
left := `operationId: someOperation
left := `operationId: someOperation
requestBody: expression-says-what
description: a nice link
server:
@@ -202,7 +202,7 @@ server:
parameters:
nice: cake`
right := `operationId: someOperation
right := `operationId: someOperation
requestBody: expression-says-what
description: a nice link
server:
@@ -210,32 +210,32 @@ server:
parameters:
nice: rice`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Link
var rDoc v3.Link
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.Link
var rDoc v3.Link
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareLinks(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, Modified, extChanges.Changes[0].ChangeType)
assert.Equal(t, "nice", extChanges.Changes[0].NewObject)
assert.Equal(t, "cake", extChanges.Changes[0].Original)
assert.Equal(t, "rice", extChanges.Changes[0].New)
// compare.
extChanges := CompareLinks(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, Modified, extChanges.Changes[0].ChangeType)
assert.Equal(t, "nice", extChanges.Changes[0].NewObject)
assert.Equal(t, "cake", extChanges.Changes[0].Original)
assert.Equal(t, "rice", extChanges.Changes[0].New)
}
func TestCompareLinks_AddParam(t *testing.T) {
left := `operationId: someOperation
left := `operationId: someOperation
requestBody: expression-says-what
description: a nice link
server:
@@ -243,7 +243,7 @@ server:
parameters:
nice: cake`
right := `operationId: someOperation
right := `operationId: someOperation
requestBody: expression-says-what
description: a nice link
server:
@@ -252,31 +252,31 @@ parameters:
nice: cake
hot: pizza`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Link
var rDoc v3.Link
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.Link
var rDoc v3.Link
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareLinks(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType)
assert.Equal(t, "hot", extChanges.Changes[0].NewObject)
assert.Equal(t, "pizza", extChanges.Changes[0].New)
// compare.
extChanges := CompareLinks(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType)
assert.Equal(t, "hot", extChanges.Changes[0].NewObject)
assert.Equal(t, "pizza", extChanges.Changes[0].New)
}
func TestCompareLinks_RemoveParam(t *testing.T) {
left := `operationId: someOperation
left := `operationId: someOperation
requestBody: expression-says-what
description: a nice link
server:
@@ -284,7 +284,7 @@ server:
parameters:
nice: cake`
right := `operationId: someOperation
right := `operationId: someOperation
requestBody: expression-says-what
description: a nice link
server:
@@ -293,24 +293,24 @@ parameters:
nice: cake
hot: pizza`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Link
var rDoc v3.Link
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.Link
var rDoc v3.Link
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareLinks(&rDoc, &lDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
assert.Equal(t, "hot", extChanges.Changes[0].OriginalObject)
// compare.
extChanges := CompareLinks(&rDoc, &lDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
assert.Equal(t, "hot", extChanges.Changes[0].OriginalObject)
assert.Equal(t, "pizza", extChanges.Changes[0].Original)
}

View File

@@ -4,146 +4,146 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
)
// MediaTypeChanges represent changes made between two OpenAPI MediaType instances.
type MediaTypeChanges struct {
*PropertyChanges
SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
ExampleChanges map[string]*ExampleChanges `json:"examples,omitempty" yaml:"examples,omitempty"`
EncodingChanges map[string]*EncodingChanges `json:"encoding,omitempty" yaml:"encoding,omitempty"`
*PropertyChanges
SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
ExampleChanges map[string]*ExampleChanges `json:"examples,omitempty" yaml:"examples,omitempty"`
EncodingChanges map[string]*EncodingChanges `json:"encoding,omitempty" yaml:"encoding,omitempty"`
}
// GetAllChanges returns a slice of all changes made between MediaType objects
func (m *MediaTypeChanges) GetAllChanges() []*Change {
var changes []*Change
changes = append(changes, m.Changes...)
if m.SchemaChanges != nil {
changes = append(changes, m.SchemaChanges.GetAllChanges()...)
}
for k := range m.ExampleChanges {
changes = append(changes, m.ExampleChanges[k].GetAllChanges()...)
}
for k := range m.EncodingChanges {
changes = append(changes, m.EncodingChanges[k].GetAllChanges()...)
}
if m.ExtensionChanges != nil {
changes = append(changes, m.ExtensionChanges.GetAllChanges()...)
}
return changes
var changes []*Change
changes = append(changes, m.Changes...)
if m.SchemaChanges != nil {
changes = append(changes, m.SchemaChanges.GetAllChanges()...)
}
for k := range m.ExampleChanges {
changes = append(changes, m.ExampleChanges[k].GetAllChanges()...)
}
for k := range m.EncodingChanges {
changes = append(changes, m.EncodingChanges[k].GetAllChanges()...)
}
if m.ExtensionChanges != nil {
changes = append(changes, m.ExtensionChanges.GetAllChanges()...)
}
return changes
}
// TotalChanges returns the total number of changes between two MediaType instances.
func (m *MediaTypeChanges) TotalChanges() int {
c := m.PropertyChanges.TotalChanges()
for k := range m.ExampleChanges {
c += m.ExampleChanges[k].TotalChanges()
}
if m.SchemaChanges != nil {
c += m.SchemaChanges.TotalChanges()
}
if len(m.EncodingChanges) > 0 {
for i := range m.EncodingChanges {
c += m.EncodingChanges[i].TotalChanges()
}
}
if m.ExtensionChanges != nil {
c += m.ExtensionChanges.TotalChanges()
}
return c
c := m.PropertyChanges.TotalChanges()
for k := range m.ExampleChanges {
c += m.ExampleChanges[k].TotalChanges()
}
if m.SchemaChanges != nil {
c += m.SchemaChanges.TotalChanges()
}
if len(m.EncodingChanges) > 0 {
for i := range m.EncodingChanges {
c += m.EncodingChanges[i].TotalChanges()
}
}
if m.ExtensionChanges != nil {
c += m.ExtensionChanges.TotalChanges()
}
return c
}
// TotalBreakingChanges returns the total number of breaking changes made between two MediaType instances.
func (m *MediaTypeChanges) TotalBreakingChanges() int {
c := m.PropertyChanges.TotalBreakingChanges()
for k := range m.ExampleChanges {
c += m.ExampleChanges[k].TotalBreakingChanges()
}
if m.SchemaChanges != nil {
c += m.SchemaChanges.TotalBreakingChanges()
}
if len(m.EncodingChanges) > 0 {
for i := range m.EncodingChanges {
c += m.EncodingChanges[i].TotalBreakingChanges()
}
}
return c
c := m.PropertyChanges.TotalBreakingChanges()
for k := range m.ExampleChanges {
c += m.ExampleChanges[k].TotalBreakingChanges()
}
if m.SchemaChanges != nil {
c += m.SchemaChanges.TotalBreakingChanges()
}
if len(m.EncodingChanges) > 0 {
for i := range m.EncodingChanges {
c += m.EncodingChanges[i].TotalBreakingChanges()
}
}
return c
}
// CompareMediaTypes compares a left and a right MediaType object for any changes. If found, a pointer to a
// MediaTypeChanges instance is returned, otherwise nothing is returned.
func CompareMediaTypes(l, r *v3.MediaType) *MediaTypeChanges {
var props []*PropertyCheck
var changes []*Change
var props []*PropertyCheck
var changes []*Change
mc := new(MediaTypeChanges)
mc := new(MediaTypeChanges)
if low.AreEqual(l, r) {
return nil
}
if low.AreEqual(l, r) {
return nil
}
// Example
if !l.Example.IsEmpty() && !r.Example.IsEmpty() {
if (utils.IsNodeMap(l.Example.ValueNode) && utils.IsNodeMap(r.Example.ValueNode)) ||
(utils.IsNodeArray(l.Example.ValueNode) && utils.IsNodeArray(r.Example.ValueNode)) {
render, _ := yaml.Marshal(l.Example.ValueNode)
render, _ = utils.ConvertYAMLtoJSON(render)
l.Example.ValueNode.Value = string(render)
render, _ = yaml.Marshal(r.Example.ValueNode)
render, _ = utils.ConvertYAMLtoJSON(render)
r.Example.ValueNode.Value = string(render)
}
addPropertyCheck(&props, l.Example.ValueNode, r.Example.ValueNode,
l.Example.Value, r.Example.Value, &changes, v3.ExampleLabel, false)
// Example
if !l.Example.IsEmpty() && !r.Example.IsEmpty() {
if (utils.IsNodeMap(l.Example.ValueNode) && utils.IsNodeMap(r.Example.ValueNode)) ||
(utils.IsNodeArray(l.Example.ValueNode) && utils.IsNodeArray(r.Example.ValueNode)) {
render, _ := yaml.Marshal(l.Example.ValueNode)
render, _ = utils.ConvertYAMLtoJSON(render)
l.Example.ValueNode.Value = string(render)
render, _ = yaml.Marshal(r.Example.ValueNode)
render, _ = utils.ConvertYAMLtoJSON(render)
r.Example.ValueNode.Value = string(render)
}
addPropertyCheck(&props, l.Example.ValueNode, r.Example.ValueNode,
l.Example.Value, r.Example.Value, &changes, v3.ExampleLabel, false)
} else {
} else {
if utils.IsNodeMap(l.Example.ValueNode) || utils.IsNodeArray(l.Example.ValueNode) {
render, _ := yaml.Marshal(l.Example.ValueNode)
render, _ = utils.ConvertYAMLtoJSON(render)
l.Example.ValueNode.Value = string(render)
}
if utils.IsNodeMap(l.Example.ValueNode) || utils.IsNodeArray(l.Example.ValueNode) {
render, _ := yaml.Marshal(l.Example.ValueNode)
render, _ = utils.ConvertYAMLtoJSON(render)
l.Example.ValueNode.Value = string(render)
}
if utils.IsNodeMap(r.Example.ValueNode) || utils.IsNodeArray(r.Example.ValueNode) {
render, _ := yaml.Marshal(r.Example.ValueNode)
render, _ = utils.ConvertYAMLtoJSON(render)
r.Example.ValueNode.Value = string(render)
}
if utils.IsNodeMap(r.Example.ValueNode) || utils.IsNodeArray(r.Example.ValueNode) {
render, _ := yaml.Marshal(r.Example.ValueNode)
render, _ = utils.ConvertYAMLtoJSON(render)
r.Example.ValueNode.Value = string(render)
}
addPropertyCheck(&props, l.Example.ValueNode, r.Example.ValueNode,
l.Example.Value, r.Example.Value, &changes, v3.ExampleLabel, false)
}
addPropertyCheck(&props, l.Example.ValueNode, r.Example.ValueNode,
l.Example.Value, r.Example.Value, &changes, v3.ExampleLabel, false)
}
CheckProperties(props)
CheckProperties(props)
// schema
if !l.Schema.IsEmpty() && !r.Schema.IsEmpty() {
mc.SchemaChanges = CompareSchemas(l.Schema.Value, r.Schema.Value)
}
if !l.Schema.IsEmpty() && r.Schema.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.SchemaLabel, l.Schema.ValueNode,
nil, true, l.Schema.Value, nil)
}
if l.Schema.IsEmpty() && !r.Schema.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.SchemaLabel, nil,
r.Schema.ValueNode, true, nil, r.Schema.Value)
}
// schema
if !l.Schema.IsEmpty() && !r.Schema.IsEmpty() {
mc.SchemaChanges = CompareSchemas(l.Schema.Value, r.Schema.Value)
}
if !l.Schema.IsEmpty() && r.Schema.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.SchemaLabel, l.Schema.ValueNode,
nil, true, l.Schema.Value, nil)
}
if l.Schema.IsEmpty() && !r.Schema.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.SchemaLabel, nil,
r.Schema.ValueNode, true, nil, r.Schema.Value)
}
// examples
mc.ExampleChanges = CheckMapForChanges(l.Examples.Value, r.Examples.Value,
&changes, v3.ExamplesLabel, CompareExamples)
// examples
mc.ExampleChanges = CheckMapForChanges(l.Examples.Value, r.Examples.Value,
&changes, v3.ExamplesLabel, CompareExamples)
// encoding
mc.EncodingChanges = CheckMapForChanges(l.Encoding.Value, r.Encoding.Value,
&changes, v3.EncodingLabel, CompareEncoding)
// encoding
mc.EncodingChanges = CheckMapForChanges(l.Encoding.Value, r.Encoding.Value,
&changes, v3.EncodingLabel, CompareEncoding)
mc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions)
mc.PropertyChanges = NewPropertyChanges(changes)
return mc
mc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions)
mc.PropertyChanges = NewPropertyChanges(changes)
return mc
}

View File

@@ -4,254 +4,254 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v3"
)
// OAuthFlowsChanges represents changes found between two OpenAPI OAuthFlows objects.
type OAuthFlowsChanges struct {
*PropertyChanges
ImplicitChanges *OAuthFlowChanges `json:"implicit,omitempty" yaml:"implicit,omitempty"`
PasswordChanges *OAuthFlowChanges `json:"password,omitempty" yaml:"password,omitempty"`
ClientCredentialsChanges *OAuthFlowChanges `json:"clientCredentials,omitempty" yaml:"clientCredentials,omitempty"`
AuthorizationCodeChanges *OAuthFlowChanges `json:"authCode,omitempty" yaml:"authCode,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
*PropertyChanges
ImplicitChanges *OAuthFlowChanges `json:"implicit,omitempty" yaml:"implicit,omitempty"`
PasswordChanges *OAuthFlowChanges `json:"password,omitempty" yaml:"password,omitempty"`
ClientCredentialsChanges *OAuthFlowChanges `json:"clientCredentials,omitempty" yaml:"clientCredentials,omitempty"`
AuthorizationCodeChanges *OAuthFlowChanges `json:"authCode,omitempty" yaml:"authCode,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
// GetAllChanges returns a slice of all changes made between OAuthFlows objects
func (o *OAuthFlowsChanges) GetAllChanges() []*Change {
var changes []*Change
changes = append(changes, o.Changes...)
if o.ImplicitChanges != nil {
changes = append(changes, o.ImplicitChanges.GetAllChanges()...)
}
if o.PasswordChanges != nil {
changes = append(changes, o.PasswordChanges.GetAllChanges()...)
}
if o.ClientCredentialsChanges != nil {
changes = append(changes, o.ClientCredentialsChanges.GetAllChanges()...)
}
if o.AuthorizationCodeChanges != nil {
changes = append(changes, o.AuthorizationCodeChanges.GetAllChanges()...)
}
if o.ExtensionChanges != nil {
changes = append(changes, o.ImplicitChanges.GetAllChanges()...)
}
return changes
var changes []*Change
changes = append(changes, o.Changes...)
if o.ImplicitChanges != nil {
changes = append(changes, o.ImplicitChanges.GetAllChanges()...)
}
if o.PasswordChanges != nil {
changes = append(changes, o.PasswordChanges.GetAllChanges()...)
}
if o.ClientCredentialsChanges != nil {
changes = append(changes, o.ClientCredentialsChanges.GetAllChanges()...)
}
if o.AuthorizationCodeChanges != nil {
changes = append(changes, o.AuthorizationCodeChanges.GetAllChanges()...)
}
if o.ExtensionChanges != nil {
changes = append(changes, o.ImplicitChanges.GetAllChanges()...)
}
return changes
}
// TotalChanges returns the number of changes made between two OAuthFlows instances.
func (o *OAuthFlowsChanges) TotalChanges() int {
c := o.PropertyChanges.TotalChanges()
if o.ImplicitChanges != nil {
c += o.ImplicitChanges.TotalChanges()
}
if o.PasswordChanges != nil {
c += o.PasswordChanges.TotalChanges()
}
if o.ClientCredentialsChanges != nil {
c += o.ClientCredentialsChanges.TotalChanges()
}
if o.AuthorizationCodeChanges != nil {
c += o.AuthorizationCodeChanges.TotalChanges()
}
if o.ExtensionChanges != nil {
c += o.ExtensionChanges.TotalChanges()
}
return c
c := o.PropertyChanges.TotalChanges()
if o.ImplicitChanges != nil {
c += o.ImplicitChanges.TotalChanges()
}
if o.PasswordChanges != nil {
c += o.PasswordChanges.TotalChanges()
}
if o.ClientCredentialsChanges != nil {
c += o.ClientCredentialsChanges.TotalChanges()
}
if o.AuthorizationCodeChanges != nil {
c += o.AuthorizationCodeChanges.TotalChanges()
}
if o.ExtensionChanges != nil {
c += o.ExtensionChanges.TotalChanges()
}
return c
}
// TotalBreakingChanges returns the number of breaking changes made between two OAuthFlows objects.
func (o *OAuthFlowsChanges) TotalBreakingChanges() int {
c := o.PropertyChanges.TotalBreakingChanges()
if o.ImplicitChanges != nil {
c += o.ImplicitChanges.TotalBreakingChanges()
}
if o.PasswordChanges != nil {
c += o.PasswordChanges.TotalBreakingChanges()
}
if o.ClientCredentialsChanges != nil {
c += o.ClientCredentialsChanges.TotalBreakingChanges()
}
if o.AuthorizationCodeChanges != nil {
c += o.AuthorizationCodeChanges.TotalBreakingChanges()
}
return c
c := o.PropertyChanges.TotalBreakingChanges()
if o.ImplicitChanges != nil {
c += o.ImplicitChanges.TotalBreakingChanges()
}
if o.PasswordChanges != nil {
c += o.PasswordChanges.TotalBreakingChanges()
}
if o.ClientCredentialsChanges != nil {
c += o.ClientCredentialsChanges.TotalBreakingChanges()
}
if o.AuthorizationCodeChanges != nil {
c += o.AuthorizationCodeChanges.TotalBreakingChanges()
}
return c
}
// CompareOAuthFlows compares a left and right OAuthFlows object. If changes are found a pointer to *OAuthFlowsChanges
// is returned, otherwise nil is returned.
func CompareOAuthFlows(l, r *v3.OAuthFlows) *OAuthFlowsChanges {
if low.AreEqual(l, r) {
return nil
}
if low.AreEqual(l, r) {
return nil
}
oa := new(OAuthFlowsChanges)
var changes []*Change
oa := new(OAuthFlowsChanges)
var changes []*Change
// client credentials
if !l.ClientCredentials.IsEmpty() && !r.ClientCredentials.IsEmpty() {
oa.ClientCredentialsChanges = CompareOAuthFlow(l.ClientCredentials.Value, r.ClientCredentials.Value)
}
if !l.ClientCredentials.IsEmpty() && r.ClientCredentials.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.ClientCredentialsLabel,
l.ClientCredentials.ValueNode, nil, true,
l.ClientCredentials.Value, nil)
}
if l.ClientCredentials.IsEmpty() && !r.ClientCredentials.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.ClientCredentialsLabel,
nil, r.ClientCredentials.ValueNode, false,
nil, r.ClientCredentials.Value)
}
// client credentials
if !l.ClientCredentials.IsEmpty() && !r.ClientCredentials.IsEmpty() {
oa.ClientCredentialsChanges = CompareOAuthFlow(l.ClientCredentials.Value, r.ClientCredentials.Value)
}
if !l.ClientCredentials.IsEmpty() && r.ClientCredentials.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.ClientCredentialsLabel,
l.ClientCredentials.ValueNode, nil, true,
l.ClientCredentials.Value, nil)
}
if l.ClientCredentials.IsEmpty() && !r.ClientCredentials.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.ClientCredentialsLabel,
nil, r.ClientCredentials.ValueNode, false,
nil, r.ClientCredentials.Value)
}
// implicit
if !l.Implicit.IsEmpty() && !r.Implicit.IsEmpty() {
oa.ImplicitChanges = CompareOAuthFlow(l.Implicit.Value, r.Implicit.Value)
}
if !l.Implicit.IsEmpty() && r.Implicit.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.ImplicitLabel,
l.Implicit.ValueNode, nil, true,
l.Implicit.Value, nil)
}
if l.Implicit.IsEmpty() && !r.Implicit.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.ImplicitLabel,
nil, r.Implicit.ValueNode, false,
nil, r.Implicit.Value)
}
// implicit
if !l.Implicit.IsEmpty() && !r.Implicit.IsEmpty() {
oa.ImplicitChanges = CompareOAuthFlow(l.Implicit.Value, r.Implicit.Value)
}
if !l.Implicit.IsEmpty() && r.Implicit.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.ImplicitLabel,
l.Implicit.ValueNode, nil, true,
l.Implicit.Value, nil)
}
if l.Implicit.IsEmpty() && !r.Implicit.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.ImplicitLabel,
nil, r.Implicit.ValueNode, false,
nil, r.Implicit.Value)
}
// password
if !l.Password.IsEmpty() && !r.Password.IsEmpty() {
oa.PasswordChanges = CompareOAuthFlow(l.Password.Value, r.Password.Value)
}
if !l.Password.IsEmpty() && r.Password.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.PasswordLabel,
l.Password.ValueNode, nil, true,
l.Password.Value, nil)
}
if l.Password.IsEmpty() && !r.Password.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.PasswordLabel,
nil, r.Password.ValueNode, false,
nil, r.Password.Value)
}
// password
if !l.Password.IsEmpty() && !r.Password.IsEmpty() {
oa.PasswordChanges = CompareOAuthFlow(l.Password.Value, r.Password.Value)
}
if !l.Password.IsEmpty() && r.Password.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.PasswordLabel,
l.Password.ValueNode, nil, true,
l.Password.Value, nil)
}
if l.Password.IsEmpty() && !r.Password.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.PasswordLabel,
nil, r.Password.ValueNode, false,
nil, r.Password.Value)
}
// auth code
if !l.AuthorizationCode.IsEmpty() && !r.AuthorizationCode.IsEmpty() {
oa.AuthorizationCodeChanges = CompareOAuthFlow(l.AuthorizationCode.Value, r.AuthorizationCode.Value)
}
if !l.AuthorizationCode.IsEmpty() && r.AuthorizationCode.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.AuthorizationCodeLabel,
l.AuthorizationCode.ValueNode, nil, true,
l.AuthorizationCode.Value, nil)
}
if l.AuthorizationCode.IsEmpty() && !r.AuthorizationCode.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.AuthorizationCodeLabel,
nil, r.AuthorizationCode.ValueNode, false,
nil, r.AuthorizationCode.Value)
}
oa.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions)
oa.PropertyChanges = NewPropertyChanges(changes)
return oa
// auth code
if !l.AuthorizationCode.IsEmpty() && !r.AuthorizationCode.IsEmpty() {
oa.AuthorizationCodeChanges = CompareOAuthFlow(l.AuthorizationCode.Value, r.AuthorizationCode.Value)
}
if !l.AuthorizationCode.IsEmpty() && r.AuthorizationCode.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.AuthorizationCodeLabel,
l.AuthorizationCode.ValueNode, nil, true,
l.AuthorizationCode.Value, nil)
}
if l.AuthorizationCode.IsEmpty() && !r.AuthorizationCode.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.AuthorizationCodeLabel,
nil, r.AuthorizationCode.ValueNode, false,
nil, r.AuthorizationCode.Value)
}
oa.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions)
oa.PropertyChanges = NewPropertyChanges(changes)
return oa
}
// OAuthFlowChanges represents an OpenAPI OAuthFlow object.
type OAuthFlowChanges struct {
*PropertyChanges
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
*PropertyChanges
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
// GetAllChanges returns a slice of all changes made between OAuthFlow objects
func (o *OAuthFlowChanges) GetAllChanges() []*Change {
var changes []*Change
changes = append(changes, o.Changes...)
if o.ExtensionChanges != nil {
changes = append(changes, o.ExtensionChanges.GetAllChanges()...)
}
return changes
var changes []*Change
changes = append(changes, o.Changes...)
if o.ExtensionChanges != nil {
changes = append(changes, o.ExtensionChanges.GetAllChanges()...)
}
return changes
}
// TotalChanges returns the total number of changes made between two OAuthFlow objects
func (o *OAuthFlowChanges) TotalChanges() int {
c := o.PropertyChanges.TotalChanges()
if o.ExtensionChanges != nil {
c += o.ExtensionChanges.TotalChanges()
}
return c
c := o.PropertyChanges.TotalChanges()
if o.ExtensionChanges != nil {
c += o.ExtensionChanges.TotalChanges()
}
return c
}
// TotalBreakingChanges returns the total number of breaking changes made between two OAuthFlow objects
func (o *OAuthFlowChanges) TotalBreakingChanges() int {
return o.PropertyChanges.TotalBreakingChanges()
return o.PropertyChanges.TotalBreakingChanges()
}
// CompareOAuthFlow checks a left and a right OAuthFlow object for changes. If found, returns a pointer to
// an OAuthFlowChanges instance, or nil if nothing is found.
func CompareOAuthFlow(l, r *v3.OAuthFlow) *OAuthFlowChanges {
if low.AreEqual(l, r) {
return nil
}
if low.AreEqual(l, r) {
return nil
}
var changes []*Change
var props []*PropertyCheck
var changes []*Change
var props []*PropertyCheck
// authorization url
props = append(props, &PropertyCheck{
LeftNode: l.AuthorizationUrl.ValueNode,
RightNode: r.AuthorizationUrl.ValueNode,
Label: v3.AuthorizationUrlLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// authorization url
props = append(props, &PropertyCheck{
LeftNode: l.AuthorizationUrl.ValueNode,
RightNode: r.AuthorizationUrl.ValueNode,
Label: v3.AuthorizationUrlLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// token url
props = append(props, &PropertyCheck{
LeftNode: l.TokenUrl.ValueNode,
RightNode: r.TokenUrl.ValueNode,
Label: v3.TokenUrlLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// token url
props = append(props, &PropertyCheck{
LeftNode: l.TokenUrl.ValueNode,
RightNode: r.TokenUrl.ValueNode,
Label: v3.TokenUrlLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// refresh url
props = append(props, &PropertyCheck{
LeftNode: l.RefreshUrl.ValueNode,
RightNode: r.RefreshUrl.ValueNode,
Label: v3.RefreshUrlLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// refresh url
props = append(props, &PropertyCheck{
LeftNode: l.RefreshUrl.ValueNode,
RightNode: r.RefreshUrl.ValueNode,
Label: v3.RefreshUrlLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
CheckProperties(props)
CheckProperties(props)
for v := range l.Scopes.Value {
if r != nil && r.FindScope(v.Value) == nil {
CreateChange(&changes, ObjectRemoved, v3.Scopes,
l.Scopes.Value[v].ValueNode, nil, true,
v.Value, nil)
continue
}
if r != nil && r.FindScope(v.Value) != nil {
if l.Scopes.Value[v].Value != r.FindScope(v.Value).Value {
CreateChange(&changes, Modified, v3.Scopes,
l.Scopes.Value[v].ValueNode, r.FindScope(v.Value).ValueNode, true,
l.Scopes.Value[v].Value, r.FindScope(v.Value).Value)
}
}
}
for v := range r.Scopes.Value {
if l != nil && l.FindScope(v.Value) == nil {
CreateChange(&changes, ObjectAdded, v3.Scopes,
nil, r.Scopes.Value[v].ValueNode, false,
nil, v.Value)
}
}
oa := new(OAuthFlowChanges)
oa.PropertyChanges = NewPropertyChanges(changes)
oa.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions)
return oa
for v := range l.Scopes.Value {
if r != nil && r.FindScope(v.Value) == nil {
CreateChange(&changes, ObjectRemoved, v3.Scopes,
l.Scopes.Value[v].ValueNode, nil, true,
v.Value, nil)
continue
}
if r != nil && r.FindScope(v.Value) != nil {
if l.Scopes.Value[v].Value != r.FindScope(v.Value).Value {
CreateChange(&changes, Modified, v3.Scopes,
l.Scopes.Value[v].ValueNode, r.FindScope(v.Value).ValueNode, true,
l.Scopes.Value[v].Value, r.FindScope(v.Value).Value)
}
}
}
for v := range r.Scopes.Value {
if l != nil && l.FindScope(v.Value) == nil {
CreateChange(&changes, ObjectAdded, v3.Scopes,
nil, r.Scopes.Value[v].ValueNode, false,
nil, v.Value)
}
}
oa := new(OAuthFlowChanges)
oa.PropertyChanges = NewPropertyChanges(changes)
oa.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions)
return oa
}

View File

@@ -4,90 +4,90 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
"github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
)
func TestCompareOAuthFlow(t *testing.T) {
left := `authorizationUrl: cheese
left := `authorizationUrl: cheese
tokenUrl: biscuits
refreshUrl: cake
scopes:
riff: raff`
right := `authorizationUrl: cheese
right := `authorizationUrl: cheese
tokenUrl: biscuits
refreshUrl: cake
scopes:
riff: raff`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.OAuthFlow
var rDoc v3.OAuthFlow
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.OAuthFlow
var rDoc v3.OAuthFlow
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareOAuthFlow(&lDoc, &rDoc)
assert.Nil(t, extChanges)
// compare
extChanges := CompareOAuthFlow(&lDoc, &rDoc)
assert.Nil(t, extChanges)
}
func TestCompareOAuthFlow_Modified(t *testing.T) {
left := `authorizationUrl: toast
left := `authorizationUrl: toast
tokenUrl: biscuits
refreshUrl: roast
scopes:
riff: raff
x-burgers: nice`
right := `authorizationUrl: cheese
right := `authorizationUrl: cheese
tokenUrl: biscuits
refreshUrl: cake
scopes:
riff: raff
x-burgers: crispy`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.OAuthFlow
var rDoc v3.OAuthFlow
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.OAuthFlow
var rDoc v3.OAuthFlow
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareOAuthFlow(&lDoc, &rDoc)
assert.Equal(t, 3, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 3)
assert.Equal(t, 2, extChanges.TotalBreakingChanges())
// compare
extChanges := CompareOAuthFlow(&lDoc, &rDoc)
assert.Equal(t, 3, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 3)
assert.Equal(t, 2, extChanges.TotalBreakingChanges())
}
func TestCompareOAuthFlow_AddScope(t *testing.T) {
left := `authorizationUrl: toast
left := `authorizationUrl: toast
tokenUrl: biscuits
refreshUrl: roast
scopes:
riff: raff
x-burgers: nice`
right := `authorizationUrl: toast
right := `authorizationUrl: toast
tokenUrl: biscuits
refreshUrl: roast
scopes:
@@ -95,37 +95,37 @@ scopes:
tiff: taff
x-burgers: nice`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.OAuthFlow
var rDoc v3.OAuthFlow
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.OAuthFlow
var rDoc v3.OAuthFlow
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareOAuthFlow(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, "taff", extChanges.Changes[0].New)
assert.Equal(t, "tiff", extChanges.Changes[0].NewObject)
// compare
extChanges := CompareOAuthFlow(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, "taff", extChanges.Changes[0].New)
assert.Equal(t, "tiff", extChanges.Changes[0].NewObject)
}
func TestCompareOAuthFlow_RemoveScope(t *testing.T) {
left := `authorizationUrl: toast
left := `authorizationUrl: toast
tokenUrl: biscuits
refreshUrl: roast
scopes:
riff: raff
x-burgers: nice`
right := `authorizationUrl: toast
right := `authorizationUrl: toast
tokenUrl: biscuits
refreshUrl: roast
scopes:
@@ -133,68 +133,68 @@ scopes:
tiff: taff
x-burgers: nice`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.OAuthFlow
var rDoc v3.OAuthFlow
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.OAuthFlow
var rDoc v3.OAuthFlow
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareOAuthFlow(&rDoc, &lDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, "taff", extChanges.Changes[0].Original)
assert.Equal(t, "tiff", extChanges.Changes[0].OriginalObject)
// compare
extChanges := CompareOAuthFlow(&rDoc, &lDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, "taff", extChanges.Changes[0].Original)
assert.Equal(t, "tiff", extChanges.Changes[0].OriginalObject)
}
func TestCompareOAuthFlow_ModifyScope(t *testing.T) {
left := `authorizationUrl: toast
left := `authorizationUrl: toast
tokenUrl: biscuits
refreshUrl: roast
scopes:
riff: ruffles
x-burgers: nice`
right := `authorizationUrl: toast
right := `authorizationUrl: toast
tokenUrl: biscuits
refreshUrl: roast
scopes:
riff: raff
x-burgers: nice`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.OAuthFlow
var rDoc v3.OAuthFlow
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.OAuthFlow
var rDoc v3.OAuthFlow
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareOAuthFlow(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, "raff", extChanges.Changes[0].New)
assert.Equal(t, "raff", extChanges.Changes[0].NewObject)
assert.Equal(t, "ruffles", extChanges.Changes[0].Original)
assert.Equal(t, "ruffles", extChanges.Changes[0].OriginalObject)
// compare
extChanges := CompareOAuthFlow(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, "raff", extChanges.Changes[0].New)
assert.Equal(t, "raff", extChanges.Changes[0].NewObject)
assert.Equal(t, "ruffles", extChanges.Changes[0].Original)
assert.Equal(t, "ruffles", extChanges.Changes[0].OriginalObject)
}
func TestCompareOAuthFlows(t *testing.T) {
left := `implicit:
left := `implicit:
authorizationUrl: cheese
password:
authorizationUrl: cake
@@ -204,7 +204,7 @@ authorizationCode:
authorizationUrl: chalk
x-coke: cola`
right := `implicit:
right := `implicit:
authorizationUrl: cheese
password:
authorizationUrl: cake
@@ -214,27 +214,27 @@ authorizationCode:
authorizationUrl: chalk
x-coke: cola`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.OAuthFlows
var rDoc v3.OAuthFlows
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.OAuthFlows
var rDoc v3.OAuthFlows
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareOAuthFlows(&lDoc, &rDoc)
assert.Nil(t, extChanges)
// compare
extChanges := CompareOAuthFlows(&lDoc, &rDoc)
assert.Nil(t, extChanges)
}
func TestCompareOAuthFlows_AddEverything(t *testing.T) {
left := `x-coke: cola`
left := `x-coke: cola`
right := `implicit:
right := `implicit:
authorizationUrl: cheese
password:
authorizationUrl: cake
@@ -244,29 +244,29 @@ authorizationCode:
authorizationUrl: chalk
x-coke: cola`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.OAuthFlows
var rDoc v3.OAuthFlows
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.OAuthFlows
var rDoc v3.OAuthFlows
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareOAuthFlows(&lDoc, &rDoc)
assert.Equal(t, 4, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 4)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
// compare
extChanges := CompareOAuthFlows(&lDoc, &rDoc)
assert.Equal(t, 4, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 4)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}
func TestCompareOAuthFlows_RemoveEverything(t *testing.T) {
left := `x-coke: cola`
left := `x-coke: cola`
right := `implicit:
right := `implicit:
authorizationUrl: cheese
password:
authorizationUrl: cake
@@ -276,27 +276,27 @@ authorizationCode:
authorizationUrl: chalk
x-coke: cola`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.OAuthFlows
var rDoc v3.OAuthFlows
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.OAuthFlows
var rDoc v3.OAuthFlows
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareOAuthFlows(&rDoc, &lDoc)
assert.Equal(t, 4, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 4)
assert.Equal(t, 4, extChanges.TotalBreakingChanges())
// compare
extChanges := CompareOAuthFlows(&rDoc, &lDoc)
assert.Equal(t, 4, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 4)
assert.Equal(t, 4, extChanges.TotalBreakingChanges())
}
func TestCompareOAuthFlows_ModifyEverything(t *testing.T) {
left := `implicit:
left := `implicit:
authorizationUrl: cheese
password:
authorizationUrl: cake
@@ -306,7 +306,7 @@ authorizationCode:
authorizationUrl: chalk
x-coke: cola`
right := `implicit:
right := `implicit:
authorizationUrl: herbs
password:
authorizationUrl: coffee
@@ -316,21 +316,21 @@ authorizationCode:
authorizationUrl: pasta
x-coke: cherry`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.OAuthFlows
var rDoc v3.OAuthFlows
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.OAuthFlows
var rDoc v3.OAuthFlows
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareOAuthFlows(&lDoc, &rDoc)
assert.Equal(t, 5, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 5)
assert.Equal(t, 4, extChanges.TotalBreakingChanges())
// compare
extChanges := CompareOAuthFlows(&lDoc, &rDoc)
assert.Equal(t, 5, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 5)
assert.Equal(t, 4, extChanges.TotalBreakingChanges())
}

View File

@@ -4,351 +4,351 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"gopkg.in/yaml.v3"
"reflect"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"gopkg.in/yaml.v3"
"reflect"
)
// ParameterChanges represents changes found between Swagger or OpenAPI Parameter objects.
type ParameterChanges struct {
*PropertyChanges
SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
*PropertyChanges
SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
// Swagger supports Items.
ItemsChanges *ItemsChanges `json:"items,omitempty" yaml:"items,omitempty"`
// Swagger supports Items.
ItemsChanges *ItemsChanges `json:"items,omitempty" yaml:"items,omitempty"`
// OpenAPI supports examples and content types.
ExamplesChanges map[string]*ExampleChanges `json:"examples,omitempty" yaml:"examples,omitempty"`
ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"`
// OpenAPI supports examples and content types.
ExamplesChanges map[string]*ExampleChanges `json:"examples,omitempty" yaml:"examples,omitempty"`
ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"`
}
// GetAllChanges returns a slice of all changes made between Parameter objects
func (p *ParameterChanges) GetAllChanges() []*Change {
var changes []*Change
changes = append(changes, p.Changes...)
if p.SchemaChanges != nil {
changes = append(changes, p.SchemaChanges.GetAllChanges()...)
}
for i := range p.ExamplesChanges {
changes = append(changes, p.ExamplesChanges[i].GetAllChanges()...)
}
if p.ItemsChanges != nil {
changes = append(changes, p.ItemsChanges.GetAllChanges()...)
}
if p.ExtensionChanges != nil {
changes = append(changes, p.ExtensionChanges.GetAllChanges()...)
}
for i := range p.ContentChanges {
changes = append(changes, p.ContentChanges[i].GetAllChanges()...)
}
return changes
var changes []*Change
changes = append(changes, p.Changes...)
if p.SchemaChanges != nil {
changes = append(changes, p.SchemaChanges.GetAllChanges()...)
}
for i := range p.ExamplesChanges {
changes = append(changes, p.ExamplesChanges[i].GetAllChanges()...)
}
if p.ItemsChanges != nil {
changes = append(changes, p.ItemsChanges.GetAllChanges()...)
}
if p.ExtensionChanges != nil {
changes = append(changes, p.ExtensionChanges.GetAllChanges()...)
}
for i := range p.ContentChanges {
changes = append(changes, p.ContentChanges[i].GetAllChanges()...)
}
return changes
}
// TotalChanges returns a count of everything that changed
func (p *ParameterChanges) TotalChanges() int {
c := p.PropertyChanges.TotalChanges()
if p.SchemaChanges != nil {
c += p.SchemaChanges.TotalChanges()
}
for i := range p.ExamplesChanges {
c += p.ExamplesChanges[i].TotalChanges()
}
if p.ItemsChanges != nil {
c += p.ItemsChanges.TotalChanges()
}
if p.ExtensionChanges != nil {
c += p.ExtensionChanges.TotalChanges()
}
for i := range p.ContentChanges {
c += p.ContentChanges[i].TotalChanges()
}
return c
c := p.PropertyChanges.TotalChanges()
if p.SchemaChanges != nil {
c += p.SchemaChanges.TotalChanges()
}
for i := range p.ExamplesChanges {
c += p.ExamplesChanges[i].TotalChanges()
}
if p.ItemsChanges != nil {
c += p.ItemsChanges.TotalChanges()
}
if p.ExtensionChanges != nil {
c += p.ExtensionChanges.TotalChanges()
}
for i := range p.ContentChanges {
c += p.ContentChanges[i].TotalChanges()
}
return c
}
// TotalBreakingChanges always returns 0 for ExternalDoc objects, they are non-binding.
func (p *ParameterChanges) TotalBreakingChanges() int {
c := p.PropertyChanges.TotalBreakingChanges()
if p.SchemaChanges != nil {
c += p.SchemaChanges.TotalBreakingChanges()
}
if p.ItemsChanges != nil {
c += p.ItemsChanges.TotalBreakingChanges()
}
for i := range p.ContentChanges {
c += p.ContentChanges[i].TotalBreakingChanges()
}
return c
c := p.PropertyChanges.TotalBreakingChanges()
if p.SchemaChanges != nil {
c += p.SchemaChanges.TotalBreakingChanges()
}
if p.ItemsChanges != nil {
c += p.ItemsChanges.TotalBreakingChanges()
}
for i := range p.ContentChanges {
c += p.ContentChanges[i].TotalBreakingChanges()
}
return c
}
func addPropertyCheck(props *[]*PropertyCheck,
lvn, rvn *yaml.Node, lv, rv any, changes *[]*Change, label string, breaking bool) {
*props = append(*props, &PropertyCheck{
LeftNode: lvn,
RightNode: rvn,
Label: label,
Changes: changes,
Breaking: breaking,
Original: lv,
New: rv,
})
lvn, rvn *yaml.Node, lv, rv any, changes *[]*Change, label string, breaking bool) {
*props = append(*props, &PropertyCheck{
LeftNode: lvn,
RightNode: rvn,
Label: label,
Changes: changes,
Breaking: breaking,
Original: lv,
New: rv,
})
}
func addOpenAPIParameterProperties(left, right low.OpenAPIParameter, changes *[]*Change) []*PropertyCheck {
var props []*PropertyCheck
var props []*PropertyCheck
// style
addPropertyCheck(&props, left.GetStyle().ValueNode, right.GetStyle().ValueNode,
left.GetStyle(), right.GetStyle(), changes, v3.StyleLabel, false)
// style
addPropertyCheck(&props, left.GetStyle().ValueNode, right.GetStyle().ValueNode,
left.GetStyle(), right.GetStyle(), changes, v3.StyleLabel, false)
// allow reserved
addPropertyCheck(&props, left.GetAllowReserved().ValueNode, right.GetAllowReserved().ValueNode,
left.GetAllowReserved(), right.GetAllowReserved(), changes, v3.AllowReservedLabel, true)
// allow reserved
addPropertyCheck(&props, left.GetAllowReserved().ValueNode, right.GetAllowReserved().ValueNode,
left.GetAllowReserved(), right.GetAllowReserved(), changes, v3.AllowReservedLabel, true)
// explode
addPropertyCheck(&props, left.GetExplode().ValueNode, right.GetExplode().ValueNode,
left.GetExplode(), right.GetExplode(), changes, v3.ExplodeLabel, false)
// explode
addPropertyCheck(&props, left.GetExplode().ValueNode, right.GetExplode().ValueNode,
left.GetExplode(), right.GetExplode(), changes, v3.ExplodeLabel, false)
// deprecated
addPropertyCheck(&props, left.GetDeprecated().ValueNode, right.GetDeprecated().ValueNode,
left.GetDeprecated(), right.GetDeprecated(), changes, v3.DeprecatedLabel, false)
// deprecated
addPropertyCheck(&props, left.GetDeprecated().ValueNode, right.GetDeprecated().ValueNode,
left.GetDeprecated(), right.GetDeprecated(), changes, v3.DeprecatedLabel, false)
// example
addPropertyCheck(&props, left.GetExample().ValueNode, right.GetExample().ValueNode,
left.GetExample(), right.GetExample(), changes, v3.ExampleLabel, false)
// example
addPropertyCheck(&props, left.GetExample().ValueNode, right.GetExample().ValueNode,
left.GetExample(), right.GetExample(), changes, v3.ExampleLabel, false)
return props
return props
}
func addSwaggerParameterProperties(left, right low.SwaggerParameter, changes *[]*Change) []*PropertyCheck {
var props []*PropertyCheck
var props []*PropertyCheck
// type
addPropertyCheck(&props, left.GetType().ValueNode, right.GetType().ValueNode,
left.GetType(), right.GetType(), changes, v3.TypeLabel, true)
// type
addPropertyCheck(&props, left.GetType().ValueNode, right.GetType().ValueNode,
left.GetType(), right.GetType(), changes, v3.TypeLabel, true)
// format
addPropertyCheck(&props, left.GetFormat().ValueNode, right.GetFormat().ValueNode,
left.GetFormat(), right.GetFormat(), changes, v3.FormatLabel, true)
// format
addPropertyCheck(&props, left.GetFormat().ValueNode, right.GetFormat().ValueNode,
left.GetFormat(), right.GetFormat(), changes, v3.FormatLabel, true)
// collection format
addPropertyCheck(&props, left.GetCollectionFormat().ValueNode, right.GetCollectionFormat().ValueNode,
left.GetCollectionFormat(), right.GetCollectionFormat(), changes, v3.CollectionFormatLabel, true)
// collection format
addPropertyCheck(&props, left.GetCollectionFormat().ValueNode, right.GetCollectionFormat().ValueNode,
left.GetCollectionFormat(), right.GetCollectionFormat(), changes, v3.CollectionFormatLabel, true)
// maximum
addPropertyCheck(&props, left.GetMaximum().ValueNode, right.GetMaximum().ValueNode,
left.GetMaximum(), right.GetMaximum(), changes, v3.MaximumLabel, true)
// maximum
addPropertyCheck(&props, left.GetMaximum().ValueNode, right.GetMaximum().ValueNode,
left.GetMaximum(), right.GetMaximum(), changes, v3.MaximumLabel, true)
// minimum
addPropertyCheck(&props, left.GetMinimum().ValueNode, right.GetMinimum().ValueNode,
left.GetMinimum(), right.GetMinimum(), changes, v3.MinimumLabel, true)
// minimum
addPropertyCheck(&props, left.GetMinimum().ValueNode, right.GetMinimum().ValueNode,
left.GetMinimum(), right.GetMinimum(), changes, v3.MinimumLabel, true)
// exclusive maximum
addPropertyCheck(&props, left.GetExclusiveMaximum().ValueNode, right.GetExclusiveMaximum().ValueNode,
left.GetExclusiveMaximum(), right.GetExclusiveMaximum(), changes, v3.ExclusiveMaximumLabel, true)
// exclusive maximum
addPropertyCheck(&props, left.GetExclusiveMaximum().ValueNode, right.GetExclusiveMaximum().ValueNode,
left.GetExclusiveMaximum(), right.GetExclusiveMaximum(), changes, v3.ExclusiveMaximumLabel, true)
// exclusive minimum
addPropertyCheck(&props, left.GetExclusiveMinimum().ValueNode, right.GetExclusiveMinimum().ValueNode,
left.GetExclusiveMinimum(), right.GetExclusiveMinimum(), changes, v3.ExclusiveMinimumLabel, true)
// exclusive minimum
addPropertyCheck(&props, left.GetExclusiveMinimum().ValueNode, right.GetExclusiveMinimum().ValueNode,
left.GetExclusiveMinimum(), right.GetExclusiveMinimum(), changes, v3.ExclusiveMinimumLabel, true)
// max length
addPropertyCheck(&props, left.GetMaxLength().ValueNode, right.GetMaxLength().ValueNode,
left.GetMaxLength(), right.GetMaxLength(), changes, v3.MaxLengthLabel, true)
// max length
addPropertyCheck(&props, left.GetMaxLength().ValueNode, right.GetMaxLength().ValueNode,
left.GetMaxLength(), right.GetMaxLength(), changes, v3.MaxLengthLabel, true)
// min length
addPropertyCheck(&props, left.GetMinLength().ValueNode, right.GetMinLength().ValueNode,
left.GetMinLength(), right.GetMinLength(), changes, v3.MinLengthLabel, true)
// min length
addPropertyCheck(&props, left.GetMinLength().ValueNode, right.GetMinLength().ValueNode,
left.GetMinLength(), right.GetMinLength(), changes, v3.MinLengthLabel, true)
// pattern
addPropertyCheck(&props, left.GetPattern().ValueNode, right.GetPattern().ValueNode,
left.GetPattern(), right.GetPattern(), changes, v3.PatternLabel, true)
// pattern
addPropertyCheck(&props, left.GetPattern().ValueNode, right.GetPattern().ValueNode,
left.GetPattern(), right.GetPattern(), changes, v3.PatternLabel, true)
// max items
addPropertyCheck(&props, left.GetMaxItems().ValueNode, right.GetMaxItems().ValueNode,
left.GetMaxItems(), right.GetMaxItems(), changes, v3.MaxItemsLabel, true)
// max items
addPropertyCheck(&props, left.GetMaxItems().ValueNode, right.GetMaxItems().ValueNode,
left.GetMaxItems(), right.GetMaxItems(), changes, v3.MaxItemsLabel, true)
// min items
addPropertyCheck(&props, left.GetMinItems().ValueNode, right.GetMinItems().ValueNode,
left.GetMinItems(), right.GetMinItems(), changes, v3.MinItemsLabel, true)
// min items
addPropertyCheck(&props, left.GetMinItems().ValueNode, right.GetMinItems().ValueNode,
left.GetMinItems(), right.GetMinItems(), changes, v3.MinItemsLabel, true)
// unique items
addPropertyCheck(&props, left.GetUniqueItems().ValueNode, right.GetUniqueItems().ValueNode,
left.GetUniqueItems(), right.GetUniqueItems(), changes, v3.UniqueItemsLabel, true)
// unique items
addPropertyCheck(&props, left.GetUniqueItems().ValueNode, right.GetUniqueItems().ValueNode,
left.GetUniqueItems(), right.GetUniqueItems(), changes, v3.UniqueItemsLabel, true)
// default
addPropertyCheck(&props, left.GetDefault().ValueNode, right.GetDefault().ValueNode,
left.GetDefault(), right.GetDefault(), changes, v3.DefaultLabel, true)
// default
addPropertyCheck(&props, left.GetDefault().ValueNode, right.GetDefault().ValueNode,
left.GetDefault(), right.GetDefault(), changes, v3.DefaultLabel, true)
// multiple of
addPropertyCheck(&props, left.GetMultipleOf().ValueNode, right.GetMultipleOf().ValueNode,
left.GetMultipleOf(), right.GetMultipleOf(), changes, v3.MultipleOfLabel, true)
// multiple of
addPropertyCheck(&props, left.GetMultipleOf().ValueNode, right.GetMultipleOf().ValueNode,
left.GetMultipleOf(), right.GetMultipleOf(), changes, v3.MultipleOfLabel, true)
return props
return props
}
func addCommonParameterProperties(left, right low.SharedParameters, changes *[]*Change) []*PropertyCheck {
var props []*PropertyCheck
var props []*PropertyCheck
addPropertyCheck(&props, left.GetName().ValueNode, right.GetName().ValueNode,
left.GetName(), right.GetName(), changes, v3.NameLabel, true)
addPropertyCheck(&props, left.GetName().ValueNode, right.GetName().ValueNode,
left.GetName(), right.GetName(), changes, v3.NameLabel, true)
// in
addPropertyCheck(&props, left.GetIn().ValueNode, right.GetIn().ValueNode,
left.GetIn(), right.GetIn(), changes, v3.InLabel, true)
// in
addPropertyCheck(&props, left.GetIn().ValueNode, right.GetIn().ValueNode,
left.GetIn(), right.GetIn(), changes, v3.InLabel, true)
// description
addPropertyCheck(&props, left.GetDescription().ValueNode, right.GetDescription().ValueNode,
left.GetDescription(), right.GetDescription(), changes, v3.DescriptionLabel, false)
// description
addPropertyCheck(&props, left.GetDescription().ValueNode, right.GetDescription().ValueNode,
left.GetDescription(), right.GetDescription(), changes, v3.DescriptionLabel, false)
// required
addPropertyCheck(&props, left.GetRequired().ValueNode, right.GetRequired().ValueNode,
left.GetRequired(), right.GetRequired(), changes, v3.RequiredLabel, true)
// required
addPropertyCheck(&props, left.GetRequired().ValueNode, right.GetRequired().ValueNode,
left.GetRequired(), right.GetRequired(), changes, v3.RequiredLabel, true)
// allow empty value
addPropertyCheck(&props, left.GetAllowEmptyValue().ValueNode, right.GetAllowEmptyValue().ValueNode,
left.GetAllowEmptyValue(), right.GetAllowEmptyValue(), changes, v3.AllowEmptyValueLabel, true)
// allow empty value
addPropertyCheck(&props, left.GetAllowEmptyValue().ValueNode, right.GetAllowEmptyValue().ValueNode,
left.GetAllowEmptyValue(), right.GetAllowEmptyValue(), changes, v3.AllowEmptyValueLabel, true)
return props
return props
}
// CompareParametersV3 is an OpenAPI type safe proxy for CompareParameters
func CompareParametersV3(l, r *v3.Parameter) *ParameterChanges {
return CompareParameters(l, r)
return CompareParameters(l, r)
}
// CompareParameters compares a left and right Swagger or OpenAPI Parameter object for any changes. If found returns
// a pointer to ParameterChanges. If nothing is found, returns nil.
func CompareParameters(l, r any) *ParameterChanges {
var changes []*Change
var props []*PropertyCheck
var changes []*Change
var props []*PropertyCheck
pc := new(ParameterChanges)
var lSchema *base.SchemaProxy
var rSchema *base.SchemaProxy
var lext, rext map[low.KeyReference[string]]low.ValueReference[any]
pc := new(ParameterChanges)
var lSchema *base.SchemaProxy
var rSchema *base.SchemaProxy
var lext, rext map[low.KeyReference[string]]low.ValueReference[any]
if reflect.TypeOf(&v2.Parameter{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Parameter{}) == reflect.TypeOf(r) {
lParam := l.(*v2.Parameter)
rParam := r.(*v2.Parameter)
if reflect.TypeOf(&v2.Parameter{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Parameter{}) == reflect.TypeOf(r) {
lParam := l.(*v2.Parameter)
rParam := r.(*v2.Parameter)
// perform hash check to avoid further processing
if low.AreEqual(lParam, rParam) {
return nil
}
// perform hash check to avoid further processing
if low.AreEqual(lParam, rParam) {
return nil
}
props = append(props, addSwaggerParameterProperties(lParam, rParam, &changes)...)
props = append(props, addCommonParameterProperties(lParam, rParam, &changes)...)
props = append(props, addSwaggerParameterProperties(lParam, rParam, &changes)...)
props = append(props, addCommonParameterProperties(lParam, rParam, &changes)...)
// extract schema
if lParam != nil {
lSchema = lParam.Schema.Value
lext = lParam.Extensions
}
if rParam != nil {
rext = rParam.Extensions
rSchema = rParam.Schema.Value
}
// extract schema
if lParam != nil {
lSchema = lParam.Schema.Value
lext = lParam.Extensions
}
if rParam != nil {
rext = rParam.Extensions
rSchema = rParam.Schema.Value
}
// items
if !lParam.Items.IsEmpty() && !rParam.Items.IsEmpty() {
if lParam.Items.Value.Hash() != rParam.Items.Value.Hash() {
pc.ItemsChanges = CompareItems(lParam.Items.Value, rParam.Items.Value)
}
}
if lParam.Items.IsEmpty() && !rParam.Items.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.ItemsLabel,
nil, rParam.Items.ValueNode, true, nil,
rParam.Items.Value)
}
if !lParam.Items.IsEmpty() && rParam.Items.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.ItemsLabel,
lParam.Items.ValueNode, nil, true, lParam.Items.Value,
nil)
}
// items
if !lParam.Items.IsEmpty() && !rParam.Items.IsEmpty() {
if lParam.Items.Value.Hash() != rParam.Items.Value.Hash() {
pc.ItemsChanges = CompareItems(lParam.Items.Value, rParam.Items.Value)
}
}
if lParam.Items.IsEmpty() && !rParam.Items.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.ItemsLabel,
nil, rParam.Items.ValueNode, true, nil,
rParam.Items.Value)
}
if !lParam.Items.IsEmpty() && rParam.Items.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.ItemsLabel,
lParam.Items.ValueNode, nil, true, lParam.Items.Value,
nil)
}
// enum
if len(lParam.Enum.Value) > 0 || len(rParam.Enum.Value) > 0 {
ExtractRawValueSliceChanges(lParam.Enum.Value, rParam.Enum.Value, &changes, v3.EnumLabel, true)
}
}
// enum
if len(lParam.Enum.Value) > 0 || len(rParam.Enum.Value) > 0 {
ExtractRawValueSliceChanges(lParam.Enum.Value, rParam.Enum.Value, &changes, v3.EnumLabel, true)
}
}
// OpenAPI
if reflect.TypeOf(&v3.Parameter{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Parameter{}) == reflect.TypeOf(r) {
// OpenAPI
if reflect.TypeOf(&v3.Parameter{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Parameter{}) == reflect.TypeOf(r) {
lParam := l.(*v3.Parameter)
rParam := r.(*v3.Parameter)
lParam := l.(*v3.Parameter)
rParam := r.(*v3.Parameter)
// perform hash check to avoid further processing
if low.AreEqual(lParam, rParam) {
return nil
}
// perform hash check to avoid further processing
if low.AreEqual(lParam, rParam) {
return nil
}
props = append(props, addOpenAPIParameterProperties(lParam, rParam, &changes)...)
props = append(props, addCommonParameterProperties(lParam, rParam, &changes)...)
if lParam != nil {
lext = lParam.Extensions
lSchema = lParam.Schema.Value
}
if rParam != nil {
rext = rParam.Extensions
rSchema = rParam.Schema.Value
}
props = append(props, addOpenAPIParameterProperties(lParam, rParam, &changes)...)
props = append(props, addCommonParameterProperties(lParam, rParam, &changes)...)
if lParam != nil {
lext = lParam.Extensions
lSchema = lParam.Schema.Value
}
if rParam != nil {
rext = rParam.Extensions
rSchema = rParam.Schema.Value
}
// example
checkParameterExample(lParam.Example, rParam.Example, changes)
// example
checkParameterExample(lParam.Example, rParam.Example, changes)
// examples
pc.ExamplesChanges = CheckMapForChanges(lParam.Examples.Value, rParam.Examples.Value,
&changes, v3.ExamplesLabel, CompareExamples)
// examples
pc.ExamplesChanges = CheckMapForChanges(lParam.Examples.Value, rParam.Examples.Value,
&changes, v3.ExamplesLabel, CompareExamples)
// content
pc.ContentChanges = CheckMapForChanges(lParam.Content.Value, rParam.Content.Value,
&changes, v3.ContentLabel, CompareMediaTypes)
}
CheckProperties(props)
// content
pc.ContentChanges = CheckMapForChanges(lParam.Content.Value, rParam.Content.Value,
&changes, v3.ContentLabel, CompareMediaTypes)
}
CheckProperties(props)
if lSchema != nil && rSchema != nil {
pc.SchemaChanges = CompareSchemas(lSchema, rSchema)
}
if lSchema != nil && rSchema == nil {
CreateChange(&changes, ObjectRemoved, v3.SchemaLabel,
lSchema.GetValueNode(), nil, true, lSchema,
nil)
}
if lSchema != nil && rSchema != nil {
pc.SchemaChanges = CompareSchemas(lSchema, rSchema)
}
if lSchema != nil && rSchema == nil {
CreateChange(&changes, ObjectRemoved, v3.SchemaLabel,
lSchema.GetValueNode(), nil, true, lSchema,
nil)
}
if lSchema == nil && rSchema != nil {
CreateChange(&changes, ObjectAdded, v3.SchemaLabel,
nil, rSchema.GetValueNode(), true, nil,
rSchema)
}
if lSchema == nil && rSchema != nil {
CreateChange(&changes, ObjectAdded, v3.SchemaLabel,
nil, rSchema.GetValueNode(), true, nil,
rSchema)
}
pc.PropertyChanges = NewPropertyChanges(changes)
pc.ExtensionChanges = CompareExtensions(lext, rext)
return pc
pc.PropertyChanges = NewPropertyChanges(changes)
pc.ExtensionChanges = CompareExtensions(lext, rext)
return pc
}
func checkParameterExample(expLeft, expRight low.NodeReference[any], changes []*Change) {
if !expLeft.IsEmpty() && !expRight.IsEmpty() {
if low.GenerateHashString(expLeft.GetValue()) != low.GenerateHashString(expRight.GetValue()) {
CreateChange(&changes, Modified, v3.ExampleLabel,
expLeft.GetValueNode(), expRight.GetValueNode(), false,
expLeft.GetValue(), expRight.GetValue())
}
}
if expLeft.Value == nil && expRight.Value != nil {
CreateChange(&changes, PropertyAdded, v3.ExampleLabel,
nil, expRight.GetValueNode(), false,
nil, expRight.GetValue())
if !expLeft.IsEmpty() && !expRight.IsEmpty() {
if low.GenerateHashString(expLeft.GetValue()) != low.GenerateHashString(expRight.GetValue()) {
CreateChange(&changes, Modified, v3.ExampleLabel,
expLeft.GetValueNode(), expRight.GetValueNode(), false,
expLeft.GetValue(), expRight.GetValue())
}
}
if expLeft.Value == nil && expRight.Value != nil {
CreateChange(&changes, PropertyAdded, v3.ExampleLabel,
nil, expRight.GetValueNode(), false,
nil, expRight.GetValue())
}
if expLeft.Value != nil && expRight.Value == nil {
CreateChange(&changes, PropertyRemoved, v3.ExampleLabel,
expLeft.GetValueNode(), nil, false,
expLeft.GetValue(), nil)
}
if expLeft.Value != nil && expRight.Value == nil {
CreateChange(&changes, PropertyRemoved, v3.ExampleLabel,
expLeft.GetValueNode(), nil, false,
expLeft.GetValue(), nil)
}
}
}

View File

@@ -4,203 +4,203 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v2"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"reflect"
"sync"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v2"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"reflect"
"sync"
)
// PathsChanges represents changes found between two Swagger or OpenAPI Paths Objects.
type PathsChanges struct {
*PropertyChanges
PathItemsChanges map[string]*PathItemChanges `json:"pathItems,omitempty" yaml:"pathItems,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
*PropertyChanges
PathItemsChanges map[string]*PathItemChanges `json:"pathItems,omitempty" yaml:"pathItems,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
// GetAllChanges returns a slice of all changes made between Paths objects
func (p *PathsChanges) GetAllChanges() []*Change {
var changes []*Change
changes = append(changes, p.Changes...)
for k := range p.PathItemsChanges {
changes = append(changes, p.PathItemsChanges[k].GetAllChanges()...)
}
if p.ExtensionChanges != nil {
changes = append(changes, p.ExtensionChanges.GetAllChanges()...)
}
return changes
var changes []*Change
changes = append(changes, p.Changes...)
for k := range p.PathItemsChanges {
changes = append(changes, p.PathItemsChanges[k].GetAllChanges()...)
}
if p.ExtensionChanges != nil {
changes = append(changes, p.ExtensionChanges.GetAllChanges()...)
}
return changes
}
// TotalChanges returns the total number of changes between two Swagger or OpenAPI Paths Objects
func (p *PathsChanges) TotalChanges() int {
c := p.PropertyChanges.TotalChanges()
for k := range p.PathItemsChanges {
c += p.PathItemsChanges[k].TotalChanges()
}
if p.ExtensionChanges != nil {
c += p.ExtensionChanges.TotalChanges()
}
return c
c := p.PropertyChanges.TotalChanges()
for k := range p.PathItemsChanges {
c += p.PathItemsChanges[k].TotalChanges()
}
if p.ExtensionChanges != nil {
c += p.ExtensionChanges.TotalChanges()
}
return c
}
// TotalBreakingChanges returns tht total number of changes found between two Swagger or OpenAPI Path Objects
func (p *PathsChanges) TotalBreakingChanges() int {
c := p.PropertyChanges.TotalBreakingChanges()
for k := range p.PathItemsChanges {
c += p.PathItemsChanges[k].TotalBreakingChanges()
}
return c
c := p.PropertyChanges.TotalBreakingChanges()
for k := range p.PathItemsChanges {
c += p.PathItemsChanges[k].TotalBreakingChanges()
}
return c
}
// ComparePaths compares a left and right Swagger or OpenAPI Paths Object for changes. If found, returns a pointer
// to a PathsChanges instance. Returns nil if nothing is found.
func ComparePaths(l, r any) *PathsChanges {
var changes []*Change
var changes []*Change
pc := new(PathsChanges)
pathChanges := make(map[string]*PathItemChanges)
pc := new(PathsChanges)
pathChanges := make(map[string]*PathItemChanges)
// Swagger
if reflect.TypeOf(&v2.Paths{}) == reflect.TypeOf(l) &&
reflect.TypeOf(&v2.Paths{}) == reflect.TypeOf(r) {
// Swagger
if reflect.TypeOf(&v2.Paths{}) == reflect.TypeOf(l) &&
reflect.TypeOf(&v2.Paths{}) == reflect.TypeOf(r) {
lPath := l.(*v2.Paths)
rPath := r.(*v2.Paths)
lPath := l.(*v2.Paths)
rPath := r.(*v2.Paths)
// perform hash check to avoid further processing
if low.AreEqual(lPath, rPath) {
return nil
}
// perform hash check to avoid further processing
if low.AreEqual(lPath, rPath) {
return nil
}
lKeys := make(map[string]low.ValueReference[*v2.PathItem])
rKeys := make(map[string]low.ValueReference[*v2.PathItem])
for k := range lPath.PathItems {
lKeys[k.Value] = lPath.PathItems[k]
}
for k := range rPath.PathItems {
rKeys[k.Value] = rPath.PathItems[k]
}
lKeys := make(map[string]low.ValueReference[*v2.PathItem])
rKeys := make(map[string]low.ValueReference[*v2.PathItem])
for k := range lPath.PathItems {
lKeys[k.Value] = lPath.PathItems[k]
}
for k := range rPath.PathItems {
rKeys[k.Value] = rPath.PathItems[k]
}
// run every comparison in a thread.
var mLock sync.Mutex
compare := func(path string, pChanges map[string]*PathItemChanges, l, r *v2.PathItem, doneChan chan bool) {
if !low.AreEqual(l, r) {
mLock.Lock()
pathChanges[path] = ComparePathItems(l, r)
mLock.Unlock()
}
doneChan <- true
}
// run every comparison in a thread.
var mLock sync.Mutex
compare := func(path string, pChanges map[string]*PathItemChanges, l, r *v2.PathItem, doneChan chan bool) {
if !low.AreEqual(l, r) {
mLock.Lock()
pathChanges[path] = ComparePathItems(l, r)
mLock.Unlock()
}
doneChan <- true
}
doneChan := make(chan bool)
pathsChecked := 0
doneChan := make(chan bool)
pathsChecked := 0
for k := range lKeys {
if _, ok := rKeys[k]; ok {
go compare(k, pathChanges, lKeys[k].Value, rKeys[k].Value, doneChan)
pathsChecked++
continue
}
g, p := lPath.FindPathAndKey(k)
CreateChange(&changes, ObjectRemoved, v3.PathLabel,
g.KeyNode, nil, true,
p.Value, nil)
}
for k := range lKeys {
if _, ok := rKeys[k]; ok {
go compare(k, pathChanges, lKeys[k].Value, rKeys[k].Value, doneChan)
pathsChecked++
continue
}
g, p := lPath.FindPathAndKey(k)
CreateChange(&changes, ObjectRemoved, v3.PathLabel,
g.KeyNode, nil, true,
p.Value, nil)
}
for k := range rKeys {
if _, ok := lKeys[k]; !ok {
g, p := rPath.FindPathAndKey(k)
CreateChange(&changes, ObjectAdded, v3.PathLabel,
nil, g.KeyNode, false,
nil, p.Value)
}
}
for k := range rKeys {
if _, ok := lKeys[k]; !ok {
g, p := rPath.FindPathAndKey(k)
CreateChange(&changes, ObjectAdded, v3.PathLabel,
nil, g.KeyNode, false,
nil, p.Value)
}
}
// wait for the things to be done.
completedChecks := 0
for completedChecks < pathsChecked {
select {
case <-doneChan:
completedChecks++
}
}
if len(pathChanges) > 0 {
pc.PathItemsChanges = pathChanges
}
// wait for the things to be done.
completedChecks := 0
for completedChecks < pathsChecked {
select {
case <-doneChan:
completedChecks++
}
}
if len(pathChanges) > 0 {
pc.PathItemsChanges = pathChanges
}
pc.ExtensionChanges = CompareExtensions(lPath.Extensions, rPath.Extensions)
}
pc.ExtensionChanges = CompareExtensions(lPath.Extensions, rPath.Extensions)
}
// OpenAPI
if reflect.TypeOf(&v3.Paths{}) == reflect.TypeOf(l) &&
reflect.TypeOf(&v3.Paths{}) == reflect.TypeOf(r) {
// OpenAPI
if reflect.TypeOf(&v3.Paths{}) == reflect.TypeOf(l) &&
reflect.TypeOf(&v3.Paths{}) == reflect.TypeOf(r) {
lPath := l.(*v3.Paths)
rPath := r.(*v3.Paths)
lPath := l.(*v3.Paths)
rPath := r.(*v3.Paths)
// perform hash check to avoid further processing
if low.AreEqual(lPath, rPath) {
return nil
}
// perform hash check to avoid further processing
if low.AreEqual(lPath, rPath) {
return nil
}
lKeys := make(map[string]low.ValueReference[*v3.PathItem])
rKeys := make(map[string]low.ValueReference[*v3.PathItem])
for k := range lPath.PathItems {
lKeys[k.Value] = lPath.PathItems[k]
}
for k := range rPath.PathItems {
rKeys[k.Value] = rPath.PathItems[k]
}
lKeys := make(map[string]low.ValueReference[*v3.PathItem])
rKeys := make(map[string]low.ValueReference[*v3.PathItem])
for k := range lPath.PathItems {
lKeys[k.Value] = lPath.PathItems[k]
}
for k := range rPath.PathItems {
rKeys[k.Value] = rPath.PathItems[k]
}
// run every comparison in a thread.
var mLock sync.Mutex
compare := func(path string, pChanges map[string]*PathItemChanges, l, r *v3.PathItem, doneChan chan bool) {
if !low.AreEqual(l, r) {
mLock.Lock()
pathChanges[path] = ComparePathItems(l, r)
mLock.Unlock()
}
doneChan <- true
}
// run every comparison in a thread.
var mLock sync.Mutex
compare := func(path string, pChanges map[string]*PathItemChanges, l, r *v3.PathItem, doneChan chan bool) {
if !low.AreEqual(l, r) {
mLock.Lock()
pathChanges[path] = ComparePathItems(l, r)
mLock.Unlock()
}
doneChan <- true
}
doneChan := make(chan bool)
pathsChecked := 0
doneChan := make(chan bool)
pathsChecked := 0
for k := range lKeys {
if _, ok := rKeys[k]; ok {
go compare(k, pathChanges, lKeys[k].Value, rKeys[k].Value, doneChan)
pathsChecked++
continue
}
g, p := lPath.FindPathAndKey(k)
CreateChange(&changes, ObjectRemoved, v3.PathLabel,
g.KeyNode, nil, true,
p.Value, nil)
}
for k := range lKeys {
if _, ok := rKeys[k]; ok {
go compare(k, pathChanges, lKeys[k].Value, rKeys[k].Value, doneChan)
pathsChecked++
continue
}
g, p := lPath.FindPathAndKey(k)
CreateChange(&changes, ObjectRemoved, v3.PathLabel,
g.KeyNode, nil, true,
p.Value, nil)
}
for k := range rKeys {
if _, ok := lKeys[k]; !ok {
g, p := rPath.FindPathAndKey(k)
CreateChange(&changes, ObjectAdded, v3.PathLabel,
nil, g.KeyNode, false,
nil, p.Value)
}
}
// wait for the things to be done.
completedChecks := 0
for completedChecks < pathsChecked {
select {
case <-doneChan:
completedChecks++
}
}
if len(pathChanges) > 0 {
pc.PathItemsChanges = pathChanges
}
for k := range rKeys {
if _, ok := lKeys[k]; !ok {
g, p := rPath.FindPathAndKey(k)
CreateChange(&changes, ObjectAdded, v3.PathLabel,
nil, g.KeyNode, false,
nil, p.Value)
}
}
// wait for the things to be done.
completedChecks := 0
for completedChecks < pathsChecked {
select {
case <-doneChan:
completedChecks++
}
}
if len(pathChanges) > 0 {
pc.PathItemsChanges = pathChanges
}
pc.ExtensionChanges = CompareExtensions(lPath.Extensions, rPath.Extensions)
}
pc.PropertyChanges = NewPropertyChanges(changes)
return pc
pc.ExtensionChanges = CompareExtensions(lPath.Extensions, rPath.Extensions)
}
pc.PropertyChanges = NewPropertyChanges(changes)
return pc
}

View File

@@ -4,90 +4,90 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v3"
)
// RequestBodyChanges represents changes made between two OpenAPI RequestBody Objects
type RequestBodyChanges struct {
*PropertyChanges
ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
*PropertyChanges
ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
// GetAllChanges returns a slice of all changes made between RequestBody objects
func (rb *RequestBodyChanges) GetAllChanges() []*Change {
var changes []*Change
changes = append(changes, rb.Changes...)
for k := range rb.ContentChanges {
changes = append(changes, rb.ContentChanges[k].GetAllChanges()...)
}
if rb.ExtensionChanges != nil {
changes = append(changes, rb.ExtensionChanges.GetAllChanges()...)
}
return changes
var changes []*Change
changes = append(changes, rb.Changes...)
for k := range rb.ContentChanges {
changes = append(changes, rb.ContentChanges[k].GetAllChanges()...)
}
if rb.ExtensionChanges != nil {
changes = append(changes, rb.ExtensionChanges.GetAllChanges()...)
}
return changes
}
// TotalChanges returns the total number of changes found between two OpenAPI RequestBody objects
func (rb *RequestBodyChanges) TotalChanges() int {
c := rb.PropertyChanges.TotalChanges()
for k := range rb.ContentChanges {
c += rb.ContentChanges[k].TotalChanges()
}
if rb.ExtensionChanges != nil {
c += rb.ExtensionChanges.TotalChanges()
}
return c
c := rb.PropertyChanges.TotalChanges()
for k := range rb.ContentChanges {
c += rb.ContentChanges[k].TotalChanges()
}
if rb.ExtensionChanges != nil {
c += rb.ExtensionChanges.TotalChanges()
}
return c
}
// TotalBreakingChanges returns the total number of breaking changes found between OpenAPI RequestBody objects
func (rb *RequestBodyChanges) TotalBreakingChanges() int {
c := rb.PropertyChanges.TotalBreakingChanges()
for k := range rb.ContentChanges {
c += rb.ContentChanges[k].TotalBreakingChanges()
}
return c
c := rb.PropertyChanges.TotalBreakingChanges()
for k := range rb.ContentChanges {
c += rb.ContentChanges[k].TotalBreakingChanges()
}
return c
}
// CompareRequestBodies compares a left and right OpenAPI RequestBody object for changes. If found returns a pointer
// to a RequestBodyChanges instance. Returns nil if nothing was found.
func CompareRequestBodies(l, r *v3.RequestBody) *RequestBodyChanges {
if low.AreEqual(l, r) {
return nil
}
if low.AreEqual(l, r) {
return nil
}
var changes []*Change
var props []*PropertyCheck
var changes []*Change
var props []*PropertyCheck
// description
props = append(props, &PropertyCheck{
LeftNode: l.Description.ValueNode,
RightNode: r.Description.ValueNode,
Label: v3.DescriptionLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// description
props = append(props, &PropertyCheck{
LeftNode: l.Description.ValueNode,
RightNode: r.Description.ValueNode,
Label: v3.DescriptionLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// required
props = append(props, &PropertyCheck{
LeftNode: l.Required.ValueNode,
RightNode: r.Required.ValueNode,
Label: v3.RequiredLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// required
props = append(props, &PropertyCheck{
LeftNode: l.Required.ValueNode,
RightNode: r.Required.ValueNode,
Label: v3.RequiredLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
CheckProperties(props)
CheckProperties(props)
rbc := new(RequestBodyChanges)
rbc.ContentChanges = CheckMapForChanges(l.Content.Value, r.Content.Value,
&changes, v3.ContentLabel, CompareMediaTypes)
rbc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions)
rbc.PropertyChanges = NewPropertyChanges(changes)
rbc := new(RequestBodyChanges)
rbc.ContentChanges = CheckMapForChanges(l.Content.Value, r.Content.Value,
&changes, v3.ContentLabel, CompareMediaTypes)
rbc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions)
rbc.PropertyChanges = NewPropertyChanges(changes)
return rbc
return rbc
}

View File

@@ -4,49 +4,49 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
)
func TestCompareRequestBodies(t *testing.T) {
left := `description: something
left := `description: something
required: true
content:
application/json:
schema:
type: int`
right := `description: something
right := `description: something
required: true
content:
application/json:
schema:
type: int`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.RequestBody
var rDoc v3.RequestBody
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.RequestBody
var rDoc v3.RequestBody
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareRequestBodies(&lDoc, &rDoc)
assert.Nil(t, extChanges)
// compare.
extChanges := CompareRequestBodies(&lDoc, &rDoc)
assert.Nil(t, extChanges)
}
func TestCompareRequestBodies_Modified(t *testing.T) {
left := `description: something
left := `description: something
required: true
x-pizza: thin
content:
@@ -54,7 +54,7 @@ content:
schema:
type: int`
right := `x-pizza: oven
right := `x-pizza: oven
description: nothing
required: false
content:
@@ -62,22 +62,22 @@ content:
schema:
type: string`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.RequestBody
var rDoc v3.RequestBody
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.RequestBody
var rDoc v3.RequestBody
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareRequestBodies(&lDoc, &rDoc)
// compare.
extChanges := CompareRequestBodies(&lDoc, &rDoc)
assert.Equal(t, 4, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 4)
assert.Equal(t, 2, extChanges.TotalBreakingChanges())
assert.Equal(t, 4, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 4)
assert.Equal(t, 2, extChanges.TotalBreakingChanges())
}

View File

@@ -4,196 +4,196 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"reflect"
"github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"reflect"
)
import (
"github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/datamodel/low/v2"
)
// ResponseChanges represents changes found between two Swagger or OpenAPI Response objects.
type ResponseChanges struct {
*PropertyChanges
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
HeadersChanges map[string]*HeaderChanges `json:"headers,omitempty" yaml:"headers,omitempty"`
*PropertyChanges
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
HeadersChanges map[string]*HeaderChanges `json:"headers,omitempty" yaml:"headers,omitempty"`
// Swagger Response Properties.
SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"`
ExamplesChanges *ExamplesChanges `json:"examples,omitempty" yaml:"examples,omitempty"`
// Swagger Response Properties.
SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"`
ExamplesChanges *ExamplesChanges `json:"examples,omitempty" yaml:"examples,omitempty"`
// OpenAPI Response Properties.
ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"`
LinkChanges map[string]*LinkChanges `json:"links,omitempty" yaml:"links,omitempty"`
// OpenAPI Response Properties.
ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"`
LinkChanges map[string]*LinkChanges `json:"links,omitempty" yaml:"links,omitempty"`
}
// GetAllChanges returns a slice of all changes made between RequestBody objects
func (r *ResponseChanges) GetAllChanges() []*Change {
var changes []*Change
changes = append(changes, r.Changes...)
if r.ExtensionChanges != nil {
changes = append(changes, r.ExtensionChanges.GetAllChanges()...)
}
if r.SchemaChanges != nil {
changes = append(changes, r.SchemaChanges.GetAllChanges()...)
}
if r.ExamplesChanges != nil {
changes = append(changes, r.ExamplesChanges.GetAllChanges()...)
}
for k := range r.HeadersChanges {
changes = append(changes, r.HeadersChanges[k].GetAllChanges()...)
}
for k := range r.ContentChanges {
changes = append(changes, r.ContentChanges[k].GetAllChanges()...)
}
for k := range r.LinkChanges {
changes = append(changes, r.LinkChanges[k].GetAllChanges()...)
}
return changes
var changes []*Change
changes = append(changes, r.Changes...)
if r.ExtensionChanges != nil {
changes = append(changes, r.ExtensionChanges.GetAllChanges()...)
}
if r.SchemaChanges != nil {
changes = append(changes, r.SchemaChanges.GetAllChanges()...)
}
if r.ExamplesChanges != nil {
changes = append(changes, r.ExamplesChanges.GetAllChanges()...)
}
for k := range r.HeadersChanges {
changes = append(changes, r.HeadersChanges[k].GetAllChanges()...)
}
for k := range r.ContentChanges {
changes = append(changes, r.ContentChanges[k].GetAllChanges()...)
}
for k := range r.LinkChanges {
changes = append(changes, r.LinkChanges[k].GetAllChanges()...)
}
return changes
}
// TotalChanges returns the total number of changes found between two Swagger or OpenAPI Response Objects
func (r *ResponseChanges) TotalChanges() int {
c := r.PropertyChanges.TotalChanges()
if r.ExtensionChanges != nil {
c += r.ExtensionChanges.TotalChanges()
}
if r.SchemaChanges != nil {
c += r.SchemaChanges.TotalChanges()
}
if r.ExamplesChanges != nil {
c += r.ExamplesChanges.TotalChanges()
}
for k := range r.HeadersChanges {
c += r.HeadersChanges[k].TotalChanges()
}
for k := range r.ContentChanges {
c += r.ContentChanges[k].TotalChanges()
}
for k := range r.LinkChanges {
c += r.LinkChanges[k].TotalChanges()
}
return c
c := r.PropertyChanges.TotalChanges()
if r.ExtensionChanges != nil {
c += r.ExtensionChanges.TotalChanges()
}
if r.SchemaChanges != nil {
c += r.SchemaChanges.TotalChanges()
}
if r.ExamplesChanges != nil {
c += r.ExamplesChanges.TotalChanges()
}
for k := range r.HeadersChanges {
c += r.HeadersChanges[k].TotalChanges()
}
for k := range r.ContentChanges {
c += r.ContentChanges[k].TotalChanges()
}
for k := range r.LinkChanges {
c += r.LinkChanges[k].TotalChanges()
}
return c
}
// TotalBreakingChanges returns the total number of breaking changes found between two swagger or OpenAPI
// Response Objects
func (r *ResponseChanges) TotalBreakingChanges() int {
c := r.PropertyChanges.TotalBreakingChanges()
if r.SchemaChanges != nil {
c += r.SchemaChanges.TotalBreakingChanges()
}
for k := range r.HeadersChanges {
c += r.HeadersChanges[k].TotalBreakingChanges()
}
for k := range r.ContentChanges {
c += r.ContentChanges[k].TotalBreakingChanges()
}
for k := range r.LinkChanges {
c += r.LinkChanges[k].TotalBreakingChanges()
}
return c
c := r.PropertyChanges.TotalBreakingChanges()
if r.SchemaChanges != nil {
c += r.SchemaChanges.TotalBreakingChanges()
}
for k := range r.HeadersChanges {
c += r.HeadersChanges[k].TotalBreakingChanges()
}
for k := range r.ContentChanges {
c += r.ContentChanges[k].TotalBreakingChanges()
}
for k := range r.LinkChanges {
c += r.LinkChanges[k].TotalBreakingChanges()
}
return c
}
// CompareResponseV2 is a Swagger type safe proxy for CompareResponse
func CompareResponseV2(l, r *v2.Response) *ResponseChanges {
return CompareResponse(l, r)
return CompareResponse(l, r)
}
// CompareResponseV3 is an OpenAPI type safe proxy for CompareResponse
func CompareResponseV3(l, r *v3.Response) *ResponseChanges {
return CompareResponse(l, r)
return CompareResponse(l, r)
}
// CompareResponse compares a left and right Swagger or OpenAPI Response object. If anything is found
// a pointer to a ResponseChanges is returned, otherwise it returns nil.
func CompareResponse(l, r any) *ResponseChanges {
var changes []*Change
var props []*PropertyCheck
var changes []*Change
var props []*PropertyCheck
rc := new(ResponseChanges)
rc := new(ResponseChanges)
if reflect.TypeOf(&v2.Response{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Response{}) == reflect.TypeOf(r) {
if reflect.TypeOf(&v2.Response{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Response{}) == reflect.TypeOf(r) {
lResponse := l.(*v2.Response)
rResponse := r.(*v2.Response)
lResponse := l.(*v2.Response)
rResponse := r.(*v2.Response)
// perform hash check to avoid further processing
if low.AreEqual(lResponse, rResponse) {
return nil
}
// perform hash check to avoid further processing
if low.AreEqual(lResponse, rResponse) {
return nil
}
// description
addPropertyCheck(&props, lResponse.Description.ValueNode, rResponse.Description.ValueNode,
lResponse.Description.Value, rResponse.Description.Value, &changes, v3.DescriptionLabel, false)
// description
addPropertyCheck(&props, lResponse.Description.ValueNode, rResponse.Description.ValueNode,
lResponse.Description.Value, rResponse.Description.Value, &changes, v3.DescriptionLabel, false)
if !lResponse.Schema.IsEmpty() && !rResponse.Schema.IsEmpty() {
rc.SchemaChanges = CompareSchemas(lResponse.Schema.Value, rResponse.Schema.Value)
}
if !lResponse.Schema.IsEmpty() && rResponse.Schema.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.SchemaLabel,
lResponse.Schema.ValueNode, nil, true,
lResponse.Schema.Value, nil)
}
if lResponse.Schema.IsEmpty() && !rResponse.Schema.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.SchemaLabel,
nil, rResponse.Schema.ValueNode, true,
nil, rResponse.Schema.Value)
}
if !lResponse.Schema.IsEmpty() && !rResponse.Schema.IsEmpty() {
rc.SchemaChanges = CompareSchemas(lResponse.Schema.Value, rResponse.Schema.Value)
}
if !lResponse.Schema.IsEmpty() && rResponse.Schema.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.SchemaLabel,
lResponse.Schema.ValueNode, nil, true,
lResponse.Schema.Value, nil)
}
if lResponse.Schema.IsEmpty() && !rResponse.Schema.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.SchemaLabel,
nil, rResponse.Schema.ValueNode, true,
nil, rResponse.Schema.Value)
}
rc.HeadersChanges =
CheckMapForChanges(lResponse.Headers.Value, rResponse.Headers.Value,
&changes, v3.HeadersLabel, CompareHeadersV2)
rc.HeadersChanges =
CheckMapForChanges(lResponse.Headers.Value, rResponse.Headers.Value,
&changes, v3.HeadersLabel, CompareHeadersV2)
if !lResponse.Examples.IsEmpty() && !rResponse.Examples.IsEmpty() {
rc.ExamplesChanges = CompareExamplesV2(lResponse.Examples.Value, rResponse.Examples.Value)
}
if !lResponse.Examples.IsEmpty() && rResponse.Examples.IsEmpty() {
CreateChange(&changes, PropertyRemoved, v3.ExamplesLabel,
lResponse.Schema.ValueNode, nil, false,
lResponse.Schema.Value, nil)
}
if lResponse.Examples.IsEmpty() && !rResponse.Examples.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.ExamplesLabel,
nil, rResponse.Schema.ValueNode, false,
nil, lResponse.Schema.Value)
}
if !lResponse.Examples.IsEmpty() && !rResponse.Examples.IsEmpty() {
rc.ExamplesChanges = CompareExamplesV2(lResponse.Examples.Value, rResponse.Examples.Value)
}
if !lResponse.Examples.IsEmpty() && rResponse.Examples.IsEmpty() {
CreateChange(&changes, PropertyRemoved, v3.ExamplesLabel,
lResponse.Schema.ValueNode, nil, false,
lResponse.Schema.Value, nil)
}
if lResponse.Examples.IsEmpty() && !rResponse.Examples.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.ExamplesLabel,
nil, rResponse.Schema.ValueNode, false,
nil, lResponse.Schema.Value)
}
rc.ExtensionChanges = CompareExtensions(lResponse.Extensions, rResponse.Extensions)
}
rc.ExtensionChanges = CompareExtensions(lResponse.Extensions, rResponse.Extensions)
}
if reflect.TypeOf(&v3.Response{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Response{}) == reflect.TypeOf(r) {
if reflect.TypeOf(&v3.Response{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Response{}) == reflect.TypeOf(r) {
lResponse := l.(*v3.Response)
rResponse := r.(*v3.Response)
lResponse := l.(*v3.Response)
rResponse := r.(*v3.Response)
// perform hash check to avoid further processing
if low.AreEqual(lResponse, rResponse) {
return nil
}
// perform hash check to avoid further processing
if low.AreEqual(lResponse, rResponse) {
return nil
}
// description
addPropertyCheck(&props, lResponse.Description.ValueNode, rResponse.Description.ValueNode,
lResponse.Description.Value, lResponse.Description.Value, &changes, v3.DescriptionLabel, false)
// description
addPropertyCheck(&props, lResponse.Description.ValueNode, rResponse.Description.ValueNode,
lResponse.Description.Value, lResponse.Description.Value, &changes, v3.DescriptionLabel, false)
rc.HeadersChanges =
CheckMapForChanges(lResponse.Headers.Value, rResponse.Headers.Value,
&changes, v3.HeadersLabel, CompareHeadersV3)
rc.HeadersChanges =
CheckMapForChanges(lResponse.Headers.Value, rResponse.Headers.Value,
&changes, v3.HeadersLabel, CompareHeadersV3)
rc.ContentChanges =
CheckMapForChanges(lResponse.Content.Value, rResponse.Content.Value,
&changes, v3.ContentLabel, CompareMediaTypes)
rc.ContentChanges =
CheckMapForChanges(lResponse.Content.Value, rResponse.Content.Value,
&changes, v3.ContentLabel, CompareMediaTypes)
rc.LinkChanges =
CheckMapForChanges(lResponse.Links.Value, rResponse.Links.Value,
&changes, v3.LinksLabel, CompareLinks)
rc.LinkChanges =
CheckMapForChanges(lResponse.Links.Value, rResponse.Links.Value,
&changes, v3.LinksLabel, CompareLinks)
rc.ExtensionChanges = CompareExtensions(lResponse.Extensions, rResponse.Extensions)
}
rc.ExtensionChanges = CompareExtensions(lResponse.Extensions, rResponse.Extensions)
}
CheckProperties(props)
rc.PropertyChanges = NewPropertyChanges(changes)
return rc
CheckProperties(props)
rc.PropertyChanges = NewPropertyChanges(changes)
return rc
}

View File

@@ -4,137 +4,137 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"reflect"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"reflect"
)
// ResponsesChanges represents changes made between two Swagger or OpenAPI Responses objects.
type ResponsesChanges struct {
*PropertyChanges
ResponseChanges map[string]*ResponseChanges `json:"response,omitempty" yaml:"response,omitempty"`
DefaultChanges *ResponseChanges `json:"default,omitempty" yaml:"default,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
*PropertyChanges
ResponseChanges map[string]*ResponseChanges `json:"response,omitempty" yaml:"response,omitempty"`
DefaultChanges *ResponseChanges `json:"default,omitempty" yaml:"default,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
// GetAllChanges returns a slice of all changes made between Responses objects
func (r *ResponsesChanges) GetAllChanges() []*Change {
var changes []*Change
changes = append(changes, r.Changes...)
for k := range r.ResponseChanges {
changes = append(changes, r.ResponseChanges[k].GetAllChanges()...)
}
if r.DefaultChanges != nil {
changes = append(changes, r.DefaultChanges.GetAllChanges()...)
}
if r.ExtensionChanges != nil {
changes = append(changes, r.ExtensionChanges.GetAllChanges()...)
}
return changes
var changes []*Change
changes = append(changes, r.Changes...)
for k := range r.ResponseChanges {
changes = append(changes, r.ResponseChanges[k].GetAllChanges()...)
}
if r.DefaultChanges != nil {
changes = append(changes, r.DefaultChanges.GetAllChanges()...)
}
if r.ExtensionChanges != nil {
changes = append(changes, r.ExtensionChanges.GetAllChanges()...)
}
return changes
}
// TotalChanges returns the total number of changes found between two Swagger or OpenAPI Responses objects
func (r *ResponsesChanges) TotalChanges() int {
c := r.PropertyChanges.TotalChanges()
for k := range r.ResponseChanges {
c += r.ResponseChanges[k].TotalChanges()
}
if r.DefaultChanges != nil {
c += r.DefaultChanges.TotalChanges()
}
if r.ExtensionChanges != nil {
c += r.ExtensionChanges.TotalChanges()
}
return c
c := r.PropertyChanges.TotalChanges()
for k := range r.ResponseChanges {
c += r.ResponseChanges[k].TotalChanges()
}
if r.DefaultChanges != nil {
c += r.DefaultChanges.TotalChanges()
}
if r.ExtensionChanges != nil {
c += r.ExtensionChanges.TotalChanges()
}
return c
}
// TotalBreakingChanges returns the total number of changes found between two Swagger or OpenAPI
// Responses Objects
func (r *ResponsesChanges) TotalBreakingChanges() int {
c := r.PropertyChanges.TotalBreakingChanges()
for k := range r.ResponseChanges {
c += r.ResponseChanges[k].TotalBreakingChanges()
}
if r.DefaultChanges != nil {
c += r.DefaultChanges.TotalBreakingChanges()
}
return c
c := r.PropertyChanges.TotalBreakingChanges()
for k := range r.ResponseChanges {
c += r.ResponseChanges[k].TotalBreakingChanges()
}
if r.DefaultChanges != nil {
c += r.DefaultChanges.TotalBreakingChanges()
}
return c
}
// CompareResponses compares a left and right Swagger or OpenAPI Responses object for any changes. If found
// returns a pointer to ResponsesChanges, or returns nil.
func CompareResponses(l, r any) *ResponsesChanges {
var changes []*Change
var changes []*Change
rc := new(ResponsesChanges)
rc := new(ResponsesChanges)
// swagger
if reflect.TypeOf(&v2.Responses{}) == reflect.TypeOf(l) &&
reflect.TypeOf(&v2.Responses{}) == reflect.TypeOf(r) {
// swagger
if reflect.TypeOf(&v2.Responses{}) == reflect.TypeOf(l) &&
reflect.TypeOf(&v2.Responses{}) == reflect.TypeOf(r) {
lResponses := l.(*v2.Responses)
rResponses := r.(*v2.Responses)
lResponses := l.(*v2.Responses)
rResponses := r.(*v2.Responses)
// perform hash check to avoid further processing
if low.AreEqual(lResponses, rResponses) {
return nil
}
// perform hash check to avoid further processing
if low.AreEqual(lResponses, rResponses) {
return nil
}
if !lResponses.Default.IsEmpty() && !rResponses.Default.IsEmpty() {
rc.DefaultChanges = CompareResponse(lResponses.Default.Value, rResponses.Default.Value)
}
if !lResponses.Default.IsEmpty() && rResponses.Default.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.DefaultLabel,
lResponses.Default.ValueNode, nil, true,
lResponses.Default.Value, nil)
}
if lResponses.Default.IsEmpty() && !rResponses.Default.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.DefaultLabel,
nil, rResponses.Default.ValueNode, false,
nil, lResponses.Default.Value)
}
if !lResponses.Default.IsEmpty() && !rResponses.Default.IsEmpty() {
rc.DefaultChanges = CompareResponse(lResponses.Default.Value, rResponses.Default.Value)
}
if !lResponses.Default.IsEmpty() && rResponses.Default.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.DefaultLabel,
lResponses.Default.ValueNode, nil, true,
lResponses.Default.Value, nil)
}
if lResponses.Default.IsEmpty() && !rResponses.Default.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.DefaultLabel,
nil, rResponses.Default.ValueNode, false,
nil, lResponses.Default.Value)
}
rc.ResponseChanges = CheckMapForChanges(lResponses.Codes, rResponses.Codes,
&changes, v3.CodesLabel, CompareResponseV2)
rc.ResponseChanges = CheckMapForChanges(lResponses.Codes, rResponses.Codes,
&changes, v3.CodesLabel, CompareResponseV2)
rc.ExtensionChanges = CompareExtensions(lResponses.Extensions, rResponses.Extensions)
}
rc.ExtensionChanges = CompareExtensions(lResponses.Extensions, rResponses.Extensions)
}
// openapi
if reflect.TypeOf(&v3.Responses{}) == reflect.TypeOf(l) &&
reflect.TypeOf(&v3.Responses{}) == reflect.TypeOf(r) {
// openapi
if reflect.TypeOf(&v3.Responses{}) == reflect.TypeOf(l) &&
reflect.TypeOf(&v3.Responses{}) == reflect.TypeOf(r) {
lResponses := l.(*v3.Responses)
rResponses := r.(*v3.Responses)
lResponses := l.(*v3.Responses)
rResponses := r.(*v3.Responses)
//perform hash check to avoid further processing
if low.AreEqual(lResponses, rResponses) {
return nil
}
//perform hash check to avoid further processing
if low.AreEqual(lResponses, rResponses) {
return nil
}
if !lResponses.Default.IsEmpty() && !rResponses.Default.IsEmpty() {
rc.DefaultChanges = CompareResponse(lResponses.Default.Value, rResponses.Default.Value)
}
if !lResponses.Default.IsEmpty() && rResponses.Default.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.DefaultLabel,
lResponses.Default.ValueNode, nil, true,
lResponses.Default.Value, nil)
}
if lResponses.Default.IsEmpty() && !rResponses.Default.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.DefaultLabel,
nil, rResponses.Default.ValueNode, false,
nil, lResponses.Default.Value)
}
if !lResponses.Default.IsEmpty() && !rResponses.Default.IsEmpty() {
rc.DefaultChanges = CompareResponse(lResponses.Default.Value, rResponses.Default.Value)
}
if !lResponses.Default.IsEmpty() && rResponses.Default.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.DefaultLabel,
lResponses.Default.ValueNode, nil, true,
lResponses.Default.Value, nil)
}
if lResponses.Default.IsEmpty() && !rResponses.Default.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.DefaultLabel,
nil, rResponses.Default.ValueNode, false,
nil, lResponses.Default.Value)
}
rc.ResponseChanges = CheckMapForChanges(lResponses.Codes, rResponses.Codes,
&changes, v3.CodesLabel, CompareResponseV3)
rc.ResponseChanges = CheckMapForChanges(lResponses.Codes, rResponses.Codes,
&changes, v3.CodesLabel, CompareResponseV3)
rc.ExtensionChanges = CompareExtensions(lResponses.Extensions, rResponses.Extensions)
rc.ExtensionChanges = CompareExtensions(lResponses.Extensions, rResponses.Extensions)
}
}
rc.PropertyChanges = NewPropertyChanges(changes)
return rc
rc.PropertyChanges = NewPropertyChanges(changes)
return rc
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,73 +4,73 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v2"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v2"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
)
// ScopesChanges represents changes between two Swagger Scopes Objects
type ScopesChanges struct {
*PropertyChanges
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
*PropertyChanges
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
// GetAllChanges returns a slice of all changes made between Scopes objects
func (s *ScopesChanges) GetAllChanges() []*Change {
var changes []*Change
changes = append(changes, s.Changes...)
if s.ExtensionChanges != nil {
changes = append(changes, s.ExtensionChanges.GetAllChanges()...)
}
return changes
var changes []*Change
changes = append(changes, s.Changes...)
if s.ExtensionChanges != nil {
changes = append(changes, s.ExtensionChanges.GetAllChanges()...)
}
return changes
}
// TotalChanges returns the total changes found between two Swagger Scopes objects.
func (s *ScopesChanges) TotalChanges() int {
c := s.PropertyChanges.TotalChanges()
if s.ExtensionChanges != nil {
c += s.ExtensionChanges.TotalChanges()
}
return c
c := s.PropertyChanges.TotalChanges()
if s.ExtensionChanges != nil {
c += s.ExtensionChanges.TotalChanges()
}
return c
}
// TotalBreakingChanges returns the total number of breaking changes between two Swagger Scopes objects.
func (s *ScopesChanges) TotalBreakingChanges() int {
return s.PropertyChanges.TotalBreakingChanges()
return s.PropertyChanges.TotalBreakingChanges()
}
// CompareScopes compares a left and right Swagger Scopes objects for changes. If anything is found, returns
// a pointer to ScopesChanges, or returns nil if nothing is found.
func CompareScopes(l, r *v2.Scopes) *ScopesChanges {
if low.AreEqual(l, r) {
return nil
}
var changes []*Change
for v := range l.Values {
if r != nil && r.FindScope(v.Value) == nil {
CreateChange(&changes, ObjectRemoved, v3.Scopes,
l.Values[v].ValueNode, nil, true,
v.Value, nil)
continue
}
if r != nil && r.FindScope(v.Value) != nil {
if l.Values[v].Value != r.FindScope(v.Value).Value {
CreateChange(&changes, Modified, v3.Scopes,
l.Values[v].ValueNode, r.FindScope(v.Value).ValueNode, true,
l.Values[v].Value, r.FindScope(v.Value).Value)
}
}
}
for v := range r.Values {
if l != nil && l.FindScope(v.Value) == nil {
CreateChange(&changes, ObjectAdded, v3.Scopes,
nil, r.Values[v].ValueNode, false,
nil, v.Value)
}
}
if low.AreEqual(l, r) {
return nil
}
var changes []*Change
for v := range l.Values {
if r != nil && r.FindScope(v.Value) == nil {
CreateChange(&changes, ObjectRemoved, v3.Scopes,
l.Values[v].ValueNode, nil, true,
v.Value, nil)
continue
}
if r != nil && r.FindScope(v.Value) != nil {
if l.Values[v].Value != r.FindScope(v.Value).Value {
CreateChange(&changes, Modified, v3.Scopes,
l.Values[v].ValueNode, r.FindScope(v.Value).ValueNode, true,
l.Values[v].Value, r.FindScope(v.Value).Value)
}
}
}
for v := range r.Values {
if l != nil && l.FindScope(v.Value) == nil {
CreateChange(&changes, ObjectAdded, v3.Scopes,
nil, r.Values[v].ValueNode, false,
nil, v.Value)
}
}
sc := new(ScopesChanges)
sc.PropertyChanges = NewPropertyChanges(changes)
sc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions)
return sc
sc := new(ScopesChanges)
sc.PropertyChanges = NewPropertyChanges(changes)
sc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions)
return sc
}

View File

@@ -4,147 +4,147 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"gopkg.in/yaml.v3"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"gopkg.in/yaml.v3"
)
// SecurityRequirementChanges represents changes found between two SecurityRequirement Objects.
type SecurityRequirementChanges struct {
*PropertyChanges
*PropertyChanges
}
// GetAllChanges returns a slice of all changes made between SecurityRequirement objects
func (s *SecurityRequirementChanges) GetAllChanges() []*Change {
return s.Changes
return s.Changes
}
// TotalChanges returns the total number of changes between two SecurityRequirement Objects.
func (s *SecurityRequirementChanges) TotalChanges() int {
return s.PropertyChanges.TotalChanges()
return s.PropertyChanges.TotalChanges()
}
// TotalBreakingChanges returns the total number of breaking changes between two SecurityRequirement Objects.
func (s *SecurityRequirementChanges) TotalBreakingChanges() int {
return s.PropertyChanges.TotalBreakingChanges()
return s.PropertyChanges.TotalBreakingChanges()
}
// CompareSecurityRequirement compares left and right SecurityRequirement objects for changes. If anything
// is found, then a pointer to SecurityRequirementChanges is returned, otherwise nil.
func CompareSecurityRequirement(l, r *base.SecurityRequirement) *SecurityRequirementChanges {
var changes []*Change
sc := new(SecurityRequirementChanges)
var changes []*Change
sc := new(SecurityRequirementChanges)
if low.AreEqual(l, r) {
return nil
}
checkSecurityRequirement(l.Requirements.Value, r.Requirements.Value, &changes)
sc.PropertyChanges = NewPropertyChanges(changes)
return sc
if low.AreEqual(l, r) {
return nil
}
checkSecurityRequirement(l.Requirements.Value, r.Requirements.Value, &changes)
sc.PropertyChanges = NewPropertyChanges(changes)
return sc
}
func removedSecurityRequirement(vn *yaml.Node, name string, changes *[]*Change) {
CreateChange(changes, ObjectRemoved, v3.SecurityLabel,
vn, nil, true, name, nil)
CreateChange(changes, ObjectRemoved, v3.SecurityLabel,
vn, nil, true, name, nil)
}
func addedSecurityRequirement(vn *yaml.Node, name string, changes *[]*Change) {
CreateChange(changes, ObjectAdded, v3.SecurityLabel,
nil, vn, false, nil, name)
CreateChange(changes, ObjectAdded, v3.SecurityLabel,
nil, vn, false, nil, name)
}
// tricky to do this correctly, this is my solution.
func checkSecurityRequirement(lSec, rSec map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]],
changes *[]*Change) {
changes *[]*Change) {
lKeys := make([]string, len(lSec))
rKeys := make([]string, len(rSec))
lValues := make(map[string]low.ValueReference[[]low.ValueReference[string]])
rValues := make(map[string]low.ValueReference[[]low.ValueReference[string]])
var n, z int
for i := range lSec {
lKeys[n] = i.Value
lValues[i.Value] = lSec[i]
n++
}
for i := range rSec {
rKeys[z] = i.Value
rValues[i.Value] = rSec[i]
z++
}
lKeys := make([]string, len(lSec))
rKeys := make([]string, len(rSec))
lValues := make(map[string]low.ValueReference[[]low.ValueReference[string]])
rValues := make(map[string]low.ValueReference[[]low.ValueReference[string]])
var n, z int
for i := range lSec {
lKeys[n] = i.Value
lValues[i.Value] = lSec[i]
n++
}
for i := range rSec {
rKeys[z] = i.Value
rValues[i.Value] = rSec[i]
z++
}
for z = range lKeys {
if z < len(rKeys) {
if _, ok := rValues[lKeys[z]]; !ok {
removedSecurityRequirement(lValues[lKeys[z]].ValueNode, lKeys[z], changes)
continue
}
for z = range lKeys {
if z < len(rKeys) {
if _, ok := rValues[lKeys[z]]; !ok {
removedSecurityRequirement(lValues[lKeys[z]].ValueNode, lKeys[z], changes)
continue
}
lValue := lValues[lKeys[z]].Value
rValue := rValues[lKeys[z]].Value
lValue := lValues[lKeys[z]].Value
rValue := rValues[lKeys[z]].Value
// check if actual values match up
lRoleKeys := make([]string, len(lValue))
rRoleKeys := make([]string, len(rValue))
lRoleValues := make(map[string]low.ValueReference[string])
rRoleValues := make(map[string]low.ValueReference[string])
var t, k int
for i := range lValue {
lRoleKeys[t] = lValue[i].Value
lRoleValues[lValue[i].Value] = lValue[i]
t++
}
for i := range rValue {
rRoleKeys[k] = rValue[i].Value
rRoleValues[rValue[i].Value] = rValue[i]
k++
}
// check if actual values match up
lRoleKeys := make([]string, len(lValue))
rRoleKeys := make([]string, len(rValue))
lRoleValues := make(map[string]low.ValueReference[string])
rRoleValues := make(map[string]low.ValueReference[string])
var t, k int
for i := range lValue {
lRoleKeys[t] = lValue[i].Value
lRoleValues[lValue[i].Value] = lValue[i]
t++
}
for i := range rValue {
rRoleKeys[k] = rValue[i].Value
rRoleValues[rValue[i].Value] = rValue[i]
k++
}
for t = range lRoleKeys {
if t < len(rRoleKeys) {
if _, ok := rRoleValues[lRoleKeys[t]]; !ok {
removedSecurityRequirement(lRoleValues[lRoleKeys[t]].ValueNode, lRoleKeys[t], changes)
continue
}
}
if t >= len(rRoleKeys) {
if _, ok := rRoleValues[lRoleKeys[t]]; !ok {
removedSecurityRequirement(lRoleValues[lRoleKeys[t]].ValueNode, lRoleKeys[t], changes)
}
}
}
for t = range rRoleKeys {
if t < len(lRoleKeys) {
if _, ok := lRoleValues[rRoleKeys[t]]; !ok {
addedSecurityRequirement(rRoleValues[rRoleKeys[t]].ValueNode, rRoleKeys[t], changes)
continue
}
}
if t >= len(lRoleKeys) {
addedSecurityRequirement(rRoleValues[rRoleKeys[t]].ValueNode, rRoleKeys[t], changes)
}
}
for t = range lRoleKeys {
if t < len(rRoleKeys) {
if _, ok := rRoleValues[lRoleKeys[t]]; !ok {
removedSecurityRequirement(lRoleValues[lRoleKeys[t]].ValueNode, lRoleKeys[t], changes)
continue
}
}
if t >= len(rRoleKeys) {
if _, ok := rRoleValues[lRoleKeys[t]]; !ok {
removedSecurityRequirement(lRoleValues[lRoleKeys[t]].ValueNode, lRoleKeys[t], changes)
}
}
}
for t = range rRoleKeys {
if t < len(lRoleKeys) {
if _, ok := lRoleValues[rRoleKeys[t]]; !ok {
addedSecurityRequirement(rRoleValues[rRoleKeys[t]].ValueNode, rRoleKeys[t], changes)
continue
}
}
if t >= len(lRoleKeys) {
addedSecurityRequirement(rRoleValues[rRoleKeys[t]].ValueNode, rRoleKeys[t], changes)
}
}
}
if z >= len(rKeys) {
if _, ok := rValues[lKeys[z]]; !ok {
removedSecurityRequirement(lValues[lKeys[z]].ValueNode, lKeys[z], changes)
}
}
}
for z = range rKeys {
if z < len(lKeys) {
if _, ok := lValues[rKeys[z]]; !ok {
addedSecurityRequirement(rValues[rKeys[z]].ValueNode, rKeys[z], changes)
continue
}
}
if z >= len(lKeys) {
if _, ok := lValues[rKeys[z]]; !ok {
addedSecurityRequirement(rValues[rKeys[z]].ValueNode, rKeys[z], changes)
}
}
}
}
if z >= len(rKeys) {
if _, ok := rValues[lKeys[z]]; !ok {
removedSecurityRequirement(lValues[lKeys[z]].ValueNode, lKeys[z], changes)
}
}
}
for z = range rKeys {
if z < len(lKeys) {
if _, ok := lValues[rKeys[z]]; !ok {
addedSecurityRequirement(rValues[rKeys[z]].ValueNode, rKeys[z], changes)
continue
}
}
if z >= len(lKeys) {
if _, ok := lValues[rKeys[z]]; !ok {
addedSecurityRequirement(rValues[rKeys[z]].ValueNode, rKeys[z], changes)
}
}
}
}

View File

@@ -4,178 +4,178 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"reflect"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"reflect"
)
// SecuritySchemeChanges represents changes made between Swagger or OpenAPI SecurityScheme Objects.
type SecuritySchemeChanges struct {
*PropertyChanges
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
*PropertyChanges
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
// OpenAPI Version
OAuthFlowChanges *OAuthFlowsChanges `json:"oAuthFlow,omitempty" yaml:"oAuthFlow,omitempty"`
// OpenAPI Version
OAuthFlowChanges *OAuthFlowsChanges `json:"oAuthFlow,omitempty" yaml:"oAuthFlow,omitempty"`
// Swagger Version
ScopesChanges *ScopesChanges `json:"scopes,omitempty" yaml:"scopes,omitempty"`
// Swagger Version
ScopesChanges *ScopesChanges `json:"scopes,omitempty" yaml:"scopes,omitempty"`
}
// GetAllChanges returns a slice of all changes made between SecurityRequirement objects
func (ss *SecuritySchemeChanges) GetAllChanges() []*Change {
var changes []*Change
changes = append(changes, ss.Changes...)
if ss.OAuthFlowChanges != nil {
changes = append(changes, ss.OAuthFlowChanges.GetAllChanges()...)
}
if ss.ScopesChanges != nil {
changes = append(changes, ss.ScopesChanges.GetAllChanges()...)
}
if ss.ExtensionChanges != nil {
changes = append(changes, ss.ExtensionChanges.GetAllChanges()...)
}
return changes
var changes []*Change
changes = append(changes, ss.Changes...)
if ss.OAuthFlowChanges != nil {
changes = append(changes, ss.OAuthFlowChanges.GetAllChanges()...)
}
if ss.ScopesChanges != nil {
changes = append(changes, ss.ScopesChanges.GetAllChanges()...)
}
if ss.ExtensionChanges != nil {
changes = append(changes, ss.ExtensionChanges.GetAllChanges()...)
}
return changes
}
// TotalChanges represents total changes found between two Swagger or OpenAPI SecurityScheme instances.
func (ss *SecuritySchemeChanges) TotalChanges() int {
c := ss.PropertyChanges.TotalChanges()
if ss.OAuthFlowChanges != nil {
c += ss.OAuthFlowChanges.TotalChanges()
}
if ss.ScopesChanges != nil {
c += ss.ScopesChanges.TotalChanges()
}
if ss.ExtensionChanges != nil {
c += ss.ExtensionChanges.TotalChanges()
}
return c
c := ss.PropertyChanges.TotalChanges()
if ss.OAuthFlowChanges != nil {
c += ss.OAuthFlowChanges.TotalChanges()
}
if ss.ScopesChanges != nil {
c += ss.ScopesChanges.TotalChanges()
}
if ss.ExtensionChanges != nil {
c += ss.ExtensionChanges.TotalChanges()
}
return c
}
// TotalBreakingChanges returns total number of breaking changes between two SecurityScheme Objects.
func (ss *SecuritySchemeChanges) TotalBreakingChanges() int {
c := ss.PropertyChanges.TotalBreakingChanges()
if ss.OAuthFlowChanges != nil {
c += ss.OAuthFlowChanges.TotalBreakingChanges()
}
if ss.ScopesChanges != nil {
c += ss.ScopesChanges.TotalBreakingChanges()
}
return c
c := ss.PropertyChanges.TotalBreakingChanges()
if ss.OAuthFlowChanges != nil {
c += ss.OAuthFlowChanges.TotalBreakingChanges()
}
if ss.ScopesChanges != nil {
c += ss.ScopesChanges.TotalBreakingChanges()
}
return c
}
// CompareSecuritySchemesV2 is a Swagger type safe proxy for CompareSecuritySchemes
func CompareSecuritySchemesV2(l, r *v2.SecurityScheme) *SecuritySchemeChanges {
return CompareSecuritySchemes(l, r)
return CompareSecuritySchemes(l, r)
}
// CompareSecuritySchemesV3 is an OpenAPI type safe proxt for CompareSecuritySchemes
func CompareSecuritySchemesV3(l, r *v3.SecurityScheme) *SecuritySchemeChanges {
return CompareSecuritySchemes(l, r)
return CompareSecuritySchemes(l, r)
}
// CompareSecuritySchemes compares left and right Swagger or OpenAPI Security Scheme objects for changes.
// If anything is found, returns a pointer to *SecuritySchemeChanges or nil if nothing is found.
func CompareSecuritySchemes(l, r any) *SecuritySchemeChanges {
var props []*PropertyCheck
var changes []*Change
var props []*PropertyCheck
var changes []*Change
sc := new(SecuritySchemeChanges)
if reflect.TypeOf(&v2.SecurityScheme{}) == reflect.TypeOf(l) &&
reflect.TypeOf(&v2.SecurityScheme{}) == reflect.TypeOf(r) {
sc := new(SecuritySchemeChanges)
if reflect.TypeOf(&v2.SecurityScheme{}) == reflect.TypeOf(l) &&
reflect.TypeOf(&v2.SecurityScheme{}) == reflect.TypeOf(r) {
lSS := l.(*v2.SecurityScheme)
rSS := r.(*v2.SecurityScheme)
lSS := l.(*v2.SecurityScheme)
rSS := r.(*v2.SecurityScheme)
if low.AreEqual(lSS, rSS) {
return nil
}
addPropertyCheck(&props, lSS.Type.ValueNode, rSS.Type.ValueNode,
lSS.Type.Value, rSS.Type.Value, &changes, v3.TypeLabel, true)
if low.AreEqual(lSS, rSS) {
return nil
}
addPropertyCheck(&props, lSS.Type.ValueNode, rSS.Type.ValueNode,
lSS.Type.Value, rSS.Type.Value, &changes, v3.TypeLabel, true)
addPropertyCheck(&props, lSS.Description.ValueNode, rSS.Description.ValueNode,
lSS.Description.Value, rSS.Description.Value, &changes, v3.DescriptionLabel, false)
addPropertyCheck(&props, lSS.Description.ValueNode, rSS.Description.ValueNode,
lSS.Description.Value, rSS.Description.Value, &changes, v3.DescriptionLabel, false)
addPropertyCheck(&props, lSS.Name.ValueNode, rSS.Name.ValueNode,
lSS.Name.Value, rSS.Name.Value, &changes, v3.NameLabel, true)
addPropertyCheck(&props, lSS.Name.ValueNode, rSS.Name.ValueNode,
lSS.Name.Value, rSS.Name.Value, &changes, v3.NameLabel, true)
addPropertyCheck(&props, lSS.In.ValueNode, rSS.In.ValueNode,
lSS.In.Value, rSS.In.Value, &changes, v3.InLabel, true)
addPropertyCheck(&props, lSS.In.ValueNode, rSS.In.ValueNode,
lSS.In.Value, rSS.In.Value, &changes, v3.InLabel, true)
addPropertyCheck(&props, lSS.Flow.ValueNode, rSS.Flow.ValueNode,
lSS.Flow.Value, rSS.Flow.Value, &changes, v3.FlowLabel, true)
addPropertyCheck(&props, lSS.Flow.ValueNode, rSS.Flow.ValueNode,
lSS.Flow.Value, rSS.Flow.Value, &changes, v3.FlowLabel, true)
addPropertyCheck(&props, lSS.AuthorizationUrl.ValueNode, rSS.AuthorizationUrl.ValueNode,
lSS.AuthorizationUrl.Value, rSS.AuthorizationUrl.Value, &changes, v3.AuthorizationUrlLabel, true)
addPropertyCheck(&props, lSS.AuthorizationUrl.ValueNode, rSS.AuthorizationUrl.ValueNode,
lSS.AuthorizationUrl.Value, rSS.AuthorizationUrl.Value, &changes, v3.AuthorizationUrlLabel, true)
addPropertyCheck(&props, lSS.TokenUrl.ValueNode, rSS.TokenUrl.ValueNode,
lSS.TokenUrl.Value, rSS.TokenUrl.Value, &changes, v3.TokenUrlLabel, true)
addPropertyCheck(&props, lSS.TokenUrl.ValueNode, rSS.TokenUrl.ValueNode,
lSS.TokenUrl.Value, rSS.TokenUrl.Value, &changes, v3.TokenUrlLabel, true)
if !lSS.Scopes.IsEmpty() && !rSS.Scopes.IsEmpty() {
if !low.AreEqual(lSS.Scopes.Value, rSS.Scopes.Value) {
sc.ScopesChanges = CompareScopes(lSS.Scopes.Value, rSS.Scopes.Value)
}
}
if lSS.Scopes.IsEmpty() && !rSS.Scopes.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.ScopesLabel,
nil, rSS.Scopes.ValueNode, false, nil, rSS.Scopes.Value)
}
if !lSS.Scopes.IsEmpty() && rSS.Scopes.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.ScopesLabel,
lSS.Scopes.ValueNode, nil, true, lSS.Scopes.Value, nil)
}
if !lSS.Scopes.IsEmpty() && !rSS.Scopes.IsEmpty() {
if !low.AreEqual(lSS.Scopes.Value, rSS.Scopes.Value) {
sc.ScopesChanges = CompareScopes(lSS.Scopes.Value, rSS.Scopes.Value)
}
}
if lSS.Scopes.IsEmpty() && !rSS.Scopes.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.ScopesLabel,
nil, rSS.Scopes.ValueNode, false, nil, rSS.Scopes.Value)
}
if !lSS.Scopes.IsEmpty() && rSS.Scopes.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.ScopesLabel,
lSS.Scopes.ValueNode, nil, true, lSS.Scopes.Value, nil)
}
sc.ExtensionChanges = CompareExtensions(lSS.Extensions, rSS.Extensions)
}
sc.ExtensionChanges = CompareExtensions(lSS.Extensions, rSS.Extensions)
}
if reflect.TypeOf(&v3.SecurityScheme{}) == reflect.TypeOf(l) &&
reflect.TypeOf(&v3.SecurityScheme{}) == reflect.TypeOf(r) {
if reflect.TypeOf(&v3.SecurityScheme{}) == reflect.TypeOf(l) &&
reflect.TypeOf(&v3.SecurityScheme{}) == reflect.TypeOf(r) {
lSS := l.(*v3.SecurityScheme)
rSS := r.(*v3.SecurityScheme)
lSS := l.(*v3.SecurityScheme)
rSS := r.(*v3.SecurityScheme)
if low.AreEqual(lSS, rSS) {
return nil
}
addPropertyCheck(&props, lSS.Type.ValueNode, rSS.Type.ValueNode,
lSS.Type.Value, rSS.Type.Value, &changes, v3.TypeLabel, true)
if low.AreEqual(lSS, rSS) {
return nil
}
addPropertyCheck(&props, lSS.Type.ValueNode, rSS.Type.ValueNode,
lSS.Type.Value, rSS.Type.Value, &changes, v3.TypeLabel, true)
addPropertyCheck(&props, lSS.Description.ValueNode, rSS.Description.ValueNode,
lSS.Description.Value, rSS.Description.Value, &changes, v3.DescriptionLabel, false)
addPropertyCheck(&props, lSS.Description.ValueNode, rSS.Description.ValueNode,
lSS.Description.Value, rSS.Description.Value, &changes, v3.DescriptionLabel, false)
addPropertyCheck(&props, lSS.Name.ValueNode, rSS.Name.ValueNode,
lSS.Name.Value, rSS.Name.Value, &changes, v3.NameLabel, true)
addPropertyCheck(&props, lSS.Name.ValueNode, rSS.Name.ValueNode,
lSS.Name.Value, rSS.Name.Value, &changes, v3.NameLabel, true)
addPropertyCheck(&props, lSS.In.ValueNode, rSS.In.ValueNode,
lSS.In.Value, rSS.In.Value, &changes, v3.InLabel, true)
addPropertyCheck(&props, lSS.In.ValueNode, rSS.In.ValueNode,
lSS.In.Value, rSS.In.Value, &changes, v3.InLabel, true)
addPropertyCheck(&props, lSS.Scheme.ValueNode, rSS.Scheme.ValueNode,
lSS.Scheme.Value, rSS.Scheme.Value, &changes, v3.SchemeLabel, true)
addPropertyCheck(&props, lSS.Scheme.ValueNode, rSS.Scheme.ValueNode,
lSS.Scheme.Value, rSS.Scheme.Value, &changes, v3.SchemeLabel, true)
addPropertyCheck(&props, lSS.BearerFormat.ValueNode, rSS.BearerFormat.ValueNode,
lSS.BearerFormat.Value, rSS.BearerFormat.Value, &changes, v3.SchemeLabel, false)
addPropertyCheck(&props, lSS.BearerFormat.ValueNode, rSS.BearerFormat.ValueNode,
lSS.BearerFormat.Value, rSS.BearerFormat.Value, &changes, v3.SchemeLabel, false)
addPropertyCheck(&props, lSS.OpenIdConnectUrl.ValueNode, rSS.OpenIdConnectUrl.ValueNode,
lSS.OpenIdConnectUrl.Value, rSS.OpenIdConnectUrl.Value, &changes, v3.OpenIdConnectUrlLabel, false)
addPropertyCheck(&props, lSS.OpenIdConnectUrl.ValueNode, rSS.OpenIdConnectUrl.ValueNode,
lSS.OpenIdConnectUrl.Value, rSS.OpenIdConnectUrl.Value, &changes, v3.OpenIdConnectUrlLabel, false)
if !lSS.Flows.IsEmpty() && !rSS.Flows.IsEmpty() {
if !low.AreEqual(lSS.Flows.Value, rSS.Flows.Value) {
sc.OAuthFlowChanges = CompareOAuthFlows(lSS.Flows.Value, rSS.Flows.Value)
}
}
if lSS.Flows.IsEmpty() && !rSS.Flows.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.FlowsLabel,
nil, rSS.Flows.ValueNode, false, nil, rSS.Flows.Value)
}
if !lSS.Flows.IsEmpty() && rSS.Flows.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.ScopesLabel,
lSS.Flows.ValueNode, nil, true, lSS.Flows.Value, nil)
}
sc.ExtensionChanges = CompareExtensions(lSS.Extensions, rSS.Extensions)
}
CheckProperties(props)
sc.PropertyChanges = NewPropertyChanges(changes)
return sc
if !lSS.Flows.IsEmpty() && !rSS.Flows.IsEmpty() {
if !low.AreEqual(lSS.Flows.Value, rSS.Flows.Value) {
sc.OAuthFlowChanges = CompareOAuthFlows(lSS.Flows.Value, rSS.Flows.Value)
}
}
if lSS.Flows.IsEmpty() && !rSS.Flows.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.FlowsLabel,
nil, rSS.Flows.ValueNode, false, nil, rSS.Flows.Value)
}
if !lSS.Flows.IsEmpty() && rSS.Flows.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.ScopesLabel,
lSS.Flows.ValueNode, nil, true, lSS.Flows.Value, nil)
}
sc.ExtensionChanges = CompareExtensions(lSS.Extensions, rSS.Extensions)
}
CheckProperties(props)
sc.PropertyChanges = NewPropertyChanges(changes)
return sc
}

View File

@@ -4,79 +4,79 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v3"
)
// ServerChanges represents changes found between two OpenAPI Server Objects
type ServerChanges struct {
*PropertyChanges
ServerVariableChanges map[string]*ServerVariableChanges `json:"serverVariables,omitempty" yaml:"serverVariables,omitempty"`
*PropertyChanges
ServerVariableChanges map[string]*ServerVariableChanges `json:"serverVariables,omitempty" yaml:"serverVariables,omitempty"`
}
// GetAllChanges returns a slice of all changes made between SecurityRequirement objects
func (s *ServerChanges) GetAllChanges() []*Change {
var changes []*Change
changes = append(changes, s.Changes...)
for k := range s.ServerVariableChanges {
changes = append(changes, s.ServerVariableChanges[k].GetAllChanges()...)
}
return changes
var changes []*Change
changes = append(changes, s.Changes...)
for k := range s.ServerVariableChanges {
changes = append(changes, s.ServerVariableChanges[k].GetAllChanges()...)
}
return changes
}
// TotalChanges returns total changes found between two OpenAPI Server Objects
func (s *ServerChanges) TotalChanges() int {
c := s.PropertyChanges.TotalChanges()
for k := range s.ServerVariableChanges {
c += s.ServerVariableChanges[k].TotalChanges()
}
return c
c := s.PropertyChanges.TotalChanges()
for k := range s.ServerVariableChanges {
c += s.ServerVariableChanges[k].TotalChanges()
}
return c
}
// TotalBreakingChanges returns the total number of breaking changes found between two OpenAPI Server objects.
func (s *ServerChanges) TotalBreakingChanges() int {
c := s.PropertyChanges.TotalBreakingChanges()
for k := range s.ServerVariableChanges {
c += s.ServerVariableChanges[k].TotalBreakingChanges()
}
return c
c := s.PropertyChanges.TotalBreakingChanges()
for k := range s.ServerVariableChanges {
c += s.ServerVariableChanges[k].TotalBreakingChanges()
}
return c
}
// CompareServers compares two OpenAPI Server objects for any changes. If anything is found, returns a pointer
// to a ServerChanges instance, or returns nil if nothing is found.
func CompareServers(l, r *v3.Server) *ServerChanges {
if low.AreEqual(l, r) {
return nil
}
var changes []*Change
var props []*PropertyCheck
if low.AreEqual(l, r) {
return nil
}
var changes []*Change
var props []*PropertyCheck
// URL
props = append(props, &PropertyCheck{
LeftNode: l.URL.ValueNode,
RightNode: r.URL.ValueNode,
Label: v3.URLLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// Description
props = append(props, &PropertyCheck{
LeftNode: l.Description.ValueNode,
RightNode: r.Description.ValueNode,
Label: v3.DescriptionLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// URL
props = append(props, &PropertyCheck{
LeftNode: l.URL.ValueNode,
RightNode: r.URL.ValueNode,
Label: v3.URLLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// Description
props = append(props, &PropertyCheck{
LeftNode: l.Description.ValueNode,
RightNode: r.Description.ValueNode,
Label: v3.DescriptionLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
CheckProperties(props)
sc := new(ServerChanges)
sc.PropertyChanges = NewPropertyChanges(changes)
sc.ServerVariableChanges = CheckMapForChanges(l.Variables.Value, r.Variables.Value,
&changes, v3.VariablesLabel, CompareServerVariables)
CheckProperties(props)
sc := new(ServerChanges)
sc.PropertyChanges = NewPropertyChanges(changes)
sc.ServerVariableChanges = CheckMapForChanges(l.Variables.Value, r.Variables.Value,
&changes, v3.VariablesLabel, CompareServerVariables)
return sc
return sc
}

View File

@@ -4,16 +4,16 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
"github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
)
func TestCompareServers(t *testing.T) {
left := `url: https://pb33f.io
left := `url: https://pb33f.io
description: a server
variables:
thing:
@@ -22,7 +22,7 @@ variables:
- biccy
default: choccy`
right := `url: https://pb33f.io
right := `url: https://pb33f.io
description: a server
variables:
thing:
@@ -31,26 +31,26 @@ variables:
- biccy
default: choccy`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Server
var rDoc v3.Server
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.Server
var rDoc v3.Server
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareServers(&lDoc, &rDoc)
assert.Nil(t, extChanges)
// compare.
extChanges := CompareServers(&lDoc, &rDoc)
assert.Nil(t, extChanges)
}
func TestCompareServers_Modified(t *testing.T) {
left := `url: https://pb33f.io
left := `url: https://pb33f.io
description: a server
variables:
thing:
@@ -59,7 +59,7 @@ variables:
- biccy
default: choccy`
right := `url: https://pb33f.io/hotness
right := `url: https://pb33f.io/hotness
description: a server that is not
variables:
thing:
@@ -68,27 +68,27 @@ variables:
- biccy
default: biccy`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Server
var rDoc v3.Server
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.Server
var rDoc v3.Server
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareServers(&lDoc, &rDoc)
assert.Equal(t, 3, extChanges.TotalChanges())
// compare.
extChanges := CompareServers(&lDoc, &rDoc)
assert.Equal(t, 3, extChanges.TotalChanges())
assert.Equal(t, 2, extChanges.TotalBreakingChanges())
assert.Equal(t, 2, extChanges.TotalBreakingChanges())
}
func TestCompareServers_Added(t *testing.T) {
left := `url: https://pb33f.io
left := `url: https://pb33f.io
variables:
thing:
enum:
@@ -96,7 +96,7 @@ variables:
- biccy
default: choccy`
right := `url: https://pb33f.io
right := `url: https://pb33f.io
description: a server
variables:
thing:
@@ -106,29 +106,29 @@ variables:
- tea
default: choccy`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Server
var rDoc v3.Server
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.Server
var rDoc v3.Server
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareServers(&lDoc, &rDoc)
assert.Equal(t, 2, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 2)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType)
assert.Equal(t, ObjectAdded, extChanges.ServerVariableChanges["thing"].Changes[0].ChangeType)
// compare.
extChanges := CompareServers(&lDoc, &rDoc)
assert.Equal(t, 2, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 2)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType)
assert.Equal(t, ObjectAdded, extChanges.ServerVariableChanges["thing"].Changes[0].ChangeType)
}
func TestCompareServers_Removed(t *testing.T) {
left := `url: https://pb33f.io
left := `url: https://pb33f.io
variables:
thing:
enum:
@@ -136,7 +136,7 @@ variables:
- biccy
default: choccy`
right := `url: https://pb33f.io
right := `url: https://pb33f.io
description: a server
variables:
thing:
@@ -146,23 +146,23 @@ variables:
- tea
default: choccy`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Server
var rDoc v3.Server
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc v3.Server
var rDoc v3.Server
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareServers(&rDoc, &lDoc)
assert.Equal(t, 2, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 2)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType)
assert.Equal(t, ObjectRemoved, extChanges.ServerVariableChanges["thing"].Changes[0].ChangeType)
// compare.
extChanges := CompareServers(&rDoc, &lDoc)
assert.Equal(t, 2, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 2)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType)
assert.Equal(t, ObjectRemoved, extChanges.ServerVariableChanges["thing"].Changes[0].ChangeType)
}

View File

@@ -4,79 +4,79 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v3"
)
// ServerVariableChanges represents changes found between two OpenAPI ServerVariable Objects
type ServerVariableChanges struct {
*PropertyChanges
*PropertyChanges
}
// GetAllChanges returns a slice of all changes made between SecurityRequirement objects
func (s *ServerVariableChanges) GetAllChanges() []*Change {
return s.Changes
return s.Changes
}
// CompareServerVariables compares a left and right OpenAPI ServerVariable object for changes.
// If anything is found, returns a pointer to a ServerVariableChanges instance, otherwise returns nil.
func CompareServerVariables(l, r *v3.ServerVariable) *ServerVariableChanges {
if low.AreEqual(l, r) {
return nil
}
if low.AreEqual(l, r) {
return nil
}
var props []*PropertyCheck
var changes []*Change
var props []*PropertyCheck
var changes []*Change
lValues := make(map[string]low.NodeReference[string])
rValues := make(map[string]low.NodeReference[string])
for i := range l.Enum {
lValues[l.Enum[i].Value] = l.Enum[i]
}
for i := range r.Enum {
rValues[r.Enum[i].Value] = r.Enum[i]
}
for k := range lValues {
if _, ok := rValues[k]; !ok {
CreateChange(&changes, ObjectRemoved, v3.EnumLabel,
lValues[k].ValueNode, nil, true,
lValues[k].Value, nil)
continue
}
}
for k := range rValues {
if _, ok := lValues[k]; !ok {
CreateChange(&changes, ObjectAdded, v3.EnumLabel,
lValues[k].ValueNode, rValues[k].ValueNode, false,
lValues[k].Value, rValues[k].Value)
}
}
lValues := make(map[string]low.NodeReference[string])
rValues := make(map[string]low.NodeReference[string])
for i := range l.Enum {
lValues[l.Enum[i].Value] = l.Enum[i]
}
for i := range r.Enum {
rValues[r.Enum[i].Value] = r.Enum[i]
}
for k := range lValues {
if _, ok := rValues[k]; !ok {
CreateChange(&changes, ObjectRemoved, v3.EnumLabel,
lValues[k].ValueNode, nil, true,
lValues[k].Value, nil)
continue
}
}
for k := range rValues {
if _, ok := lValues[k]; !ok {
CreateChange(&changes, ObjectAdded, v3.EnumLabel,
lValues[k].ValueNode, rValues[k].ValueNode, false,
lValues[k].Value, rValues[k].Value)
}
}
// default
props = append(props, &PropertyCheck{
LeftNode: l.Default.ValueNode,
RightNode: r.Default.ValueNode,
Label: v3.DefaultLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// default
props = append(props, &PropertyCheck{
LeftNode: l.Default.ValueNode,
RightNode: r.Default.ValueNode,
Label: v3.DefaultLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// description
props = append(props, &PropertyCheck{
LeftNode: l.Description.ValueNode,
RightNode: r.Description.ValueNode,
Label: v3.DescriptionLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// description
props = append(props, &PropertyCheck{
LeftNode: l.Description.ValueNode,
RightNode: r.Description.ValueNode,
Label: v3.DescriptionLabel,
Changes: &changes,
Breaking: false,
Original: l,
New: r,
})
// check everything.
CheckProperties(props)
sc := new(ServerVariableChanges)
sc.PropertyChanges = NewPropertyChanges(changes)
return sc
// check everything.
CheckProperties(props)
sc := new(ServerVariableChanges)
sc.PropertyChanges = NewPropertyChanges(changes)
return sc
}

View File

@@ -4,46 +4,46 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/datamodel/low/v3"
)
// TagChanges represents changes made to the Tags object of an OpenAPI document.
type TagChanges struct {
*PropertyChanges
ExternalDocs *ExternalDocChanges `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
*PropertyChanges
ExternalDocs *ExternalDocChanges `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
// GetAllChanges returns a slice of all changes made between Tag objects
func (t *TagChanges) GetAllChanges() []*Change {
var changes []*Change
changes = append(changes, t.Changes...)
if t.ExternalDocs != nil {
changes = append(changes, t.ExternalDocs.GetAllChanges()...)
}
if t.ExtensionChanges != nil {
changes = append(changes, t.ExtensionChanges.GetAllChanges()...)
}
return changes
var changes []*Change
changes = append(changes, t.Changes...)
if t.ExternalDocs != nil {
changes = append(changes, t.ExternalDocs.GetAllChanges()...)
}
if t.ExtensionChanges != nil {
changes = append(changes, t.ExtensionChanges.GetAllChanges()...)
}
return changes
}
// TotalChanges returns a count of everything that changed within tags.
func (t *TagChanges) TotalChanges() int {
c := t.PropertyChanges.TotalChanges()
if t.ExternalDocs != nil {
c += t.ExternalDocs.TotalChanges()
}
if t.ExtensionChanges != nil {
c += t.ExtensionChanges.TotalChanges()
}
return c
c := t.PropertyChanges.TotalChanges()
if t.ExternalDocs != nil {
c += t.ExternalDocs.TotalChanges()
}
if t.ExtensionChanges != nil {
c += t.ExtensionChanges.TotalChanges()
}
return c
}
// TotalBreakingChanges returns the number of breaking changes made by Tags
func (t *TagChanges) TotalBreakingChanges() int {
return t.PropertyChanges.TotalBreakingChanges()
return t.PropertyChanges.TotalBreakingChanges()
}
// CompareTags will compare a left (original) and a right (new) slice of ValueReference nodes for
@@ -51,100 +51,100 @@ func (t *TagChanges) TotalBreakingChanges() int {
// nil is returned instead.
func CompareTags(l, r []low.ValueReference[*base.Tag]) []*TagChanges {
var tagResults []*TagChanges
var tagResults []*TagChanges
// look at the original and then look through the new.
seenLeft := make(map[string]*low.ValueReference[*base.Tag])
seenRight := make(map[string]*low.ValueReference[*base.Tag])
for i := range l {
h := l[i]
seenLeft[l[i].Value.Name.Value] = &h
}
for i := range r {
h := r[i]
seenRight[r[i].Value.Name.Value] = &h
}
// look at the original and then look through the new.
seenLeft := make(map[string]*low.ValueReference[*base.Tag])
seenRight := make(map[string]*low.ValueReference[*base.Tag])
for i := range l {
h := l[i]
seenLeft[l[i].Value.Name.Value] = &h
}
for i := range r {
h := r[i]
seenRight[r[i].Value.Name.Value] = &h
}
//var changes []*Change
//var changes []*Change
// check for removals, modifications and moves
for i := range seenLeft {
tc := new(TagChanges)
var changes []*Change
// check for removals, modifications and moves
for i := range seenLeft {
tc := new(TagChanges)
var changes []*Change
CheckForObjectAdditionOrRemoval[*base.Tag](seenLeft, seenRight, i, &changes, false, true)
CheckForObjectAdditionOrRemoval[*base.Tag](seenLeft, seenRight, i, &changes, false, true)
// if the existing tag exists, let's check it.
if seenRight[i] != nil {
// if the existing tag exists, let's check it.
if seenRight[i] != nil {
var props []*PropertyCheck
var props []*PropertyCheck
// Name
props = append(props, &PropertyCheck{
LeftNode: seenLeft[i].Value.Name.ValueNode,
RightNode: seenRight[i].Value.Name.ValueNode,
Label: v3.NameLabel,
Changes: &changes,
Breaking: true,
Original: seenLeft[i].Value,
New: seenRight[i].Value,
})
// Name
props = append(props, &PropertyCheck{
LeftNode: seenLeft[i].Value.Name.ValueNode,
RightNode: seenRight[i].Value.Name.ValueNode,
Label: v3.NameLabel,
Changes: &changes,
Breaking: true,
Original: seenLeft[i].Value,
New: seenRight[i].Value,
})
// Description
props = append(props, &PropertyCheck{
LeftNode: seenLeft[i].Value.Description.ValueNode,
RightNode: seenRight[i].Value.Description.ValueNode,
Label: v3.DescriptionLabel,
Changes: &changes,
Breaking: false,
Original: seenLeft[i].Value,
New: seenRight[i].Value,
})
// Description
props = append(props, &PropertyCheck{
LeftNode: seenLeft[i].Value.Description.ValueNode,
RightNode: seenRight[i].Value.Description.ValueNode,
Label: v3.DescriptionLabel,
Changes: &changes,
Breaking: false,
Original: seenLeft[i].Value,
New: seenRight[i].Value,
})
// check properties
CheckProperties(props)
// check properties
CheckProperties(props)
// compare external docs
if !seenLeft[i].Value.ExternalDocs.IsEmpty() && !seenRight[i].Value.ExternalDocs.IsEmpty() {
tc.ExternalDocs = CompareExternalDocs(seenLeft[i].Value.ExternalDocs.Value,
seenRight[i].Value.ExternalDocs.Value)
}
if seenLeft[i].Value.ExternalDocs.IsEmpty() && !seenRight[i].Value.ExternalDocs.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.ExternalDocsLabel, nil, seenRight[i].GetValueNode(),
false, nil, seenRight[i].Value.ExternalDocs.Value)
}
if !seenLeft[i].Value.ExternalDocs.IsEmpty() && seenRight[i].Value.ExternalDocs.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.ExternalDocsLabel, seenLeft[i].GetValueNode(), nil,
false, seenLeft[i].Value.ExternalDocs.Value, nil)
}
// compare external docs
if !seenLeft[i].Value.ExternalDocs.IsEmpty() && !seenRight[i].Value.ExternalDocs.IsEmpty() {
tc.ExternalDocs = CompareExternalDocs(seenLeft[i].Value.ExternalDocs.Value,
seenRight[i].Value.ExternalDocs.Value)
}
if seenLeft[i].Value.ExternalDocs.IsEmpty() && !seenRight[i].Value.ExternalDocs.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.ExternalDocsLabel, nil, seenRight[i].GetValueNode(),
false, nil, seenRight[i].Value.ExternalDocs.Value)
}
if !seenLeft[i].Value.ExternalDocs.IsEmpty() && seenRight[i].Value.ExternalDocs.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.ExternalDocsLabel, seenLeft[i].GetValueNode(), nil,
false, seenLeft[i].Value.ExternalDocs.Value, nil)
}
// check extensions
tc.ExtensionChanges = CompareExtensions(seenLeft[i].Value.Extensions, seenRight[i].Value.Extensions)
tc.PropertyChanges = NewPropertyChanges(changes)
if tc.TotalChanges() > 0 {
tagResults = append(tagResults, tc)
}
continue
}
// check extensions
tc.ExtensionChanges = CompareExtensions(seenLeft[i].Value.Extensions, seenRight[i].Value.Extensions)
tc.PropertyChanges = NewPropertyChanges(changes)
if tc.TotalChanges() > 0 {
tagResults = append(tagResults, tc)
}
continue
}
if len(changes) > 0 {
tc.PropertyChanges = NewPropertyChanges(changes)
tagResults = append(tagResults, tc)
}
if len(changes) > 0 {
tc.PropertyChanges = NewPropertyChanges(changes)
tagResults = append(tagResults, tc)
}
}
for i := range seenRight {
if seenLeft[i] == nil {
tc := new(TagChanges)
var changes []*Change
}
for i := range seenRight {
if seenLeft[i] == nil {
tc := new(TagChanges)
var changes []*Change
CreateChange(&changes, ObjectAdded, i, nil, seenRight[i].GetValueNode(),
false, nil, seenRight[i].GetValue())
CreateChange(&changes, ObjectAdded, i, nil, seenRight[i].GetValueNode(),
false, nil, seenRight[i].GetValue())
tc.PropertyChanges = NewPropertyChanges(changes)
tagResults = append(tagResults, tc)
tc.PropertyChanges = NewPropertyChanges(changes)
tagResults = append(tagResults, tc)
}
}
return tagResults
}
}
return tagResults
}

View File

@@ -4,111 +4,111 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low/base"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/datamodel/low/base"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
)
// XMLChanges represents changes made to the XML object of an OpenAPI document.
type XMLChanges struct {
*PropertyChanges
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
*PropertyChanges
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
// GetAllChanges returns a slice of all changes made between XML objects
func (x *XMLChanges) GetAllChanges() []*Change {
var changes []*Change
changes = append(changes, x.Changes...)
if x.ExtensionChanges != nil {
changes = append(changes, x.ExtensionChanges.GetAllChanges()...)
}
return changes
var changes []*Change
changes = append(changes, x.Changes...)
if x.ExtensionChanges != nil {
changes = append(changes, x.ExtensionChanges.GetAllChanges()...)
}
return changes
}
// TotalChanges returns a count of everything that was changed within an XML object.
func (x *XMLChanges) TotalChanges() int {
c := x.PropertyChanges.TotalChanges()
if x.ExtensionChanges != nil {
c += x.ExtensionChanges.TotalChanges()
}
return c
c := x.PropertyChanges.TotalChanges()
if x.ExtensionChanges != nil {
c += x.ExtensionChanges.TotalChanges()
}
return c
}
// TotalBreakingChanges returns the number of breaking changes made by the XML object.
func (x *XMLChanges) TotalBreakingChanges() int {
return x.PropertyChanges.TotalBreakingChanges()
return x.PropertyChanges.TotalBreakingChanges()
}
// CompareXML will compare a left (original) and a right (new) XML instance, and check for
// any changes between them. If changes are found, the function returns a pointer to XMLChanges,
// otherwise, if nothing changed - it will return nil
func CompareXML(l, r *base.XML) *XMLChanges {
xc := new(XMLChanges)
var changes []*Change
var props []*PropertyCheck
xc := new(XMLChanges)
var changes []*Change
var props []*PropertyCheck
// Name (breaking change)
props = append(props, &PropertyCheck{
LeftNode: l.Name.ValueNode,
RightNode: r.Name.ValueNode,
Label: v3.NameLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// Name (breaking change)
props = append(props, &PropertyCheck{
LeftNode: l.Name.ValueNode,
RightNode: r.Name.ValueNode,
Label: v3.NameLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// Namespace (breaking change)
props = append(props, &PropertyCheck{
LeftNode: l.Namespace.ValueNode,
RightNode: r.Namespace.ValueNode,
Label: v3.NamespaceLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// Namespace (breaking change)
props = append(props, &PropertyCheck{
LeftNode: l.Namespace.ValueNode,
RightNode: r.Namespace.ValueNode,
Label: v3.NamespaceLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// Prefix (breaking change)
props = append(props, &PropertyCheck{
LeftNode: l.Prefix.ValueNode,
RightNode: r.Prefix.ValueNode,
Label: v3.PrefixLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// Prefix (breaking change)
props = append(props, &PropertyCheck{
LeftNode: l.Prefix.ValueNode,
RightNode: r.Prefix.ValueNode,
Label: v3.PrefixLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// Attribute (breaking change)
props = append(props, &PropertyCheck{
LeftNode: l.Attribute.ValueNode,
RightNode: r.Attribute.ValueNode,
Label: v3.AttributeLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// Attribute (breaking change)
props = append(props, &PropertyCheck{
LeftNode: l.Attribute.ValueNode,
RightNode: r.Attribute.ValueNode,
Label: v3.AttributeLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// Wrapped (breaking change)
props = append(props, &PropertyCheck{
LeftNode: l.Wrapped.ValueNode,
RightNode: r.Wrapped.ValueNode,
Label: v3.WrappedLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// Wrapped (breaking change)
props = append(props, &PropertyCheck{
LeftNode: l.Wrapped.ValueNode,
RightNode: r.Wrapped.ValueNode,
Label: v3.WrappedLabel,
Changes: &changes,
Breaking: true,
Original: l,
New: r,
})
// check properties
CheckProperties(props)
// check properties
CheckProperties(props)
// check extensions
xc.ExtensionChanges = CheckExtensions(l, r)
xc.PropertyChanges = NewPropertyChanges(changes)
if xc.TotalChanges() <= 0 {
return nil
}
return xc
// check extensions
xc.ExtensionChanges = CheckExtensions(l, r)
xc.PropertyChanges = NewPropertyChanges(changes)
if xc.TotalChanges() <= 0 {
return nil
}
return xc
}

View File

@@ -4,144 +4,144 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
)
func TestCompareXML_NameChanged(t *testing.T) {
left := `name: xml thing
left := `name: xml thing
namespace: something
prefix: another
attribute: true
wrapped: true`
right := `namespace: something
right := `namespace: something
prefix: another
name: changed xml thing
attribute: true
wrapped: true`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc base.XML
var rDoc base.XML
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc base.XML
var rDoc base.XML
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareXML(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, Modified, extChanges.Changes[0].ChangeType)
// compare.
extChanges := CompareXML(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, Modified, extChanges.Changes[0].ChangeType)
}
func TestCompareXML_NameRemoved(t *testing.T) {
left := `name: xml thing
left := `name: xml thing
namespace: something
prefix: another
attribute: true
wrapped: true`
right := `wrapped: true
right := `wrapped: true
prefix: another
attribute: true
namespace: something`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc base.XML
var rDoc base.XML
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc base.XML
var rDoc base.XML
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareXML(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
// compare.
extChanges := CompareXML(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
}
func TestCompareXML_ExtensionAdded(t *testing.T) {
left := `name: xml thing
left := `name: xml thing
namespace: something
prefix: another
attribute: true
wrapped: true`
right := `name: xml thing
right := `name: xml thing
namespace: something
prefix: another
attribute: true
wrapped: true
x-coffee: time`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc base.XML
var rDoc base.XML
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc base.XML
var rDoc base.XML
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareXML(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, ObjectAdded, extChanges.ExtensionChanges.Changes[0].ChangeType)
// compare.
extChanges := CompareXML(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, ObjectAdded, extChanges.ExtensionChanges.Changes[0].ChangeType)
}
func TestCompareXML_Identical(t *testing.T) {
left := `name: xml thing
left := `name: xml thing
namespace: something
prefix: another
attribute: true
wrapped: true`
right := `name: xml thing
right := `name: xml thing
namespace: something
prefix: another
attribute: true
wrapped: true`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc base.XML
var rDoc base.XML
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// create low level objects
var lDoc base.XML
var rDoc base.XML
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare.
extChanges := CompareXML(&lDoc, &rDoc)
// compare.
extChanges := CompareXML(&lDoc, &rDoc)
assert.Nil(t, extChanges)
}

View File

@@ -4,85 +4,85 @@
package reports
import (
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/what-changed/model"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/what-changed/model"
)
// Changed provides a simple wrapper for changed counts
type Changed struct {
Total int `json:"totalChanges"`
Breaking int `json:"breakingChanges"`
Total int `json:"totalChanges"`
Breaking int `json:"breakingChanges"`
}
// OverallReport provides a Document level overview of all changes to an OpenAPI doc.
type OverallReport struct {
ChangeReport map[string]*Changed `json:"overallSummaryReport"`
ChangeReport map[string]*Changed `json:"overallSummaryReport"`
}
// CreateOverallReport will create a high level report for all top level changes (but with deep counts)
func CreateOverallReport(changes *model.DocumentChanges) *OverallReport {
changedReport := make(map[string]*Changed)
if changes.InfoChanges != nil {
changedReport[v3.InfoLabel] = createChangedModel(changes.InfoChanges)
}
if changes.PathsChanges != nil {
changedReport[v3.PathsLabel] = createChangedModel(changes.PathsChanges)
}
if changes.TagChanges != nil {
j := make([]HasChanges, len(changes.TagChanges))
for k := range changes.TagChanges {
j[k] = HasChanges(changes.TagChanges[k])
}
changedReport[v3.TagsLabel] = createChangedModelFromSlice(j)
}
if changes.ExternalDocChanges != nil {
changedReport[v3.ExternalDocsLabel] = createChangedModel(changes.ExternalDocChanges)
}
if changes.WebhookChanges != nil {
j := make([]HasChanges, len(changes.WebhookChanges))
z := 0
for k := range changes.WebhookChanges {
j[z] = HasChanges(changes.WebhookChanges[k])
z++
}
ch := createChangedModelFromSlice(j)
if ch.Total > 0 {
changedReport[v3.WebhooksLabel] = ch
}
}
if changes.ServerChanges != nil {
j := make([]HasChanges, len(changes.ServerChanges))
for k := range changes.ServerChanges {
j[k] = HasChanges(changes.ServerChanges[k])
}
changedReport[v3.ServersLabel] = createChangedModelFromSlice(j)
}
if changes.SecurityRequirementChanges != nil {
j := make([]HasChanges, len(changes.SecurityRequirementChanges))
for k := range changes.SecurityRequirementChanges {
j[k] = HasChanges(changes.SecurityRequirementChanges[k])
}
changedReport[v3.SecurityLabel] = createChangedModelFromSlice(j)
}
if changes.ComponentsChanges != nil {
changedReport[v3.ComponentsLabel] = createChangedModel(changes.ComponentsChanges)
}
return &OverallReport{
ChangeReport: changedReport,
}
changedReport := make(map[string]*Changed)
if changes.InfoChanges != nil {
changedReport[v3.InfoLabel] = createChangedModel(changes.InfoChanges)
}
if changes.PathsChanges != nil {
changedReport[v3.PathsLabel] = createChangedModel(changes.PathsChanges)
}
if changes.TagChanges != nil {
j := make([]HasChanges, len(changes.TagChanges))
for k := range changes.TagChanges {
j[k] = HasChanges(changes.TagChanges[k])
}
changedReport[v3.TagsLabel] = createChangedModelFromSlice(j)
}
if changes.ExternalDocChanges != nil {
changedReport[v3.ExternalDocsLabel] = createChangedModel(changes.ExternalDocChanges)
}
if changes.WebhookChanges != nil {
j := make([]HasChanges, len(changes.WebhookChanges))
z := 0
for k := range changes.WebhookChanges {
j[z] = HasChanges(changes.WebhookChanges[k])
z++
}
ch := createChangedModelFromSlice(j)
if ch.Total > 0 {
changedReport[v3.WebhooksLabel] = ch
}
}
if changes.ServerChanges != nil {
j := make([]HasChanges, len(changes.ServerChanges))
for k := range changes.ServerChanges {
j[k] = HasChanges(changes.ServerChanges[k])
}
changedReport[v3.ServersLabel] = createChangedModelFromSlice(j)
}
if changes.SecurityRequirementChanges != nil {
j := make([]HasChanges, len(changes.SecurityRequirementChanges))
for k := range changes.SecurityRequirementChanges {
j[k] = HasChanges(changes.SecurityRequirementChanges[k])
}
changedReport[v3.SecurityLabel] = createChangedModelFromSlice(j)
}
if changes.ComponentsChanges != nil {
changedReport[v3.ComponentsLabel] = createChangedModel(changes.ComponentsChanges)
}
return &OverallReport{
ChangeReport: changedReport,
}
}
func createChangedModel(ch HasChanges) *Changed {
return &Changed{ch.TotalChanges(), ch.TotalBreakingChanges()}
return &Changed{ch.TotalChanges(), ch.TotalBreakingChanges()}
}
func createChangedModelFromSlice(ch []HasChanges) *Changed {
t := 0
b := 0
for n := range ch {
t += ch[n].TotalChanges()
b += ch[n].TotalBreakingChanges()
}
return &Changed{t, b}
t := 0
b := 0
for n := range ch {
t += ch[n].TotalChanges()
b += ch[n].TotalBreakingChanges()
}
return &Changed{t, b}
}

View File

@@ -4,35 +4,35 @@
package reports
import (
"github.com/pb33f/libopenapi"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/what-changed/model"
"github.com/stretchr/testify/assert"
"io/ioutil"
"testing"
"github.com/pb33f/libopenapi"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/what-changed/model"
"github.com/stretchr/testify/assert"
"io/ioutil"
"testing"
)
func createDiff() *model.DocumentChanges {
burgerShopOriginal, _ := ioutil.ReadFile("../../test_specs/burgershop.openapi.yaml")
burgerShopUpdated, _ := ioutil.ReadFile("../../test_specs/burgershop.openapi-modified.yaml")
originalDoc, _ := libopenapi.NewDocument(burgerShopOriginal)
updatedDoc, _ := libopenapi.NewDocument(burgerShopUpdated)
documentChanges, _ := libopenapi.CompareDocuments(originalDoc, updatedDoc)
return documentChanges
burgerShopOriginal, _ := ioutil.ReadFile("../../test_specs/burgershop.openapi.yaml")
burgerShopUpdated, _ := ioutil.ReadFile("../../test_specs/burgershop.openapi-modified.yaml")
originalDoc, _ := libopenapi.NewDocument(burgerShopOriginal)
updatedDoc, _ := libopenapi.NewDocument(burgerShopUpdated)
documentChanges, _ := libopenapi.CompareDocuments(originalDoc, updatedDoc)
return documentChanges
}
func TestCreateSummary_OverallReport(t *testing.T) {
changes := createDiff()
report := CreateOverallReport(changes)
assert.Equal(t, 1, report.ChangeReport[v3.InfoLabel].Total)
assert.Equal(t, 43, report.ChangeReport[v3.PathsLabel].Total)
assert.Equal(t, 9, report.ChangeReport[v3.PathsLabel].Breaking)
assert.Equal(t, 3, report.ChangeReport[v3.TagsLabel].Total)
assert.Equal(t, 1, report.ChangeReport[v3.ExternalDocsLabel].Total)
assert.Equal(t, 2, report.ChangeReport[v3.WebhooksLabel].Total)
assert.Equal(t, 2, report.ChangeReport[v3.ServersLabel].Total)
assert.Equal(t, 1, report.ChangeReport[v3.ServersLabel].Breaking)
assert.Equal(t, 1, report.ChangeReport[v3.SecurityLabel].Total)
assert.Equal(t, 17, report.ChangeReport[v3.ComponentsLabel].Total)
assert.Equal(t, 6, report.ChangeReport[v3.ComponentsLabel].Breaking)
changes := createDiff()
report := CreateOverallReport(changes)
assert.Equal(t, 1, report.ChangeReport[v3.InfoLabel].Total)
assert.Equal(t, 43, report.ChangeReport[v3.PathsLabel].Total)
assert.Equal(t, 9, report.ChangeReport[v3.PathsLabel].Breaking)
assert.Equal(t, 3, report.ChangeReport[v3.TagsLabel].Total)
assert.Equal(t, 1, report.ChangeReport[v3.ExternalDocsLabel].Total)
assert.Equal(t, 2, report.ChangeReport[v3.WebhooksLabel].Total)
assert.Equal(t, 2, report.ChangeReport[v3.ServersLabel].Total)
assert.Equal(t, 1, report.ChangeReport[v3.ServersLabel].Breaking)
assert.Equal(t, 1, report.ChangeReport[v3.SecurityLabel].Total)
assert.Equal(t, 17, report.ChangeReport[v3.ComponentsLabel].Total)
assert.Equal(t, 6, report.ChangeReport[v3.ComponentsLabel].Breaking)
}

View File

@@ -6,9 +6,9 @@ package reports
// HasChanges represents a change model that provides a total change count and a breaking change count.
type HasChanges interface {
// TotalChanges represents number of all changes found
TotalChanges() int
// TotalChanges represents number of all changes found
TotalChanges() int
// TotalBreakingChanges represents the number of contract breaking changes only.
TotalBreakingChanges() int
// TotalBreakingChanges represents the number of contract breaking changes only.
TotalBreakingChanges() int
}