mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-10 04:20:24 +00:00
This patch bumps up support for creating valid JSON paths from references. It addresses #48 and makes invalid specs, valid! This addresses issues #48 and #45 that report references cannot be found. Now `libopenapi` can support much more funky lookups.
This commit is contained in:
@@ -90,11 +90,9 @@ func LocateRefNode(root *yaml.Node, idx *index.SpecIndex) (*yaml.Node, error) {
|
||||
}
|
||||
|
||||
// cant be found? last resort is to try a path lookup
|
||||
cleaned := strings.ReplaceAll(rv, "#/paths/", "")
|
||||
cleaned = strings.ReplaceAll(cleaned, "/", ".")
|
||||
cleaned = strings.ReplaceAll(cleaned, "~1", "/")
|
||||
yamlPath := fmt.Sprintf("$.paths.%s", cleaned)
|
||||
path, err := yamlpath.NewPath(yamlPath)
|
||||
_, friendly := utils.ConvertComponentIdIntoFriendlyPathSearch(rv)
|
||||
if friendly != "" {
|
||||
path, err := yamlpath.NewPath(friendly)
|
||||
if err == nil {
|
||||
nodes, fErr := path.Find(idx.GetRootNode())
|
||||
if fErr == nil {
|
||||
@@ -103,6 +101,7 @@ func LocateRefNode(root *yaml.Node, idx *index.SpecIndex) (*yaml.Node, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("reference '%s' at line %d, column %d was not found",
|
||||
rv, root.Line, root.Column)
|
||||
}
|
||||
|
||||
@@ -55,8 +55,7 @@ func TestCallback_Build_Error(t *testing.T) {
|
||||
idx := index.NewSpecIndex(&idxNode)
|
||||
|
||||
yml := `'{$request.query.queryUrl}':
|
||||
post:
|
||||
$ref: #/does/not/exist/and/invalid`
|
||||
$ref: '#/does/not/exist/and/invalid'`
|
||||
|
||||
var rootNode yaml.Node
|
||||
mErr = yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
|
||||
@@ -1832,16 +1832,17 @@ func (index *SpecIndex) FindComponentInRoot(componentId string) *Reference {
|
||||
if index.root != nil {
|
||||
name, friendlySearch := utils.ConvertComponentIdIntoFriendlyPathSearch(componentId)
|
||||
friendlySearch = strings.ReplaceAll(friendlySearch, "~1", "/")
|
||||
path, _ := yamlpath.NewPath(friendlySearch)
|
||||
path, err := yamlpath.NewPath(friendlySearch)
|
||||
if path == nil || err != nil {
|
||||
return nil // no component found
|
||||
}
|
||||
res, _ := path.Find(index.root)
|
||||
|
||||
if len(res) == 1 {
|
||||
ref := &Reference{
|
||||
Definition: componentId,
|
||||
Name: name,
|
||||
Node: res[0],
|
||||
}
|
||||
|
||||
return ref
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,7 +454,54 @@ func TestSpecIndex_ExtractComponentsFromRefs(t *testing.T) {
|
||||
assert.Len(t, index.GetReferenceIndexErrors(), 1)
|
||||
}
|
||||
|
||||
func TestSpecIndex_FindComponent(t *testing.T) {
|
||||
func TestSpecIndex_FindComponent_WithACrazyAssPath(t *testing.T) {
|
||||
yml := `paths:
|
||||
/crazy/ass/references:
|
||||
get:
|
||||
parameters:
|
||||
- name: a param
|
||||
schema:
|
||||
type: string
|
||||
description: Show information about one architecture.
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/xml; charset=utf-8:
|
||||
schema:
|
||||
example:
|
||||
name: x86_64
|
||||
description: OK. The request has succeeded.
|
||||
"404":
|
||||
content:
|
||||
application/xml; charset=utf-8:
|
||||
example:
|
||||
code: unknown_architecture
|
||||
summary: "Architecture does not exist: x999"
|
||||
schema:
|
||||
$ref: "#/paths/~1crazy~1ass~1references/get/parameters/0"
|
||||
"400":
|
||||
content:
|
||||
application/xml; charset=utf-8:
|
||||
example:
|
||||
code: unknown_architecture
|
||||
summary: "Architecture does not exist: x999"
|
||||
schema:
|
||||
$ref: "#/paths/~1crazy~1ass~1references/get/responses/404/content/application~1xml;%20charset=utf-8/schema"
|
||||
description: Not Found.`
|
||||
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
|
||||
index := NewSpecIndex(&rootNode)
|
||||
assert.Equal(t, "#/paths/~1crazy~1ass~1references/get/parameters/0",
|
||||
index.FindComponent("#/paths/~1crazy~1ass~1references/get/responses/404/content/application~1xml;%20charset=utf-8/schema", nil).Node.Content[1].Value)
|
||||
|
||||
assert.Equal(t, "a param",
|
||||
index.FindComponent("#/paths/~1crazy~1ass~1references/get/parameters/0", nil).Node.Content[1].Value)
|
||||
|
||||
}
|
||||
|
||||
func TestSpecIndex_FindComponenth(t *testing.T) {
|
||||
yml := `components:
|
||||
schemas:
|
||||
pizza:
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
|
||||
"gopkg.in/yaml.v3"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -480,13 +481,49 @@ func IsHttpVerb(verb string) bool {
|
||||
func ConvertComponentIdIntoFriendlyPathSearch(id string) (string, string) {
|
||||
segs := strings.Split(id, "/")
|
||||
name := strings.ReplaceAll(segs[len(segs)-1], "~1", "/")
|
||||
var cleaned []string
|
||||
|
||||
replaced := strings.ReplaceAll(fmt.Sprintf("%s['%s']",
|
||||
strings.Join(segs[:len(segs)-1], "."), name), "#", "$")
|
||||
// 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])
|
||||
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--
|
||||
}
|
||||
cleaned[h-1] = fmt.Sprintf("%s%s", segs[i-1], segs[i])
|
||||
} else {
|
||||
cleaned = append(cleaned, segs[i])
|
||||
}
|
||||
}
|
||||
|
||||
nameIntVal, 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), "#", "$")
|
||||
} 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] != '$' {
|
||||
replaced = fmt.Sprintf("$%s", replaced)
|
||||
}
|
||||
}
|
||||
return name, replaced
|
||||
}
|
||||
|
||||
|
||||
@@ -611,6 +611,35 @@ 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)
|
||||
assert.Equal(t, "schema", segment)
|
||||
|
||||
}
|
||||
|
||||
func TestConvertComponentIdIntoFriendlyPathSearch_Crazy(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_CrazyShort(t *testing.T) {
|
||||
segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1crazy~1ass~1references")
|
||||
assert.Equal(t, "$.paths['/crazy/ass/references']", path)
|
||||
assert.Equal(t, "/crazy/ass/references", segment)
|
||||
}
|
||||
|
||||
func TestConvertComponentIdIntoFriendlyPathSearch_Array(t *testing.T) {
|
||||
segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/paths/~1crazy~1ass~1references/get/parameters/0")
|
||||
assert.Equal(t, "$.paths['/crazy/ass/references'].get.parameters[0]", path)
|
||||
assert.Equal(t, "0", segment)
|
||||
}
|
||||
|
||||
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, "404", segment)
|
||||
}
|
||||
|
||||
func TestConvertComponentIdIntoPath(t *testing.T) {
|
||||
@@ -628,7 +657,6 @@ func TestDetectCase(t *testing.T) {
|
||||
assert.Equal(t, KebabCase, DetectCase("chicken-be-be-beef-or-pork"))
|
||||
assert.Equal(t, RegularCase, DetectCase("kebab-TimeIn_london-TOWN"))
|
||||
assert.Equal(t, UnknownCase, DetectCase(""))
|
||||
|
||||
}
|
||||
|
||||
func TestIsNodeRefValue(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user