mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-08 12:37:49 +00:00
Tuning parameter exraction for circular ref handling
Lots and lots of variations. means lots of branches to check. Signed-off-by: quobix <dave@quobix.com>
This commit is contained in:
@@ -75,7 +75,7 @@ func FindComponent(root *yaml.Node, componentId, absoluteFilePath string, index
|
|||||||
Path: friendlySearch,
|
Path: friendlySearch,
|
||||||
RemoteLocation: absoluteFilePath,
|
RemoteLocation: absoluteFilePath,
|
||||||
Index: index,
|
Index: index,
|
||||||
RequiredRefProperties: extractDefinitionRequiredRefProperties(resNode, map[string][]string{}, fullDef),
|
RequiredRefProperties: extractDefinitionRequiredRefProperties(resNode, map[string][]string{}, fullDef, index),
|
||||||
}
|
}
|
||||||
return ref
|
return ref
|
||||||
}
|
}
|
||||||
@@ -174,7 +174,7 @@ func (index *SpecIndex) lookupRolodex(uri []string) *Reference {
|
|||||||
IsRemote: true,
|
IsRemote: true,
|
||||||
RemoteLocation: absoluteFileLocation,
|
RemoteLocation: absoluteFileLocation,
|
||||||
Path: "$",
|
Path: "$",
|
||||||
RequiredRefProperties: extractDefinitionRequiredRefProperties(parsedDocument, map[string][]string{}, absoluteFileLocation),
|
RequiredRefProperties: extractDefinitionRequiredRefProperties(parsedDocument, map[string][]string{}, absoluteFileLocation, index),
|
||||||
}
|
}
|
||||||
return foundRef
|
return foundRef
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -87,9 +87,30 @@ func (resolver *Resolver) GetResolvingErrors() []*ResolvingError {
|
|||||||
return resolver.resolvingErrors
|
return resolver.resolvingErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCircularErrors returns all circular reference errors found.
|
func (resolver *Resolver) GetCircularReferences() []*CircularReferenceResult {
|
||||||
func (resolver *Resolver) GetCircularErrors() []*CircularReferenceResult {
|
return resolver.GetSafeCircularReferences()
|
||||||
return resolver.circularReferences
|
}
|
||||||
|
|
||||||
|
// GetSafeCircularReferences returns all circular reference errors found.
|
||||||
|
func (resolver *Resolver) GetSafeCircularReferences() []*CircularReferenceResult {
|
||||||
|
var refs []*CircularReferenceResult
|
||||||
|
for _, ref := range resolver.circularReferences {
|
||||||
|
if !ref.IsInfiniteLoop {
|
||||||
|
refs = append(refs, ref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return refs
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInfiniteCircularReferences returns all circular reference errors found that are infinite / unrecoverable
|
||||||
|
func (resolver *Resolver) GetInfiniteCircularReferences() []*CircularReferenceResult {
|
||||||
|
var refs []*CircularReferenceResult
|
||||||
|
for _, ref := range resolver.circularReferences {
|
||||||
|
if ref.IsInfiniteLoop {
|
||||||
|
refs = append(refs, ref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return refs
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPolymorphicCircularErrors returns all circular errors that stem from polymorphism
|
// GetPolymorphicCircularErrors returns all circular errors that stem from polymorphism
|
||||||
@@ -339,9 +360,17 @@ func (resolver *Resolver) isInfiniteCircularDependency(ref *Reference, visitedDe
|
|||||||
|
|
||||||
for refDefinition := range ref.RequiredRefProperties {
|
for refDefinition := range ref.RequiredRefProperties {
|
||||||
r, _ := resolver.specIndex.SearchIndexForReference(refDefinition)
|
r, _ := resolver.specIndex.SearchIndexForReference(refDefinition)
|
||||||
if initialRef != nil && initialRef.Definition == r.Definition {
|
if initialRef != nil && initialRef.FullDefinition == r.FullDefinition {
|
||||||
return true, visitedDefinitions
|
return true, visitedDefinitions
|
||||||
}
|
}
|
||||||
|
if len(visitedDefinitions) > 0 && ref.FullDefinition == r.FullDefinition {
|
||||||
|
return true, visitedDefinitions
|
||||||
|
}
|
||||||
|
|
||||||
|
if visitedDefinitions[r.FullDefinition] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
visitedDefinitions[r.Definition] = true
|
visitedDefinitions[r.Definition] = true
|
||||||
|
|
||||||
ir := initialRef
|
ir := initialRef
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ func TestResolver_CheckForCircularReferences(t *testing.T) {
|
|||||||
|
|
||||||
assert.Len(t, rolo.GetCaughtErrors(), 3)
|
assert.Len(t, rolo.GetCaughtErrors(), 3)
|
||||||
assert.Len(t, rolo.GetRootIndex().GetResolver().GetResolvingErrors(), 3)
|
assert.Len(t, rolo.GetRootIndex().GetResolver().GetResolvingErrors(), 3)
|
||||||
assert.Len(t, rolo.GetRootIndex().GetResolver().GetCircularErrors(), 3)
|
assert.Len(t, rolo.GetRootIndex().GetResolver().GetInfiniteCircularReferences(), 3)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,8 +107,8 @@ components:
|
|||||||
circ := resolver.CheckForCircularReferences()
|
circ := resolver.CheckForCircularReferences()
|
||||||
assert.Len(t, circ, 1)
|
assert.Len(t, circ, 1)
|
||||||
assert.Len(t, resolver.GetResolvingErrors(), 1) // infinite loop is a resolving error.
|
assert.Len(t, resolver.GetResolvingErrors(), 1) // infinite loop is a resolving error.
|
||||||
assert.Len(t, resolver.GetCircularErrors(), 1)
|
assert.Len(t, resolver.GetInfiniteCircularReferences(), 1)
|
||||||
assert.True(t, resolver.GetCircularErrors()[0].IsArrayResult)
|
assert.True(t, resolver.GetInfiniteCircularReferences()[0].IsArrayResult)
|
||||||
|
|
||||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -144,7 +144,7 @@ components:
|
|||||||
circ := resolver.CheckForCircularReferences()
|
circ := resolver.CheckForCircularReferences()
|
||||||
assert.Len(t, circ, 0)
|
assert.Len(t, circ, 0)
|
||||||
assert.Len(t, resolver.GetResolvingErrors(), 0)
|
assert.Len(t, resolver.GetResolvingErrors(), 0)
|
||||||
assert.Len(t, resolver.GetCircularErrors(), 0)
|
assert.Len(t, resolver.GetCircularReferences(), 0)
|
||||||
|
|
||||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -180,7 +180,7 @@ components:
|
|||||||
circ := resolver.CheckForCircularReferences()
|
circ := resolver.CheckForCircularReferences()
|
||||||
assert.Len(t, circ, 0)
|
assert.Len(t, circ, 0)
|
||||||
assert.Len(t, resolver.GetResolvingErrors(), 0)
|
assert.Len(t, resolver.GetResolvingErrors(), 0)
|
||||||
assert.Len(t, resolver.GetCircularErrors(), 0)
|
assert.Len(t, resolver.GetCircularReferences(), 0)
|
||||||
|
|
||||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -216,7 +216,7 @@ components:
|
|||||||
circ := resolver.CheckForCircularReferences()
|
circ := resolver.CheckForCircularReferences()
|
||||||
assert.Len(t, circ, 0)
|
assert.Len(t, circ, 0)
|
||||||
assert.Len(t, resolver.GetResolvingErrors(), 0)
|
assert.Len(t, resolver.GetResolvingErrors(), 0)
|
||||||
assert.Len(t, resolver.GetCircularErrors(), 0)
|
assert.Len(t, resolver.GetCircularReferences(), 0)
|
||||||
|
|
||||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -252,7 +252,7 @@ components:
|
|||||||
circ := resolver.CheckForCircularReferences()
|
circ := resolver.CheckForCircularReferences()
|
||||||
assert.Len(t, circ, 0)
|
assert.Len(t, circ, 0)
|
||||||
assert.Len(t, resolver.GetResolvingErrors(), 0)
|
assert.Len(t, resolver.GetResolvingErrors(), 0)
|
||||||
assert.Len(t, resolver.GetCircularErrors(), 0)
|
assert.Len(t, resolver.GetCircularReferences(), 0)
|
||||||
|
|
||||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -286,8 +286,8 @@ components:
|
|||||||
circ := resolver.CheckForCircularReferences()
|
circ := resolver.CheckForCircularReferences()
|
||||||
assert.Len(t, circ, 0)
|
assert.Len(t, circ, 0)
|
||||||
assert.Len(t, resolver.GetResolvingErrors(), 0) // not an infinite loop if poly.
|
assert.Len(t, resolver.GetResolvingErrors(), 0) // not an infinite loop if poly.
|
||||||
assert.Len(t, resolver.GetCircularErrors(), 1)
|
assert.Len(t, resolver.GetCircularReferences(), 1)
|
||||||
assert.Equal(t, "anyOf", resolver.GetCircularErrors()[0].PolymorphicType)
|
assert.Equal(t, "anyOf", resolver.GetCircularReferences()[0].PolymorphicType)
|
||||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
@@ -320,9 +320,9 @@ components:
|
|||||||
circ := resolver.CheckForCircularReferences()
|
circ := resolver.CheckForCircularReferences()
|
||||||
assert.Len(t, circ, 0)
|
assert.Len(t, circ, 0)
|
||||||
assert.Len(t, resolver.GetResolvingErrors(), 0) // not an infinite loop if poly.
|
assert.Len(t, resolver.GetResolvingErrors(), 0) // not an infinite loop if poly.
|
||||||
assert.Len(t, resolver.GetCircularErrors(), 1)
|
assert.Len(t, resolver.GetCircularReferences(), 1)
|
||||||
assert.Equal(t, "allOf", resolver.GetCircularErrors()[0].PolymorphicType)
|
assert.Equal(t, "allOf", resolver.GetCircularReferences()[0].PolymorphicType)
|
||||||
assert.True(t, resolver.GetCircularErrors()[0].IsPolymorphicResult)
|
assert.True(t, resolver.GetCircularReferences()[0].IsPolymorphicResult)
|
||||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
@@ -595,7 +595,7 @@ func TestResolver_ResolveComponents_MixedRef(t *testing.T) {
|
|||||||
index := rolo.GetRootIndex
|
index := rolo.GetRootIndex
|
||||||
resolver := index().GetResolver()
|
resolver := index().GetResolver()
|
||||||
|
|
||||||
assert.Len(t, resolver.GetCircularErrors(), 0)
|
assert.Len(t, resolver.GetCircularReferences(), 0)
|
||||||
assert.Equal(t, 2, resolver.GetIndexesVisited())
|
assert.Equal(t, 2, resolver.GetIndexesVisited())
|
||||||
|
|
||||||
// in v0.8.2 a new check was added when indexing, to prevent re-indexing the same file multiple times.
|
// in v0.8.2 a new check was added when indexing, to prevent re-indexing the same file multiple times.
|
||||||
@@ -778,3 +778,81 @@ func TestResolver_isInfiniteCircularDep_NoRef(t *testing.T) {
|
|||||||
assert.False(t, a)
|
assert.False(t, a)
|
||||||
assert.Nil(t, b)
|
assert.Nil(t, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResolver_AllowedCircle(t *testing.T) {
|
||||||
|
|
||||||
|
d := `openapi: 3.1.0
|
||||||
|
paths:
|
||||||
|
/test:
|
||||||
|
get:
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: OK
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
Obj:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
other:
|
||||||
|
$ref: '#/components/schemas/Obj2'
|
||||||
|
Obj2:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
other:
|
||||||
|
$ref: '#/components/schemas/Obj'
|
||||||
|
required:
|
||||||
|
- other`
|
||||||
|
|
||||||
|
var rootNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
||||||
|
|
||||||
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
||||||
|
|
||||||
|
resolver := NewResolver(idx)
|
||||||
|
assert.NotNil(t, resolver)
|
||||||
|
|
||||||
|
circ := resolver.Resolve()
|
||||||
|
assert.Len(t, circ, 0)
|
||||||
|
assert.Len(t, resolver.GetInfiniteCircularReferences(), 0)
|
||||||
|
assert.Len(t, resolver.GetSafeCircularReferences(), 1)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolver_NotAllowedDeepCircle(t *testing.T) {
|
||||||
|
|
||||||
|
d := `openapi: 3.0
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
Three:
|
||||||
|
description: "test three"
|
||||||
|
properties:
|
||||||
|
bester:
|
||||||
|
"$ref": "#/components/schemas/Seven"
|
||||||
|
required:
|
||||||
|
- bester
|
||||||
|
Seven:
|
||||||
|
properties:
|
||||||
|
wow:
|
||||||
|
"$ref": "#/components/schemas/Three"
|
||||||
|
required:
|
||||||
|
- wow`
|
||||||
|
|
||||||
|
var rootNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
||||||
|
|
||||||
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
||||||
|
|
||||||
|
resolver := NewResolver(idx)
|
||||||
|
assert.NotNil(t, resolver)
|
||||||
|
|
||||||
|
circ := resolver.Resolve()
|
||||||
|
assert.Len(t, circ, 1)
|
||||||
|
assert.Len(t, resolver.GetInfiniteCircularReferences(), 1)
|
||||||
|
assert.Len(t, resolver.GetSafeCircularReferences(), 0)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|||||||
@@ -49,20 +49,22 @@ type RolodexFS interface {
|
|||||||
// and the ability to resolve references across those file systems. It is used to hold references to external
|
// and the ability to resolve references across those file systems. It is used to hold references to external
|
||||||
// files, and the indexes they hold. The rolodex is the master lookup for all references.
|
// files, and the indexes they hold. The rolodex is the master lookup for all references.
|
||||||
type Rolodex struct {
|
type Rolodex struct {
|
||||||
localFS map[string]fs.FS
|
localFS map[string]fs.FS
|
||||||
remoteFS map[string]fs.FS
|
remoteFS map[string]fs.FS
|
||||||
indexed bool
|
indexed bool
|
||||||
built bool
|
built bool
|
||||||
manualBuilt bool
|
manualBuilt bool
|
||||||
resolved bool
|
resolved bool
|
||||||
circChecked bool
|
circChecked bool
|
||||||
indexConfig *SpecIndexConfig
|
indexConfig *SpecIndexConfig
|
||||||
indexingDuration time.Duration
|
indexingDuration time.Duration
|
||||||
indexes []*SpecIndex
|
indexes []*SpecIndex
|
||||||
rootIndex *SpecIndex
|
rootIndex *SpecIndex
|
||||||
rootNode *yaml.Node
|
rootNode *yaml.Node
|
||||||
caughtErrors []error
|
caughtErrors []error
|
||||||
ignoredCircularReferences []*CircularReferenceResult
|
safeCircularReferences []*CircularReferenceResult
|
||||||
|
infiniteCircularReferences []*CircularReferenceResult
|
||||||
|
ignoredCircularReferences []*CircularReferenceResult
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRolodex creates a new rolodex with the provided index configuration.
|
// NewRolodex creates a new rolodex with the provided index configuration.
|
||||||
@@ -316,6 +318,8 @@ func (r *Rolodex) CheckForCircularReferences() {
|
|||||||
if len(r.rootIndex.resolver.ignoredArrayReferences) > 0 {
|
if len(r.rootIndex.resolver.ignoredArrayReferences) > 0 {
|
||||||
r.ignoredCircularReferences = append(r.ignoredCircularReferences, r.rootIndex.resolver.ignoredArrayReferences...)
|
r.ignoredCircularReferences = append(r.ignoredCircularReferences, r.rootIndex.resolver.ignoredArrayReferences...)
|
||||||
}
|
}
|
||||||
|
r.safeCircularReferences = append(r.safeCircularReferences, r.rootIndex.resolver.GetSafeCircularReferences()...)
|
||||||
|
r.infiniteCircularReferences = append(r.infiniteCircularReferences, r.rootIndex.resolver.GetInfiniteCircularReferences()...)
|
||||||
}
|
}
|
||||||
r.circChecked = true
|
r.circChecked = true
|
||||||
}
|
}
|
||||||
@@ -334,6 +338,8 @@ func (r *Rolodex) Resolve() {
|
|||||||
if len(r.rootIndex.resolver.ignoredArrayReferences) > 0 {
|
if len(r.rootIndex.resolver.ignoredArrayReferences) > 0 {
|
||||||
r.ignoredCircularReferences = append(r.ignoredCircularReferences, r.rootIndex.resolver.ignoredArrayReferences...)
|
r.ignoredCircularReferences = append(r.ignoredCircularReferences, r.rootIndex.resolver.ignoredArrayReferences...)
|
||||||
}
|
}
|
||||||
|
r.safeCircularReferences = append(r.safeCircularReferences, r.rootIndex.resolver.GetSafeCircularReferences()...)
|
||||||
|
r.infiniteCircularReferences = append(r.infiniteCircularReferences, r.rootIndex.resolver.GetInfiniteCircularReferences()...)
|
||||||
}
|
}
|
||||||
r.resolved = true
|
r.resolved = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -352,7 +352,6 @@ components:
|
|||||||
rolodex.AddLocalFS(baseDir, fileFS)
|
rolodex.AddLocalFS(baseDir, fileFS)
|
||||||
err = rolodex.IndexTheRolodex()
|
err = rolodex.IndexTheRolodex()
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Equal(t, "infinite circular reference detected: CircleTest: CircleTest -> -> CircleTest [5:7]", err.Error())
|
|
||||||
assert.Len(t, rolodex.GetCaughtErrors(), 1)
|
assert.Len(t, rolodex.GetCaughtErrors(), 1)
|
||||||
assert.Len(t, rolodex.GetIgnoredCircularReferences(), 0)
|
assert.Len(t, rolodex.GetIgnoredCircularReferences(), 0)
|
||||||
}
|
}
|
||||||
@@ -1417,7 +1416,9 @@ components:
|
|||||||
rolo.CheckForCircularReferences()
|
rolo.CheckForCircularReferences()
|
||||||
assert.Len(t, rolo.GetIgnoredCircularReferences(), 0)
|
assert.Len(t, rolo.GetIgnoredCircularReferences(), 0)
|
||||||
assert.Len(t, rolo.GetCaughtErrors(), 1)
|
assert.Len(t, rolo.GetCaughtErrors(), 1)
|
||||||
assert.Len(t, rolo.GetRootIndex().GetResolver().GetCircularErrors(), 1)
|
assert.Len(t, rolo.GetRootIndex().GetResolver().GetInfiniteCircularReferences(), 1)
|
||||||
|
assert.Len(t, rolo.GetRootIndex().GetResolver().GetSafeCircularReferences(), 0)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRolodex_CircularReferencesArrayIgnored(t *testing.T) {
|
func TestRolodex_CircularReferencesArrayIgnored(t *testing.T) {
|
||||||
|
|||||||
@@ -32,14 +32,14 @@ func (index *SpecIndex) extractDefinitionsAndSchemas(schemasNode *yaml.Node, pat
|
|||||||
Node: schema,
|
Node: schema,
|
||||||
Path: fmt.Sprintf("$.components.schemas.%s", name),
|
Path: fmt.Sprintf("$.components.schemas.%s", name),
|
||||||
ParentNode: schemasNode,
|
ParentNode: schemasNode,
|
||||||
RequiredRefProperties: extractDefinitionRequiredRefProperties(schemasNode, map[string][]string{}, fullDef),
|
RequiredRefProperties: extractDefinitionRequiredRefProperties(schemasNode, map[string][]string{}, fullDef, index),
|
||||||
}
|
}
|
||||||
index.allComponentSchemaDefinitions[def] = ref
|
index.allComponentSchemaDefinitions[def] = ref
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractDefinitionRequiredRefProperties goes through the direct properties of a schema and extracts the map of required definitions from within it
|
// extractDefinitionRequiredRefProperties goes through the direct properties of a schema and extracts the map of required definitions from within it
|
||||||
func extractDefinitionRequiredRefProperties(schemaNode *yaml.Node, reqRefProps map[string][]string, fulldef string) map[string][]string {
|
func extractDefinitionRequiredRefProperties(schemaNode *yaml.Node, reqRefProps map[string][]string, fulldef string, idx *SpecIndex) map[string][]string {
|
||||||
if schemaNode == nil {
|
if schemaNode == nil {
|
||||||
return reqRefProps
|
return reqRefProps
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ func extractDefinitionRequiredRefProperties(schemaNode *yaml.Node, reqRefProps m
|
|||||||
// Check to see if the current property is directly embedded within the current schema, and handle its properties if so
|
// Check to see if the current property is directly embedded within the current schema, and handle its properties if so
|
||||||
_, paramPropertiesMapNode := utils.FindKeyNodeTop("properties", param.Content)
|
_, paramPropertiesMapNode := utils.FindKeyNodeTop("properties", param.Content)
|
||||||
if paramPropertiesMapNode != nil {
|
if paramPropertiesMapNode != nil {
|
||||||
reqRefProps = extractDefinitionRequiredRefProperties(param, reqRefProps, fulldef)
|
reqRefProps = extractDefinitionRequiredRefProperties(param, reqRefProps, fulldef, idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check to see if the current property is polymorphic, and dive into that model if so
|
// Check to see if the current property is polymorphic, and dive into that model if so
|
||||||
@@ -82,7 +82,7 @@ func extractDefinitionRequiredRefProperties(schemaNode *yaml.Node, reqRefProps m
|
|||||||
_, ofNode := utils.FindKeyNodeTop(key, param.Content)
|
_, ofNode := utils.FindKeyNodeTop(key, param.Content)
|
||||||
if ofNode != nil {
|
if ofNode != nil {
|
||||||
for _, ofNodeItem := range ofNode.Content {
|
for _, ofNodeItem := range ofNode.Content {
|
||||||
reqRefProps = extractRequiredReferenceProperties(fulldef, ofNodeItem, name, reqRefProps)
|
reqRefProps = extractRequiredReferenceProperties(fulldef, idx, ofNodeItem, name, reqRefProps)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -95,19 +95,19 @@ func extractDefinitionRequiredRefProperties(schemaNode *yaml.Node, reqRefProps m
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
reqRefProps = extractRequiredReferenceProperties(fulldef, requiredPropDefNode, requiredPropertyNode.Value, reqRefProps)
|
reqRefProps = extractRequiredReferenceProperties(fulldef, idx, requiredPropDefNode, requiredPropertyNode.Value, reqRefProps)
|
||||||
}
|
}
|
||||||
|
|
||||||
return reqRefProps
|
return reqRefProps
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractRequiredReferenceProperties returns a map of definition names to the property or properties which reference it within a node
|
// extractRequiredReferenceProperties returns a map of definition names to the property or properties which reference it within a node
|
||||||
func extractRequiredReferenceProperties(fulldef string, requiredPropDefNode *yaml.Node, propName string, reqRefProps map[string][]string) map[string][]string {
|
func extractRequiredReferenceProperties(fulldef string, idx *SpecIndex, requiredPropDefNode *yaml.Node, propName string, reqRefProps map[string][]string) map[string][]string {
|
||||||
isRef, _, _ := utils.IsNodeRefValue(requiredPropDefNode)
|
isRef, _, refName := utils.IsNodeRefValue(requiredPropDefNode)
|
||||||
if !isRef {
|
if !isRef {
|
||||||
_, defItems := utils.FindKeyNodeTop("items", requiredPropDefNode.Content)
|
_, defItems := utils.FindKeyNodeTop("items", requiredPropDefNode.Content)
|
||||||
if defItems != nil {
|
if defItems != nil {
|
||||||
isRef, _, _ = utils.IsNodeRefValue(defItems)
|
isRef, _, refName = utils.IsNodeRefValue(defItems)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,28 +117,65 @@ func extractRequiredReferenceProperties(fulldef string, requiredPropDefNode *yam
|
|||||||
|
|
||||||
defPath := fulldef
|
defPath := fulldef
|
||||||
|
|
||||||
// explode defpath
|
if strings.HasPrefix(refName, "http") || filepath.IsAbs(refName) {
|
||||||
exp := strings.Split(fulldef, "#/")
|
defPath = refName
|
||||||
if len(exp) == 2 {
|
|
||||||
if exp[0] != "" {
|
|
||||||
if !strings.HasPrefix(exp[0], "http") {
|
|
||||||
|
|
||||||
if !filepath.IsAbs(exp[0]) {
|
|
||||||
abs, _ := filepath.Abs(filepath.Join(filepath.Dir(fulldef), exp[0]))
|
|
||||||
defPath = fmt.Sprintf("%s#/%s", abs, exp[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if strings.HasPrefix(exp[0], "http") {
|
exp := strings.Split(fulldef, "#/")
|
||||||
defPath = exp[0]
|
if len(exp) == 2 {
|
||||||
} else {
|
if exp[0] != "" {
|
||||||
|
if strings.HasPrefix(exp[0], "http") {
|
||||||
|
u, _ := url.Parse(exp[0])
|
||||||
|
r := strings.Split(refName, "#/")
|
||||||
|
if len(r) == 2 {
|
||||||
|
var abs string
|
||||||
|
if r[0] == "" {
|
||||||
|
abs = u.Path
|
||||||
|
} else {
|
||||||
|
abs, _ = filepath.Abs(filepath.Join(filepath.Dir(u.Path), r[0]))
|
||||||
|
}
|
||||||
|
|
||||||
if filepath.IsAbs(exp[0]) {
|
u.Path = abs
|
||||||
defPath = exp[0]
|
u.Fragment = ""
|
||||||
|
defPath = fmt.Sprintf("%s#/%s", u.String(), r[1])
|
||||||
|
} else {
|
||||||
|
u.Path = filepath.Join(filepath.Dir(u.Path), r[0])
|
||||||
|
u.Fragment = ""
|
||||||
|
defPath = u.String()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
r := strings.Split(refName, "#/")
|
||||||
|
if len(r) == 2 {
|
||||||
|
var abs string
|
||||||
|
if r[0] == "" {
|
||||||
|
abs, _ = filepath.Abs(exp[0])
|
||||||
|
} else {
|
||||||
|
abs, _ = filepath.Abs(filepath.Join(filepath.Dir(exp[0]), r[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
defPath = fmt.Sprintf("%s#/%s", abs, r[1])
|
||||||
|
} else {
|
||||||
|
defPath, _ = filepath.Abs(filepath.Join(filepath.Dir(exp[0]), r[0]))
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
defPath, _ = filepath.Abs(filepath.Join(filepath.Dir(fulldef), exp[0]))
|
defPath = refName
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if strings.HasPrefix(exp[0], "http") {
|
||||||
|
u, _ := url.Parse(exp[0])
|
||||||
|
r := strings.Split(refName, "#/")
|
||||||
|
if len(r) == 2 {
|
||||||
|
abs, _ := filepath.Abs(filepath.Join(filepath.Dir(u.Path), r[0]))
|
||||||
|
u.Path = abs
|
||||||
|
u.Fragment = ""
|
||||||
|
defPath = fmt.Sprintf("%s#/%s", u.String(), r[1])
|
||||||
|
} else {
|
||||||
|
u.Path = filepath.Join(filepath.Dir(u.Path), r[0])
|
||||||
|
u.Fragment = ""
|
||||||
|
defPath = u.String()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
defPath, _ = filepath.Abs(filepath.Join(filepath.Dir(exp[0]), refName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ func Test_extractRequiredReferenceProperties(t *testing.T) {
|
|||||||
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
||||||
props := make(map[string][]string)
|
props := make(map[string][]string)
|
||||||
|
|
||||||
data := extractRequiredReferenceProperties("the-big.yaml#/cheese/thing",
|
data := extractRequiredReferenceProperties("the-big.yaml#/cheese/thing", nil,
|
||||||
rootNode.Content[0], "cakes", props)
|
rootNode.Content[0], "cakes", props)
|
||||||
assert.Len(t, props, 1)
|
assert.Len(t, props, 1)
|
||||||
assert.NotNil(t, data)
|
assert.NotNil(t, data)
|
||||||
@@ -71,7 +71,7 @@ func Test_extractRequiredReferenceProperties_singleFile(t *testing.T) {
|
|||||||
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
||||||
props := make(map[string][]string)
|
props := make(map[string][]string)
|
||||||
|
|
||||||
data := extractRequiredReferenceProperties("dingo-bingo-bango.yaml",
|
data := extractRequiredReferenceProperties("dingo-bingo-bango.yaml", nil,
|
||||||
rootNode.Content[0], "cakes", props)
|
rootNode.Content[0], "cakes", props)
|
||||||
assert.Len(t, props, 1)
|
assert.Len(t, props, 1)
|
||||||
assert.NotNil(t, data)
|
assert.NotNil(t, data)
|
||||||
@@ -85,7 +85,7 @@ func Test_extractRequiredReferenceProperties_http(t *testing.T) {
|
|||||||
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
||||||
props := make(map[string][]string)
|
props := make(map[string][]string)
|
||||||
|
|
||||||
data := extractRequiredReferenceProperties("http://dingo-bingo-bango.yaml/camel.yaml",
|
data := extractRequiredReferenceProperties("http://dingo-bingo-bango.yaml/camel.yaml", nil,
|
||||||
rootNode.Content[0], "cakes", props)
|
rootNode.Content[0], "cakes", props)
|
||||||
assert.Len(t, props, 1)
|
assert.Len(t, props, 1)
|
||||||
assert.NotNil(t, data)
|
assert.NotNil(t, data)
|
||||||
@@ -99,12 +99,163 @@ func Test_extractRequiredReferenceProperties_abs(t *testing.T) {
|
|||||||
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
||||||
props := make(map[string][]string)
|
props := make(map[string][]string)
|
||||||
|
|
||||||
data := extractRequiredReferenceProperties("/camel.yaml",
|
data := extractRequiredReferenceProperties("/camel.yaml", nil,
|
||||||
rootNode.Content[0], "cakes", props)
|
rootNode.Content[0], "cakes", props)
|
||||||
assert.Len(t, props, 1)
|
assert.Len(t, props, 1)
|
||||||
assert.NotNil(t, data)
|
assert.NotNil(t, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_extractDefinitionRequiredRefProperties_nil(t *testing.T) {
|
func Test_extractRequiredReferenceProperties_abs3(t *testing.T) {
|
||||||
assert.Nil(t, extractDefinitionRequiredRefProperties(nil, nil, ""))
|
|
||||||
|
d := `$ref: oh/pillow.yaml`
|
||||||
|
|
||||||
|
var rootNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
||||||
|
props := make(map[string][]string)
|
||||||
|
|
||||||
|
data := extractRequiredReferenceProperties("/big/fat/camel.yaml#/milk", nil,
|
||||||
|
rootNode.Content[0], "cakes", props)
|
||||||
|
assert.Len(t, props, 1)
|
||||||
|
assert.Equal(t, "cakes", props["/big/fat/oh/pillow.yaml"][0])
|
||||||
|
assert.NotNil(t, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_extractRequiredReferenceProperties_rel_full(t *testing.T) {
|
||||||
|
|
||||||
|
d := `$ref: "#/a/nice/picture/of/cake"`
|
||||||
|
|
||||||
|
var rootNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
||||||
|
props := make(map[string][]string)
|
||||||
|
|
||||||
|
data := extractRequiredReferenceProperties("/chalky/milky/camel.yaml#/milk", nil,
|
||||||
|
rootNode.Content[0], "cakes", props)
|
||||||
|
assert.Len(t, props, 1)
|
||||||
|
assert.Equal(t, "cakes", props["/chalky/milky/camel.yaml#/a/nice/picture/of/cake"][0])
|
||||||
|
assert.NotNil(t, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_extractRequiredReferenceProperties_rel(t *testing.T) {
|
||||||
|
|
||||||
|
d := `$ref: oh/camel.yaml#/rum/cake`
|
||||||
|
|
||||||
|
var rootNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
||||||
|
props := make(map[string][]string)
|
||||||
|
|
||||||
|
data := extractRequiredReferenceProperties("/camel.yaml#/milk", nil,
|
||||||
|
rootNode.Content[0], "cakes", props)
|
||||||
|
assert.Len(t, props, 1)
|
||||||
|
assert.Equal(t, "cakes", props["/oh/camel.yaml#/rum/cake"][0])
|
||||||
|
assert.NotNil(t, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_extractRequiredReferenceProperties_abs2(t *testing.T) {
|
||||||
|
|
||||||
|
d := `$ref: /oh/my/camel.yaml#/rum/cake`
|
||||||
|
|
||||||
|
var rootNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
||||||
|
props := make(map[string][]string)
|
||||||
|
|
||||||
|
data := extractRequiredReferenceProperties("../flannel.yaml#/milk", nil,
|
||||||
|
rootNode.Content[0], "cakes", props)
|
||||||
|
assert.Len(t, props, 1)
|
||||||
|
assert.Equal(t, "cakes", props["/oh/my/camel.yaml#/rum/cake"][0])
|
||||||
|
assert.NotNil(t, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_extractRequiredReferenceProperties_http_rel(t *testing.T) {
|
||||||
|
|
||||||
|
d := `$ref: my/wet/camel.yaml#/rum/cake`
|
||||||
|
|
||||||
|
var rootNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
||||||
|
props := make(map[string][]string)
|
||||||
|
|
||||||
|
data := extractRequiredReferenceProperties("http://beer-world.com/lost/in/space.yaml#/vase", nil,
|
||||||
|
rootNode.Content[0], "cakes", props)
|
||||||
|
assert.Len(t, props, 1)
|
||||||
|
assert.Equal(t, "cakes", props["http://beer-world.com/lost/in/my/wet/camel.yaml#/rum/cake"][0])
|
||||||
|
assert.NotNil(t, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_extractRequiredReferenceProperties_http_rel_nocomponent(t *testing.T) {
|
||||||
|
|
||||||
|
d := `$ref: my/wet/camel.yaml`
|
||||||
|
|
||||||
|
var rootNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
||||||
|
props := make(map[string][]string)
|
||||||
|
|
||||||
|
data := extractRequiredReferenceProperties("http://beer-world.com/lost/in/space.yaml#/vase", nil,
|
||||||
|
rootNode.Content[0], "cakes", props)
|
||||||
|
assert.Len(t, props, 1)
|
||||||
|
assert.Equal(t, "cakes", props["http://beer-world.com/lost/in/my/wet/camel.yaml"][0])
|
||||||
|
assert.NotNil(t, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_extractRequiredReferenceProperties_nocomponent(t *testing.T) {
|
||||||
|
|
||||||
|
d := `$ref: my/wet/camel.yaml`
|
||||||
|
|
||||||
|
var rootNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
||||||
|
props := make(map[string][]string)
|
||||||
|
|
||||||
|
data := extractRequiredReferenceProperties("#/rotund/cakes", nil,
|
||||||
|
rootNode.Content[0], "cakes", props)
|
||||||
|
assert.Len(t, props, 1)
|
||||||
|
assert.Equal(t, "cakes", props["my/wet/camel.yaml"][0])
|
||||||
|
assert.NotNil(t, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_extractRequiredReferenceProperties_component_http(t *testing.T) {
|
||||||
|
|
||||||
|
d := `$ref: go-to-bed.com/no/more/cake.yaml#/lovely/jam`
|
||||||
|
|
||||||
|
var rootNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
||||||
|
props := make(map[string][]string)
|
||||||
|
|
||||||
|
data := extractRequiredReferenceProperties("http://bunny-bun-bun.com/no.yaml", nil,
|
||||||
|
rootNode.Content[0], "cakes", props)
|
||||||
|
assert.Len(t, props, 1)
|
||||||
|
assert.Equal(t, "cakes", props["http://bunny-bun-bun.com/go-to-bed.com/no/more/cake.yaml#/lovely/jam"][0])
|
||||||
|
assert.NotNil(t, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_extractRequiredReferenceProperties_nocomponent_http(t *testing.T) {
|
||||||
|
|
||||||
|
d := `$ref: go-to-bed.com/no/more/cake.yaml`
|
||||||
|
|
||||||
|
var rootNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
||||||
|
props := make(map[string][]string)
|
||||||
|
|
||||||
|
data := extractRequiredReferenceProperties("http://bunny-bun-bun.com/no.yaml", nil,
|
||||||
|
rootNode.Content[0], "cakes", props)
|
||||||
|
assert.Len(t, props, 1)
|
||||||
|
assert.Equal(t, "cakes", props["http://bunny-bun-bun.com/go-to-bed.com/no/more/cake.yaml"][0])
|
||||||
|
assert.NotNil(t, data)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_extractRequiredReferenceProperties_nocomponent_http2(t *testing.T) {
|
||||||
|
|
||||||
|
d := `$ref: go-to-bed.com/no/more/cake.yaml`
|
||||||
|
|
||||||
|
var rootNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
||||||
|
props := make(map[string][]string)
|
||||||
|
|
||||||
|
data := extractRequiredReferenceProperties("/why.yaml", nil,
|
||||||
|
rootNode.Content[0], "cakes", props)
|
||||||
|
assert.Len(t, props, 1)
|
||||||
|
assert.Equal(t, "cakes", props["/go-to-bed.com/no/more/cake.yaml"][0])
|
||||||
|
assert.NotNil(t, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_extractDefinitionRequiredRefProperties_nil(t *testing.T) {
|
||||||
|
assert.Nil(t, extractDefinitionRequiredRefProperties(nil, nil, "", nil))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user