mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-10 04:20:24 +00:00
(enhancement): Improved resolving/lookup code for #45
This commit is contained in:
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user