mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 12:37:49 +00:00
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:
@@ -4,9 +4,11 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"gopkg.in/yaml.v3"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 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)
|
||||
func (s *SecurityRequirement) Build(root *yaml.Node, _ *index.SpecIndex) error {
|
||||
var labelNode *yaml.Node
|
||||
var arr []low.ValueReference[string]
|
||||
valueMap := make(map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]])
|
||||
var arr []low.ValueReference[string]
|
||||
for i := range root.Content {
|
||||
if i%2 == 0 {
|
||||
labelNode = root.Content[i]
|
||||
arr = []low.ValueReference[string]{} // reset roles.
|
||||
continue
|
||||
}
|
||||
for j := range root.Content[i].Content {
|
||||
@@ -50,3 +53,14 @@ func (s *SecurityRequirement) Build(root *yaml.Node, _ *index.SpecIndex) error {
|
||||
}
|
||||
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, "|")))
|
||||
}
|
||||
|
||||
@@ -100,4 +100,5 @@ const (
|
||||
AllowReservedLabel = "allowReserved"
|
||||
ExplodeLabel = "explode"
|
||||
ContentTypeLabel = "contentType"
|
||||
SecurityDefinitionLabel = "securityDefinition"
|
||||
)
|
||||
|
||||
@@ -17,7 +17,7 @@ type ParameterChanges struct {
|
||||
SchemaChanges *SchemaChanges
|
||||
ExtensionChanges *ExtensionChanges
|
||||
|
||||
// V2 change types
|
||||
// v2 change types
|
||||
ItemsChanges *ItemsChanges
|
||||
|
||||
// v3 change types
|
||||
|
||||
134
what-changed/security_requirement.go
Normal file
134
what-changed/security_requirement.go
Normal 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
|
||||
}
|
||||
348
what-changed/security_requirement_test.go
Normal file
348
what-changed/security_requirement_test.go
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user