Refactored SecurityRequirement **breaking change**

The v3 model is wrong and out of sync with the spec. It's been corrected, so the v2 and v2 model for SecurityRequirement have been collapsed down into a base model., they are the same data structures. This has allowed me to delete the complexity of sharing two different models for the same structure, by unifying the model correctly. I am not sure why I decided to change the v3 model, oh well, its been corrected. Long live swagger!
This commit is contained in:
Dave Shanley
2022-11-02 10:02:53 -04:00
parent 4b30fe3622
commit d27e66ff3d
21 changed files with 308 additions and 469 deletions

View File

@@ -1,9 +1,11 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package v2
package base
import low "github.com/pb33f/libopenapi/datamodel/low/v2"
import (
"github.com/pb33f/libopenapi/datamodel/low/base"
)
// SecurityRequirement is a high-level representation of a Swagger / OpenAPI 2 SecurityRequirement object.
//
@@ -14,19 +16,19 @@ import low "github.com/pb33f/libopenapi/datamodel/low/v2"
// - https://swagger.io/specification/v2/#securityDefinitionsObject
type SecurityRequirement struct {
Requirements map[string][]string
low *low.SecurityRequirement
low *base.SecurityRequirement
}
// NewSecurityRequirement creates a new high-level SecurityRequirement from a low-level one.
func NewSecurityRequirement(req *low.SecurityRequirement) *SecurityRequirement {
func NewSecurityRequirement(req *base.SecurityRequirement) *SecurityRequirement {
r := new(SecurityRequirement)
r.low = req
values := make(map[string][]string)
// to keep things fast, avoiding copying anything - makes it a little hard to read.
for reqK := range req.Values.Value {
for reqK := range req.Requirements.Value {
var vals []string
for valK := range req.Values.Value[reqK].Value {
vals = append(vals, req.Values.Value[reqK].Value[valK].Value)
for valK := range req.Requirements.Value[reqK].Value {
vals = append(vals, req.Requirements.Value[reqK].Value[valK].Value)
}
values[reqK.Value] = vals
}
@@ -35,6 +37,6 @@ func NewSecurityRequirement(req *low.SecurityRequirement) *SecurityRequirement {
}
// GoLow returns the low-level SecurityRequirement used to create the high-level one.
func (s *SecurityRequirement) GoLow() *low.SecurityRequirement {
func (s *SecurityRequirement) GoLow() *base.SecurityRequirement {
return s.low
}

View File

@@ -24,7 +24,7 @@ type Operation struct {
Responses *Responses
Schemes []string
Deprecated bool
Security []*SecurityRequirement
Security []*base.SecurityRequirement
Extensions map[string]any
low *low.Operation
}
@@ -88,9 +88,9 @@ func NewOperation(operation *low.Operation) *Operation {
o.Deprecated = operation.Deprecated.Value
}
if !operation.Security.IsEmpty() {
var sec []*SecurityRequirement
var sec []*base.SecurityRequirement
for s := range operation.Security.Value {
sec = append(sec, NewSecurityRequirement(operation.Security.Value[s].Value))
sec = append(sec, base.NewSecurityRequirement(operation.Security.Value[s].Value))
}
o.Security = sec
}

View File

@@ -38,7 +38,7 @@ type Swagger struct {
// The basePath does not support path templating.
BasePath string
// Schemes represents the transfer protocol of the API. Values MUST be from the list: "http", "https", "ws", "wss".
// Schemes represents the transfer protocol of the API. Requirements MUST be from the list: "http", "https", "ws", "wss".
// If the schemes is not included, the default scheme to be used is the one used to access
// the Swagger definition itself.
Schemes []string
@@ -77,7 +77,7 @@ type Swagger struct {
// describes alternative security schemes that can be used (that is, there is a logical OR between the security
// requirements). Individual operations can override this definition.
// - https://swagger.io/specification/v2/#securityRequirementObject
Security []*SecurityRequirement
Security []*base.SecurityRequirement
// Tags are A list of tags used by the specification with additional metadata.
// The order of the tags can be used to reflect on their order by the parsing tools. Not all tags that are used
@@ -150,9 +150,9 @@ func NewSwaggerDocument(document *low.Swagger) *Swagger {
d.SecurityDefinitions = NewSecurityDefinitions(document.SecurityDefinitions.Value)
}
if !document.Security.IsEmpty() {
var security []*SecurityRequirement
var security []*base.SecurityRequirement
for s := range document.Security.Value {
security = append(security, NewSecurityRequirement(document.Security.Value[s].Value))
security = append(security, base.NewSecurityRequirement(document.Security.Value[s].Value))
}
d.Security = security
}

View File

@@ -103,8 +103,8 @@ func TestNewSwaggerDocument_Security(t *testing.T) {
assert.Len(t, highDoc.Security[0].Requirements["global_auth"], 2)
wentLow := highDoc.Security[0].GoLow()
assert.Equal(t, 25, wentLow.Values.ValueNode.Line)
assert.Equal(t, 5, wentLow.Values.ValueNode.Column)
assert.Equal(t, 25, wentLow.Requirements.ValueNode.Line)
assert.Equal(t, 5, wentLow.Requirements.ValueNode.Column)
}

View File

@@ -48,7 +48,7 @@ type Document struct {
// to authorize a request. Individual operations can override this definition. To make security optional,
// an empty security requirement ({}) can be included in the array.
// - https://spec.openapis.org/oas/v3.1.0#security-requirement-object
Security *SecurityRequirement
Security *base.SecurityRequirement
// Tags is a slice of base.Tag instances defined by the specification
// A list of tags used by the document with additional metadata. The order of the tags can be used to reflect on

View File

@@ -337,10 +337,10 @@ func TestNewDocument_Paths(t *testing.T) {
assert.Len(t, okResp.Links, 2)
assert.Equal(t, "locateBurger", okResp.Links["LocateBurger"].OperationId)
assert.Equal(t, 305, okResp.Links["LocateBurger"].GoLow().OperationId.ValueNode.Line)
assert.Len(t, burgersOp.Post.Security.ValueRequirements[0], 1)
assert.Len(t, burgersOp.Post.Security.ValueRequirements[0]["OAuthScheme"], 2)
assert.Equal(t, "read:burgers", burgersOp.Post.Security.ValueRequirements[0]["OAuthScheme"][0])
assert.Equal(t, 118, burgersOp.Post.Security.GoLow().ValueRequirements[0].ValueNode.Line)
assert.Len(t, burgersOp.Post.Security[0].Requirements, 1)
assert.Len(t, burgersOp.Post.Security[0].Requirements["OAuthScheme"], 2)
assert.Equal(t, "read:burgers", burgersOp.Post.Security[0].Requirements["OAuthScheme"][0])
assert.Equal(t, 118, burgersOp.Post.Security[0].GoLow().Requirements.ValueNode.Line)
assert.Len(t, burgersOp.Post.Servers, 1)
assert.Equal(t, "https://pb33f.io", burgersOp.Post.Servers[0].URL)

View File

@@ -23,8 +23,8 @@ type Operation struct {
RequestBody *RequestBody
Responses *Responses
Callbacks map[string]*Callback
Deprecated bool
Security *SecurityRequirement
Deprecated *bool
Security []*base.SecurityRequirement
Servers []*Server
Extensions map[string]any
low *low.Operation
@@ -42,6 +42,7 @@ func NewOperation(operation *low.Operation) *Operation {
}
o.Tags = tags
o.Summary = operation.Summary.Value
o.Deprecated = &operation.Deprecated.Value
o.Description = operation.Description.Value
if !operation.ExternalDocs.IsEmpty() {
o.ExternalDocs = base.NewExternalDoc(operation.ExternalDocs.Value)
@@ -61,7 +62,11 @@ func NewOperation(operation *low.Operation) *Operation {
o.Responses = NewResponses(operation.Responses.Value)
}
if !operation.Security.IsEmpty() {
o.Security = NewSecurityRequirement(operation.Security.Value)
var sec []*base.SecurityRequirement
for s := range operation.Security.Value {
sec = append(sec, base.NewSecurityRequirement(operation.Security.Value[s].Value))
}
o.Security = sec
}
var servers []*Server
for i := range operation.Servers.Value {

View File

@@ -1,49 +0,0 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package v3
import low "github.com/pb33f/libopenapi/datamodel/low/v3"
// SecurityRequirement is a high-level representation of an OpenAPI 3+ SecurityRequirement object that is backed
// by a low-level one.
//
// It lists the required security schemes to execute this operation. The name used for each property MUST correspond
// to a security scheme declared in the Security Schemes under the Components Object.
//
// Security Requirement Objects that contain multiple schemes require that all schemes MUST be satisfied for a
// request to be authorized. This enables support for scenarios where multiple query parameters or HTTP headers are
// required to convey security information.
//
// When a list of Security Requirement Objects is defined on the OpenAPI Object or Operation Object, only one of the
// Security Requirement Objects in the list needs to be satisfied to authorize the request.
// - https://spec.openapis.org/oas/v3.1.0#security-requirement-object
type SecurityRequirement struct {
ValueRequirements []map[string][]string
low *low.SecurityRequirement
}
// NewSecurityRequirement will create a new high-level SecurityRequirement instance, from a low-level one.
func NewSecurityRequirement(req *low.SecurityRequirement) *SecurityRequirement {
r := new(SecurityRequirement)
r.low = req
var values []map[string][]string
for i := range req.ValueRequirements {
valmap := make(map[string][]string)
for k, v := range req.ValueRequirements[i].Value {
var mItems []string
for h := range v.Value {
mItems = append(mItems, v.Value[h].Value)
}
valmap[k.Value] = mItems
}
values = append(values, valmap)
}
r.ValueRequirements = values
return r
}
// GoLow returns the low-level SecurityRequirement instance used to create the high-level one.
func (s *SecurityRequirement) GoLow() *low.SecurityRequirement {
return s.low
}

View File

@@ -1,7 +1,7 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package v2
package base
import (
"crypto/sha256"
@@ -13,15 +13,16 @@ import (
"strings"
)
// SecurityRequirement is a low-level representation of a Swagger / OpenAPI 2 SecurityRequirement object.
// SecurityRequirement is a low-level representation of a Swagger / OpenAPI 3 SecurityRequirement object.
//
// SecurityRequirement lists the required security schemes to execute this operation. The object can have multiple
// security schemes declared in it which are all required (that is, there is a logical AND between the schemes).
//
// The name used for each property MUST correspond to a security scheme declared in the Security Definitions
// - https://swagger.io/specification/v2/#securityDefinitionsObject
// - https://swagger.io/specification/#security-requirement-object
type SecurityRequirement struct {
Values low.ValueReference[map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]]
Requirements low.ValueReference[map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]]
}
// Build will extract security requirements from the node (the structure is odd, to be honest)
@@ -49,22 +50,32 @@ func (s *SecurityRequirement) Build(root *yaml.Node, _ *index.SpecIndex) error {
ValueNode: root.Content[i],
}
}
s.Values = low.ValueReference[map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]]{
s.Requirements = low.ValueReference[map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]]{
Value: valueMap,
ValueNode: root,
}
return nil
}
// FindRequirement will attempt to locate a security requirement string from a supplied name.
func (s *SecurityRequirement) FindRequirement(name string) []low.ValueReference[string] {
for k := range s.Requirements.Value {
if k.Value == name {
return s.Requirements.Value[k].Value
}
}
return nil
}
// Hash will return a consistent SHA256 Hash of the SecurityRequirement object
func (s *SecurityRequirement) Hash() [32]byte {
var f []string
values := make(map[string][]string, len(s.Values.Value))
values := make(map[string][]string, len(s.Requirements.Value))
var valKeys []string
for k := range s.Values.Value {
for k := range s.Requirements.Value {
var vals []string
for y := range s.Values.Value[k].Value {
vals = append(vals, s.Values.Value[k].Value[y].Value)
for y := range s.Requirements.Value[k].Value {
vals = append(vals, s.Requirements.Value[k].Value[y].Value)
// lol, I know. -------^^^^^ <- this is the actual value.
}
sort.Strings(vals)

View File

@@ -487,7 +487,7 @@ func ExtractMap[PT Buildable[N], N any](
return nil, labelNode, valueNode, nil
}
// ExtractExtensions will extract any 'x-' prefixed key nodes from a root node into a map. Values have been pre-cast:
// ExtractExtensions will extract any 'x-' prefixed key nodes from a root node into a map. Requirements have been pre-cast:
//
// Maps
// map[string]interface{} for maps

View File

@@ -30,7 +30,7 @@ type Operation struct {
Responses low.NodeReference[*Responses]
Schemes low.NodeReference[[]low.ValueReference[string]]
Deprecated low.NodeReference[bool]
Security low.NodeReference[[]low.ValueReference[*SecurityRequirement]]
Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]
Extensions map[low.KeyReference[string]]low.ValueReference[any]
}
@@ -65,13 +65,13 @@ func (o *Operation) Build(root *yaml.Node, idx *index.SpecIndex) error {
}
o.Responses = respBody
// extract parameters
sec, sln, svn, sErr := low.ExtractArray[*SecurityRequirement](SecurityLabel, root, idx)
// extract security
sec, sln, svn, sErr := low.ExtractArray[*base.SecurityRequirement](SecurityLabel, root, idx)
if sErr != nil {
return sErr
}
if sec != nil {
o.Security = low.NodeReference[[]low.ValueReference[*SecurityRequirement]]{
o.Security = low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]{
Value: sec,
KeyNode: sln,
ValueNode: svn,

View File

@@ -44,7 +44,7 @@ type Swagger struct {
// The basePath does not support path templating.
BasePath low.NodeReference[string]
// Schemes represents the transfer protocol of the API. Values MUST be from the list: "http", "https", "ws", "wss".
// Schemes represents the transfer protocol of the API. Requirements MUST be from the list: "http", "https", "ws", "wss".
// If the schemes is not included, the default scheme to be used is the one used to access
// the Swagger definition itself.
Schemes low.NodeReference[[]low.ValueReference[string]]
@@ -83,7 +83,7 @@ type Swagger struct {
// describes alternative security schemes that can be used (that is, there is a logical OR between the security
// requirements). Individual operations can override this definition.
// - https://swagger.io/specification/v2/#securityRequirementObject
Security low.NodeReference[[]low.ValueReference[*SecurityRequirement]]
Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]
// Tags are A list of tags used by the specification with additional metadata.
// The order of the tags can be used to reflect on their order by the parsing tools. Not all tags that are used
@@ -245,12 +245,12 @@ func extractTags(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- b
}
func extractSecurity(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
sec, ln, vn, err := low.ExtractArray[*SecurityRequirement](SecurityLabel, root, idx)
sec, ln, vn, err := low.ExtractArray[*base.SecurityRequirement](SecurityLabel, root, idx)
if err != nil {
e <- err
return
}
doc.Security = low.NodeReference[[]low.ValueReference[*SecurityRequirement]]{
doc.Security = low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]{
Value: sec,
KeyNode: ln,
ValueNode: vn,

View File

@@ -76,11 +76,15 @@ func extractInfo(info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex)
}
func extractSecurity(info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex) error {
sec, sErr := low.ExtractObject[*SecurityRequirement](SecurityLabel, info.RootNode, idx)
if sErr != nil {
return sErr
sec, ln, vn, err := low.ExtractArray[*base.SecurityRequirement](SecurityLabel, info.RootNode, idx)
if err != nil {
return err
}
doc.Security = low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]{
Value: sec,
KeyNode: ln,
ValueNode: vn,
}
doc.Security = sec
return nil
}

View File

@@ -317,11 +317,7 @@ func TestCreateDocument_Paths(t *testing.T) {
assert.Equal(t, "$response.body#/id", burgerIdParam.Value)
// check security requirements
security := burgersPost.Security.Value
assert.NotNil(t, security)
assert.Len(t, security.ValueRequirements, 1)
oAuthReq := security.FindRequirement("OAuthScheme")
oAuthReq := burgersPost.FindSecurityRequirement("OAuthScheme")
assert.Len(t, oAuthReq, 2)
assert.Equal(t, "read:burgers", oAuthReq[0].Value)
@@ -453,11 +449,7 @@ func TestCreateDocument_Components_Links(t *testing.T) {
func TestCreateDocument_Doc_Security(t *testing.T) {
initTest()
security := doc.Security.Value
assert.NotNil(t, security)
assert.Len(t, security.ValueRequirements, 1)
oAuth := security.FindRequirement("OAuthScheme")
oAuth := doc.FindSecurityRequirement("OAuthScheme")
assert.Len(t, oAuth, 2)
}

View File

@@ -59,7 +59,7 @@ type Document struct {
// to authorize a request. Individual operations can override this definition. To make security optional,
// an empty security requirement ({}) can be included in the array.
// - https://spec.openapis.org/oas/v3.1.0#security-requirement-object
Security low.NodeReference[*SecurityRequirement]
Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]
// Tags is a slice of base.Tag instances defined by the specification
// A list of tags used by the document with additional metadata. The order of the tags can be used to reflect on
@@ -84,6 +84,18 @@ type Document struct {
Index *index.SpecIndex
}
// FindSecurityRequirement will attempt to locate a security requirement string from a supplied name.
func (d *Document) FindSecurityRequirement(name string) []low.ValueReference[string] {
for k := range d.Security.Value {
for i := range d.Security.Value[k].Value.Requirements.Value {
if i.Value == name {
return d.Security.Value[k].Value.Requirements.Value[i].Value
}
}
}
return nil
}
// TODO: this is early prototype mutation/modification code, keeping it around for later.
//func (d *Document) AddTag() *base.Tag {
// t := base.NewTag()

View File

@@ -30,7 +30,7 @@ type Operation struct {
Responses low.NodeReference[*Responses]
Callbacks low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Callback]]
Deprecated low.NodeReference[bool]
Security low.NodeReference[*SecurityRequirement]
Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]
Servers low.NodeReference[[]low.ValueReference[*Server]]
Extensions map[low.KeyReference[string]]low.ValueReference[any]
}
@@ -40,6 +40,18 @@ func (o *Operation) FindCallback(callback string) *low.ValueReference[*Callback]
return low.FindItemInMap[*Callback](callback, o.Callbacks.Value)
}
// FindSecurityRequirement will attempt to locate a security requirement string from a supplied name.
func (o *Operation) FindSecurityRequirement(name string) []low.ValueReference[string] {
for k := range o.Security.Value {
for i := range o.Security.Value[k].Value.Requirements.Value {
if i.Value == name {
return o.Security.Value[k].Value.Requirements.Value[i].Value
}
}
}
return nil
}
// Build will extract external docs, parameters, request body, responses, callbacks, security and servers.
func (o *Operation) Build(root *yaml.Node, idx *index.SpecIndex) error {
o.Extensions = low.ExtractExtensions(root)
@@ -92,11 +104,17 @@ func (o *Operation) Build(root *yaml.Node, idx *index.SpecIndex) error {
}
// extract security
sec, sErr := low.ExtractObject[*SecurityRequirement](SecurityLabel, root, idx)
sec, sln, svn, sErr := low.ExtractArray[*base.SecurityRequirement](SecurityLabel, root, idx)
if sErr != nil {
return sErr
}
o.Security = sec
if sec != nil {
o.Security = low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]{
Value: sec,
KeyNode: sln,
ValueNode: svn,
}
}
// extract servers
servers, sl, sn, serErr := low.ExtractArray[*Server](ServersLabel, root, idx)

View File

@@ -65,10 +65,10 @@ servers:
assert.Equal(t, "a nice callback",
n.FindCallback("niceCallback").Value.FindExpression("ohISee").Value.Description.Value)
assert.True(t, n.Deprecated.Value)
assert.Len(t, n.Security.Value.ValueRequirements, 1)
assert.Len(t, n.Security.Value.FindRequirement("books"), 2)
assert.Equal(t, "read:books", n.Security.Value.FindRequirement("books")[0].Value)
assert.Equal(t, "write:books", n.Security.Value.FindRequirement("books")[1].Value)
assert.Len(t, n.Security.Value, 1)
assert.Len(t, n.FindSecurityRequirement("books"), 2)
assert.Equal(t, "read:books", n.FindSecurityRequirement("books")[0].Value)
assert.Equal(t, "write:books", n.FindSecurityRequirement("books")[1].Value)
assert.Len(t, n.Servers.Value, 1)
assert.Equal(t, "https://pb33f.io", n.Servers.Value[0].Value.URL.Value)
}

View File

@@ -8,9 +8,7 @@ import (
"fmt"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort"
"strings"
)
@@ -48,11 +46,11 @@ type SecurityScheme struct {
// When a list of Security Requirement Objects is defined on the OpenAPI Object or Operation Object, only one of the
// Security Requirement Objects in the list needs to be satisfied to authorize the request.
// - https://spec.openapis.org/oas/v3.1.0#security-requirement-object
type SecurityRequirement struct {
// FYI, I hate this data structure. Even without the low level wrapping, it sucks.
ValueRequirements []low.ValueReference[map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]]
}
//type SecurityRequirement struct {
//
// // FYI, I hate this data structure. Even without the low level wrapping, it sucks.
// ValueRequirements []low.ValueReference[map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]]
//}
// FindExtension attempts to locate an extension using the supplied key.
func (ss *SecurityScheme) FindExtension(ext string) *low.ValueReference[any] {
@@ -105,89 +103,3 @@ func (ss *SecurityScheme) Hash() [32]byte {
}
return sha256.Sum256([]byte(strings.Join(f, "|")))
}
// FindRequirement will attempt to locate a security requirement string from a supplied name.
func (sr *SecurityRequirement) FindRequirement(name string) []low.ValueReference[string] {
for _, r := range sr.ValueRequirements {
for k, v := range r.Value {
if k.Value == name {
return v.Value
}
}
}
return nil
}
// Build will extract all security requirements
func (sr *SecurityRequirement) Build(root *yaml.Node, _ *index.SpecIndex) error {
if utils.IsNodeArray(root) {
var requirements []low.ValueReference[map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]]
for _, n := range root.Content {
var currSec *yaml.Node
if utils.IsNodeMap(n) {
res := make(map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]])
var dat []low.ValueReference[string]
for i, r := range n.Content {
if i%2 == 0 {
currSec = r
continue
}
if utils.IsNodeArray(r) {
// value (should be) an array of strings
var keyValues []low.ValueReference[string]
for _, strN := range r.Content {
keyValues = append(keyValues, low.ValueReference[string]{
Value: strN.Value,
ValueNode: strN,
})
}
dat = keyValues
}
}
if currSec != nil {
res[low.KeyReference[string]{
Value: currSec.Value,
KeyNode: currSec,
}] = low.ValueReference[[]low.ValueReference[string]]{
Value: dat,
ValueNode: currSec,
}
requirements = append(requirements,
low.ValueReference[map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]]{
Value: res,
ValueNode: n,
})
}
}
}
sr.ValueRequirements = requirements
}
return nil
}
// Hash will return a consistent SHA256 Hash of the SecurityRequirement object
func (sr *SecurityRequirement) Hash() [32]byte {
var f []string
for i := range sr.ValueRequirements {
req := sr.ValueRequirements[i].Value
values := make(map[string][]string, len(req))
var valKeys []string
for k := range req {
var vals []string
for y := range req[k].Value {
vals = append(vals, req[k].Value[y].Value)
}
sort.Strings(vals)
valKeys = append(valKeys, k.Value)
if len(vals) > 0 {
values[k.Value] = vals
}
}
sort.Strings(valKeys)
for val := range valKeys {
f = append(f, fmt.Sprintf("%s-%s", valKeys[val],
strings.Join(values[valKeys[val]], "|")))
}
}
return sha256.Sum256([]byte(strings.Join(f, "|")))
}

View File

@@ -5,6 +5,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
@@ -12,7 +13,7 @@ import (
)
func TestSecurityRequirement_Build(t *testing.T) {
yml := `- something:
yml := `something:
- read:me
- write:me`
@@ -20,13 +21,14 @@ func TestSecurityRequirement_Build(t *testing.T) {
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n SecurityRequirement
var n base.SecurityRequirement
err := low.BuildModel(&idxNode, &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.NoError(t, err)
assert.Len(t, n.ValueRequirements, 1)
assert.Len(t, n.Requirements.Value, 1)
assert.Equal(t, "read:me", n.FindRequirement("something")[0].Value)
assert.Equal(t, "write:me", n.FindRequirement("something")[1].Value)
assert.Nil(t, n.FindRequirement("none"))

View File

@@ -5,10 +5,9 @@ package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"gopkg.in/yaml.v3"
"reflect"
)
type SecurityRequirementChanges struct {
@@ -33,76 +32,15 @@ func addedSecurityRequirement(vn *yaml.Node, name string, changes *[]*Change) {
nil, vn, false, nil, name)
}
func CompareSecurityRequirement(l, r any) *SecurityRequirementChanges {
func CompareSecurityRequirement(l, r *base.SecurityRequirement) *SecurityRequirementChanges {
var changes []*Change
sc := new(SecurityRequirementChanges)
if reflect.TypeOf(&v2.SecurityRequirement{}) == reflect.TypeOf(l) &&
reflect.TypeOf(&v2.SecurityRequirement{}) == reflect.TypeOf(r) {
lSec := l.(*v2.SecurityRequirement)
rSec := r.(*v2.SecurityRequirement)
if low.AreEqual(lSec, rSec) {
if low.AreEqual(l, r) {
return nil
}
checkSecurityRequirement(lSec.Values.Value, rSec.Values.Value, &changes)
}
if reflect.TypeOf(&v3.SecurityRequirement{}) == reflect.TypeOf(l) &&
reflect.TypeOf(&v3.SecurityRequirement{}) == reflect.TypeOf(r) {
lSec := l.(*v3.SecurityRequirement)
rSec := r.(*v3.SecurityRequirement)
if low.AreEqual(lSec, rSec) {
return nil
}
// can we find anyone to dance with?
findPartner := func(key string,
search map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]) map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]] {
for k := range search {
if k.Value == key {
return search[k]
}
}
return nil
}
// Yes, this exists.
lValues := make(map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]])
rValues := make(map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]])
for i := range lSec.ValueRequirements {
for k := range lSec.ValueRequirements[i].Value {
lValues[k] = lSec.ValueRequirements[i].Value
}
}
for i := range rSec.ValueRequirements {
for k := range rSec.ValueRequirements[i].Value {
rValues[k] = rSec.ValueRequirements[i].Value
}
}
// look through left and right slices to see if we recognize anything.
for k := range lValues {
if p := findPartner(k.Value, rValues); p != nil {
checkSecurityRequirement(lValues[k], p, &changes)
continue
}
CreateChange(&changes, ObjectRemoved, v3.SecurityLabel,
k.KeyNode, nil, true, lValues[k], nil)
}
for k := range rValues {
if ok := findPartner(k.Value, lValues); ok == nil {
CreateChange(&changes, ObjectAdded, v3.SecurityLabel,
nil, k.KeyNode, false, nil, rValues[k])
}
}
}
checkSecurityRequirement(l.Requirements.Value, r.Requirements.Value, &changes)
sc.Changes = changes
return sc
}
@@ -199,11 +137,3 @@ func checkSecurityRequirement(lSec, rSec map[low.KeyReference[string]]low.ValueR
}
}
}
func CompareSecurityRequirementV3(l, r *v3.SecurityRequirement) *SecurityRequirementChanges {
return CompareSecurityRequirement(l, r)
}
func CompareSecurityRequirementV2(l, r *v2.SecurityRequirement) *SecurityRequirementChanges {
return CompareSecurityRequirement(l, r)
}

View File

@@ -5,8 +5,7 @@ package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
@@ -27,15 +26,15 @@ func TestCompareSecurityRequirement_V2(t *testing.T) {
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityRequirement
var rDoc v2.SecurityRequirement
var lDoc base.SecurityRequirement
var rDoc base.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirementV2(&lDoc, &rDoc)
extChanges := CompareSecurityRequirement(&lDoc, &rDoc)
assert.Nil(t, extChanges)
}
@@ -60,15 +59,15 @@ biscuit:
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityRequirement
var rDoc v2.SecurityRequirement
var lDoc base.SecurityRequirement
var rDoc base.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirementV2(&lDoc, &rDoc)
extChanges := CompareSecurityRequirement(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType)
@@ -92,15 +91,15 @@ biscuit:
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityRequirement
var rDoc v2.SecurityRequirement
var lDoc base.SecurityRequirement
var rDoc base.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirementV2(&rDoc, &lDoc)
extChanges := CompareSecurityRequirement(&rDoc, &lDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
}
@@ -124,15 +123,15 @@ milk:
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityRequirement
var rDoc v2.SecurityRequirement
var lDoc base.SecurityRequirement
var rDoc base.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirementV2(&lDoc, &rDoc)
extChanges := CompareSecurityRequirement(&lDoc, &rDoc)
assert.Equal(t, 4, extChanges.TotalChanges())
assert.Equal(t, 2, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
@@ -160,15 +159,15 @@ milk:
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityRequirement
var rDoc v2.SecurityRequirement
var lDoc base.SecurityRequirement
var rDoc base.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirementV2(&lDoc, &rDoc)
extChanges := CompareSecurityRequirement(&lDoc, &rDoc)
assert.Equal(t, 2, extChanges.TotalChanges())
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
@@ -195,15 +194,15 @@ biscuit:
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityRequirement
var rDoc v2.SecurityRequirement
var lDoc base.SecurityRequirement
var rDoc base.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirementV2(&lDoc, &rDoc)
extChanges := CompareSecurityRequirement(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType)
@@ -233,15 +232,15 @@ biscuit:
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityRequirement
var rDoc v2.SecurityRequirement
var lDoc base.SecurityRequirement
var rDoc base.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirementV2(&lDoc, &rDoc)
extChanges := CompareSecurityRequirement(&lDoc, &rDoc)
assert.Equal(t, 2, extChanges.TotalChanges())
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType)
@@ -267,15 +266,15 @@ biscuit:
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityRequirement
var rDoc v2.SecurityRequirement
var lDoc base.SecurityRequirement
var rDoc base.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirementV2(&lDoc, &rDoc)
extChanges := CompareSecurityRequirement(&lDoc, &rDoc)
assert.Equal(t, 3, extChanges.TotalChanges())
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
}
@@ -301,15 +300,15 @@ biscuit:
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityRequirement
var rDoc v2.SecurityRequirement
var lDoc base.SecurityRequirement
var rDoc base.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirementV2(&lDoc, &rDoc)
extChanges := CompareSecurityRequirement(&lDoc, &rDoc)
assert.Nil(t, extChanges)
}
@@ -333,15 +332,15 @@ biscuit:
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityRequirement
var rDoc v2.SecurityRequirement
var lDoc base.SecurityRequirement
var rDoc base.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirementV2(&rDoc, &lDoc)
extChanges := CompareSecurityRequirement(&rDoc, &lDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
@@ -369,167 +368,168 @@ biscuit:
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v2.SecurityRequirement
var rDoc v2.SecurityRequirement
var lDoc base.SecurityRequirement
var rDoc base.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirementV2(&lDoc, &rDoc)
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_V3(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 v3.SecurityRequirement
var rDoc v3.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirementV3(&lDoc, &rDoc)
assert.Nil(t, extChanges)
}
func TestCompareSecurityRequirement_V3_AddARole(t *testing.T) {
left := `- auth:
- pizza
- pie`
right := `- auth:
- pie
- pizza
- beer`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.SecurityRequirement
var rDoc v3.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirementV3(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType)
}
func TestCompareSecurityRequirement_V3_RemoveRole(t *testing.T) {
left := `- auth:
- pizza
- pie`
right := `- auth:
- pie
- pizza
- beer`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.SecurityRequirement
var rDoc v3.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirementV3(&rDoc, &lDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
}
func TestCompareSecurityRequirement_V3_AddAReq(t *testing.T) {
left := `- auth:
- pizza
- pie`
right := `- auth:
- pie
- pizza
- coffee:
- filter
- espresso`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.SecurityRequirement
var rDoc v3.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirementV3(&lDoc, &rDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType)
}
func TestCompareSecurityRequirement_V3_RemoveAReq(t *testing.T) {
left := `- coffee:
- filter
- espresso`
right := `- coffee:
- filter
- espresso
- auth:
- pizza
- pie`
var lNode, rNode yaml.Node
_ = yaml.Unmarshal([]byte(left), &lNode)
_ = yaml.Unmarshal([]byte(right), &rNode)
// create low level objects
var lDoc v3.SecurityRequirement
var rDoc v3.SecurityRequirement
_ = low.BuildModel(&lNode, &lDoc)
_ = low.BuildModel(&rNode, &rDoc)
_ = lDoc.Build(lNode.Content[0], nil)
_ = rDoc.Build(rNode.Content[0], nil)
// compare
extChanges := CompareSecurityRequirementV3(&rDoc, &lDoc)
assert.Equal(t, 1, extChanges.TotalChanges())
assert.Equal(t, 1, extChanges.TotalBreakingChanges())
assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
}
//
//func TestCompareSecurityRequirement_V3(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 v3.SecurityRequirement
// var rDoc v3.SecurityRequirement
// _ = low.BuildModel(&lNode, &lDoc)
// _ = low.BuildModel(&rNode, &rDoc)
// _ = lDoc.Build(lNode.Content[0], nil)
// _ = rDoc.Build(rNode.Content[0], nil)
//
// // compare
// extChanges := CompareSecurityRequirementV3(&lDoc, &rDoc)
// assert.Nil(t, extChanges)
//}
//
//func TestCompareSecurityRequirement_V3_AddARole(t *testing.T) {
//
// left := `- auth:
// - pizza
// - pie`
//
// right := `- auth:
// - pie
// - pizza
// - beer`
//
// var lNode, rNode yaml.Node
// _ = yaml.Unmarshal([]byte(left), &lNode)
// _ = yaml.Unmarshal([]byte(right), &rNode)
//
// // create low level objects
// var lDoc v3.SecurityRequirement
// var rDoc v3.SecurityRequirement
// _ = low.BuildModel(&lNode, &lDoc)
// _ = low.BuildModel(&rNode, &rDoc)
// _ = lDoc.Build(lNode.Content[0], nil)
// _ = rDoc.Build(rNode.Content[0], nil)
//
// // compare
// extChanges := CompareSecurityRequirementV3(&lDoc, &rDoc)
// assert.Equal(t, 1, extChanges.TotalChanges())
// assert.Equal(t, 0, extChanges.TotalBreakingChanges())
// assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType)
//}
//
//func TestCompareSecurityRequirement_V3_RemoveRole(t *testing.T) {
//
// left := `- auth:
// - pizza
// - pie`
//
// right := `- auth:
// - pie
// - pizza
// - beer`
//
// var lNode, rNode yaml.Node
// _ = yaml.Unmarshal([]byte(left), &lNode)
// _ = yaml.Unmarshal([]byte(right), &rNode)
//
// // create low level objects
// var lDoc v3.SecurityRequirement
// var rDoc v3.SecurityRequirement
// _ = low.BuildModel(&lNode, &lDoc)
// _ = low.BuildModel(&rNode, &rDoc)
// _ = lDoc.Build(lNode.Content[0], nil)
// _ = rDoc.Build(rNode.Content[0], nil)
//
// // compare
// extChanges := CompareSecurityRequirementV3(&rDoc, &lDoc)
// assert.Equal(t, 1, extChanges.TotalChanges())
// assert.Equal(t, 1, extChanges.TotalBreakingChanges())
// assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
//}
//
//func TestCompareSecurityRequirement_V3_AddAReq(t *testing.T) {
//
// left := `- auth:
// - pizza
// - pie`
//
// right := `- auth:
// - pie
// - pizza
//- coffee:
// - filter
// - espresso`
//
// var lNode, rNode yaml.Node
// _ = yaml.Unmarshal([]byte(left), &lNode)
// _ = yaml.Unmarshal([]byte(right), &rNode)
//
// // create low level objects
// var lDoc v3.SecurityRequirement
// var rDoc v3.SecurityRequirement
// _ = low.BuildModel(&lNode, &lDoc)
// _ = low.BuildModel(&rNode, &rDoc)
// _ = lDoc.Build(lNode.Content[0], nil)
// _ = rDoc.Build(rNode.Content[0], nil)
//
// // compare
// extChanges := CompareSecurityRequirementV3(&lDoc, &rDoc)
// assert.Equal(t, 1, extChanges.TotalChanges())
// assert.Equal(t, 0, extChanges.TotalBreakingChanges())
// assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType)
//}
//
//func TestCompareSecurityRequirement_V3_RemoveAReq(t *testing.T) {
//
// left := `- coffee:
// - filter
// - espresso`
//
// right := `- coffee:
// - filter
// - espresso
//- auth:
// - pizza
// - pie`
//
// var lNode, rNode yaml.Node
// _ = yaml.Unmarshal([]byte(left), &lNode)
// _ = yaml.Unmarshal([]byte(right), &rNode)
//
// // create low level objects
// var lDoc v3.SecurityRequirement
// var rDoc v3.SecurityRequirement
// _ = low.BuildModel(&lNode, &lDoc)
// _ = low.BuildModel(&rNode, &rDoc)
// _ = lDoc.Build(lNode.Content[0], nil)
// _ = rDoc.Build(rNode.Content[0], nil)
//
// // compare
// extChanges := CompareSecurityRequirementV3(&rDoc, &lDoc)
// assert.Equal(t, 1, extChanges.TotalChanges())
// assert.Equal(t, 1, extChanges.TotalBreakingChanges())
// assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
//}