(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)
var err []error
lowDoc, err = lowv3.CreateDocument(info)
assert.Len(t, err, 21)
assert.Len(t, err, 23)
d := NewDocument(lowDoc)
fmt.Println(d)
}

View File

@@ -94,7 +94,7 @@ func TestCreateDocumentStripe(t *testing.T) {
data, _ := ioutil.ReadFile("../../../test_specs/stripe.yaml")
info, _ := datamodel.ExtractSpecInfo(data)
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, "Stripe API", d.Info.Value.Title.Value)

View File

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

View File

@@ -21,8 +21,7 @@ func TestSpecIndex_ExtractRefsStripe(t *testing.T) {
index := NewSpecIndex(&rootNode)
assert.Len(t, index.allRefs, 385)
assert.Len(t, index.allMappedRefs, 385)
assert.Equal(t, 537, len(index.allMappedRefs))
combined := index.GetAllCombinedReferences()
assert.Equal(t, 537, len(combined))
@@ -68,7 +67,7 @@ func TestSpecIndex_Asana(t *testing.T) {
index := NewSpecIndex(&rootNode)
assert.Len(t, index.allRefs, 152)
assert.Len(t, index.allMappedRefs, 152)
assert.Len(t, index.allMappedRefs, 171)
combined := index.GetAllCombinedReferences()
assert.Equal(t, 171, len(combined))
assert.Equal(t, 118, index.pathCount)
@@ -90,7 +89,7 @@ func TestSpecIndex_k8s(t *testing.T) {
index := NewSpecIndex(&rootNode)
assert.Len(t, index.allRefs, 558)
assert.Len(t, index.allMappedRefs, 558)
assert.Equal(t, 563, len(index.allMappedRefs))
combined := index.GetAllCombinedReferences()
assert.Equal(t, 563, len(combined))
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
ref := resolver.specIndex.GetMappedReferences()[value]
if ref == nil {

View File

@@ -86,10 +86,10 @@ func TestResolver_ResolveComponents_Stripe(t *testing.T) {
assert.NotNil(t, resolver)
circ := resolver.Resolve()
assert.Len(t, circ, 21)
assert.Len(t, circ, 23)
assert.Len(t, resolver.GetNonPolymorphicCircularErrors(), 2)
assert.Len(t, resolver.GetPolymorphicCircularErrors(), 19)
assert.Len(t, resolver.GetNonPolymorphicCircularErrors(), 3)
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",
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,44 +480,54 @@ func IsHttpVerb(verb string) bool {
func ConvertComponentIdIntoFriendlyPathSearch(id string) (string, string) {
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
// check for strange spaces, chars and if found, wrap them up, clean them and create a new cleaned path.
for i := range segs {
reg, _ := regexp.MatchString("[%=;~]", segs[i])
reg, _ := regexp.MatchString("[%=;~.]", segs[i])
if reg {
segs[i], _ = url.QueryUnescape(strings.ReplaceAll(segs[i], "~1", "/"))
segs[i] = fmt.Sprintf("['%s']", segs[i])
h := i
if h-1 == len(cleaned) {
h--
if len(cleaned) > 0 {
cleaned[len(cleaned)-1] = fmt.Sprintf("%s%s", segs[i-1], segs[i])
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 {
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])
}
}
nameIntVal, err := strconv.ParseInt(name, 10, 32)
_, err := strconv.ParseInt(name, 10, 32)
var replaced string
if err != nil {
if len(cleaned) > 2 {
replaced = strings.ReplaceAll(fmt.Sprintf("%s['%s']",
strings.Join(cleaned[:len(cleaned)-1], "."), name), "#", "$")
replaced = strings.ReplaceAll(fmt.Sprintf("%s",
strings.Join(cleaned, ".")), "#", "$")
} else {
replaced = strings.ReplaceAll(fmt.Sprintf("%s",
strings.Join(cleaned, ".")), "#", "$")
}
} else {
if nameIntVal <= 99 { // codes start at 100
replaced = strings.ReplaceAll(fmt.Sprintf("%s[%d]",
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 replaced[0] != '$' {

View File

@@ -603,27 +603,47 @@ func TestIsHttpVerb(t *testing.T) {
func TestConvertComponentIdIntoFriendlyPathSearch(t *testing.T) {
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)
}
func TestConvertComponentIdIntoFriendlyPathSearch_WithRootSymbol(t *testing.T) {
segment, path := ConvertComponentIdIntoFriendlyPathSearch("/chicken/chips/pizza/cake")
assert.Equal(t, "$.chicken.chips.pizza['cake']", 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)
func TestConvertComponentIdIntoFriendlyPathSearch_SuperCrazy(t *testing.T) {
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)
}
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")
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)
}
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) {
segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1crazy~1ass~1references")
assert.Equal(t, "$.paths['/crazy/ass/references']", path)
@@ -638,7 +658,7 @@ func TestConvertComponentIdIntoFriendlyPathSearch_Array(t *testing.T) {
func TestConvertComponentIdIntoFriendlyPathSearch_HTTPCode(t *testing.T) {
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)
}