Fixed issue with optional parameters when diffing

optional parameters being added are not breaking, this was reported in openapi-changes  https://github.com/pb33f/openapi-changes/issues/87

Signed-off-by: quobix <dave@quobix.com>
This commit is contained in:
quobix
2024-01-19 17:03:10 -05:00
parent e0f0f387bd
commit 906f8128ec
4 changed files with 213 additions and 15 deletions

View File

@@ -262,8 +262,15 @@ func CompareOperations(l, r any) *OperationChanges {
nil)
}
if lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() {
rParams := rParamsUntyped.Value.([]low.ValueReference[*v2.Parameter])
breaking := false
for i := range rParams {
if rParams[i].Value.Required.Value {
breaking = true
}
}
CreateChange(&changes, PropertyAdded, v3.ParametersLabel,
nil, rParamsUntyped.ValueNode, true, nil,
nil, rParamsUntyped.ValueNode, breaking, nil,
rParamsUntyped.Value)
}
@@ -358,8 +365,15 @@ func CompareOperations(l, r any) *OperationChanges {
nil)
}
if lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() {
rParams := rParamsUntyped.Value.([]low.ValueReference[*v3.Parameter])
breaking := false
for i := range rParams {
if rParams[i].Value.Required.Value {
breaking = true
}
}
CreateChange(&changes, PropertyAdded, v3.ParametersLabel,
nil, rParamsUntyped.ValueNode, true, nil,
nil, rParamsUntyped.ValueNode, breaking, nil,
rParamsUntyped.Value)
}

View File

@@ -1588,3 +1588,90 @@ requestBody:
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType)
}
func TestComparePathItem_V3_AddOptionalParam(t *testing.T) {
left := `operationId: listBurgerDressings`
right := `operationId: listBurgerDressings
parameters:
- in: head
name: burgerId
required: false`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Operation
var rDoc v3.Operation
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)
// compare.
extChanges := CompareOperations(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}
func TestComparePathItem_V2_AddOptionalParam(t *testing.T) {
left := `operationId: listBurgerDressings`
right := `operationId: listBurgerDressings
parameters:
- in: head
name: burgerId
required: false`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.Operation
var rDoc v2.Operation
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)
// compare.
extChanges := CompareOperations(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}
func TestComparePathItem_V3_AddRequiredParam(t *testing.T) {
left := `operationId: listBurgerDressings`
right := `operationId: listBurgerDressings
parameters:
- in: head
name: burgerId
required: true`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.Operation
var rDoc v3.Operation
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)
// compare.
extChanges := CompareOperations(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
}

View File

@@ -335,8 +335,16 @@ func compareSwaggerPathItem(lPath, rPath *v2.PathItem, changes *[]*Change, pc *P
nil)
}
if lPath.Parameters.IsEmpty() && !rPath.Parameters.IsEmpty() {
breaking := false
for i := range rPath.Parameters.Value {
param := rPath.Parameters.Value[i].Value
if param.Required.Value {
breaking = true
break
}
}
CreateChange(changes, PropertyAdded, v3.ParametersLabel,
nil, rPath.Parameters.ValueNode, true, nil,
nil, rPath.Parameters.ValueNode, breaking, nil,
rPath.Parameters.Value)
}
@@ -589,8 +597,16 @@ func compareOpenAPIPathItem(lPath, rPath *v3.PathItem, changes *[]*Change, pc *P
nil)
}
if lPath.Parameters.IsEmpty() && !rPath.Parameters.IsEmpty() {
breaking := false
for i := range rPath.Parameters.Value {
param := rPath.Parameters.Value[i].Value
if param.Required.Value {
breaking = true
break
}
}
CreateChange(changes, PropertyAdded, v3.ParametersLabel,
nil, rPath.Parameters.ValueNode, true, nil,
nil, rPath.Parameters.ValueNode, breaking, nil,
rPath.Parameters.Value)
}

View File

@@ -635,16 +635,11 @@ trace:
assert.Equal(t, 8, extChanges.TotalBreakingChanges())
}
func TestComparePathItem_V3_ChangeParam(t *testing.T) {
func TestComparePathItem_V3_AddParam(t *testing.T) {
left := `get:
operationId: listBurgerDressings
parameters:
- in: query
name: burgerId`
left := `summary: hello`
right := `get:
operationId: listBurgerDressings
right := `summary: hello
parameters:
- in: head
name: burgerId`
@@ -661,6 +656,92 @@ func TestComparePathItem_V3_ChangeParam(t *testing.T) {
_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)
// compare.
extChanges := ComparePathItems(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}
func TestComparePathItem_V2_AddParamOptional(t *testing.T) {
left := `summary: hello`
right := `summary: hello
parameters:
- in: head
name: burgerId`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.PathItem
var rDoc v2.PathItem
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)
// compare.
extChanges := ComparePathItems(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}
func TestComparePathItem_V3_AddParamRequired(t *testing.T) {
left := `summary: hello`
right := `summary: hello
parameters:
- in: head
name: burgerId
required: true`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.PathItem
var rDoc v3.PathItem
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)
// compare.
extChanges := ComparePathItems(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Len(t, extChanges.GetAllChanges(), 1)
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
}
func TestComparePathItem_V2_AddParamRequired(t *testing.T) {
left := `summary: hello`
right := `summary: hello
parameters:
- in: head
name: burgerId
required: true`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.PathItem
var rDoc v2.PathItem
_ = low.BuildModel(lNode.Content[0], &lDoc)
_ = low.BuildModel(rNode.Content[0], &rDoc)
_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)
// compare.
extChanges := ComparePathItems(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())