What-changed is ready

Needs documenting, but the feature is ready for an application to really stress test the model and logic.

Guess what's next :) (after docs)

Signed-off-by: Dave Shanley <dave@quobix.com>
This commit is contained in:
Dave Shanley
2022-11-20 14:28:08 -05:00
parent 6c3cc97b04
commit ddf772270e
32 changed files with 392 additions and 356 deletions

View File

@@ -715,12 +715,8 @@ definitions:
type: object
properties:
id:
type: integer
type: float
format: int64
name:
type: string
xml:
name: Category
Pet:
type: object
required:
@@ -728,7 +724,7 @@ definitions:
- photoUrls
properties:
id:
type: integer
type: float
format: int64
category:
"$ref": "#/definitions/Category"
@@ -768,8 +764,6 @@ definitions:
format: int64
name:
type: string
xml:
name: Tag
Order:
type: object
properties:
@@ -781,10 +775,10 @@ definitions:
format: int64
quantity:
type: integer
format: int32
format: float
shipDate:
type: string
format: date-time
format: pizza
status:
type: string
description: Order Status
@@ -821,7 +815,7 @@ definitions:
xml:
name: User
externalDocs:
description: Find out more about Swagger
description: Find out more about Swagger updated!
url: http://swagger.io
externalPaths:
# this is illegal

View File

