Swagger securityRequirement added to what-changed

This one was perhaps one of the more frustrating models. It's a simple design, checking maps of string arrays for accurate changes - but the combinations make it a bit of a challenge. I hope I've caught the variations correctly. Bloody swagger, the sooner you go away, the easier all of our lies will be.
This commit is contained in:
Dave Shanley
2022-10-23 11:12:04 -04:00
parent e90d934a4b
commit 53e04b5346
5 changed files with 499 additions and 2 deletions

View File

@@ -4,9 +4,11 @@
package v2 package v2
import ( import (
"crypto/sha256"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"strings"
) )
// SecurityRequirement is a low-level representation of a Swagger / OpenAPI 2 SecurityRequirement object. // SecurityRequirement is a low-level representation of a Swagger / OpenAPI 2 SecurityRequirement object.
@@ -23,11 +25,12 @@ type SecurityRequirement struct {
// Build will extract security requirements from the node (the structure is odd, to be honest) // Build will extract security requirements from the node (the structure is odd, to be honest)
func (s *SecurityRequirement) Build(root *yaml.Node, _ *index.SpecIndex) error { func (s *SecurityRequirement) Build(root *yaml.Node, _ *index.SpecIndex) error {
var labelNode *yaml.Node var labelNode *yaml.Node
var arr []low.ValueReference[string]
valueMap := make(map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]) valueMap := make(map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]])
var arr []low.ValueReference[string]
for i := range root.Content { for i := range root.Content {
if i%2 == 0 { if i%2 == 0 {
labelNode = root.Content[i] labelNode = root.Content[i]
arr = []low.ValueReference[string]{} // reset roles.
continue continue
} }
for j := range root.Content[i].Content { for j := range root.Content[i].Content {
@@ -50,3 +53,14 @@ func (s *SecurityRequirement) Build(root *yaml.Node, _ *index.SpecIndex) error {
} }
return nil return nil
} }
func (s *SecurityRequirement) Hash() [32]byte {
var f []string
for k := range s.Values.Value {
for y := range s.Values.Value[k].Value {
f = append(f, s.Values.Value[k].Value[y].Value)
// lol, I know. -------^^^^^ <- this is the actual value.
}
}
return sha256.Sum256([]byte(strings.Join(f, "|")))
}

View File

@@ -100,4 +100,5 @@ const (
AllowReservedLabel = "allowReserved" AllowReservedLabel = "allowReserved"
ExplodeLabel = "explode" ExplodeLabel = "explode"
ContentTypeLabel = "contentType" ContentTypeLabel = "contentType"
SecurityDefinitionLabel = "securityDefinition"
) )

View File

@@ -17,7 +17,7 @@ type ParameterChanges struct {
SchemaChanges *SchemaChanges SchemaChanges *SchemaChanges
ExtensionChanges *ExtensionChanges ExtensionChanges *ExtensionChanges
// V2 change types // v2 change types
ItemsChanges *ItemsChanges ItemsChanges *ItemsChanges
// v3 change types // v3 change types

View File

@@ -0,0 +1,134 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package what_changed
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v2"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"gopkg.in/yaml.v3"
)
type SecurityRequirementChanges struct {
PropertyChanges
}
func (s *SecurityRequirementChanges) TotalChanges() int {
return s.PropertyChanges.TotalChanges()
}
func (s *SecurityRequirementChanges) TotalBreakingChanges() int {
return s.PropertyChanges.TotalBreakingChanges()
}
func CompareSecurityRequirement(l, r *v2.SecurityRequirement) *SecurityRequirementChanges {
if low.AreEqual(l, r) {
return nil
}
var changes []*Change
lKeys := make([]string, len(l.Values.Value))
rKeys := make([]string, len(r.Values.Value))
lValues := make(map[string]low.ValueReference[[]low.ValueReference[string]])
rValues := make(map[string]low.ValueReference[[]low.ValueReference[string]])
var n, z int
for i := range l.Values.Value {
lKeys[n] = i.Value
lValues[i.Value] = l.Values.Value[i]
n++
}
for i := range r.Values.Value {
rKeys[z] = i.Value
rValues[i.Value] = r.Values.Value[i]
z++
}
removed := func(z int, vn *yaml.Node, name string) {
CreateChange(&changes, ObjectRemoved, v3.SecurityDefinitionLabel,
vn, nil, true, name, nil)
}
added := func(z int, vn *yaml.Node, name string) {
CreateChange(&changes, ObjectAdded, v3.SecurityDefinitionLabel,
nil, vn, false, nil, name)
}
for z = range lKeys {
if z < len(rKeys) {
if _, ok := rValues[lKeys[z]]; !ok {
removed(z, lValues[lKeys[z]].ValueNode, lKeys[z])
continue
}
lValue := lValues[lKeys[z]].Value
rValue := rValues[lKeys[z]].Value
// check if actual values match up
lRoleKeys := make([]string, len(lValue))
rRoleKeys := make([]string, len(rValue))
lRoleValues := make(map[string]low.ValueReference[string])
rRoleValues := make(map[string]low.ValueReference[string])
var t, k int
for i := range lValue {
lRoleKeys[t] = lValue[i].Value
lRoleValues[lValue[i].Value] = lValue[i]
t++
}
for i := range rValue {
rRoleKeys[k] = rValue[i].Value
rRoleValues[rValue[i].Value] = rValue[i]
k++
}
for t = range lRoleKeys {
if t < len(rRoleKeys) {
if _, ok := rRoleValues[lRoleKeys[t]]; !ok {
removed(t, lRoleValues[lRoleKeys[t]].ValueNode, lRoleKeys[t])
continue
}
}
if t >= len(rRoleKeys) {
if _, ok := rRoleValues[lRoleKeys[t]]; !ok {
removed(t, lRoleValues[lRoleKeys[t]].ValueNode, lRoleKeys[t])
}
}
}
for t = range rRoleKeys {
if t < len(lRoleKeys) {
if _, ok := lRoleValues[rRoleKeys[t]]; !ok {
added(t, rRoleValues[rRoleKeys[t]].ValueNode, rRoleKeys[t])
continue
}
}
if t >= len(lRoleKeys) {
added(t, rRoleValues[rRoleKeys[t]].ValueNode, rRoleKeys[t])
}
}
}
if z >= len(rKeys) {
if _, ok := rValues[lKeys[z]]; !ok {
removed(z, lValues[lKeys[z]].ValueNode, lKeys[z])
}
}
}
for z = range rKeys {
if z < len(lKeys) {
if _, ok := lValues[rKeys[z]]; !ok {
added(z, rValues[rKeys[z]].ValueNode, rKeys[z])
continue
}
}
if z >= len(lKeys) {
if _, ok := lValues[rKeys[z]]; !ok {
added(z, rValues[rKeys[z]].ValueNode, rKeys[z])
}
}
}
sc := new(SecurityRequirementChanges)
sc.Changes = changes
if sc.TotalChanges() <= 0 {
return nil
}
return sc
}

