(enhancement): Improved resolving/lookup code for #45

This commit is contained in:
Dave Shanley
2022-12-15 15:11:14 -05:00
parent 48467b60d7
commit 32f48385f2
8 changed files with 94 additions and 52 deletions

View File

@@ -362,7 +362,7 @@ func TestStripeAsDoc(t *testing.T) {
info, _ := datamodel.ExtractSpecInfo(data) info, _ := datamodel.ExtractSpecInfo(data)
var err []error var err []error
lowDoc, err = lowv3.CreateDocument(info) lowDoc, err = lowv3.CreateDocument(info)
assert.Len(t, err, 21) assert.Len(t, err, 23)
d := NewDocument(lowDoc) d := NewDocument(lowDoc)
fmt.Println(d) fmt.Println(d)
} }

View File

@@ -94,7 +94,7 @@ func TestCreateDocumentStripe(t *testing.T) {
data, _ := ioutil.ReadFile("../../../test_specs/stripe.yaml") data, _ := ioutil.ReadFile("../../../test_specs/stripe.yaml")
info, _ := datamodel.ExtractSpecInfo(data) info, _ := datamodel.ExtractSpecInfo(data)
d, err := CreateDocument(info) d, err := CreateDocument(info)
assert.Len(t, err, 21) assert.Len(t, err, 23)
assert.Equal(t, "3.0.0", d.Version.Value) assert.Equal(t, "3.0.0", d.Version.Value)
assert.Equal(t, "Stripe API", d.Info.Value.Title.Value) assert.Equal(t, "Stripe API", d.Info.Value.Title.Value)

View File

@@ -243,6 +243,16 @@ func NewSpecIndex(rootNode *yaml.Node) *SpecIndex {
// pull out references // pull out references
index.ExtractComponentsFromRefs(results) index.ExtractComponentsFromRefs(results)
// map poly refs
poly := make([]*Reference, len(index.polymorphicRefs))
z := 0
for i := range index.polymorphicRefs {
poly[z] = index.polymorphicRefs[i]
z++
}
index.ExtractComponentsFromRefs(poly)
index.ExtractExternalDocuments(index.root) index.ExtractExternalDocuments(index.root)
index.GetPathCount() index.GetPathCount()
@@ -593,14 +603,15 @@ func (index *SpecIndex) ExtractRefs(node, parent *yaml.Node, seenPath []string,
segs := strings.Split(value, "/") segs := strings.Split(value, "/")
name := segs[len(segs)-1] name := segs[len(segs)-1]
// name := strings.ReplaceAll(segs[len(segs)-1], "~1", "/") //name := strings.ReplaceAll(segs[len(segs)-1], "~1", "/")
_, p := utils.ConvertComponentIdIntoFriendlyPathSearch(value)
ref := &Reference{ ref := &Reference{
Definition: value, Definition: value,
Name: name, Name: name,
Node: node, Node: node,
Path: fmt.Sprintf("$.%s", strings.Join(seenPath, ".")), Path: p,
} }
//utils.ConvertComponentIdIntoFriendlyPathSearch(ref.Definition)
// add to raw sequenced refs // add to raw sequenced refs
index.rawSequencedRefs = append(index.rawSequencedRefs, ref) index.rawSequencedRefs = append(index.rawSequencedRefs, ref)
@@ -623,7 +634,7 @@ func (index *SpecIndex) ExtractRefs(node, parent *yaml.Node, seenPath []string,
Definition: ref.Definition, Definition: ref.Definition,
Name: ref.Name, Name: ref.Name,
Node: &copiedNode, Node: &copiedNode,
Path: ref.Path, Path: p,
} }
// protect this data using a copy, prevent the resolver from destroying things. // protect this data using a copy, prevent the resolver from destroying things.
index.refsWithSiblings[value] = copied index.refsWithSiblings[value] = copied
@@ -1527,12 +1538,14 @@ func (index *SpecIndex) ExtractComponentsFromRefs(refs []*Reference) []*Referenc
located := index.FindComponent(ref.Definition, ref.Node) located := index.FindComponent(ref.Definition, ref.Node)
if located != nil { if located != nil {
found = append(found, located) if index.allMappedRefs[ref.Definition] == nil {
index.allMappedRefs[ref.Definition] = located found = append(found, located)
index.allMappedRefsSequenced = append(index.allMappedRefsSequenced, &ReferenceMapped{ index.allMappedRefs[ref.Definition] = located
Reference: located, index.allMappedRefsSequenced = append(index.allMappedRefsSequenced, &ReferenceMapped{
Definition: ref.Definition, Reference: located,
}) Definition: ref.Definition,
})
}
} else { } else {
_, path := utils.ConvertComponentIdIntoFriendlyPathSearch(ref.Definition) _, path := utils.ConvertComponentIdIntoFriendlyPathSearch(ref.Definition)
@@ -1816,7 +1829,6 @@ func (index *SpecIndex) performExternalLookup(uri []string, componentId string,
func (index *SpecIndex) FindComponentInRoot(componentId string) *Reference { func (index *SpecIndex) FindComponentInRoot(componentId string) *Reference {
if index.root != nil { if index.root != nil {
name, friendlySearch := utils.ConvertComponentIdIntoFriendlyPathSearch(componentId) name, friendlySearch := utils.ConvertComponentIdIntoFriendlyPathSearch(componentId)
friendlySearch = strings.ReplaceAll(friendlySearch, "~1", "/")
path, err := yamlpath.NewPath(friendlySearch) path, err := yamlpath.NewPath(friendlySearch)
if path == nil || err != nil { if path == nil || err != nil {
return nil // no component found return nil // no component found

View File

@@ -21,8 +21,7 @@ func TestSpecIndex_ExtractRefsStripe(t *testing.T) {
index := NewSpecIndex(&rootNode) index := NewSpecIndex(&rootNode)
assert.Len(t, index.allRefs, 385) assert.Len(t, index.allRefs, 385)
assert.Len(t, index.allMappedRefs, 385) assert.Equal(t, 537, len(index.allMappedRefs))
combined := index.GetAllCombinedReferences() combined := index.GetAllCombinedReferences()
assert.Equal(t, 537, len(combined)) assert.Equal(t, 537, len(combined))
@@ -68,7 +67,7 @@ func TestSpecIndex_Asana(t *testing.T) {
index := NewSpecIndex(&rootNode) index := NewSpecIndex(&rootNode)
assert.Len(t, index.allRefs, 152) assert.Len(t, index.allRefs, 152)
assert.Len(t, index.allMappedRefs, 152) assert.Len(t, index.allMappedRefs, 171)
combined := index.GetAllCombinedReferences() combined := index.GetAllCombinedReferences()
assert.Equal(t, 171, len(combined)) assert.Equal(t, 171, len(combined))
assert.Equal(t, 118, index.pathCount) assert.Equal(t, 118, index.pathCount)
@@ -90,7 +89,7 @@ func TestSpecIndex_k8s(t *testing.T) {
index := NewSpecIndex(&rootNode) index := NewSpecIndex(&rootNode)
assert.Len(t, index.allRefs, 558) assert.Len(t, index.allRefs, 558)
assert.Len(t, index.allMappedRefs, 558) assert.Equal(t, 563, len(index.allMappedRefs))
combined := index.GetAllCombinedReferences() combined := index.GetAllCombinedReferences()
assert.Equal(t, 563, len(combined)) assert.Equal(t, 563, len(combined))
assert.Equal(t, 436, index.pathCount) assert.Equal(t, 436, index.pathCount)

View File

@@ -239,6 +239,7 @@ func (resolver *Resolver) extractRelatives(node *yaml.Node,
} }
value := node.Content[i+1].Value value := node.Content[i+1].Value
ref := resolver.specIndex.GetMappedReferences()[value] ref := resolver.specIndex.GetMappedReferences()[value]
if ref == nil { if ref == nil {

View File

@@ -86,10 +86,10 @@ func TestResolver_ResolveComponents_Stripe(t *testing.T) {
assert.NotNil(t, resolver) assert.NotNil(t, resolver)
circ := resolver.Resolve() circ := resolver.Resolve()
assert.Len(t, circ, 21) assert.Len(t, circ, 23)
assert.Len(t, resolver.GetNonPolymorphicCircularErrors(), 2) assert.Len(t, resolver.GetNonPolymorphicCircularErrors(), 3)
assert.Len(t, resolver.GetPolymorphicCircularErrors(), 19) assert.Len(t, resolver.GetPolymorphicCircularErrors(), 20)
} }
@@ -202,6 +202,6 @@ func ExampleNewResolver() {
// //
fmt.Printf("There are %d circular reference errors, %d of them are polymorphic errors, %d are not", fmt.Printf("There are %d circular reference errors, %d of them are polymorphic errors, %d are not",
len(circularErrors), len(resolver.GetPolymorphicCircularErrors()), len(resolver.GetNonPolymorphicCircularErrors())) len(circularErrors), len(resolver.GetPolymorphicCircularErrors()), len(resolver.GetNonPolymorphicCircularErrors()))
// Output: There are 21 circular reference errors, 19 of them are polymorphic errors, 2 are not // Output: There are 23 circular reference errors, 20 of them are polymorphic errors, 3 are not
} }

View File

@@ -480,43 +480,53 @@ func IsHttpVerb(verb string) bool {
func ConvertComponentIdIntoFriendlyPathSearch(id string) (string, string) { func ConvertComponentIdIntoFriendlyPathSearch(id string) (string, string) {
segs := strings.Split(id, "/") segs := strings.Split(id, "/")
name := strings.ReplaceAll(segs[len(segs)-1], "~1", "/") name, _ := url.QueryUnescape(strings.ReplaceAll(segs[len(segs)-1], "~1", "/"))
var cleaned []string var cleaned []string
// check for strange spaces, chars and if found, wrap them up, clean them and create a new cleaned path. // check for strange spaces, chars and if found, wrap them up, clean them and create a new cleaned path.
for i := range segs { for i := range segs {
reg, _ := regexp.MatchString("[%=;~]", segs[i]) reg, _ := regexp.MatchString("[%=;~.]", segs[i])
if reg { if reg {
segs[i], _ = url.QueryUnescape(strings.ReplaceAll(segs[i], "~1", "/")) segs[i], _ = url.QueryUnescape(strings.ReplaceAll(segs[i], "~1", "/"))
segs[i] = fmt.Sprintf("['%s']", segs[i]) segs[i] = fmt.Sprintf("['%s']", segs[i])
h := i if len(cleaned) > 0 {
if h-1 == len(cleaned) { cleaned[len(cleaned)-1] = fmt.Sprintf("%s%s", segs[i-1], segs[i])
h-- continue
} }
cleaned[h-1] = fmt.Sprintf("%s%s", segs[i-1], segs[i]) cleaned = append(cleaned, fmt.Sprintf("%s%s", segs[i], segs[i]))
continue
} else { } else {
intVal, err := strconv.ParseInt(segs[i], 10, 32)
if err == nil && intVal <= 99 {
segs[i] = fmt.Sprintf("[%d]", intVal)
if i < len(cleaned) {
cleaned[len(cleaned)-1] = fmt.Sprintf("%s%s", segs[i-1], segs[i])
} else {
cleaned[len(cleaned)-1] = fmt.Sprintf("%s%s", cleaned[len(cleaned)-1], segs[i])
}
continue
}
if err == nil && intVal > 99 {
segs[i] = fmt.Sprintf("['%d']", intVal)
if i < len(cleaned) {
cleaned[len(cleaned)-1] = fmt.Sprintf("%s%s", segs[i-1], segs[i])
} else {
cleaned[len(cleaned)-1] = fmt.Sprintf("%s%s", cleaned[len(cleaned)-1], segs[i])
}
continue
}
cleaned = append(cleaned, segs[i]) cleaned = append(cleaned, segs[i])
} }
} }
nameIntVal, err := strconv.ParseInt(name, 10, 32) _, err := strconv.ParseInt(name, 10, 32)
var replaced string var replaced string
if err != nil { if err != nil {
if len(cleaned) > 2 { replaced = strings.ReplaceAll(fmt.Sprintf("%s",
replaced = strings.ReplaceAll(fmt.Sprintf("%s['%s']", strings.Join(cleaned, ".")), "#", "$")
strings.Join(cleaned[:len(cleaned)-1], "."), name), "#", "$")
} else {
replaced = strings.ReplaceAll(fmt.Sprintf("%s",
strings.Join(cleaned, ".")), "#", "$")
}
} else { } else {
if nameIntVal <= 99 { // codes start at 100 replaced = strings.ReplaceAll(fmt.Sprintf("%s",
replaced = strings.ReplaceAll(fmt.Sprintf("%s[%d]", strings.Join(cleaned, ".")), "#", "$")
strings.Join(cleaned[:len(cleaned)-1], "."), nameIntVal), "#", "$")
} else {
replaced = strings.ReplaceAll(fmt.Sprintf("%s.%d",
strings.Join(cleaned[:len(cleaned)-1], "."), nameIntVal), "#", "$")
}
} }
if len(replaced) > 0 { if len(replaced) > 0 {

View File

@@ -603,27 +603,47 @@ func TestIsHttpVerb(t *testing.T) {
func TestConvertComponentIdIntoFriendlyPathSearch(t *testing.T) { func TestConvertComponentIdIntoFriendlyPathSearch(t *testing.T) {
segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/chicken/chips/pizza/cake") segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/chicken/chips/pizza/cake")
assert.Equal(t, "$.chicken.chips.pizza['cake']", path) assert.Equal(t, "$.chicken.chips.pizza.cake", path)
assert.Equal(t, "cake", segment) assert.Equal(t, "cake", segment)
} }
func TestConvertComponentIdIntoFriendlyPathSearch_WithRootSymbol(t *testing.T) { func TestConvertComponentIdIntoFriendlyPathSearch_SuperCrazy(t *testing.T) {
segment, path := ConvertComponentIdIntoFriendlyPathSearch("/chicken/chips/pizza/cake") segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1crazy~1ass~1references/get/responses/404/content/application~1xml;%20charset=utf-8/schema")
assert.Equal(t, "$.chicken.chips.pizza['cake']", path) assert.Equal(t, "$.paths['/crazy/ass/references'].get.responses['404'].content['application/xml; charset=utf-8'].schema", path)
assert.Equal(t, "cake", segment)
segment, path = ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1crazy~1ass~1references/get/responses/404/content/application~1xml;%20charset=utf-8/schema")
assert.Equal(t, "$.paths['/crazy/ass/references'].get.responses.404.content['application/xml; charset=utf-8']['schema']", path)
assert.Equal(t, "schema", segment) assert.Equal(t, "schema", segment)
} }
func TestConvertComponentIdIntoFriendlyPathSearch_Crazy(t *testing.T) { func TestConvertComponentIdIntoFriendlyPathSearch_Crazy(t *testing.T) {
segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/components/schemas/gpg-key/properties/subkeys/example/0/expires_at")
assert.Equal(t, "$.components.schemas.gpg-key.properties.subkeys.example[0].expires_at", path)
assert.Equal(t, "expires_at", segment)
}
func TestConvertComponentIdIntoFriendlyPathSearch_Simple(t *testing.T) {
segment, path := ConvertComponentIdIntoFriendlyPathSearch("#//~1fresh~1pizza/get")
assert.Equal(t, "$.['/fresh/pizza'].get", path)
assert.Equal(t, "get", segment)
}
func TestConvertComponentIdIntoFriendlyPathSearch_Crazy_Github(t *testing.T) {
segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1crazy~1ass~1references/get/responses/404/content/application~1xml;%20charset=utf-8/schema") segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1crazy~1ass~1references/get/responses/404/content/application~1xml;%20charset=utf-8/schema")
assert.Equal(t, "$.paths['/crazy/ass/references'].get.responses.404.content['application/xml; charset=utf-8']['schema']", path) assert.Equal(t, "$.paths['/crazy/ass/references'].get.responses['404'].content['application/xml; charset=utf-8'].schema", path)
assert.Equal(t, "schema", segment) assert.Equal(t, "schema", segment)
} }
func TestConvertComponentIdIntoFriendlyPathSearch_Crazy_DigitalOcean(t *testing.T) {
segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1v2~1customers~1my~1invoices~1%7Binvoice_uuid%7D/get/parameters/0")
assert.Equal(t, "$.paths['/v2/customers/my/invoices/{invoice_uuid}'].get.parameters[0]", path)
assert.Equal(t, "0", segment)
}
func TestConvertComponentIdIntoFriendlyPathSearch_Crazy_DigitalOcean_More(t *testing.T) {
segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1v2~1certificates/post/responses/201/content/application~1json/examples/Custom%20Certificate")
assert.Equal(t, "$.paths['/v2/certificates'].post.responses['201'].content['application/json'].examples['Custom Certificate']", path)
assert.Equal(t, "Custom Certificate", segment)
}
func TestConvertComponentIdIntoFriendlyPathSearch_CrazyShort(t *testing.T) { func TestConvertComponentIdIntoFriendlyPathSearch_CrazyShort(t *testing.T) {
segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1crazy~1ass~1references") segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1crazy~1ass~1references")
assert.Equal(t, "$.paths['/crazy/ass/references']", path) assert.Equal(t, "$.paths['/crazy/ass/references']", path)
@@ -638,7 +658,7 @@ func TestConvertComponentIdIntoFriendlyPathSearch_Array(t *testing.T) {
func TestConvertComponentIdIntoFriendlyPathSearch_HTTPCode(t *testing.T) { func TestConvertComponentIdIntoFriendlyPathSearch_HTTPCode(t *testing.T) {
segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1crazy~1ass~1references/get/responses/404") segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1crazy~1ass~1references/get/responses/404")
assert.Equal(t, "$.paths['/crazy/ass/references'].get.responses.404", path) assert.Equal(t, "$.paths['/crazy/ass/references'].get.responses['404']", path)
assert.Equal(t, "404", segment) assert.Equal(t, "404", segment)
} }