@@ -10,8 +10,8 @@ import (
type CallbackChanges struct {
PropertyChanges
ExpressionChanges map[string]*PathItemChanges
ExtensionChanges *ExtensionChanges
ExpressionChanges map[string]*PathItemChanges `json:"expressions,omitempty" yaml:"expressions,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
func (c *CallbackChanges) TotalChanges() int {

View File

@@ -28,21 +28,18 @@ const (
// WhatChanged is a summary object that contains a high level summary of everything changed.
type WhatChanged struct {
Added int
Removed int
ModifiedAndMoved int
Modified int
Moved int
TotalChanges int
//Changes *Changes
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
OriginalColumn int
NewLine int
NewColumn int
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.
@@ -50,7 +47,19 @@ 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 {
return c.NewLine != c.OriginalLine || c.NewColumn != c.OriginalColumn
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.
@@ -59,33 +68,33 @@ 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
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
ChangeType int `json:"change,omitempty" yaml:"change,omitempty"`
// Property is the property name key being changed.
Property string
Property string `json:"property,omitempty" yaml:"property,omitempty"`
// Original is the original value represented as a string.
Original string
Original string `json:"original,omitempty" yaml:"original,omitempty"`
// New is the new value represented as a string.
New string
New string `json:"new,omitempty" yaml:"new,omitempty"`
// Breaking determines if the change is a breaking one or not.
Breaking bool
Breaking bool `json:"breaking" yaml:"breaking"`
// OriginalObject represents the original object that was changed.
OriginalObject any
OriginalObject any `json:"-" yaml:"-"`
// NewObject represents the new object that has been modified.
NewObject any
NewObject any `json:"-" yaml:"-"`
}
// PropertyChanges holds a slice of Change pointers
type PropertyChanges struct {
Changes []*Change
Changes []*Change `json:"changes,omitempty" yaml:"changes,omitempty"`
}
// TotalChanges returns the total number of property changes made.
@@ -130,7 +139,3 @@ type PropertyCheck struct {
// Changes represents a pointer to the slice to contain all changes found.
Changes *[]*Change
}
//type Changes struct {
// TagChanges *model.TagChanges
//}

View File

@@ -51,18 +51,12 @@ func CreateChange(changes *[]*Change, changeType int, property string, leftValue
func CreateContext(l, r *yaml.Node) *ChangeContext {
ctx := new(ChangeContext)
if l != nil {
ctx.OriginalLine = l.Line
ctx.OriginalColumn = l.Column
} else {
ctx.OriginalLine = -1
ctx.OriginalColumn = -1
ctx.OriginalLine = &l.Line
ctx.OriginalColumn = &l.Column
}
if r != nil {
ctx.NewLine = r.Line
ctx.NewColumn = r.Column
} else {
ctx.NewLine = -1
ctx.NewColumn = -1
ctx.NewLine = &r.Line
ctx.NewColumn = &r.Column
}
return ctx
}

View File

@@ -13,19 +13,14 @@ import (
type ComponentsChanges struct {
PropertyChanges
SchemaChanges map[string]*SchemaChanges
// todo: disable these after cleaning up swagger code.
ResponsesChanges map[string]*ResponseChanges
ParameterChanges map[string]*ParameterChanges
SchemaChanges map[string]*SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"`
SecuritySchemeChanges map[string]*SecuritySchemeChanges `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
//ExamplesChanges map[string]*ExampleChanges
//RequestBodyChanges map[string]*RequestBodyChanges
//HeaderChanges map[string]*HeaderChanges
SecuritySchemeChanges map[string]*SecuritySchemeChanges
//LinkChanges map[string]*LinkChanges
//CallbackChanges map[string]*CallbackChanges
ExtensionChanges *ExtensionChanges
}
func CompareComponents(l, r any) *ComponentsChanges {

View File

@@ -11,7 +11,7 @@ import (
// DiscriminatorChanges represents changes made to a Discriminator OpenAPI object
type DiscriminatorChanges struct {
PropertyChanges
MappingChanges []*Change
MappingChanges []*Change `json:"mappings,omitempty" yaml:"mappings,omitempty"`
}
// TotalChanges returns a count of everything changed within the Discriminator object

View File

@@ -13,15 +13,15 @@ import (
type DocumentChanges struct {
PropertyChanges
InfoChanges *InfoChanges
PathsChanges *PathsChanges
TagChanges []*TagChanges
ExternalDocChanges *ExternalDocChanges
WebhookChanges map[string]*PathItemChanges
ServerChanges []*ServerChanges
SecurityRequirementChanges []*SecurityRequirementChanges
ComponentsChanges *ComponentsChanges
ExtensionChanges *ExtensionChanges
InfoChanges *InfoChanges `json:"info,omitempty" yaml:"info,omitempty"`
PathsChanges *PathsChanges `json:"paths,omitempty" yaml:"paths,omitempty"`
TagChanges []*TagChanges `json:"tags,omitempty" yaml:"tags,omitempty"`
ExternalDocChanges *ExternalDocChanges `json:"externalDoc,omitempty" yaml:"externalDoc,omitempty"`
WebhookChanges map[string]*PathItemChanges `json:"webhooks,omitempty" yaml:"webhooks,omitempty"`
ServerChanges []*ServerChanges `json:"servers,omitempty" yaml:"servers,omitempty"`
SecurityRequirementChanges []*SecurityRequirementChanges `json:"securityRequirements,omitempty" yaml:"securityRequirements,omitempty"`
ComponentsChanges *ComponentsChanges `json:"components,omitempty" yaml:"components,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
func (d *DocumentChanges) TotalChanges() int {

View File

@@ -8,8 +8,8 @@ import (
)
type EncodingChanges struct {
ParameterChanges
HeaderChanges map[string]*HeaderChanges
PropertyChanges
HeaderChanges map[string]*HeaderChanges `json:"headers,omitempty" yaml:"headers,omitempty"`
}
func (e *EncodingChanges) TotalChanges() int {

View File

@@ -14,7 +14,7 @@ import (
// ExampleChanges represent changes to an Example object, part of an OpenAPI specification.
type ExampleChanges struct {
PropertyChanges
ExtensionChanges *ExtensionChanges
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
// TotalChanges returns the total number of changes made to Example

View File

@@ -50,8 +50,8 @@ x-test: 1`
assert.Len(t, extChanges.Changes, 1)
assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
assert.Equal(t, 2, extChanges.Changes[0].Context.OriginalLine)
assert.Equal(t, -1, extChanges.Changes[0].Context.NewLine)
assert.Equal(t, 2, *extChanges.Changes[0].Context.OriginalLine)
assert.Nil(t, extChanges.Changes[0].Context.NewLine)
assert.Equal(t, "1", extChanges.Changes[0].Original)
assert.True(t, extChanges.Changes[0].Context.HasChanged())
}
@@ -74,8 +74,8 @@ x-test: 1`
assert.Len(t, extChanges.Changes, 1)
assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType)
assert.Equal(t, -1, extChanges.Changes[0].Context.OriginalLine)
assert.Equal(t, 2, extChanges.Changes[0].Context.NewLine)
assert.Nil(t, extChanges.Changes[0].Context.OriginalLine)
assert.Equal(t, 2, *extChanges.Changes[0].Context.NewLine)
assert.Equal(t, "1", extChanges.Changes[0].New)
assert.True(t, extChanges.Changes[0].Context.HasChanged())
}

View File

@@ -11,7 +11,7 @@ import (
// ExternalDocChanges represents changes made to any ExternalDoc object from an OpenAPI document.
type ExternalDocChanges struct {
PropertyChanges
ExtensionChanges *ExtensionChanges
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
// TotalChanges returns a count of everything that changed

View File

@@ -4,245 +4,245 @@
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
lowv3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
"github.com/pb33f/libopenapi/datamodel/low"
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
lowv3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
)
func TestCompareExternalDocs(t *testing.T) {
left := `url: https://pb33f.io
left := `url: https://pb33f.io
description: this is a test
x-testing: hello`
right := `url: https://quobix.com
right := `url: https://quobix.com
description: this is another test
x-testing: hiya!`
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 lowbase.ExternalDoc
var rDoc lowbase.ExternalDoc
_ = 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 lowbase.ExternalDoc
var rDoc lowbase.ExternalDoc
_ = 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 := CompareExternalDocs(&lDoc, &rDoc)
assert.Len(t, extChanges.ExtensionChanges.Changes, 1)
assert.Len(t, extChanges.Changes, 2)
assert.Equal(t, 3, extChanges.TotalChanges())
// compare.
extChanges := CompareExternalDocs(&lDoc, &rDoc)
assert.Len(t, extChanges.ExtensionChanges.Changes, 1)
assert.Len(t, extChanges.Changes, 2)
assert.Equal(t, 3, extChanges.TotalChanges())
// validate property changes
urlChange := extChanges.Changes[0]
assert.Equal(t, Modified, urlChange.ChangeType)
assert.False(t, urlChange.Context.HasChanged())
assert.Equal(t, "https://pb33f.io", urlChange.Original)
assert.Equal(t, "https://quobix.com", urlChange.New)
assert.Equal(t, 1, urlChange.Context.OriginalLine)
assert.Equal(t, lowv3.URLLabel, urlChange.Property)
// validate property changes
urlChange := extChanges.Changes[0]
assert.Equal(t, Modified, urlChange.ChangeType)
assert.False(t, urlChange.Context.HasChanged())
assert.Equal(t, "https://pb33f.io", urlChange.Original)
assert.Equal(t, "https://quobix.com", urlChange.New)
assert.Equal(t, 1, *urlChange.Context.OriginalLine)
assert.Equal(t, lowv3.URLLabel, urlChange.Property)
descChange := extChanges.Changes[1]
assert.Equal(t, Modified, descChange.ChangeType)
assert.False(t, descChange.Context.HasChanged())
assert.Equal(t, "this is another test", descChange.New)
assert.Equal(t, "this is a test", descChange.Original)
assert.Equal(t, 2, descChange.Context.OriginalLine)
assert.Equal(t, 14, descChange.Context.OriginalColumn)
descChange := extChanges.Changes[1]
assert.Equal(t, Modified, descChange.ChangeType)
assert.False(t, descChange.Context.HasChanged())
assert.Equal(t, "this is another test", descChange.New)
assert.Equal(t, "this is a test", descChange.Original)
assert.Equal(t, 2, *descChange.Context.OriginalLine)
assert.Equal(t, 14, *descChange.Context.OriginalColumn)
// validate extensions
extChange := extChanges.ExtensionChanges.Changes[0]
assert.Equal(t, Modified, extChange.ChangeType)
assert.False(t, extChange.Context.HasChanged())
assert.Equal(t, "hiya!", extChange.New)
assert.Equal(t, "hello", extChange.Original)
assert.Equal(t, 3, extChange.Context.OriginalLine)
assert.Equal(t, 12, extChange.Context.OriginalColumn)
// validate extensions
extChange := extChanges.ExtensionChanges.Changes[0]
assert.Equal(t, Modified, extChange.ChangeType)
assert.False(t, extChange.Context.HasChanged())
assert.Equal(t, "hiya!", extChange.New)
assert.Equal(t, "hello", extChange.Original)
assert.Equal(t, 3, *extChange.Context.OriginalLine)
assert.Equal(t, 12, *extChange.Context.OriginalColumn)
}
func TestCompareExternalDocs_Moved(t *testing.T) {
left := `url: https://pb33f.io
left := `url: https://pb33f.io
description: this is a test
x-testing: hello`
right := `description: this is another test
right := `description: this is another test
x-testing: hiya!
url: https://quobix.com`
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 lowbase.ExternalDoc
var rDoc lowbase.ExternalDoc
_ = 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 lowbase.ExternalDoc
var rDoc lowbase.ExternalDoc
_ = 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 := CompareExternalDocs(&lDoc, &rDoc)
assert.Len(t, extChanges.ExtensionChanges.Changes, 1)
assert.Len(t, extChanges.Changes, 2)
// compare.
extChanges := CompareExternalDocs(&lDoc, &rDoc)
assert.Len(t, extChanges.ExtensionChanges.Changes, 1)
assert.Len(t, extChanges.Changes, 2)
// validate property changes
urlChange := extChanges.Changes[0]
assert.Equal(t, Modified, urlChange.ChangeType)
assert.True(t, urlChange.Context.HasChanged())
assert.Equal(t, "https://pb33f.io", urlChange.Original)
assert.Equal(t, "https://quobix.com", urlChange.New)
assert.Equal(t, lowv3.URLLabel, urlChange.Property)
// validate property changes
urlChange := extChanges.Changes[0]
assert.Equal(t, Modified, urlChange.ChangeType)
assert.True(t, urlChange.Context.HasChanged())
assert.Equal(t, "https://pb33f.io", urlChange.Original)
assert.Equal(t, "https://quobix.com", urlChange.New)
assert.Equal(t, lowv3.URLLabel, urlChange.Property)
descChange := extChanges.Changes[1]
assert.Equal(t, Modified, descChange.ChangeType)
assert.True(t, descChange.Context.HasChanged())
assert.Equal(t, "this is another test", descChange.New)
assert.Equal(t, "this is a test", descChange.Original)
descChange := extChanges.Changes[1]
assert.Equal(t, Modified, descChange.ChangeType)
assert.True(t, descChange.Context.HasChanged())
assert.Equal(t, "this is another test", descChange.New)
assert.Equal(t, "this is a test", descChange.Original)
// validate extensions
extChange := extChanges.ExtensionChanges.Changes[0]
assert.Equal(t, Modified, extChange.ChangeType)
assert.True(t, extChange.Context.HasChanged())
assert.Equal(t, "hiya!", extChange.New)
assert.Equal(t, "hello", extChange.Original)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
// validate extensions
extChange := extChanges.ExtensionChanges.Changes[0]
assert.Equal(t, Modified, extChange.ChangeType)
assert.True(t, extChange.Context.HasChanged())
assert.Equal(t, "hiya!", extChange.New)
assert.Equal(t, "hello", extChange.Original)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}
func TestCompareExternalDocs_Identical(t *testing.T) {
left := `url: https://pb33f.io
left := `url: https://pb33f.io
description: this is a test
x-testing: hello`
right := `url: https://pb33f.io
right := `url: https://pb33f.io
description: this is a test
x-testing: hello`
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 lowbase.ExternalDoc
var rDoc lowbase.ExternalDoc
_ = 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 lowbase.ExternalDoc
var rDoc lowbase.ExternalDoc
_ = 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 := CompareExternalDocs(&lDoc, &rDoc)
assert.Nil(t, extChanges)
// compare.
extChanges := CompareExternalDocs(&lDoc, &rDoc)
assert.Nil(t, extChanges)
}
func TestCompareExternalDocs_DescriptionAdded(t *testing.T) {
left := `url: https://pb33f.io
left := `url: https://pb33f.io
x-testing: hello`
right := `url: https://pb33f.io
right := `url: https://pb33f.io
description: this is a test
x-testing: hello`
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 lowbase.ExternalDoc
var rDoc lowbase.ExternalDoc
_ = 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 lowbase.ExternalDoc
var rDoc lowbase.ExternalDoc
_ = 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 := CompareExternalDocs(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType)
// compare.
extChanges := CompareExternalDocs(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType)
}
func TestCompareExternalDocs_URLAdded(t *testing.T) {
left := `description: hi!`
left := `description: hi!`
right := `description: hi!
right := `description: hi!
url: https://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 lowbase.ExternalDoc
var rDoc lowbase.ExternalDoc
_ = 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 lowbase.ExternalDoc
var rDoc lowbase.ExternalDoc
_ = 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 := CompareExternalDocs(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType)
// compare.
extChanges := CompareExternalDocs(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType)
}
func TestCompareExternalDocs_DescriptionRemoved(t *testing.T) {
left := `url: https://pb33f.io
left := `url: https://pb33f.io
description: something`
right := `url: https://pb33f.io`
right := `url: https://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 lowbase.ExternalDoc
var rDoc lowbase.ExternalDoc
_ = 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 lowbase.ExternalDoc
var rDoc lowbase.ExternalDoc
_ = 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 := CompareExternalDocs(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType)
// compare.
extChanges := CompareExternalDocs(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType)
}
func TestCompareExternalDocs_URLRemoved(t *testing.T) {
left := `description: something
left := `description: something
url: https://pb33f.io`
right := `description: something`
right := `description: 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 lowbase.ExternalDoc
var rDoc lowbase.ExternalDoc
_ = 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 lowbase.ExternalDoc
var rDoc lowbase.ExternalDoc
_ = 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 := CompareExternalDocs(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType)
// compare
extChanges := CompareExternalDocs(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType)
}

View File

@@ -12,13 +12,13 @@ import (
type HeaderChanges struct {
PropertyChanges
SchemaChanges *SchemaChanges
ExamplesChanges map[string]*ExampleChanges
ContentChanges map[string]*MediaTypeChanges
ExtensionChanges *ExtensionChanges
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"`
// V2 changes
ItemsChanges *ItemsChanges
ItemsChanges *ItemsChanges `json:"items,omitempty" yaml:"items,omitempty"`
}
func (h *HeaderChanges) TotalChanges() int {

View File

@@ -11,8 +11,8 @@ import (
// InfoChanges represents the number of changes to an Info object. Part of an OpenAPI document
type InfoChanges struct {
PropertyChanges
ContactChanges *ContactChanges
LicenseChanges *LicenseChanges
ContactChanges *ContactChanges `json:"contact,omitempty" yaml:"contact,omitempty"`
LicenseChanges *LicenseChanges `json:"license,omitempty" yaml:"license,omitempty"`
}
// TotalChanges represents the total number of changes made to an Info object.

View File

@@ -10,7 +10,7 @@ import (
type ItemsChanges struct {
PropertyChanges
ItemsChanges *ItemsChanges
ItemsChanges *ItemsChanges `json:"items,omitempty" yaml:"items,omitempty"`
}
func (i *ItemsChanges) TotalChanges() int {

View File

@@ -10,8 +10,8 @@ import (
type LinkChanges struct {
PropertyChanges
ExtensionChanges *ExtensionChanges
ServerChanges *ServerChanges
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
ServerChanges *ServerChanges `json:"server,omitempty" yaml:"server,omitempty"`
}
func (l *LinkChanges) TotalChanges() int {

View File

@@ -9,10 +9,10 @@ import (
type MediaTypeChanges struct {
PropertyChanges
SchemaChanges *SchemaChanges
ExtensionChanges *ExtensionChanges
ExampleChanges map[string]*ExampleChanges
EncodingChanges map[string]*EncodingChanges
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"`
}
func (m *MediaTypeChanges) TotalChanges() int {

View File

@@ -5,16 +5,16 @@ package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/datamodel/low/v3"
)
type OAuthFlowsChanges struct {
PropertyChanges
ImplicitChanges *OAuthFlowChanges
PasswordChanges *OAuthFlowChanges
ClientCredentialsChanges *OAuthFlowChanges
AuthorizationCodeChanges *OAuthFlowChanges
ExtensionChanges *ExtensionChanges
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:"autCode,omitempty" yaml:"authCode,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
func (o *OAuthFlowsChanges) TotalChanges() int {

View File

@@ -16,16 +16,16 @@ import (
type OperationChanges struct {
PropertyChanges
ExternalDocChanges *ExternalDocChanges
ParameterChanges []*ParameterChanges
ResponsesChanges *ResponsesChanges
SecurityRequirementChanges []*SecurityRequirementChanges
ExternalDocChanges *ExternalDocChanges `json:"externalDoc,omitempty" yaml:"externalDoc,omitempty"`
ParameterChanges []*ParameterChanges `json:"parameters,omitempty" yaml:"parameters,omitempty"`
ResponsesChanges *ResponsesChanges `json:"responses,omitempty" yaml:"responses,omitempty"`
SecurityRequirementChanges []*SecurityRequirementChanges `json:"securityRequirements,omitempty" yaml:"securityRequirements,omitempty"`
// v3
RequestBodyChanges *RequestBodyChanges
ServerChanges []*ServerChanges
ExtensionChanges *ExtensionChanges
CallbackChanges map[string]*CallbackChanges
RequestBodyChanges *RequestBodyChanges `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"`
ServerChanges []*ServerChanges `json:"servers,omitempty" yaml:"servers,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
CallbackChanges map[string]*CallbackChanges `json:"callbacks,omitempty" yaml:"callbacks,omitempty"`
}
func (o *OperationChanges) TotalChanges() int {

View File

@@ -14,15 +14,15 @@ import (
type ParameterChanges struct {
PropertyChanges
SchemaChanges *SchemaChanges
ExtensionChanges *ExtensionChanges
SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
// v2 change types
ItemsChanges *ItemsChanges
ItemsChanges *ItemsChanges `json:"items,omitempty" yaml:"items,omitempty"`
// v3 change types
ExamplesChanges map[string]*ExampleChanges
ContentChanges map[string]*MediaTypeChanges
ExamplesChanges map[string]*ExampleChanges `json:"examples,omitempty" yaml:"examples,omitempty"`
ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"`
}
// TotalChanges returns a count of everything that changed

View File

@@ -12,17 +12,17 @@ import (
type PathItemChanges struct {
PropertyChanges
GetChanges *OperationChanges
PutChanges *OperationChanges
PostChanges *OperationChanges
DeleteChanges *OperationChanges
OptionsChanges *OperationChanges
HeadChanges *OperationChanges
PatchChanges *OperationChanges
TraceChanges *OperationChanges
ServerChanges []*ServerChanges
ParameterChanges []*ParameterChanges
ExtensionChanges *ExtensionChanges
GetChanges *OperationChanges `json:"get,omitempty" yaml:"get,omitempty"`
PutChanges *OperationChanges `json:"put,omitempty" yaml:"put,omitempty"`
PostChanges *OperationChanges `json:"post,omitempty" yaml:"post,omitempty"`
DeleteChanges *OperationChanges `json:"delete,omitempty" yaml:"delete,omitempty"`
OptionsChanges *OperationChanges `json:"options,omitempty" yaml:"options,omitempty"`
HeadChanges *OperationChanges `json:"head,omitempty" yaml:"head,omitempty"`
PatchChanges *OperationChanges `json:"patch,omitempty" yaml:"patch,omitempty"`
TraceChanges *OperationChanges `json:"trace,omitempty" yaml:"trace,omitempty"`
ServerChanges []*ServerChanges `json:"servers,omitempty" yaml:"servers,omitempty"`
ParameterChanges []*ParameterChanges `json:"parameters,omitempty" yaml:"parameters,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
func (p *PathItemChanges) TotalChanges() int {

View File

@@ -13,8 +13,8 @@ import (
type PathsChanges struct {
PropertyChanges
PathItemsChanges map[string]*PathItemChanges
ExtensionChanges *ExtensionChanges
PathItemsChanges map[string]*PathItemChanges `json:"pathItems,omitempty" yaml:"pathItems,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
func (p *PathsChanges) TotalChanges() int {

View File

@@ -10,8 +10,8 @@ import (
type RequestBodyChanges struct {
PropertyChanges
ContentChanges map[string]*MediaTypeChanges
ExtensionChanges *ExtensionChanges
ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
func (rb *RequestBodyChanges) TotalChanges() int {

View File

@@ -12,17 +12,17 @@ import (
type ResponseChanges struct {
PropertyChanges
ExtensionChanges *ExtensionChanges
HeadersChanges map[string]*HeaderChanges
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
HeadersChanges map[string]*HeaderChanges `json:"headers,omitempty" yaml:"headers,omitempty"`
// v2
SchemaChanges *SchemaChanges
ExamplesChanges *ExamplesChanges
SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"`
ExamplesChanges *ExamplesChanges `json:"examples,omitempty" yaml:"examples,omitempty"`
// v3
ContentChanges map[string]*MediaTypeChanges
LinkChanges map[string]*LinkChanges
ServerChanges *ServerChanges
ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"`
LinkChanges map[string]*LinkChanges `json:"links,omitempty" yaml:"links,omitempty"`
ServerChanges *ServerChanges `json:"server,omitempty" yaml:"server,omitempty"`
}
func (r *ResponseChanges) TotalChanges() int {

View File

@@ -12,9 +12,9 @@ import (
type ResponsesChanges struct {
PropertyChanges
ResponseChanges map[string]*ResponseChanges
DefaultChanges *ResponseChanges
ExtensionChanges *ExtensionChanges
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"`
}
func (r *ResponsesChanges) TotalChanges() int {

View File

@@ -22,16 +22,16 @@ import (
// PropertyChanges.Changes, and not in the AnyOfChanges property.
type SchemaChanges struct {
PropertyChanges
DiscriminatorChanges *DiscriminatorChanges
AllOfChanges []*SchemaChanges
AnyOfChanges []*SchemaChanges
OneOfChanges []*SchemaChanges
NotChanges []*SchemaChanges
ItemsChanges []*SchemaChanges
SchemaPropertyChanges map[string]*SchemaChanges
ExternalDocChanges *ExternalDocChanges
XMLChanges *XMLChanges
ExtensionChanges *ExtensionChanges
DiscriminatorChanges *DiscriminatorChanges `json:"discriminator,omitempty" yaml:"discriminator,omitempty"`
AllOfChanges []*SchemaChanges `json:"allOf,omitempty" yaml:"allOf,omitempty"`
AnyOfChanges []*SchemaChanges `json:"anyOf,omitempty" yaml:"anyOf,omitempty"`
OneOfChanges []*SchemaChanges `json:"oneOf,omitempty" yaml:"oneOf,omitempty"`
NotChanges []*SchemaChanges `json:"not,omitempty" yaml:"not,omitempty"`
ItemsChanges []*SchemaChanges `json:"items,omitempty" yaml:"items,omitempty"`
SchemaPropertyChanges map[string]*SchemaChanges `json:"properties,omitempty" yaml:"properties,omitempty"`
ExternalDocChanges *ExternalDocChanges `json:"externalDoc,omitempty" yaml:"externalDoc,omitempty"`
XMLChanges *XMLChanges `json:"xml,omitempty" yaml:"xml,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
// TotalChanges returns a count of the total number of changes made to this schema and all sub-schemas

View File

@@ -11,7 +11,7 @@ import (
type ScopesChanges struct {
PropertyChanges
ExtensionChanges *ExtensionChanges
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
func (s *ScopesChanges) TotalChanges() int {

View File

@@ -12,11 +12,11 @@ import (
type SecuritySchemeChanges struct {
PropertyChanges
ExtensionChanges *ExtensionChanges
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
// v3
OAuthFlowChanges *OAuthFlowsChanges
OAuthFlowChanges *OAuthFlowsChanges `json:"oAuthFlow,omitempty" yaml:"oAuthFlow,omitempty"`
// v2
ScopesChanges *ScopesChanges
ScopesChanges *ScopesChanges `json:"scopes,omitempty" yaml:"scopes,omitempty"`
}
func (ss *SecuritySchemeChanges) TotalChanges() int {

View File

@@ -4,64 +4,64 @@
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"
)
type ServerChanges struct {
PropertyChanges
ServerVariableChanges map[string]*ServerVariableChanges
PropertyChanges
ServerVariableChanges map[string]*ServerVariableChanges `json:"serverVariables,omitempty" yaml:"serverVariables,omitempty"`
}
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
}
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
}
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.Changes = changes
sc.ServerVariableChanges = CheckMapForChanges(l.Variables.Value, r.Variables.Value,
&changes, v3.VariablesLabel, CompareServerVariables)
CheckProperties(props)
sc := new(ServerChanges)
sc.Changes = changes
sc.ServerVariableChanges = CheckMapForChanges(l.Variables.Value, r.Variables.Value,
&changes, v3.VariablesLabel, CompareServerVariables)
return sc
return sc
}

View File

@@ -12,8 +12,8 @@ import (
// TagChanges represents changes made to the Tags object of an OpenAPI document.
type TagChanges struct {
PropertyChanges
ExternalDocs *ExternalDocChanges
ExtensionChanges *ExtensionChanges
ExternalDocs *ExternalDocChanges `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
// TotalChanges returns a count of everything that changed within tags.

View File

@@ -11,7 +11,7 @@ import (
// XMLChanges represents changes made to the XML object of an OpenAPI document.
type XMLChanges struct {
PropertyChanges
ExtensionChanges *ExtensionChanges
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
}
// TotalChanges returns a count of everything that was changed within an XML object.

View File

@@ -39,8 +39,11 @@ func TestCompareSwaggerDocuments(t *testing.T) {
modDoc, _ := v2.CreateDocument(infoMod)
changes := CompareSwaggerDocuments(origDoc, modDoc)
assert.Equal(t, 44, changes.TotalChanges())
assert.Equal(t, 20, changes.TotalBreakingChanges())
assert.Equal(t, 52, changes.TotalChanges())
assert.Equal(t, 27, changes.TotalBreakingChanges())
//out, _ := json.MarshalIndent(changes, "", " ")
//_ = ioutil.WriteFile("output.json", out, 0776)
}
@@ -59,6 +62,21 @@ func Benchmark_CompareOpenAPIDocuments(b *testing.B) {
}
}
func Benchmark_CompareSwaggerDocuments(b *testing.B) {
original, _ := ioutil.ReadFile("../test_specs/petstorev2-complete.yaml")
modified, _ := ioutil.ReadFile("../test_specs/petstorev2-complete-modified.yaml")
infoOrig, _ := datamodel.ExtractSpecInfo(original)
infoMod, _ := datamodel.ExtractSpecInfo(modified)
origDoc, _ := v2.CreateDocument(infoOrig)
modDoc, _ := v2.CreateDocument(infoMod)
for i := 0; i < b.N; i++ {
CompareSwaggerDocuments(origDoc, modDoc)
}
}
func Benchmark_CompareOpenAPIDocuments_NoChange(b *testing.B) {
original, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml")
@@ -73,3 +91,33 @@ func Benchmark_CompareOpenAPIDocuments_NoChange(b *testing.B) {
CompareOpenAPIDocuments(origDoc, modDoc)
}
}
func Benchmark_CompareK8s(b *testing.B) {
original, _ := ioutil.ReadFile("../test_specs/k8s.json")
modified, _ := ioutil.ReadFile("../test_specs/k8s.json")
infoOrig, _ := datamodel.ExtractSpecInfo(original)
infoMod, _ := datamodel.ExtractSpecInfo(modified)
origDoc, _ := v2.CreateDocument(infoOrig)
modDoc, _ := v2.CreateDocument(infoMod)
for i := 0; i < b.N; i++ {
CompareSwaggerDocuments(origDoc, modDoc)
}
}
func Benchmark_CompareStripe(b *testing.B) {
original, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
modified, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
infoOrig, _ := datamodel.ExtractSpecInfo(original)
infoMod, _ := datamodel.ExtractSpecInfo(modified)
origDoc, _ := v3.CreateDocument(infoOrig)
modDoc, _ := v3.CreateDocument(infoMod)
for i := 0; i < b.N; i++ {
CompareOpenAPIDocuments(origDoc, modDoc)
}
}