View File

@@ -0,0 +1,348 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package what_changed
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
)
func TestCompareSecurityRequirement(t *testing.T) {
left := `auth:
- pizza
- pie`
right := `auth:
- pie
- pizza`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityRequirement
var rDoc v2.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirement(&lDoc, &rDoc)
assert.Nil(t, extChanges)
}
func TestCompareSecurityRequirement_NewReq(t *testing.T) {
left := `tip:
- tap
auth:
- pizza
- pie`
right := `auth:
- pie
- pizza
tip:
- tap
biscuit:
- digestive`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityRequirement
var rDoc v2.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirement(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType)
assert.Equal(t, "biscuit", extChanges.Changes[0].NewObject)
}
func TestCompareSecurityRequirement_RemoveReq(t *testing.T) {
left := `auth:
- pizza
- pie`
right := `auth:
- pie
- pizza
biscuit:
- digestive`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityRequirement
var rDoc v2.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirement(&rDoc, &lDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
}
func TestCompareSecurityRequirement_SwapOut(t *testing.T) {
left := `cheese:
- pizza
- pie
biscuit:
- digestive`
right := `bread:
- pie
- pizza
milk:
- digestive`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityRequirement
var rDoc v2.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirement(&lDoc, &rDoc)
assert.Equal(t, 4, extChanges.TotalChanges())
assert.Equal(t, 2, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
assert.Equal(t, ObjectRemoved, extChanges.Changes[1].ChangeType)
assert.Equal(t, ObjectAdded, extChanges.Changes[2].ChangeType)
assert.Equal(t, ObjectAdded, extChanges.Changes[3].ChangeType)
}
func TestCompareSecurityRequirement_SwapLeft(t *testing.T) {
left := `cheese:
- pizza
- pie
biscuit:
- digestive`
right := `cheese:
- pie
- pizza
milk:
- digestive`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityRequirement
var rDoc v2.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirement(&lDoc, &rDoc)
assert.Equal(t, 2, extChanges.TotalChanges())
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
assert.Equal(t, ObjectAdded, extChanges.Changes[1].ChangeType)
}
func TestCompareSecurityRequirement_AddedRole(t *testing.T) {
left := `cheese:
- pizza
- pie
biscuit:
- digestive`
right := `cheese:
- pizza
- pie
biscuit:
- digestive
- rich tea`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityRequirement
var rDoc v2.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirement(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType)
assert.Equal(t, "rich tea", extChanges.Changes[0].New)
}
func TestCompareSecurityRequirement_AddedMultiple(t *testing.T) {
left := `cheese:
- pizza
- pie
biscuit:
- digestive`
right := `cheese:
- pizza
- pie
cake:
- vanilla
- choccy
biscuit:
- digestive
- rich tea`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityRequirement
var rDoc v2.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirement(&lDoc, &rDoc)
assert.Equal(t, 2, extChanges.TotalChanges())
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType)
}
func TestCompareSecurityRequirement_ReplaceRole(t *testing.T) {
left := `cheese:
- pizza
- pie
biscuit:
- digestive`
right := `cheese:
- pizza
- pie
biscuit:
- biscotti
- rich tea`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityRequirement
var rDoc v2.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirement(&lDoc, &rDoc)
assert.Equal(t, 3, extChanges.TotalChanges())
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
}
func TestCompareSecurityRequirement_Identical(t *testing.T) {
left := `cheese:
- pizza
- pie
biscuit:
- biscotti
- rich tea`
right := `cheese:
- pizza
- pie
biscuit:
- biscotti
- rich tea`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityRequirement
var rDoc v2.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirement(&lDoc, &rDoc)
assert.Nil(t, extChanges)
}
func TestCompareSecurityRequirement_RemovedRole(t *testing.T) {
left := `cheese:
- pizza
- pie
biscuit:
- digestive`
right := `cheese:
- pizza
- pie
biscuit:
- digestive
- rich tea`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityRequirement
var rDoc v2.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirement(&rDoc, &lDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
assert.Equal(t, "rich tea", extChanges.Changes[0].Original)
}