Working through what-changed fixes and tuneups.

more testing is required, however things feel quite reasonable and rounded. All changes correctly detected in a spec so far.
This commit is contained in:
Dave Shanley
2022-11-19 15:05:37 -05:00
parent 97a03315d6
commit 26d3535e75
13 changed files with 1255 additions and 1393 deletions

View File

@@ -494,13 +494,20 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
_, addPLabel, addPNode := utils.FindKeyNodeFullTop(AdditionalPropertiesLabel, root.Content)
if addPNode != nil {
if utils.IsNodeMap(addPNode) {
schema, serr := low.ExtractObjectRaw[*Schema](addPNode, idx)
if serr != nil {
return serr
// check if this is a reference, or an inline schema.
isRef, _, _ := utils.IsNodeRefValue(addPNode)
sp := &SchemaProxy{
kn: addPLabel,
vn: addPNode,
idx: idx,
}
s.AdditionalProperties = low.NodeReference[any]{Value: schema, KeyNode: addPLabel, ValueNode: addPNode}
if isRef {
sp.isReference = true
_, vn := utils.FindKeyNodeTop("$ref", addPNode.Content)
sp.referenceLookup = vn.Value
}
s.AdditionalProperties = low.NodeReference[any]{Value: sp, KeyNode: addPLabel, ValueNode: addPNode}
}
if utils.IsNodeBoolValue(addPNode) {
b, _ := strconv.ParseBool(addPNode.Value)
s.AdditionalProperties = low.NodeReference[any]{Value: b, KeyNode: addPLabel, ValueNode: addPNode}
@@ -537,14 +544,15 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
// for property, build in a new thread!
bChan := make(chan schemaProxyBuildResult)
var buildProperty = func(label *yaml.Node, value *yaml.Node, c chan schemaProxyBuildResult) {
var buildProperty = func(label *yaml.Node, value *yaml.Node, c chan schemaProxyBuildResult, isRef bool,
refString string) {
c <- schemaProxyBuildResult{
k: low.KeyReference[string]{
KeyNode: label,
Value: label.Value,
},
v: low.ValueReference[*SchemaProxy]{
Value: &SchemaProxy{kn: label, vn: value, idx: idx},
Value: &SchemaProxy{kn: label, vn: value, idx: idx, isReference: isRef, referenceLookup: refString},
ValueNode: value,
},
}
@@ -563,17 +571,21 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
}
// check our prop isn't reference
if h, _, _ := utils.IsNodeRefValue(prop); h {
isRef := false
refString := ""
if h, _, l := utils.IsNodeRefValue(prop); h {
ref, _ := low.LocateRefNode(prop, idx)
if ref != nil {
isRef = true
prop = ref
refString = l
} else {
return fmt.Errorf("schema properties build failed: cannot find reference %s, line %d, col %d",
prop.Content[1].Value, prop.Content[1].Column, prop.Content[1].Line)
}
}
totalProps++
go buildProperty(currentProp, prop, bChan)
go buildProperty(currentProp, prop, bChan, isRef, refString)
}
completedProps := 0
for completedProps < totalProps {

View File

@@ -119,7 +119,11 @@ func (sp *SchemaProxy) GetValueNode() *yaml.Node {
// Hash will return a consistent SHA256 Hash of the SchemaProxy object (it will resolve it)
func (sp *SchemaProxy) Hash() [32]byte {
if sp.rendered != nil {
return sp.rendered.Hash()
if !sp.isReference {
return sp.rendered.Hash()
}
// we only hash inline schemas
return sha256.Sum256([]byte(sp.referenceLookup))
} else {
if !sp.isReference {
// only resolve this proxy if it's not a ref.

View File

@@ -442,24 +442,6 @@ examples:
//
//}
func TestSchema_Build_ErrorAdditionalProps(t *testing.T) {
yml := `additionalProperties:
$ref: #borko`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Schema
err := low.BuildModel(&idxNode, &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}
func TestSchema_Build_PropsLookup(t *testing.T) {
yml := `components:

View File

@@ -488,8 +488,8 @@ func TestCreateDocument_CheckAdditionalProperties_Schema(t *testing.T) {
components := doc.Components.Value
d := components.FindSchema("Dressing")
assert.NotNil(t, d.Value.Schema().AdditionalProperties.Value)
if n, ok := d.Value.Schema().AdditionalProperties.Value.(*base.Schema); ok {
assert.Equal(t, "something in here.", n.Description.Value)
if n, ok := d.Value.Schema().AdditionalProperties.Value.(*base.SchemaProxy); ok {
assert.Equal(t, "something in here.", n.Schema().Description.Value)
} else {
assert.Fail(t, "should be a schema")
}

View File

@@ -191,13 +191,13 @@ paths:
- in: query
name: burgerId
schema:
type: string
example: big-mac
type: int
example: 12345
description: the name of the our fantastic burger. You can pick a name from our menu
required: true
responses:
"200":
$ref: '#/components/responses/DressingResponse'
$ref: '#/components/schemas/Error'
"404":
description: Cannot find your burger in which to list dressings. Sorry
content:
@@ -207,7 +207,7 @@ paths:
example:
message: There is no burger here
"500":
description: Unexpected error listing dressings for burger. Sorry.
description: Unexpected error listing dressings for burger. Sorry friend
content:
application/json:
schema:
@@ -219,6 +219,7 @@ paths:
operationId: getDressing
tags:
- "Dressing"
- "Pepsi"
summary: Get a specific dressing - you can choose the dressing from our menu
description: Same as the summary, get a dressing, by its ID
parameters:
@@ -226,10 +227,18 @@ paths:
name: dressingId
schema:
type: string
example: cheese
example: cheesey
description: This is the unique identifier for the dressing items.
required: true
responses:
"201":
description: a dressing again with a different code
content:
application/json:
schema:
$ref: '#/components/schemas/Dressing'
example:
name: Butter Sauce
"200":
description: a dressing
content:
@@ -291,7 +300,7 @@ paths:
components:
callbacks:
BurgerCallback:
x-break-everything: please
x-break-everything: please change me
"{$request.query.queryUrl}":
post:
requestBody:
@@ -305,12 +314,12 @@ components:
description: callback successfully processes
links:
LocateBurger:
operationId: locateBurger
operationId: locateBurgers
parameters:
burgerId: '$response.body#/id'
description: Go and get a tasty burger
AnotherLocateBurger:
operationId: locateBurger
operationId: locateBurgers
parameters:
burgerId: '$response.body#/id'
description: Go and get another really tasty burger
@@ -323,7 +332,7 @@ components:
type: string
requestBodies:
BurgerRequest:
description: Give us the new burger!
description: Give us the new burger! please!
content:
application/json:
schema:
@@ -341,12 +350,17 @@ components:
numPatties: 5
examples:
QuarterPounder:
summary: A juicy two hander sammich
summary: A juicy two hander sammich yummy
value:
name: Quarter Pounder with Cheese
numPatties: 1
QuarterPounder2:
summary: A juicy two hander sammich yummy
value:
name: Quarter Pounder with Cheese
numPatties: 1
responses:
DressingResponse:
DressingResponse2:
description: all the dressings for a burger.
content:
application/json:
@@ -359,11 +373,11 @@ components:
securitySchemes:
APIKeyScheme:
type: apiKey
description: an apiKey security scheme
description: an apiKey security scheme updated
name: apiKeyScheme
in: query
JWTScheme:
type: http
type: https
description: an JWT security scheme
name: aJWTThing
scheme: bearer
@@ -377,9 +391,9 @@ components:
authorizationUrl: https://pb33f.io/oauth
scopes:
write:burgers: modify and add new burgers
read:burgers: read all burgers
read:burgers: read all burgers changed
authorizationCode:
authorizationUrl: https://pb33f.io/oauth
authorizationUrl: https://pb33f.io/oauth/changed
tokenUrl: https://api.pb33f.io/oauth/token
scopes:
write:burgers: modify burgers and stuff
@@ -392,7 +406,7 @@ components:
properties:
burgerTheme:
type: string
description: something about a theme goes in here?
description: something about a theme goes in here? changed
burgerTime:
type: number
description: number of burgers ordered so far this year.
@@ -428,6 +442,14 @@ components:
example: big-mac
description: the name of the burger. use this to order your tasty burger
required: true
BurgerId2:
in: path
name: burgerId
schema:
type: string
example: big-mac
description: the name of the burger. use this to order your tasty burger
required: true
schemas:
Error:
type: object
@@ -435,11 +457,11 @@ components:
properties:
message:
type: string
description: returns the error message if something wrong happens
description: returns the error message if something wrong happens, updated.
example: No such burger as 'Big-Whopper'
Burger:
type: object
description: The tastiest food on the planet you would love to eat everyday
description: The tastiest food on the planet you would love to eat everyday, yum!
required:
- name
- numPatties
@@ -447,7 +469,7 @@ components:
name:
type: string
description: The name of your tasty burger - burger names are listed in our menus
example: Big Mac
example: Big Mac and fries!
numPatties:
type: integer
description: The number of burger patties used
@@ -464,13 +486,14 @@ components:
required:
- potatoShape
- favoriteDrink
- seasoning
properties:
seasoning:
type: array
description: herbs and spices for your golden joy
description: herbs and spices for your golden joy and fun
items:
type: string
description: type of herb or spice used to liven up the yummy
description: type of herb or spice used to liven up the yummy things
example: salt
potatoShape:
type: string
@@ -490,7 +513,7 @@ components:
example: Cheese
additionalProperties:
type: object
description: something in here.
description: something in here. please
Drink:
type: object
description: a frosty cold beverage can be coke or sprite
@@ -521,8 +544,6 @@ components:
name: is html programming? yes.
externalDocs:
url: https://pb33f.io/docs
oneOf:
- $ref: '#/components/schemas/Drink'
anyOf:
- $ref: '#/components/schemas/Drink'
allOf:
@@ -531,21 +552,23 @@ components:
type: string
items:
- $ref: '#/components/schemas/Drink'
x-screaming-baby: loud
x-something-something: darkside
x-screaming-baby: very loud
x-something-something: darkside changed
externalDocs:
description: "Find out more information about our products and services"
url: "https://pb33f.io"
jsonSchemaDialect: https://pb33f.io/schema
url: "https://pb33f.io/new"
jsonSchemaDialect: https://pb33f.io/schema/new
webhooks:
someHook:
post:
requestBody:
description: Information about a new burger
description: Information about a new burger but updated
content:
application/json:
schema:
$ref: "#/components/schemas/Burger"
responses:
"200":
description: the hook is good! you have a new burger.
description: the hook is good! you have a new burger.
"201":
description: a new thing

View File

@@ -186,6 +186,34 @@ func CheckForModification[T any](l, r *yaml.Node, label string, changes *[]*Chan
// values. The compareFunc argument should reference the correct comparison function for the generic type.
func CheckMapForChanges[T any, R any](expLeft, expRight map[low.KeyReference[string]]low.ValueReference[T],
changes *[]*Change, label string, compareFunc func(l, r T) R) map[string]R {
return CheckMapForChangesWithComp(expLeft, expRight, changes, label, compareFunc, true)
}
func CheckMapForAdditionRemoval[T any](expLeft, expRight map[low.KeyReference[string]]low.ValueReference[T],
changes *[]*Change, label string) any {
// do nothing
doNothing := func(l, r T) any {
return nil
}
return CheckMapForChangesWithComp(expLeft, expRight, changes, label, doNothing, false)
}
//// CheckMapForAdditionRemoval checks a left and right low level map for any additions or subtractions, but not modifications
//func CheckMapForAdditionRemoval[T any, R any](expLeft, expRight map[low.KeyReference[string]]low.ValueReference[T],
// changes *[]*Change, label string) map[string]R {
//
// // do nothing
// doNothing := func(l, r T) R {
// return nil
// }
// return CheckMapForChangesWithComp(expLeft, expRight, changes, label, doNothing, false)
//}
// CheckMapForChangesWithComp checks a left and right low level map for any additions, subtractions or modifications to
// values. The compareFunc argument should reference the correct comparison function for the generic type. The compare
// bit determines if the comparison should be run or not.
func CheckMapForChangesWithComp[T any, R any](expLeft, expRight map[low.KeyReference[string]]low.ValueReference[T],
changes *[]*Change, label string, compareFunc func(l, r T) R, compare bool) map[string]R {
// stop concurrent threads screwing up changes.
var chLock sync.Mutex
@@ -226,10 +254,11 @@ func CheckMapForChanges[T any, R any](expLeft, expRight map[low.KeyReference[str
return
}
// run comparison.
chLock.Lock()
expChanges[k] = compareFunc(p[k].Value, h[k].Value)
chLock.Unlock()
if compare {
chLock.Lock()
expChanges[k] = compareFunc(p[k].Value, h[k].Value)
chLock.Unlock()
}
doneChan <- true
}

View File

@@ -13,16 +13,19 @@ import (
type ComponentsChanges struct {
PropertyChanges
SchemaChanges map[string]*SchemaChanges
ResponsesChanges map[string]*ResponseChanges
ParameterChanges map[string]*ParameterChanges
ExamplesChanges map[string]*ExampleChanges
RequestBodyChanges map[string]*RequestBodyChanges
HeaderChanges map[string]*HeaderChanges
SchemaChanges map[string]*SchemaChanges
// todo: disable these after cleaning up swagger code.
ResponsesChanges map[string]*ResponseChanges
ParameterChanges map[string]*ParameterChanges
//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
//LinkChanges map[string]*LinkChanges
//CallbackChanges map[string]*CallbackChanges
ExtensionChanges *ExtensionChanges
}
func CompareComponents(l, r any) *ComponentsChanges {
@@ -43,7 +46,7 @@ func CompareComponents(l, r any) *ComponentsChanges {
if rDef != nil {
b = rDef.Definitions
}
cc.ParameterChanges = CheckMapForChanges(a, b, &changes, v3.ParametersLabel, CompareParametersV2)
CheckMapForAdditionRemoval(a, b, &changes, v3.ParametersLabel)
}
// Swagger Responses
@@ -58,7 +61,7 @@ func CompareComponents(l, r any) *ComponentsChanges {
if rDef != nil {
b = rDef.Definitions
}
cc.ResponsesChanges = CheckMapForChanges(a, b, &changes, v3.ResponsesLabel, CompareResponseV2)
CheckMapForAdditionRemoval(a, b, &changes, v3.ResponsesLabel)
}
// Swagger Schemas
@@ -73,8 +76,7 @@ func CompareComponents(l, r any) *ComponentsChanges {
if rDef != nil {
b = rDef.Schemas
}
cc.SchemaChanges = CheckMapForChanges(a, b, &changes,
v2.DefinitionsLabel, CompareSchemas)
cc.SchemaChanges = CheckMapForChanges(a, b, &changes, v2.DefinitionsLabel, CompareSchemas)
}
// Swagger Security Definitions
@@ -100,9 +102,9 @@ func CompareComponents(l, r any) *ComponentsChanges {
lComponents := l.(*v3.Components)
rComponents := r.(*v3.Components)
if low.AreEqual(lComponents, rComponents) {
return nil
}
//if low.AreEqual(lComponents, rComponents) {
// return nil
//}
doneChan := make(chan componentComparison)
comparisons := 0
@@ -175,23 +177,23 @@ func CompareComponents(l, r any) *ComponentsChanges {
break
case v3.ResponsesLabel:
completedComponents++
cc.ResponsesChanges = res.result.(map[string]*ResponseChanges)
//cc.ResponsesChanges = res.result.(map[string]*ResponseChanges)
break
case v3.ParametersLabel:
completedComponents++
cc.ParameterChanges = res.result.(map[string]*ParameterChanges)
//cc.ParameterChanges = res.result.(map[string]*ParameterChanges)
break
case v3.ExamplesLabel:
completedComponents++
cc.ExamplesChanges = res.result.(map[string]*ExampleChanges)
//cc.ExamplesChanges = res.result.(map[string]*ExampleChanges)
break
case v3.RequestBodiesLabel:
completedComponents++
cc.RequestBodyChanges = res.result.(map[string]*RequestBodyChanges)
//cc.RequestBodyChanges = res.result.(map[string]*RequestBodyChanges)
break
case v3.HeadersLabel:
completedComponents++
cc.HeaderChanges = res.result.(map[string]*HeaderChanges)
//cc.HeaderChanges = res.result.(map[string]*HeaderChanges)
break
case v3.SecuritySchemesLabel:
completedComponents++
@@ -199,11 +201,11 @@ func CompareComponents(l, r any) *ComponentsChanges {
break
case v3.LinksLabel:
completedComponents++
cc.LinkChanges = res.result.(map[string]*LinkChanges)
//cc.LinkChanges = res.result.(map[string]*LinkChanges)
break
case v3.CallbacksLabel:
completedComponents++
cc.CallbackChanges = res.result.(map[string]*CallbackChanges)
//cc.CallbackChanges = res.result.(map[string]*CallbackChanges)
break
}
}
@@ -225,9 +227,19 @@ type componentComparison struct {
// run a generic comparison in a thread which in turn splits checks into further threads.
func runComparison[T any, R any](l, r map[low.KeyReference[string]]low.ValueReference[T],
changes *[]*Change, label string, compareFunc func(l, r T) R, doneChan chan componentComparison) {
doneChan <- componentComparison{
prop: label,
result: CheckMapForChanges(l, r, changes, label, compareFunc),
// for schemas
if label == v3.SchemasLabel || label == v2.DefinitionsLabel || label == v3.SecuritySchemesLabel {
doneChan <- componentComparison{
prop: label,
result: CheckMapForChanges(l, r, changes, label, compareFunc),
}
return
} else {
doneChan <- componentComparison{
prop: label,
result: CheckMapForAdditionRemoval(l, r, changes, label),
}
}
}
@@ -236,30 +248,30 @@ func (c *ComponentsChanges) TotalChanges() int {
for k := range c.SchemaChanges {
v += c.SchemaChanges[k].TotalChanges()
}
for k := range c.ResponsesChanges {
v += c.ResponsesChanges[k].TotalChanges()
}
for k := range c.ParameterChanges {
v += c.ParameterChanges[k].TotalChanges()
}
for k := range c.ExamplesChanges {
v += c.ExamplesChanges[k].TotalChanges()
}
for k := range c.RequestBodyChanges {
v += c.RequestBodyChanges[k].TotalChanges()
}
for k := range c.HeaderChanges {
v += c.HeaderChanges[k].TotalChanges()
}
//for k := range c.ResponsesChanges {
// v += c.ResponsesChanges[k].TotalChanges()
//}
//for k := range c.ParameterChanges {
// v += c.ParameterChanges[k].TotalChanges()
//}
//for k := range c.ExamplesChanges {
// v += c.ExamplesChanges[k].TotalChanges()
//}
//for k := range c.RequestBodyChanges {
// v += c.RequestBodyChanges[k].TotalChanges()
//}
//for k := range c.HeaderChanges {
// v += c.HeaderChanges[k].TotalChanges()
//}
for k := range c.SecuritySchemeChanges {
v += c.SecuritySchemeChanges[k].TotalChanges()
}
for k := range c.LinkChanges {
v += c.LinkChanges[k].TotalChanges()
}
for k := range c.CallbackChanges {
v += c.CallbackChanges[k].TotalChanges()
}
//for k := range c.LinkChanges {
// v += c.LinkChanges[k].TotalChanges()
//}
//for k := range c.CallbackChanges {
// v += c.CallbackChanges[k].TotalChanges()
//}
if c.ExtensionChanges != nil {
v += c.ExtensionChanges.TotalChanges()
}
@@ -271,26 +283,26 @@ func (c *ComponentsChanges) TotalBreakingChanges() int {
for k := range c.SchemaChanges {
v += c.SchemaChanges[k].TotalBreakingChanges()
}
for k := range c.ResponsesChanges {
v += c.ResponsesChanges[k].TotalBreakingChanges()
}
for k := range c.ParameterChanges {
v += c.ParameterChanges[k].TotalBreakingChanges()
}
for k := range c.RequestBodyChanges {
v += c.RequestBodyChanges[k].TotalBreakingChanges()
}
for k := range c.HeaderChanges {
v += c.HeaderChanges[k].TotalBreakingChanges()
}
//for k := range c.ResponsesChanges {
// v += c.ResponsesChanges[k].TotalBreakingChanges()
//}
//for k := range c.ParameterChanges {
// v += c.ParameterChanges[k].TotalBreakingChanges()
//}
//for k := range c.RequestBodyChanges {
// v += c.RequestBodyChanges[k].TotalBreakingChanges()
//}
//for k := range c.HeaderChanges {
// v += c.HeaderChanges[k].TotalBreakingChanges()
//}
for k := range c.SecuritySchemeChanges {
v += c.SecuritySchemeChanges[k].TotalBreakingChanges()
}
for k := range c.LinkChanges {
v += c.LinkChanges[k].TotalBreakingChanges()
}
for k := range c.CallbackChanges {
v += c.CallbackChanges[k].TotalBreakingChanges()
}
//for k := range c.LinkChanges {
// v += c.LinkChanges[k].TotalBreakingChanges()
//}
//for k := range c.CallbackChanges {
// v += c.CallbackChanges[k].TotalBreakingChanges()
//}
return v
}

View File

@@ -5,7 +5,7 @@ package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v2"
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/resolver"
@@ -17,18 +17,18 @@ import (
func TestCompareComponents_Swagger_Definitions_Equal(t *testing.T) {
left := `thing1:
type: int
description: a thing
thing2:
type: string
description: another thing.`
type: int
description: a thing
thing2:
type: string
description: another thing.`
right := `thing1:
type: int
description: a thing
thing2:
type: string
description: another thing.`
type: int
description: a thing
thing2:
type: string
description: another thing.`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
@@ -50,18 +50,15 @@ thing2:
func TestCompareComponents_Swagger_Definitions_Modified(t *testing.T) {
left := `thing1:
type: int
description: a thing
thing2:
type: int
description: another thing.`
type: int
description: a thing
thing2:
type: int
description: another thing.`
right := `thing1:
type: int
description: a thing
thing2:
type: string
description: another thing.`
type: int
description: a thing that changed`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
@@ -77,30 +74,29 @@ thing2:
// compare.
extChanges := CompareComponents(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, 2, extChanges.TotalChanges())
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, 1, extChanges.SchemaChanges["thing2"].TotalChanges())
}
func TestCompareComponents_Swagger_Definitions_Added(t *testing.T) {
left := `thing1:
type: int
description: a thing
thing2:
type: string
description: another thing.`
type: int
description: a thing
thing2:
type: string
description: another thing.`
right := `thing1:
type: int
description: a thing
thing2:
type: string
description: another thing.
type: int
description: a thing
thing2:
type: string
description: another thing.
thing3:
type: int
description: added a thing`
type: int
description: added a thing`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
@@ -125,21 +121,21 @@ thing3:
func TestCompareComponents_Swagger_Definitions_Removed(t *testing.T) {
left := `thing1:
type: int
description: a thing
thing2:
type: string
description: another thing.`
type: int
description: a thing
thing2:
type: string
description: another thing.`
right := `thing1:
type: int
description: a thing
thing2:
type: string
description: another thing.
type: int
description: a thing
thing2:
type: string
description: another thing.
thing3:
type: int
description: added a thing`
type: int
description: added a thing`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
@@ -161,95 +157,23 @@ thing3:
assert.Equal(t, "thing3", extChanges.Changes[0].Original)
}
func TestCompareComponents_Swagger_Parameters_Equal(t *testing.T) {
left := `param1:
name: nap
param2:
name: sleep
param3:
name: snooze
`
right := `param1:
name: nap
param2:
name: sleep
param3:
name: snooze`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.ParameterDefinitions
var rDoc v2.ParameterDefinitions
_ = 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 := CompareComponents(&lDoc, &rDoc)
assert.Nil(t, extChanges)
}
func TestCompareComponents_Swagger_Parameters_Modified(t *testing.T) {
left := `param1:
name: nap
param2:
name: sleep
param3:
name: snooze
`
right := `param1:
name: WIDE AWAKE
param2:
name: sleep
param3:
name: KINDA SNOOZ`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.ParameterDefinitions
var rDoc v2.ParameterDefinitions
_ = 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 := CompareComponents(&lDoc, &rDoc)
assert.Equal(t, 2, extChanges.TotalChanges())
assert.Equal(t, 2, extChanges.TotalBreakingChanges())
assert.Equal(t, Modified, extChanges.ParameterChanges["param1"].Changes[0].ChangeType)
assert.Equal(t, "WIDE AWAKE", extChanges.ParameterChanges["param1"].Changes[0].New)
assert.Equal(t, "KINDA SNOOZ", extChanges.ParameterChanges["param3"].Changes[0].New)
assert.Equal(t, v3.NameLabel, extChanges.ParameterChanges["param1"].Changes[0].Property)
}
func TestCompareComponents_Swagger_Parameters_Added(t *testing.T) {
left := `param1:
name: nap
name: nap
param2:
name: sleep
param3:
name: snooze
name: sleep
param3:
name: snooze
`
right := `param1:
name: nap
name: nap
param2:
name: sleep
param3:
name: snooze
name: sleep
param3:
name: snooze
param4:
name: I woke up!`
name: I woke up!`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
@@ -274,20 +198,20 @@ param4:
func TestCompareComponents_Swagger_Parameters_Removed(t *testing.T) {
left := `param1:
name: nap
name: nap
param2:
name: sleep
param3:
name: snooze
name: sleep
param3:
name: snooze
`
right := `param1:
name: nap
name: nap
param2:
name: sleep
param3:
name: snooze
name: sleep
param3:
name: snooze
param4:
name: I woke up!`
name: I woke up!`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
@@ -309,81 +233,19 @@ param4:
assert.Equal(t, "param4", extChanges.Changes[0].Original)
}
func TestCompareComponents_Swagger_Responses_Equal(t *testing.T) {
left := `resp1:
description: hi!
resp2:
description: bye!
`
right := `resp1:
description: hi!
resp2:
description: bye!`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.ResponsesDefinitions
var rDoc v2.ResponsesDefinitions
_ = 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 := CompareComponents(&lDoc, &rDoc)
assert.Nil(t, extChanges)
}
func TestCompareComponents_Swagger_Responses_Modified(t *testing.T) {
left := `resp1:
description: hi!
resp2:
description: bye!
`
right := `resp1:
description: hi!
resp2:
description: oh, so you want to change huh?`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.ResponsesDefinitions
var rDoc v2.ResponsesDefinitions
_ = 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 := CompareComponents(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, 1, extChanges.ResponsesChanges["resp2"].TotalChanges())
assert.Equal(t, v3.DescriptionLabel, extChanges.ResponsesChanges["resp2"].Changes[0].Property)
}
func TestCompareComponents_Swagger_Responses_Added(t *testing.T) {
left := `resp1:
description: hi!
description: hi!
resp2:
description: bye!
description: bye!
`
right := `resp1:
description: hi!
description: hi!
resp2:
description: bye!
resp3:
description: another response!`
description: bye!
resp3:
description: another response!`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
@@ -410,16 +272,16 @@ resp3:
func TestCompareComponents_Swagger_Responses_Removed(t *testing.T) {
left := `resp1:
description: hi!
description: hi!
resp2:
description: bye!
description: bye!
`
right := `resp1:
description: hi!
description: hi!
resp2:
description: bye!
resp3:
description: another response!`
description: bye!
resp3:
description: another response!`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
@@ -443,46 +305,15 @@ resp3:
assert.Equal(t, v2.ResponsesLabel, extChanges.Changes[0].Property)
}
func TestCompareComponents_Swagger_SecurityDefinitions_Equal(t *testing.T) {
left := `scheme1:
description: hi!
scheme2:
description: bye!
`
right := `scheme1:
description: hi!
scheme2:
description: bye!`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityDefinitions
var rDoc v2.SecurityDefinitions
_ = 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 := CompareComponents(&lDoc, &rDoc)
assert.Nil(t, extChanges)
}
func TestCompareComponents_Swagger_SecurityDefinitions_Modified(t *testing.T) {
left := `scheme1:
description: hi!
scheme2:
description: bye!
`
description: hi!`
right := `scheme1:
description: hi!
description: hi! again
scheme2:
description: bye! again!`
description: bye! again!`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
@@ -498,10 +329,10 @@ scheme2:
// compare.
extChanges := CompareComponents(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, 2, extChanges.TotalChanges())
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, 1, extChanges.SecuritySchemeChanges["scheme2"].TotalChanges())
assert.Equal(t, v3.DescriptionLabel, extChanges.SecuritySchemeChanges["scheme2"].Changes[0].Property)
assert.Equal(t, 1, extChanges.SecuritySchemeChanges["scheme1"].TotalChanges())
assert.Equal(t, v3.DescriptionLabel, extChanges.SecuritySchemeChanges["scheme1"].Changes[0].Property)
}
func TestCompareComponents_OpenAPI_Schemas_Equal(t *testing.T) {
@@ -704,12 +535,11 @@ responses:
}
func TestCompareComponents_OpenAPI_Responses_FullBuild(t *testing.T) {
left := `components:
responses:
coffee:
description: tasty
tv:
$ref: '#/components/responses/coffee'`
left := `responses:
coffee:
description: tasty
tv:
$ref: '#/components/responses/coffee'`
right := left
@@ -734,6 +564,44 @@ func TestCompareComponents_OpenAPI_Responses_FullBuild(t *testing.T) {
assert.Nil(t, extChanges)
}
func TestCompareComponents_OpenAPI_ResponsesAdd_FullBuild(t *testing.T) {
left := `responses:
coffee:
description: tasty
tv:
$ref: '#/responses/coffee'`
right := `responses:
herbs:
description: refreshing
coffee:
description: tasty
tv:
$ref: '#/responses/coffee'`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Components
var rDoc v3.Components
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
idx := index.NewSpecIndex(&lNode)
_ = lDoc.Build(lNode.Content[0], idx)
_ = rDoc.Build(rNode.Content[0], idx)
// compare.
extChanges := CompareComponents(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}
func TestCompareComponents_OpenAPI_Responses_FullBuild_IdenticalRef(t *testing.T) {
left := `components:
responses:
@@ -815,37 +683,37 @@ func TestCompareComponents_OpenAPI_Responses_FullBuild_CircularRef(t *testing.T)
assert.Nil(t, extChanges)
}
func TestCompareComponents_OpenAPI_Responses_Modify(t *testing.T) {
left := `responses:
niceResponse:
description: hello
badResponse:
description: go away please`
right := `responses:
niceResponse:
description: hello my matey
badResponse:
description: go away please, now!`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Components
var rDoc v3.Components
_ = 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 := CompareComponents(&rDoc, &lDoc)
assert.Equal(t, 2, extChanges.TotalChanges())
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}
//func TestCompareComponents_OpenAPI_Responses_Modify(t *testing.T) {
//
// left := `responses:
// niceResponse:
// description: hello
// badResponse:
// description: go away please`
//
// right := `responses:
// niceResponse:
// description: hello my matey
// badResponse:
// description: go away please, now!`
//
// var lNode, rNode yaml.Node
// _ = yaml.Unmarshal([]byte(left), &lNode)
// _ = yaml.Unmarshal([]byte(right), &rNode)
//
// // create low level objects
// var lDoc v3.Components
// var rDoc v3.Components
// _ = 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 := CompareComponents(&rDoc, &lDoc)
// assert.Equal(t, 2, extChanges.TotalChanges())
// assert.Equal(t, 0, extChanges.TotalBreakingChanges())
//}
func TestCompareComponents_OpenAPI_Responses_Add(t *testing.T) {
@@ -944,38 +812,6 @@ func TestCompareComponents_OpenAPI_Parameters_Equal(t *testing.T) {
assert.Nil(t, extChanges)
}
func TestCompareComponents_OpenAPI_Parameters_Modified(t *testing.T) {
left := `parameters:
param1:
name: a parameter
param2:
name: another param`
right := `parameters:
param1:
name: a parameter modified
param2:
name: another param but modified`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Components
var rDoc v3.Components
_ = 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 := CompareComponents(&lDoc, &rDoc)
assert.Equal(t, 2, extChanges.TotalChanges())
assert.Equal(t, 2, extChanges.TotalBreakingChanges())
}
func TestCompareComponents_OpenAPI_Parameters_Added(t *testing.T) {
left := `parameters:
@@ -1046,99 +882,11 @@ func TestCompareComponents_OpenAPI_Parameters_Removed(t *testing.T) {
assert.Equal(t, "param3", extChanges.Changes[0].Original)
}
func TestCompareComponents_OpenAPI_Examples_Equal(t *testing.T) {
left := `examples:
example1:
description: an example
example2:
description: another example`
right := left
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Components
var rDoc v3.Components
_ = 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 := CompareComponents(&lDoc, &rDoc)
assert.Nil(t, extChanges)
}
func TestCompareComponents_OpenAPI_Examples_Modified(t *testing.T) {
left := `examples:
example1:
description: an example
example2:
description: another example`
right := `examples:
example1:
description: change me
example2:
description: grow me`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Components
var rDoc v3.Components
_ = 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 := CompareComponents(&lDoc, &rDoc)
assert.Equal(t, 2, extChanges.TotalChanges())
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}
func TestCompareComponents_OpenAPI_RequestBodies_Equal(t *testing.T) {
left := `requestBodies:
body1:
description: a request
body2:
description: another request`
right := left
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Components
var rDoc v3.Components
_ = 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 := CompareComponents(&lDoc, &rDoc)
assert.Nil(t, extChanges)
}
func TestCompareComponents_OpenAPI_RequestBodies_Modified(t *testing.T) {
left := `requestBodies:
body1:
description: a request
body2:
description: another request`
description: a request`
right := `requestBodies:
body1:
@@ -1160,50 +908,21 @@ func TestCompareComponents_OpenAPI_RequestBodies_Modified(t *testing.T) {
// compare.
extChanges := CompareComponents(&lDoc, &rDoc)
assert.Equal(t, 2, extChanges.TotalChanges())
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}
func TestCompareComponents_OpenAPI_Headers_Equal(t *testing.T) {
func TestCompareComponents_OpenAPI_Headers_Add(t *testing.T) {
left := `headers:
header1:
description: a header
header2:
description: another header`
right := left
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Components
var rDoc v3.Components
_ = 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 := CompareComponents(&lDoc, &rDoc)
assert.Nil(t, extChanges)
}
func TestCompareComponents_OpenAPI_Headers_Modified(t *testing.T) {
left := `headers:
header1:
description: a header
header2:
description: another header`
description: a header`
right := `headers:
header1:
description: a header but different
description: a header
header2:
description: another header, also different`
description: another header`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
@@ -1219,7 +938,7 @@ func TestCompareComponents_OpenAPI_Headers_Modified(t *testing.T) {
// compare.
extChanges := CompareComponents(&lDoc, &rDoc)
assert.Equal(t, 2, extChanges.TotalChanges())
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}
@@ -1282,40 +1001,11 @@ func TestCompareComponents_OpenAPI_SecuritySchemes_Modified(t *testing.T) {
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}
func TestCompareComponents_OpenAPI_Links_Equal(t *testing.T) {
func TestCompareComponents_OpenAPI_Links_Added(t *testing.T) {
left := `links:
link1:
operationId: link1
link2:
operationId: link2`
right := left
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Components
var rDoc v3.Components
_ = 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 := CompareComponents(&lDoc, &rDoc)
assert.Nil(t, extChanges)
}
func TestCompareComponents_OpenAPI_Links_Modified(t *testing.T) {
left := `links:
link1:
operationId: link1
link2:
operationId: link2`
operationId: link1`
right := `links:
link1:
@@ -1337,35 +1027,8 @@ func TestCompareComponents_OpenAPI_Links_Modified(t *testing.T) {
// compare.
extChanges := CompareComponents(&lDoc, &rDoc)
assert.Equal(t, 2, extChanges.TotalChanges())
assert.Equal(t, 2, extChanges.TotalBreakingChanges())
}
func TestCompareComponents_OpenAPI_Callbacks_Equal(t *testing.T) {
left := `callbacks:
link1:
url: https://pb33f.io
link2:
url: https://pb33f.io`
right := left
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Components
var rDoc v3.Components
_ = 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 := CompareComponents(&lDoc, &rDoc)
assert.Nil(t, extChanges)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}
func TestCompareComponents_OpenAPI_Callbacks_Modified(t *testing.T) {
@@ -1374,11 +1037,7 @@ func TestCompareComponents_OpenAPI_Callbacks_Modified(t *testing.T) {
link1:
'{$request.query.queryUrl}':
post:
description: a nice callback
link2:
'{$pizza.cake.burgers}':
post:
description: pizza and cake, and burgers.`
description: a nice callback`
right := `callbacks:
link1:
@@ -1404,8 +1063,8 @@ func TestCompareComponents_OpenAPI_Callbacks_Modified(t *testing.T) {
// compare.
extChanges := CompareComponents(&lDoc, &rDoc)
assert.Equal(t, 3, extChanges.TotalChanges())
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}
func TestCompareComponents_OpenAPI_Extensions_Modified(t *testing.T) {

View File

@@ -151,15 +151,15 @@ func CompareDocuments(l, r any) *DocumentChanges {
if n := CompareComponents(lDoc.Definitions.Value, rDoc.Definitions.Value); n != nil {
cc.SchemaChanges = n.SchemaChanges
}
if n := CompareComponents(lDoc.SecurityDefinitions.Value, rDoc.SecurityDefinitions.Value); n != nil {
cc.SecuritySchemeChanges = n.SecuritySchemeChanges
}
if n := CompareComponents(lDoc.Parameters.Value, rDoc.Parameters.Value); n != nil {
cc.ParameterChanges = n.ParameterChanges
}
if n := CompareComponents(lDoc.Responses.Value, rDoc.Responses.Value); n != nil {
cc.ResponsesChanges = n.ResponsesChanges
}
//if n := CompareComponents(lDoc.SecurityDefinitions.Value, rDoc.SecurityDefinitions.Value); n != nil {
// cc.SecuritySchemeChanges = n.SecuritySchemeChanges
//}
//if n := CompareComponents(lDoc.Parameters.Value, rDoc.Parameters.Value); n != nil {
// cc.ParameterChanges = n.ParameterChanges
//}
//if n := CompareComponents(lDoc.Responses.Value, rDoc.Responses.Value); n != nil {
// cc.ResponsesChanges = n.ResponsesChanges
//}
dc.ExtensionChanges = CompareExtensions(lDoc.Extensions, rDoc.Extensions)
if cc.TotalChanges() > 0 {
dc.ComponentsChanges = cc

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,7 @@ import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort"
"sync"
@@ -290,9 +291,7 @@ func checkPropertiesPropertyOfASchema(
var propLock sync.Mutex
checkProperty := func(key string, lp, rp *base.SchemaProxy, propChanges map[string]*SchemaChanges, done chan bool) {
if lp != nil && rp != nil {
ls := lp.Schema()
rs := rp.Schema()
if low.AreEqual(ls, rs) {
if low.AreEqual(lp, rp) {
done <- true
return
}
@@ -553,18 +552,21 @@ func checkSchemaPropertyChanges(
New: rSchema,
})
// AdditionalProperties
props = append(props, &PropertyCheck{
LeftNode: lSchema.AdditionalProperties.ValueNode,
RightNode: rSchema.AdditionalProperties.ValueNode,
Label: v3.AdditionalPropertiesLabel,
Changes: changes,
Breaking: false,
Original: lSchema,
New: rSchema,
})
// AdditionalProperties (only if not an object)
if !utils.IsNodeMap(lSchema.AdditionalProperties.ValueNode) &&
!utils.IsNodeMap(rSchema.AdditionalProperties.ValueNode) {
props = append(props, &PropertyCheck{
LeftNode: lSchema.AdditionalProperties.ValueNode,
RightNode: rSchema.AdditionalProperties.ValueNode,
Label: v3.AdditionalPropertiesLabel,
Changes: changes,
Breaking: false,
Original: lSchema,
New: rSchema,
})
}
// Description
//Description
props = append(props, &PropertyCheck{
LeftNode: lSchema.Description.ValueNode,
RightNode: rSchema.Description.ValueNode,
@@ -750,6 +752,20 @@ func checkSchemaPropertyChanges(
// check extensions
sc.ExtensionChanges = CompareExtensions(lSchema.Extensions, rSchema.Extensions)
// if additional properties is an object, then hash it
// AdditionalProperties (only if not an object)
if utils.IsNodeMap(lSchema.AdditionalProperties.ValueNode) ||
utils.IsNodeMap(rSchema.AdditionalProperties.ValueNode) {
lHash := low.GenerateHashString(lSchema.AdditionalProperties.ValueNode)
rHash := low.GenerateHashString(rSchema.AdditionalProperties.ValueNode)
if lHash != rHash {
CreateChange(changes, Modified, v3.AdditionalPropertiesLabel,
lSchema.AdditionalProperties.ValueNode, rSchema.AdditionalProperties.ValueNode, false,
lSchema.AdditionalProperties.Value, rSchema.AdditionalProperties.Value)
}
}
// check core properties
CheckProperties(props)
}
@@ -846,21 +862,17 @@ func extractSchemaChanges(
rEntities := make(map[string]*base.SchemaProxy)
for h := range lSchema {
q := lSchema[h].Value
if !q.IsSchemaReference() {
w := q.Schema()
z := fmt.Sprintf(x, w.Hash())
lKeys = append(lKeys, z)
lEntities[z] = q
}
z := fmt.Sprintf(x, q.Hash())
lKeys = append(lKeys, z)
lEntities[z] = q
}
for h := range rSchema {
q := rSchema[h].Value
if !q.IsSchemaReference() {
w := q.Schema()
z := fmt.Sprintf(x, w.Hash())
rKeys = append(rKeys, z)
rEntities[z] = q
}
z := fmt.Sprintf(x, q.Hash())
rKeys = append(rKeys, z)
rEntities[z] = q
}
// sort slices so that like for like is all sequenced.

View File

@@ -1436,3 +1436,105 @@ components:
assert.Equal(t, ObjectRemoved, changes.Changes[0].ChangeType)
assert.Equal(t, "big xml", changes.Changes[0].OriginalObject.(*base.XML).Name.Value)
}
func TestCompareSchemas_SchemaRefChecks(t *testing.T) {
left := `openapi: 3.0
components:
schemas:
Burger:
type: object
properties:
fries:
$ref: '#/components/schemas/Fries'
Fries:
type: object
required:
- potatoShape
- favoriteDrink
- seasoning`
right := `openapi: 3.0
components:
schemas:
Burger:
type: object
properties:
fries:
$ref: '#/components/schemas/Fries'
Fries:
type: object
required:
- potatoShape
- favoriteDrink`
leftDoc, rightDoc := test_BuildDoc(left, right)
// extract left reference schema and non reference schema.
changes := CompareDocuments(leftDoc, rightDoc)
assert.NotNil(t, changes)
assert.Equal(t, 1, changes.TotalChanges())
assert.Equal(t, 1, changes.TotalBreakingChanges())
}
func TestCompareSchemas_SchemaAdditionalPropertiesCheck(t *testing.T) {
left := `openapi: 3.0
components:
schemas:
Dressing:
type: object
additionalProperties:
type: object
description: something in here. please`
right := `openapi: 3.0
components:
schemas:
Dressing:
type: object
additionalProperties:
type: object
description: something in here. please, but changed`
leftDoc, rightDoc := test_BuildDoc(left, right)
// extract left reference schema and non reference schema.
changes := CompareDocuments(leftDoc, rightDoc)
assert.NotNil(t, changes)
assert.Equal(t, 1, changes.TotalChanges())
assert.Equal(t, 0, changes.TotalBreakingChanges())
}
func TestCompareSchemas_Schema_DeletePoly(t *testing.T) {
left := `openapi: 3.0
components:
schemas:
Drink:
type: int
SomePayload:
type: string
anyOf:
- $ref: '#/components/schemas/Drink'
allOf:
- $ref: '#/components/schemas/Drink'`
right := `openapi: 3.0
components:
schemas:
Drink:
type: int
SomePayload:
type: string
anyOf:
- $ref: '#/components/schemas/Drink'`
leftDoc, rightDoc := test_BuildDoc(left, right)
// extract left reference schema and non reference schema.
changes := CompareDocuments(leftDoc, rightDoc)
assert.NotNil(t, changes)
assert.Equal(t, 1, changes.TotalChanges())
assert.Equal(t, 1, changes.TotalBreakingChanges())
}

View File

@@ -22,8 +22,8 @@ func TestCompareOpenAPIDocuments(t *testing.T) {
modDoc, _ := v3.CreateDocument(infoMod)
changes := CompareOpenAPIDocuments(origDoc, modDoc)
assert.Equal(t, 30, changes.TotalChanges())
assert.Equal(t, 6, changes.TotalBreakingChanges())
assert.Equal(t, 67, changes.TotalChanges())
assert.Equal(t, 17, changes.TotalBreakingChanges())
}