mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-09 12:37:49 +00:00
Added tag model build out with tests.
Working through patterns and re-applying them as I go, cleaning things up as I cook.
This commit is contained in:
@@ -1,17 +1,17 @@
|
|||||||
package v3
|
package v3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Document struct {
|
type Document struct {
|
||||||
Version low.NodeReference[string]
|
Version low.NodeReference[string]
|
||||||
Info low.NodeReference[*Info]
|
Info low.NodeReference[*Info]
|
||||||
Servers []low.NodeReference[*Server]
|
Servers []low.NodeReference[*Server]
|
||||||
Paths *Paths
|
Paths *Paths
|
||||||
Components *Components
|
Components *Components
|
||||||
Security []*SecurityRequirement
|
Security []*SecurityRequirement
|
||||||
Tags []*Tag
|
Tags []low.NodeReference[*Tag]
|
||||||
ExternalDocs *ExternalDoc
|
ExternalDocs *ExternalDoc
|
||||||
Extensions map[string]low.ObjectReference
|
Extensions map[string]low.ObjectReference
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
package v3
|
package v3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ExternalDoc struct {
|
type ExternalDoc struct {
|
||||||
Node *yaml.Node
|
Description low.NodeReference[string]
|
||||||
Description low.NodeReference[string]
|
URL low.NodeReference[string]
|
||||||
URL low.NodeReference[string]
|
Extensions map[string]low.ObjectReference
|
||||||
Extensions map[string]low.ObjectReference
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +1,53 @@
|
|||||||
package v3
|
package v3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel"
|
"github.com/pb33f/libopenapi/datamodel"
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"github.com/pb33f/libopenapi/utils"
|
"github.com/pb33f/libopenapi/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Variables = "variables"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
URL low.NodeReference[string]
|
URL low.NodeReference[string]
|
||||||
Description low.NodeReference[string]
|
Description low.NodeReference[string]
|
||||||
Variables low.NodeReference[*map[string]low.NodeReference[*ServerVariable]]
|
Variables low.NodeReference[map[string]low.NodeReference[*ServerVariable]]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Build(root *yaml.Node) error {
|
func (s *Server) Build(root *yaml.Node) error {
|
||||||
kn, vars := utils.FindKeyNode("variables", root.Content)
|
kn, vars := utils.FindKeyNode(Variables, root.Content)
|
||||||
if vars == nil {
|
if vars == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
variablesMap := make(map[string]low.NodeReference[*ServerVariable])
|
variablesMap := make(map[string]low.NodeReference[*ServerVariable])
|
||||||
if utils.IsNodeMap(vars) {
|
if utils.IsNodeMap(vars) {
|
||||||
var currentNode string
|
var currentNode string
|
||||||
var keyNode *yaml.Node
|
var keyNode *yaml.Node
|
||||||
for i, varNode := range vars.Content {
|
for i, varNode := range vars.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
currentNode = varNode.Value
|
currentNode = varNode.Value
|
||||||
keyNode = varNode
|
keyNode = varNode
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
variable := ServerVariable{}
|
variable := ServerVariable{}
|
||||||
err := datamodel.BuildModel(varNode, &variable)
|
err := datamodel.BuildModel(varNode, &variable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
variablesMap[currentNode] = low.NodeReference[*ServerVariable]{
|
variablesMap[currentNode] = low.NodeReference[*ServerVariable]{
|
||||||
ValueNode: varNode,
|
ValueNode: varNode,
|
||||||
KeyNode: keyNode,
|
KeyNode: keyNode,
|
||||||
Value: &variable,
|
Value: &variable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.Variables = low.NodeReference[*map[string]low.NodeReference[*ServerVariable]]{
|
s.Variables = low.NodeReference[map[string]low.NodeReference[*ServerVariable]]{
|
||||||
KeyNode: kn,
|
KeyNode: kn,
|
||||||
ValueNode: vars,
|
ValueNode: vars,
|
||||||
Value: &variablesMap,
|
Value: variablesMap,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,44 @@
|
|||||||
package v3
|
package v3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel"
|
||||||
"gopkg.in/yaml.v3"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
|
"github.com/pb33f/libopenapi/utils"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Tags = "tags"
|
||||||
|
ExternalDocs = "externalDocs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Tag struct {
|
type Tag struct {
|
||||||
Node *yaml.Node
|
Name low.NodeReference[string]
|
||||||
Name low.NodeReference[string]
|
Description low.NodeReference[string]
|
||||||
Description low.NodeReference[string]
|
ExternalDocs low.NodeReference[*ExternalDoc]
|
||||||
ExternalDocs ExternalDoc
|
Extensions map[low.NodeReference[string]]low.NodeReference[any]
|
||||||
Extensions map[string]low.ObjectReference
|
}
|
||||||
|
|
||||||
|
func (t *Tag) Build(root *yaml.Node) error {
|
||||||
|
_, ln, exDocs := utils.FindKeyNodeFull(ExternalDocs, root.Content)
|
||||||
|
|
||||||
|
// extract extensions
|
||||||
|
extensionMap, err := datamodel.ExtractExtensions(root)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Extensions = extensionMap
|
||||||
|
|
||||||
|
// extract external docs
|
||||||
|
var externalDoc ExternalDoc
|
||||||
|
err = datamodel.BuildModel(exDocs, &externalDoc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.ExternalDocs = low.NodeReference[*ExternalDoc]{
|
||||||
|
Value: &externalDoc,
|
||||||
|
KeyNode: ln,
|
||||||
|
ValueNode: exDocs,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,21 +3,21 @@ package low
|
|||||||
import "gopkg.in/yaml.v3"
|
import "gopkg.in/yaml.v3"
|
||||||
|
|
||||||
type HasNode interface {
|
type HasNode interface {
|
||||||
GetNode() *yaml.Node
|
GetNode() *yaml.Node
|
||||||
}
|
}
|
||||||
|
|
||||||
type Buildable interface {
|
type Buildable interface {
|
||||||
Build(node *yaml.Node) error
|
Build(node *yaml.Node) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type NodeReference[T any] struct {
|
type NodeReference[T any] struct {
|
||||||
Value T
|
Value T
|
||||||
ValueNode *yaml.Node
|
ValueNode *yaml.Node
|
||||||
KeyNode *yaml.Node
|
KeyNode *yaml.Node
|
||||||
}
|
}
|
||||||
|
|
||||||
type ObjectReference struct {
|
type ObjectReference struct {
|
||||||
Value map[string]interface{}
|
Value interface{}
|
||||||
ValueNode *yaml.Node
|
ValueNode *yaml.Node
|
||||||
KeyNode *yaml.Node
|
KeyNode *yaml.Node
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,354 +1,411 @@
|
|||||||
package datamodel
|
package datamodel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"github.com/pb33f/libopenapi/utils"
|
"github.com/pb33f/libopenapi/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
func BuildModel(node *yaml.Node, model interface{}) error {
|
func BuildModel(node *yaml.Node, model interface{}) error {
|
||||||
|
|
||||||
if reflect.ValueOf(model).Type().Kind() != reflect.Pointer {
|
if reflect.ValueOf(model).Type().Kind() != reflect.Pointer {
|
||||||
return fmt.Errorf("cannot build model on non-pointer: %v", reflect.ValueOf(model).Type().Kind())
|
return fmt.Errorf("cannot build model on non-pointer: %v", reflect.ValueOf(model).Type().Kind())
|
||||||
}
|
}
|
||||||
v := reflect.ValueOf(model).Elem()
|
v := reflect.ValueOf(model).Elem()
|
||||||
for i := 0; i < v.NumField(); i++ {
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
|
||||||
fName := v.Type().Field(i).Name
|
fName := v.Type().Field(i).Name
|
||||||
|
|
||||||
// we need to find a matching field in the YAML, the cases may be off, so take no chances.
|
// we need to find a matching field in the YAML, the cases may be off, so take no chances.
|
||||||
cases := []utils.Case{utils.PascalCase, utils.CamelCase, utils.ScreamingSnakeCase,
|
cases := []utils.Case{utils.PascalCase, utils.CamelCase, utils.ScreamingSnakeCase,
|
||||||
utils.SnakeCase, utils.KebabCase, utils.RegularCase}
|
utils.SnakeCase, utils.KebabCase, utils.RegularCase}
|
||||||
|
|
||||||
var vn, kn *yaml.Node
|
var vn, kn *yaml.Node
|
||||||
for _, tryCase := range cases {
|
for _, tryCase := range cases {
|
||||||
kn, vn = utils.FindKeyNode(utils.ConvertCase(fName, tryCase), node.Content)
|
kn, vn = utils.FindKeyNode(utils.ConvertCase(fName, tryCase), node.Content)
|
||||||
if vn != nil {
|
if vn != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if vn == nil {
|
if vn == nil {
|
||||||
// no point in going on.
|
// no point in going on.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
field := v.FieldByName(fName)
|
field := v.FieldByName(fName)
|
||||||
kind := field.Kind()
|
kind := field.Kind()
|
||||||
switch kind {
|
switch kind {
|
||||||
case reflect.Struct, reflect.Slice, reflect.Map, reflect.Pointer:
|
case reflect.Struct, reflect.Slice, reflect.Map, reflect.Pointer:
|
||||||
err := SetField(field, vn, kn)
|
err := SetField(field, vn, kn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unable to parse unsupported type: %v", kind)
|
return fmt.Errorf("unable to parse unsupported type: %v", kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) error {
|
func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) error {
|
||||||
switch field.Type() {
|
switch field.Type() {
|
||||||
|
|
||||||
case reflect.TypeOf(map[string]low.ObjectReference{}):
|
case reflect.TypeOf(map[string]low.ObjectReference{}):
|
||||||
if valueNode != nil {
|
if valueNode != nil {
|
||||||
if utils.IsNodeMap(valueNode) {
|
if utils.IsNodeMap(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
items := make(map[string]low.ObjectReference)
|
items := make(map[string]low.ObjectReference)
|
||||||
var currentLabel string
|
var currentLabel string
|
||||||
for i, sliceItem := range valueNode.Content {
|
for i, sliceItem := range valueNode.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
currentLabel = sliceItem.Value
|
currentLabel = sliceItem.Value
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var decoded map[string]interface{}
|
var decoded map[string]interface{}
|
||||||
err := sliceItem.Decode(&decoded)
|
err := sliceItem.Decode(&decoded)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
items[currentLabel] = low.ObjectReference{
|
items[currentLabel] = low.ObjectReference{
|
||||||
Value: decoded,
|
Value: decoded,
|
||||||
ValueNode: sliceItem,
|
ValueNode: sliceItem,
|
||||||
KeyNode: valueNode,
|
KeyNode: valueNode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(items))
|
field.Set(reflect.ValueOf(items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
case reflect.TypeOf(map[string]low.NodeReference[string]{}):
|
case reflect.TypeOf(map[string]low.NodeReference[string]{}):
|
||||||
if valueNode != nil {
|
if valueNode != nil {
|
||||||
if utils.IsNodeMap(valueNode) {
|
if utils.IsNodeMap(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
items := make(map[string]low.NodeReference[string])
|
items := make(map[string]low.NodeReference[string])
|
||||||
var currentLabel string
|
var currentLabel string
|
||||||
for i, sliceItem := range valueNode.Content {
|
for i, sliceItem := range valueNode.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
currentLabel = sliceItem.Value
|
currentLabel = sliceItem.Value
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
items[currentLabel] = low.NodeReference[string]{
|
items[currentLabel] = low.NodeReference[string]{
|
||||||
Value: sliceItem.Value,
|
Value: sliceItem.Value,
|
||||||
ValueNode: sliceItem,
|
ValueNode: sliceItem,
|
||||||
KeyNode: valueNode,
|
KeyNode: valueNode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(items))
|
field.Set(reflect.ValueOf(items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case reflect.TypeOf(low.ObjectReference{}):
|
case reflect.TypeOf(low.ObjectReference{}):
|
||||||
if valueNode != nil {
|
if valueNode != nil {
|
||||||
var decoded map[string]interface{}
|
var decoded map[string]interface{}
|
||||||
err := valueNode.Decode(&decoded)
|
err := valueNode.Decode(&decoded)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if utils.IsNodeMap(valueNode) {
|
if utils.IsNodeMap(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
or := low.ObjectReference{Value: decoded, ValueNode: valueNode}
|
or := low.ObjectReference{Value: decoded, ValueNode: valueNode}
|
||||||
field.Set(reflect.ValueOf(or))
|
field.Set(reflect.ValueOf(or))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case reflect.TypeOf([]low.ObjectReference{}):
|
case reflect.TypeOf([]low.ObjectReference{}):
|
||||||
if valueNode != nil {
|
if valueNode != nil {
|
||||||
if utils.IsNodeArray(valueNode) {
|
if utils.IsNodeArray(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
var items []low.ObjectReference
|
var items []low.ObjectReference
|
||||||
for _, sliceItem := range valueNode.Content {
|
for _, sliceItem := range valueNode.Content {
|
||||||
var decoded map[string]interface{}
|
var decoded map[string]interface{}
|
||||||
err := sliceItem.Decode(&decoded)
|
err := sliceItem.Decode(&decoded)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
items = append(items, low.ObjectReference{
|
items = append(items, low.ObjectReference{
|
||||||
Value: decoded,
|
Value: decoded,
|
||||||
ValueNode: sliceItem,
|
ValueNode: sliceItem,
|
||||||
KeyNode: valueNode,
|
KeyNode: valueNode,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(items))
|
field.Set(reflect.ValueOf(items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case reflect.TypeOf(low.NodeReference[string]{}):
|
case reflect.TypeOf(low.NodeReference[string]{}):
|
||||||
if valueNode != nil {
|
if valueNode != nil {
|
||||||
if utils.IsNodeStringValue(valueNode) {
|
if utils.IsNodeStringValue(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
nr := low.NodeReference[string]{
|
nr := low.NodeReference[string]{
|
||||||
Value: valueNode.Value,
|
Value: valueNode.Value,
|
||||||
ValueNode: valueNode,
|
ValueNode: valueNode,
|
||||||
KeyNode: keyNode,
|
KeyNode: keyNode,
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(nr))
|
field.Set(reflect.ValueOf(nr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case reflect.TypeOf(low.NodeReference[bool]{}):
|
case reflect.TypeOf(low.NodeReference[bool]{}):
|
||||||
if valueNode != nil {
|
if valueNode != nil {
|
||||||
if utils.IsNodeBoolValue(valueNode) {
|
if utils.IsNodeBoolValue(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
bv, _ := strconv.ParseBool(valueNode.Value)
|
bv, _ := strconv.ParseBool(valueNode.Value)
|
||||||
nr := low.NodeReference[bool]{
|
nr := low.NodeReference[bool]{
|
||||||
Value: bv,
|
Value: bv,
|
||||||
ValueNode: valueNode,
|
ValueNode: valueNode,
|
||||||
KeyNode: keyNode,
|
KeyNode: keyNode,
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(nr))
|
field.Set(reflect.ValueOf(nr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case reflect.TypeOf(low.NodeReference[int]{}):
|
case reflect.TypeOf(low.NodeReference[int]{}):
|
||||||
if valueNode != nil {
|
if valueNode != nil {
|
||||||
if utils.IsNodeIntValue(valueNode) {
|
if utils.IsNodeIntValue(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
fv, _ := strconv.Atoi(valueNode.Value)
|
fv, _ := strconv.Atoi(valueNode.Value)
|
||||||
nr := low.NodeReference[int]{
|
nr := low.NodeReference[int]{
|
||||||
Value: fv,
|
Value: fv,
|
||||||
ValueNode: valueNode,
|
ValueNode: valueNode,
|
||||||
KeyNode: keyNode,
|
KeyNode: keyNode,
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(nr))
|
field.Set(reflect.ValueOf(nr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case reflect.TypeOf(low.NodeReference[int64]{}):
|
case reflect.TypeOf(low.NodeReference[int64]{}):
|
||||||
if valueNode != nil {
|
if valueNode != nil {
|
||||||
if utils.IsNodeIntValue(valueNode) || utils.IsNodeFloatValue(valueNode) { //
|
if utils.IsNodeIntValue(valueNode) || utils.IsNodeFloatValue(valueNode) { //
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
fv, _ := strconv.ParseInt(valueNode.Value, 10, 64)
|
fv, _ := strconv.ParseInt(valueNode.Value, 10, 64)
|
||||||
nr := low.NodeReference[int64]{
|
nr := low.NodeReference[int64]{
|
||||||
Value: fv,
|
Value: fv,
|
||||||
ValueNode: valueNode,
|
ValueNode: valueNode,
|
||||||
KeyNode: keyNode,
|
KeyNode: keyNode,
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(nr))
|
field.Set(reflect.ValueOf(nr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case reflect.TypeOf(low.NodeReference[float32]{}):
|
case reflect.TypeOf(low.NodeReference[float32]{}):
|
||||||
if valueNode != nil {
|
if valueNode != nil {
|
||||||
if utils.IsNodeFloatValue(valueNode) {
|
if utils.IsNodeFloatValue(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
fv, _ := strconv.ParseFloat(valueNode.Value, 32)
|
fv, _ := strconv.ParseFloat(valueNode.Value, 32)
|
||||||
nr := low.NodeReference[float32]{
|
nr := low.NodeReference[float32]{
|
||||||
Value: float32(fv),
|
Value: float32(fv),
|
||||||
ValueNode: valueNode,
|
ValueNode: valueNode,
|
||||||
KeyNode: keyNode,
|
KeyNode: keyNode,
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(nr))
|
field.Set(reflect.ValueOf(nr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case reflect.TypeOf(low.NodeReference[float64]{}):
|
case reflect.TypeOf(low.NodeReference[float64]{}):
|
||||||
if valueNode != nil {
|
if valueNode != nil {
|
||||||
if utils.IsNodeFloatValue(valueNode) {
|
if utils.IsNodeFloatValue(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
fv, _ := strconv.ParseFloat(valueNode.Value, 64)
|
fv, _ := strconv.ParseFloat(valueNode.Value, 64)
|
||||||
nr := low.NodeReference[float64]{
|
nr := low.NodeReference[float64]{
|
||||||
Value: fv,
|
Value: fv,
|
||||||
ValueNode: valueNode,
|
ValueNode: valueNode,
|
||||||
KeyNode: keyNode,
|
KeyNode: keyNode,
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(nr))
|
field.Set(reflect.ValueOf(nr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case reflect.TypeOf([]low.NodeReference[string]{}):
|
case reflect.TypeOf([]low.NodeReference[string]{}):
|
||||||
if valueNode != nil {
|
if valueNode != nil {
|
||||||
if utils.IsNodeArray(valueNode) {
|
if utils.IsNodeArray(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
var items []low.NodeReference[string]
|
var items []low.NodeReference[string]
|
||||||
for _, sliceItem := range valueNode.Content {
|
for _, sliceItem := range valueNode.Content {
|
||||||
items = append(items, low.NodeReference[string]{
|
items = append(items, low.NodeReference[string]{
|
||||||
Value: sliceItem.Value,
|
Value: sliceItem.Value,
|
||||||
ValueNode: sliceItem,
|
ValueNode: sliceItem,
|
||||||
KeyNode: valueNode,
|
KeyNode: valueNode,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(items))
|
field.Set(reflect.ValueOf(items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case reflect.TypeOf([]low.NodeReference[float32]{}):
|
case reflect.TypeOf([]low.NodeReference[float32]{}):
|
||||||
if valueNode != nil {
|
if valueNode != nil {
|
||||||
if utils.IsNodeArray(valueNode) {
|
if utils.IsNodeArray(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
var items []low.NodeReference[float32]
|
var items []low.NodeReference[float32]
|
||||||
for _, sliceItem := range valueNode.Content {
|
for _, sliceItem := range valueNode.Content {
|
||||||
fv, _ := strconv.ParseFloat(sliceItem.Value, 32)
|
fv, _ := strconv.ParseFloat(sliceItem.Value, 32)
|
||||||
items = append(items, low.NodeReference[float32]{
|
items = append(items, low.NodeReference[float32]{
|
||||||
Value: float32(fv),
|
Value: float32(fv),
|
||||||
ValueNode: sliceItem,
|
ValueNode: sliceItem,
|
||||||
KeyNode: valueNode,
|
KeyNode: valueNode,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(items))
|
field.Set(reflect.ValueOf(items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case reflect.TypeOf([]low.NodeReference[float64]{}):
|
case reflect.TypeOf([]low.NodeReference[float64]{}):
|
||||||
if valueNode != nil {
|
if valueNode != nil {
|
||||||
if utils.IsNodeArray(valueNode) {
|
if utils.IsNodeArray(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
var items []low.NodeReference[float64]
|
var items []low.NodeReference[float64]
|
||||||
for _, sliceItem := range valueNode.Content {
|
for _, sliceItem := range valueNode.Content {
|
||||||
fv, _ := strconv.ParseFloat(sliceItem.Value, 64)
|
fv, _ := strconv.ParseFloat(sliceItem.Value, 64)
|
||||||
items = append(items, low.NodeReference[float64]{Value: fv, ValueNode: sliceItem})
|
items = append(items, low.NodeReference[float64]{Value: fv, ValueNode: sliceItem})
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(items))
|
field.Set(reflect.ValueOf(items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case reflect.TypeOf([]low.NodeReference[int]{}):
|
case reflect.TypeOf([]low.NodeReference[int]{}):
|
||||||
if valueNode != nil {
|
if valueNode != nil {
|
||||||
if utils.IsNodeArray(valueNode) {
|
if utils.IsNodeArray(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
var items []low.NodeReference[int]
|
var items []low.NodeReference[int]
|
||||||
for _, sliceItem := range valueNode.Content {
|
for _, sliceItem := range valueNode.Content {
|
||||||
iv, _ := strconv.Atoi(sliceItem.Value)
|
iv, _ := strconv.Atoi(sliceItem.Value)
|
||||||
items = append(items, low.NodeReference[int]{
|
items = append(items, low.NodeReference[int]{
|
||||||
Value: iv,
|
Value: iv,
|
||||||
ValueNode: sliceItem,
|
ValueNode: sliceItem,
|
||||||
KeyNode: valueNode,
|
KeyNode: valueNode,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(items))
|
field.Set(reflect.ValueOf(items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case reflect.TypeOf([]low.NodeReference[int64]{}):
|
case reflect.TypeOf([]low.NodeReference[int64]{}):
|
||||||
if valueNode != nil {
|
if valueNode != nil {
|
||||||
if utils.IsNodeArray(valueNode) {
|
if utils.IsNodeArray(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
var items []low.NodeReference[int64]
|
var items []low.NodeReference[int64]
|
||||||
for _, sliceItem := range valueNode.Content {
|
for _, sliceItem := range valueNode.Content {
|
||||||
iv, _ := strconv.ParseInt(sliceItem.Value, 10, 64)
|
iv, _ := strconv.ParseInt(sliceItem.Value, 10, 64)
|
||||||
items = append(items, low.NodeReference[int64]{
|
items = append(items, low.NodeReference[int64]{
|
||||||
Value: iv,
|
Value: iv,
|
||||||
ValueNode: sliceItem,
|
ValueNode: sliceItem,
|
||||||
KeyNode: valueNode,
|
KeyNode: valueNode,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(items))
|
field.Set(reflect.ValueOf(items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case reflect.TypeOf([]low.NodeReference[bool]{}):
|
case reflect.TypeOf([]low.NodeReference[bool]{}):
|
||||||
if valueNode != nil {
|
if valueNode != nil {
|
||||||
if utils.IsNodeArray(valueNode) {
|
if utils.IsNodeArray(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
var items []low.NodeReference[bool]
|
var items []low.NodeReference[bool]
|
||||||
for _, sliceItem := range valueNode.Content {
|
for _, sliceItem := range valueNode.Content {
|
||||||
bv, _ := strconv.ParseBool(sliceItem.Value)
|
bv, _ := strconv.ParseBool(sliceItem.Value)
|
||||||
items = append(items, low.NodeReference[bool]{
|
items = append(items, low.NodeReference[bool]{
|
||||||
Value: bv,
|
Value: bv,
|
||||||
ValueNode: sliceItem,
|
ValueNode: sliceItem,
|
||||||
KeyNode: valueNode,
|
KeyNode: valueNode,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(items))
|
field.Set(reflect.ValueOf(items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
// we want to ignore everything else.
|
// we want to ignore everything else.
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildModelAsync(n *yaml.Node, model interface{}, lwg *sync.WaitGroup, errors *[]error) {
|
func BuildModelAsync(n *yaml.Node, model interface{}, lwg *sync.WaitGroup, errors *[]error) {
|
||||||
if n != nil {
|
if n != nil {
|
||||||
err := BuildModel(n, model)
|
err := BuildModel(n, model)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
*errors = append(*errors, err)
|
*errors = append(*errors, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lwg.Done()
|
lwg.Done()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExtractExtensions(root *yaml.Node) (map[low.NodeReference[string]]low.NodeReference[any], error) {
|
||||||
|
extensions := utils.FindExtensionNodes(root.Content)
|
||||||
|
extensionMap := make(map[low.NodeReference[string]]low.NodeReference[any])
|
||||||
|
for _, ext := range extensions {
|
||||||
|
if utils.IsNodeMap(ext.Value) {
|
||||||
|
var v interface{}
|
||||||
|
err := ext.Value.Decode(&v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
extensionMap[low.NodeReference[string]{
|
||||||
|
Value: ext.Key.Value,
|
||||||
|
KeyNode: ext.Key,
|
||||||
|
}] = low.NodeReference[any]{Value: v, KeyNode: ext.Key}
|
||||||
|
}
|
||||||
|
if utils.IsNodeStringValue(ext.Value) {
|
||||||
|
extensionMap[low.NodeReference[string]{
|
||||||
|
Value: ext.Key.Value,
|
||||||
|
KeyNode: ext.Key,
|
||||||
|
}] = low.NodeReference[any]{Value: ext.Value.Value, ValueNode: ext.Value}
|
||||||
|
}
|
||||||
|
if utils.IsNodeFloatValue(ext.Value) {
|
||||||
|
fv, _ := strconv.ParseFloat(ext.Value.Value, 64)
|
||||||
|
extensionMap[low.NodeReference[string]{
|
||||||
|
Value: ext.Key.Value,
|
||||||
|
KeyNode: ext.Key,
|
||||||
|
}] = low.NodeReference[any]{Value: fv, ValueNode: ext.Value}
|
||||||
|
}
|
||||||
|
if utils.IsNodeIntValue(ext.Value) {
|
||||||
|
iv, _ := strconv.ParseInt(ext.Value.Value, 10, 64)
|
||||||
|
extensionMap[low.NodeReference[string]{
|
||||||
|
Value: ext.Key.Value,
|
||||||
|
KeyNode: ext.Key,
|
||||||
|
}] = low.NodeReference[any]{Value: iv, ValueNode: ext.Value}
|
||||||
|
}
|
||||||
|
if utils.IsNodeBoolValue(ext.Value) {
|
||||||
|
bv, _ := strconv.ParseBool(ext.Value.Value)
|
||||||
|
extensionMap[low.NodeReference[string]{
|
||||||
|
Value: ext.Key.Value,
|
||||||
|
KeyNode: ext.Key,
|
||||||
|
}] = low.NodeReference[any]{Value: bv, ValueNode: ext.Value}
|
||||||
|
}
|
||||||
|
if utils.IsNodeArray(ext.Value) {
|
||||||
|
var v []interface{}
|
||||||
|
err := ext.Value.Decode(&v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
extensionMap[low.NodeReference[string]{
|
||||||
|
Value: ext.Key.Value,
|
||||||
|
KeyNode: ext.Key,
|
||||||
|
}] = low.NodeReference[any]{Value: v, ValueNode: ext.Value}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return extensionMap, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,78 +1,182 @@
|
|||||||
package openapi
|
package openapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel"
|
"github.com/pb33f/libopenapi/datamodel"
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
v3 "github.com/pb33f/libopenapi/datamodel/low/3.0"
|
v3 "github.com/pb33f/libopenapi/datamodel/low/3.0"
|
||||||
"github.com/pb33f/libopenapi/utils"
|
"github.com/pb33f/libopenapi/utils"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateDocument(spec []byte) (*v3.Document, error) {
|
const (
|
||||||
|
Info = "info"
|
||||||
|
Servers = "servers"
|
||||||
|
)
|
||||||
|
|
||||||
// extract details from spec
|
func CreateDocument(info *datamodel.SpecInfo) (*v3.Document, error) {
|
||||||
info, err := datamodel.ExtractSpecInfo(spec)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
doc := v3.Document{Version: low.NodeReference[string]{Value: info.Version, ValueNode: info.RootNode}}
|
doc := v3.Document{Version: low.NodeReference[string]{Value: info.Version, ValueNode: info.RootNode}}
|
||||||
|
|
||||||
// build an index
|
// build an index
|
||||||
//idx := index.NewSpecIndex(info.RootNode)
|
//idx := index.NewSpecIndex(info.RootNode)
|
||||||
//datamodel.BuildModel(info.RootNode.Content[0], &doc)
|
//datamodel.BuildModel(info.RootNode.Content[0], &doc)
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
var errors []error
|
||||||
|
var runExtraction = func(info *datamodel.SpecInfo, doc *v3.Document,
|
||||||
|
runFunc func(i *datamodel.SpecInfo, d *v3.Document) error,
|
||||||
|
ers *[]error,
|
||||||
|
wg *sync.WaitGroup) {
|
||||||
|
|
||||||
// extract info
|
if er := runFunc(info, doc); er != nil {
|
||||||
extractErr := extractInfo(info, &doc)
|
*ers = append(*ers, er)
|
||||||
if extractErr != nil {
|
}
|
||||||
return nil, extractErr
|
|
||||||
}
|
|
||||||
|
|
||||||
// extract servers
|
wg.Done()
|
||||||
extractErr = extractServers(info, &doc)
|
}
|
||||||
if extractErr != nil {
|
|
||||||
return nil, extractErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return &doc, nil
|
wg.Add(3)
|
||||||
|
go runExtraction(info, &doc, extractInfo, &errors, &wg)
|
||||||
|
go runExtraction(info, &doc, extractServers, &errors, &wg)
|
||||||
|
go runExtraction(info, &doc, extractTags, &errors, &wg)
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
// todo fix this.
|
||||||
|
if len(errors) > 0 {
|
||||||
|
return &doc, errors[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return &doc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractInfo(info *datamodel.SpecInfo, doc *v3.Document) error {
|
func extractInfo(info *datamodel.SpecInfo, doc *v3.Document) error {
|
||||||
_, ln, vn := utils.FindKeyNodeFull("info", info.RootNode.Content)
|
_, ln, vn := utils.FindKeyNodeFull(Info, info.RootNode.Content)
|
||||||
if vn != nil {
|
if vn != nil {
|
||||||
ir := v3.Info{}
|
ir := v3.Info{}
|
||||||
err := datamodel.BuildModel(vn, &ir)
|
err := datamodel.BuildModel(vn, &ir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = ir.Build(vn)
|
err = ir.Build(vn)
|
||||||
nr := low.NodeReference[*v3.Info]{Value: &ir, ValueNode: vn, KeyNode: ln}
|
nr := low.NodeReference[*v3.Info]{Value: &ir, ValueNode: vn, KeyNode: ln}
|
||||||
doc.Info = nr
|
doc.Info = nr
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractServers(info *datamodel.SpecInfo, doc *v3.Document) error {
|
func extractServers(info *datamodel.SpecInfo, doc *v3.Document) error {
|
||||||
_, ln, vn := utils.FindKeyNodeFull("servers", info.RootNode.Content)
|
_, ln, vn := utils.FindKeyNodeFull(Servers, info.RootNode.Content)
|
||||||
if vn != nil {
|
if vn != nil {
|
||||||
if utils.IsNodeArray(vn) {
|
if utils.IsNodeArray(vn) {
|
||||||
var servers []low.NodeReference[*v3.Server]
|
var servers []low.NodeReference[*v3.Server]
|
||||||
for _, srvN := range vn.Content {
|
for _, srvN := range vn.Content {
|
||||||
if utils.IsNodeMap(srvN) {
|
if utils.IsNodeMap(srvN) {
|
||||||
srvr := v3.Server{}
|
srvr := v3.Server{}
|
||||||
err := datamodel.BuildModel(srvN, &srvr)
|
err := datamodel.BuildModel(srvN, &srvr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
srvr.Build(srvN)
|
srvr.Build(srvN)
|
||||||
servers = append(servers, low.NodeReference[*v3.Server]{
|
servers = append(servers, low.NodeReference[*v3.Server]{
|
||||||
Value: &srvr,
|
Value: &srvr,
|
||||||
ValueNode: srvN,
|
ValueNode: srvN,
|
||||||
KeyNode: ln,
|
KeyNode: ln,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
doc.Servers = servers
|
doc.Servers = servers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractTags(info *datamodel.SpecInfo, doc *v3.Document) error {
|
||||||
|
_, ln, vn := utils.FindKeyNodeFull(v3.Tags, info.RootNode.Content)
|
||||||
|
if vn != nil {
|
||||||
|
if utils.IsNodeArray(vn) {
|
||||||
|
var tags []low.NodeReference[*v3.Tag]
|
||||||
|
for _, tagN := range vn.Content {
|
||||||
|
if utils.IsNodeMap(tagN) {
|
||||||
|
tag := v3.Tag{}
|
||||||
|
err := datamodel.BuildModel(tagN, &tag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tag.Build(tagN)
|
||||||
|
tags = append(tags, low.NodeReference[*v3.Tag]{
|
||||||
|
Value: &tag,
|
||||||
|
ValueNode: tagN,
|
||||||
|
KeyNode: ln,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
doc.Tags = tags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExtractExtensions(root *yaml.Node) (map[low.NodeReference[string]]low.NodeReference[any], error) {
|
||||||
|
extensions := utils.FindExtensionNodes(root.Content)
|
||||||
|
extensionMap := make(map[low.NodeReference[string]]low.NodeReference[any])
|
||||||
|
for _, ext := range extensions {
|
||||||
|
// this is an object, decode into an unknown map.
|
||||||
|
if utils.IsNodeMap(ext.Value) {
|
||||||
|
var v interface{}
|
||||||
|
err := ext.Value.Decode(&v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
extensionMap[low.NodeReference[string]{
|
||||||
|
Value: ext.Key.Value,
|
||||||
|
KeyNode: ext.Key,
|
||||||
|
ValueNode: ext.Value,
|
||||||
|
}] = low.NodeReference[any]{Value: v, KeyNode: ext.Key}
|
||||||
|
}
|
||||||
|
if utils.IsNodeStringValue(ext.Value) {
|
||||||
|
extensionMap[low.NodeReference[string]{
|
||||||
|
Value: ext.Key.Value,
|
||||||
|
KeyNode: ext.Key,
|
||||||
|
ValueNode: ext.Value,
|
||||||
|
}] = low.NodeReference[any]{Value: ext.Value.Value, ValueNode: ext.Value}
|
||||||
|
}
|
||||||
|
if utils.IsNodeFloatValue(ext.Value) {
|
||||||
|
fv, _ := strconv.ParseFloat(ext.Value.Value, 64)
|
||||||
|
extensionMap[low.NodeReference[string]{
|
||||||
|
Value: ext.Key.Value,
|
||||||
|
KeyNode: ext.Key,
|
||||||
|
ValueNode: ext.Value,
|
||||||
|
}] = low.NodeReference[any]{Value: fv, ValueNode: ext.Value}
|
||||||
|
}
|
||||||
|
if utils.IsNodeIntValue(ext.Value) {
|
||||||
|
iv, _ := strconv.ParseInt(ext.Value.Value, 10, 64)
|
||||||
|
extensionMap[low.NodeReference[string]{
|
||||||
|
Value: ext.Key.Value,
|
||||||
|
KeyNode: ext.Key,
|
||||||
|
ValueNode: ext.Value,
|
||||||
|
}] = low.NodeReference[any]{Value: iv, ValueNode: ext.Value}
|
||||||
|
}
|
||||||
|
if utils.IsNodeBoolValue(ext.Value) {
|
||||||
|
bv, _ := strconv.ParseBool(ext.Value.Value)
|
||||||
|
extensionMap[low.NodeReference[string]{
|
||||||
|
Value: ext.Key.Value,
|
||||||
|
KeyNode: ext.Key,
|
||||||
|
ValueNode: ext.Value,
|
||||||
|
}] = low.NodeReference[any]{Value: bv, ValueNode: ext.Value}
|
||||||
|
}
|
||||||
|
if utils.IsNodeArray(ext.Value) {
|
||||||
|
var v []interface{}
|
||||||
|
err := ext.Value.Decode(&v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
extensionMap[low.NodeReference[string]{
|
||||||
|
Value: ext.Key.Value,
|
||||||
|
KeyNode: ext.Key,
|
||||||
|
ValueNode: ext.Value,
|
||||||
|
}] = low.NodeReference[any]{Value: v, ValueNode: ext.Value}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return extensionMap, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,109 @@
|
|||||||
package openapi
|
package openapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/pb33f/libopenapi/datamodel"
|
||||||
|
v3 "github.com/pb33f/libopenapi/datamodel/low/3.0"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateDocument_NoData(t *testing.T) {
|
var doc *v3.Document
|
||||||
doc, err := CreateDocument(nil)
|
|
||||||
assert.Nil(t, doc)
|
func init() {
|
||||||
assert.Error(t, err)
|
data, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml")
|
||||||
|
info, _ := datamodel.ExtractSpecInfo(data)
|
||||||
|
doc, _ = CreateDocument(info)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkCreateDocument(b *testing.B) {
|
||||||
|
data, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml")
|
||||||
|
info, _ := datamodel.ExtractSpecInfo(data)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
doc, _ = CreateDocument(info)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument(t *testing.T) {
|
func TestCreateDocument(t *testing.T) {
|
||||||
data, aErr := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml")
|
|
||||||
assert.NoError(t, aErr)
|
|
||||||
|
|
||||||
doc, err := CreateDocument(data)
|
|
||||||
assert.NotNil(t, doc)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, "3.0.1", doc.Version.Value)
|
assert.Equal(t, "3.0.1", doc.Version.Value)
|
||||||
assert.Equal(t, "Burger Shop", doc.Info.Value.Title.Value)
|
assert.Equal(t, "Burger Shop", doc.Info.Value.Title.Value)
|
||||||
assert.NotEmpty(t, doc.Info.Value.Title.Value)
|
assert.NotEmpty(t, doc.Info.Value.Title.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateDocument_Info(t *testing.T) {
|
||||||
assert.Equal(t, "https://pb33f.io", doc.Info.Value.TermsOfService.Value)
|
assert.Equal(t, "https://pb33f.io", doc.Info.Value.TermsOfService.Value)
|
||||||
assert.Equal(t, "pb33f", doc.Info.Value.Contact.Value.Name.Value)
|
assert.Equal(t, "pb33f", doc.Info.Value.Contact.Value.Name.Value)
|
||||||
assert.Equal(t, "buckaroo@pb33f.io", doc.Info.Value.Contact.Value.Email.Value)
|
assert.Equal(t, "buckaroo@pb33f.io", doc.Info.Value.Contact.Value.Email.Value)
|
||||||
assert.Equal(t, "https://pb33f.io", doc.Info.Value.Contact.Value.URL.Value)
|
assert.Equal(t, "https://pb33f.io", doc.Info.Value.Contact.Value.URL.Value)
|
||||||
|
assert.Equal(t, "pb33f", doc.Info.Value.License.Value.Name.Value)
|
||||||
|
assert.Equal(t, "https://pb33f.io/made-up", doc.Info.Value.License.Value.URL.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateDocument_Servers(t *testing.T) {
|
||||||
|
assert.Len(t, doc.Servers, 2)
|
||||||
|
server1 := doc.Servers[0]
|
||||||
|
server2 := doc.Servers[1]
|
||||||
|
|
||||||
|
// server 1
|
||||||
|
assert.Equal(t, "{scheme}://api.pb33f.io", server1.Value.URL.Value)
|
||||||
|
assert.NotEmpty(t, server1.Value.Description.Value)
|
||||||
|
assert.Len(t, server1.Value.Variables.Value, 1)
|
||||||
|
assert.Len(t, server1.Value.Variables.Value["scheme"].Value.Enum, 2)
|
||||||
|
assert.Equal(t, server1.Value.Variables.Value["scheme"].Value.Default.Value, "https")
|
||||||
|
assert.NotEmpty(t, server1.Value.Variables.Value["scheme"].Value.Description.Value)
|
||||||
|
|
||||||
|
// server 2
|
||||||
|
assert.Equal(t, "https://{domain}.{host}.com", server2.Value.URL.Value)
|
||||||
|
assert.NotEmpty(t, server2.Value.Description.Value)
|
||||||
|
assert.Len(t, server2.Value.Variables.Value, 2)
|
||||||
|
assert.Equal(t, server2.Value.Variables.Value["domain"].Value.Default.Value, "api")
|
||||||
|
assert.NotEmpty(t, server2.Value.Variables.Value["domain"].Value.Description.Value)
|
||||||
|
assert.NotEmpty(t, server2.Value.Variables.Value["host"].Value.Description.Value)
|
||||||
|
assert.Equal(t, server2.Value.Variables.Value["host"].Value.Default.Value, "pb33f.io")
|
||||||
assert.Equal(t, "1.2", doc.Info.Value.Version.Value)
|
assert.Equal(t, "1.2", doc.Info.Value.Version.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateDocument_Tags(t *testing.T) {
|
||||||
|
assert.Len(t, doc.Tags, 2)
|
||||||
|
|
||||||
|
// tag1
|
||||||
|
assert.Equal(t, "Burgers", doc.Tags[0].Value.Name.Value)
|
||||||
|
assert.NotEmpty(t, doc.Tags[0].Value.Description.Value)
|
||||||
|
assert.NotNil(t, doc.Tags[0].Value.ExternalDocs.Value)
|
||||||
|
assert.Equal(t, "https://pb33f.io", doc.Tags[0].Value.ExternalDocs.Value.URL.Value)
|
||||||
|
assert.NotEmpty(t, doc.Tags[0].Value.ExternalDocs.Value.URL.Value)
|
||||||
|
assert.Len(t, doc.Tags[0].Value.Extensions, 7)
|
||||||
|
|
||||||
|
for key, extension := range doc.Tags[0].Value.Extensions {
|
||||||
|
switch key.Value {
|
||||||
|
case "x-internal-ting":
|
||||||
|
assert.Equal(t, "somethingSpecial", extension.Value)
|
||||||
|
case "x-internal-tong":
|
||||||
|
assert.Equal(t, int64(1), extension.Value)
|
||||||
|
case "x-internal-tang":
|
||||||
|
assert.Equal(t, 1.2, extension.Value)
|
||||||
|
case "x-internal-tung":
|
||||||
|
assert.Equal(t, true, extension.Value)
|
||||||
|
case "x-internal-arr":
|
||||||
|
assert.Len(t, extension.Value, 2)
|
||||||
|
assert.Equal(t, "one", extension.Value.([]interface{})[0].(string))
|
||||||
|
case "x-internal-arrmap":
|
||||||
|
assert.Len(t, extension.Value, 2)
|
||||||
|
assert.Equal(t, "now", extension.Value.([]interface{})[0].(map[string]interface{})["what"])
|
||||||
|
case "x-something-else":
|
||||||
|
// crazy times in the upside down. this API should be avoided for the higher up use cases.
|
||||||
|
// this is why we will need a higher level API to this model, this looks cool and all, but dude.
|
||||||
|
assert.Equal(t, "now?", extension.Value.(map[string]interface{})["ok"].([]interface{})[0].(map[string]interface{})["what"])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// tag2
|
||||||
|
assert.Equal(t, "Dressing", doc.Tags[1].Value.Name.Value)
|
||||||
|
assert.NotEmpty(t, doc.Tags[1].Value.Description.Value)
|
||||||
|
assert.NotNil(t, doc.Tags[1].Value.ExternalDocs.Value)
|
||||||
|
assert.Equal(t, "https://pb33f.io", doc.Tags[1].Value.ExternalDocs.Value.URL.Value)
|
||||||
|
assert.NotEmpty(t, doc.Tags[1].Value.ExternalDocs.Value.URL.Value)
|
||||||
|
assert.Len(t, doc.Tags[1].Value.Extensions, 0)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,25 +10,38 @@ info:
|
|||||||
url: https://pb33f.io
|
url: https://pb33f.io
|
||||||
license:
|
license:
|
||||||
name: pb33f
|
name: pb33f
|
||||||
url: https://quobix.com/made-up
|
url: https://pb33f.io/made-up
|
||||||
version: "1.2"
|
version: "1.2"
|
||||||
tags:
|
tags:
|
||||||
- name: "Burgers"
|
- name: "Burgers"
|
||||||
description: "All kinds of yummy burgers."
|
description: "All kinds of yummy burgers."
|
||||||
externalDocs:
|
externalDocs:
|
||||||
description: "Find out more"
|
description: "Find out more"
|
||||||
url: "https://quobix.com/"
|
url: "https://pb33f.io"
|
||||||
|
x-internal-ting: somethingSpecial
|
||||||
|
x-internal-tong: 1
|
||||||
|
x-internal-tang: 1.2
|
||||||
|
x-internal-tung: true
|
||||||
|
x-internal-arr:
|
||||||
|
- one
|
||||||
|
- two
|
||||||
|
x-internal-arrmap:
|
||||||
|
- what: now
|
||||||
|
- why: that
|
||||||
|
x-something-else:
|
||||||
|
ok:
|
||||||
|
- what: now?
|
||||||
- name: "Dressing"
|
- name: "Dressing"
|
||||||
description: "Variety of dressings: cheese, veggie, oil and a lot more"
|
description: "Variety of dressings: cheese, veggie, oil and a lot more"
|
||||||
externalDocs:
|
externalDocs:
|
||||||
description: "Find out more information about our products)"
|
description: "Find out more information about our products)"
|
||||||
url: "https://quobix.com/"
|
url: "https://pb33f.io"
|
||||||
servers:
|
servers:
|
||||||
- url: "{scheme}://api.quobix.com"
|
- url: "{scheme}://api.pb33f.io"
|
||||||
description: "this is our main API server, for all fun API things."
|
description: "this is our main API server, for all fun API things."
|
||||||
variables:
|
variables:
|
||||||
scheme:
|
scheme:
|
||||||
enum: [https]
|
enum: [https, wss]
|
||||||
default: https
|
default: https
|
||||||
description: this is a server variable for the scheme
|
description: this is a server variable for the scheme
|
||||||
- url: "https://{domain}.{host}.com"
|
- url: "https://{domain}.{host}.com"
|
||||||
@@ -38,8 +51,8 @@ servers:
|
|||||||
default: "api"
|
default: "api"
|
||||||
description: the default API domain is 'api'
|
description: the default API domain is 'api'
|
||||||
host:
|
host:
|
||||||
default: "quobix.com"
|
default: "pb33f.io"
|
||||||
description: the default host for this API is 'quobix.com'
|
description: the default host for this API is 'pb33f.com'
|
||||||
paths:
|
paths:
|
||||||
/burgers:
|
/burgers:
|
||||||
post:
|
post:
|
||||||
|
|||||||
738
utils/utils.go
738
utils/utils.go
@@ -1,265 +1,285 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/iancoleman/strcase"
|
"github.com/iancoleman/strcase"
|
||||||
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
|
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Case int8
|
type Case int8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// OpenApi3 is used by all OpenAPI 3+ docs
|
// OpenApi3 is used by all OpenAPI 3+ docs
|
||||||
OpenApi3 = "openapi"
|
OpenApi3 = "openapi"
|
||||||
|
|
||||||
// OpenApi2 is used by all OpenAPI 2 docs, formerly known as swagger.
|
// OpenApi2 is used by all OpenAPI 2 docs, formerly known as swagger.
|
||||||
OpenApi2 = "swagger"
|
OpenApi2 = "swagger"
|
||||||
|
|
||||||
// AsyncApi is used by akk AsyncAPI docs, all versions.
|
// AsyncApi is used by akk AsyncAPI docs, all versions.
|
||||||
AsyncApi = "asyncapi"
|
AsyncApi = "asyncapi"
|
||||||
|
|
||||||
PascalCase Case = iota
|
PascalCase Case = iota
|
||||||
CamelCase
|
CamelCase
|
||||||
ScreamingSnakeCase
|
ScreamingSnakeCase
|
||||||
SnakeCase
|
SnakeCase
|
||||||
KebabCase
|
KebabCase
|
||||||
ScreamingKebabCase
|
ScreamingKebabCase
|
||||||
RegularCase
|
RegularCase
|
||||||
)
|
)
|
||||||
|
|
||||||
// FindNodes will find a node based on JSONPath, it accepts raw yaml/json as input.
|
// FindNodes will find a node based on JSONPath, it accepts raw yaml/json as input.
|
||||||
func FindNodes(yamlData []byte, jsonPath string) ([]*yaml.Node, error) {
|
func FindNodes(yamlData []byte, jsonPath string) ([]*yaml.Node, error) {
|
||||||
jsonPath = FixContext(jsonPath)
|
jsonPath = FixContext(jsonPath)
|
||||||
|
|
||||||
var node yaml.Node
|
var node yaml.Node
|
||||||
yaml.Unmarshal(yamlData, &node)
|
yaml.Unmarshal(yamlData, &node)
|
||||||
|
|
||||||
path, err := yamlpath.NewPath(jsonPath)
|
path, err := yamlpath.NewPath(jsonPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
results, _ := path.Find(&node)
|
results, _ := path.Find(&node)
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindLastChildNode(node *yaml.Node) *yaml.Node {
|
func FindLastChildNode(node *yaml.Node) *yaml.Node {
|
||||||
s := len(node.Content) - 1
|
s := len(node.Content) - 1
|
||||||
if s < 0 {
|
if s < 0 {
|
||||||
s = 0
|
s = 0
|
||||||
}
|
}
|
||||||
if len(node.Content) > 0 && len(node.Content[s].Content) > 0 {
|
if len(node.Content) > 0 && len(node.Content[s].Content) > 0 {
|
||||||
return FindLastChildNode(node.Content[s])
|
return FindLastChildNode(node.Content[s])
|
||||||
} else {
|
} else {
|
||||||
if len(node.Content) > 0 {
|
if len(node.Content) > 0 {
|
||||||
return node.Content[s]
|
return node.Content[s]
|
||||||
}
|
}
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildPath will construct a JSONPath from a base and an array of strings.
|
// BuildPath will construct a JSONPath from a base and an array of strings.
|
||||||
func BuildPath(basePath string, segs []string) string {
|
func BuildPath(basePath string, segs []string) string {
|
||||||
|
|
||||||
path := strings.Join(segs, ".")
|
path := strings.Join(segs, ".")
|
||||||
|
|
||||||
// trim that last period.
|
// trim that last period.
|
||||||
if len(path) > 0 && path[len(path)-1] == '.' {
|
if len(path) > 0 && path[len(path)-1] == '.' {
|
||||||
path = path[:len(path)-1]
|
path = path[:len(path)-1]
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s.%s", basePath, path)
|
return fmt.Sprintf("%s.%s", basePath, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindNodesWithoutDeserializing will find a node based on JSONPath, without deserializing from yaml/json
|
// FindNodesWithoutDeserializing will find a node based on JSONPath, without deserializing from yaml/json
|
||||||
func FindNodesWithoutDeserializing(node *yaml.Node, jsonPath string) ([]*yaml.Node, error) {
|
func FindNodesWithoutDeserializing(node *yaml.Node, jsonPath string) ([]*yaml.Node, error) {
|
||||||
jsonPath = FixContext(jsonPath)
|
jsonPath = FixContext(jsonPath)
|
||||||
|
|
||||||
path, err := yamlpath.NewPath(jsonPath)
|
path, err := yamlpath.NewPath(jsonPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
results, _ := path.Find(node)
|
results, _ := path.Find(node)
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConvertInterfaceIntoStringMap will convert an unknown input into a string map.
|
// ConvertInterfaceIntoStringMap will convert an unknown input into a string map.
|
||||||
func ConvertInterfaceIntoStringMap(context interface{}) map[string]string {
|
func ConvertInterfaceIntoStringMap(context interface{}) map[string]string {
|
||||||
converted := make(map[string]string)
|
converted := make(map[string]string)
|
||||||
if context != nil {
|
if context != nil {
|
||||||
if v, ok := context.(map[string]interface{}); ok {
|
if v, ok := context.(map[string]interface{}); ok {
|
||||||
for k, n := range v {
|
for k, n := range v {
|
||||||
if s, okB := n.(string); okB {
|
if s, okB := n.(string); okB {
|
||||||
converted[k] = s
|
converted[k] = s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if v, ok := context.(map[string]string); ok {
|
if v, ok := context.(map[string]string); ok {
|
||||||
for k, n := range v {
|
for k, n := range v {
|
||||||
converted[k] = n
|
converted[k] = n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return converted
|
return converted
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConvertInterfaceToStringArray will convert an unknown input map type into a string array/slice
|
// ConvertInterfaceToStringArray will convert an unknown input map type into a string array/slice
|
||||||
func ConvertInterfaceToStringArray(raw interface{}) []string {
|
func ConvertInterfaceToStringArray(raw interface{}) []string {
|
||||||
if vals, ok := raw.(map[string]interface{}); ok {
|
if vals, ok := raw.(map[string]interface{}); ok {
|
||||||
var s []string
|
var s []string
|
||||||
for _, v := range vals {
|
for _, v := range vals {
|
||||||
if g, y := v.([]interface{}); y {
|
if g, y := v.([]interface{}); y {
|
||||||
for _, q := range g {
|
for _, q := range g {
|
||||||
s = append(s, fmt.Sprint(q))
|
s = append(s, fmt.Sprint(q))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
if vals, ok := raw.(map[string][]string); ok {
|
if vals, ok := raw.(map[string][]string); ok {
|
||||||
var s []string
|
var s []string
|
||||||
for _, v := range vals {
|
for _, v := range vals {
|
||||||
s = append(s, v...)
|
s = append(s, v...)
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConvertInterfaceArrayToStringArray will convert an unknown interface array type, into a string slice
|
// ConvertInterfaceArrayToStringArray will convert an unknown interface array type, into a string slice
|
||||||
func ConvertInterfaceArrayToStringArray(raw interface{}) []string {
|
func ConvertInterfaceArrayToStringArray(raw interface{}) []string {
|
||||||
if vals, ok := raw.([]interface{}); ok {
|
if vals, ok := raw.([]interface{}); ok {
|
||||||
s := make([]string, len(vals))
|
s := make([]string, len(vals))
|
||||||
for i, v := range vals {
|
for i, v := range vals {
|
||||||
s[i] = fmt.Sprint(v)
|
s[i] = fmt.Sprint(v)
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
if vals, ok := raw.([]string); ok {
|
if vals, ok := raw.([]string); ok {
|
||||||
return vals
|
return vals
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtractValueFromInterfaceMap pulls out an unknown value from a map using a string key
|
// ExtractValueFromInterfaceMap pulls out an unknown value from a map using a string key
|
||||||
func ExtractValueFromInterfaceMap(name string, raw interface{}) interface{} {
|
func ExtractValueFromInterfaceMap(name string, raw interface{}) interface{} {
|
||||||
|
|
||||||
if propMap, ok := raw.(map[string]interface{}); ok {
|
if propMap, ok := raw.(map[string]interface{}); ok {
|
||||||
if props, okn := propMap[name].([]interface{}); okn {
|
if props, okn := propMap[name].([]interface{}); okn {
|
||||||
return props
|
return props
|
||||||
} else {
|
} else {
|
||||||
return propMap[name]
|
return propMap[name]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if propMap, ok := raw.(map[string][]string); ok {
|
if propMap, ok := raw.(map[string][]string); ok {
|
||||||
return propMap[name]
|
return propMap[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindFirstKeyNode will locate the first key and value yaml.Node based on a key.
|
// FindFirstKeyNode will locate the first key and value yaml.Node based on a key.
|
||||||
func FindFirstKeyNode(key string, nodes []*yaml.Node, depth int) (keyNode *yaml.Node, valueNode *yaml.Node) {
|
func FindFirstKeyNode(key string, nodes []*yaml.Node, depth int) (keyNode *yaml.Node, valueNode *yaml.Node) {
|
||||||
if depth > 40 {
|
if depth > 40 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
for i, v := range nodes {
|
for i, v := range nodes {
|
||||||
if key != "" && key == v.Value {
|
if key != "" && key == v.Value {
|
||||||
if i+1 >= len(nodes) {
|
if i+1 >= len(nodes) {
|
||||||
return v, nodes[i] // next node is what we need.
|
return v, nodes[i] // next node is what we need.
|
||||||
}
|
}
|
||||||
return v, nodes[i+1] // next node is what we need.
|
return v, nodes[i+1] // next node is what we need.
|
||||||
}
|
}
|
||||||
if len(v.Content) > 0 {
|
if len(v.Content) > 0 {
|
||||||
depth++
|
depth++
|
||||||
x, y := FindFirstKeyNode(key, v.Content, depth)
|
x, y := FindFirstKeyNode(key, v.Content, depth)
|
||||||
if x != nil && y != nil {
|
if x != nil && y != nil {
|
||||||
return x, y
|
return x, y
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// KeyNodeResult is a result from a KeyNodeSearch performed by the FindAllKeyNodesWithPath
|
// KeyNodeResult is a result from a KeyNodeSearch performed by the FindAllKeyNodesWithPath
|
||||||
type KeyNodeResult struct {
|
type KeyNodeResult struct {
|
||||||
KeyNode *yaml.Node
|
KeyNode *yaml.Node
|
||||||
ValueNode *yaml.Node
|
ValueNode *yaml.Node
|
||||||
Parent *yaml.Node
|
Parent *yaml.Node
|
||||||
Path []yaml.Node
|
Path []yaml.Node
|
||||||
}
|
}
|
||||||
|
|
||||||
// KeyNodeSearch keeps a track of everything we have found on our adventure down the trees.
|
// KeyNodeSearch keeps a track of everything we have found on our adventure down the trees.
|
||||||
type KeyNodeSearch struct {
|
type KeyNodeSearch struct {
|
||||||
Key string
|
Key string
|
||||||
Ignore []string
|
Ignore []string
|
||||||
Results []*KeyNodeResult
|
Results []*KeyNodeResult
|
||||||
AllowExtensions bool
|
AllowExtensions bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindKeyNodeTop is a non-recursive search of top level nodes for a key, will not look at content.
|
// FindKeyNodeTop is a non-recursive search of top level nodes for a key, will not look at content.
|
||||||
// Returns the key and value
|
// Returns the key and value
|
||||||
func FindKeyNodeTop(key string, nodes []*yaml.Node) (keyNode *yaml.Node, valueNode *yaml.Node) {
|
func FindKeyNodeTop(key string, nodes []*yaml.Node) (keyNode *yaml.Node, valueNode *yaml.Node) {
|
||||||
|
|
||||||
for i, v := range nodes {
|
for i, v := range nodes {
|
||||||
if key == v.Value {
|
if key == v.Value {
|
||||||
return v, nodes[i+1] // next node is what we need.
|
return v, nodes[i+1] // next node is what we need.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindKeyNode is a non-recursive search of a *yaml.Node Content for a child node with a key.
|
// FindKeyNode is a non-recursive search of a *yaml.Node Content for a child node with a key.
|
||||||
// Returns the key and value
|
// Returns the key and value
|
||||||
func FindKeyNode(key string, nodes []*yaml.Node) (keyNode *yaml.Node, valueNode *yaml.Node) {
|
func FindKeyNode(key string, nodes []*yaml.Node) (keyNode *yaml.Node, valueNode *yaml.Node) {
|
||||||
|
|
||||||
//numNodes := len(nodes)
|
//numNodes := len(nodes)
|
||||||
for i, v := range nodes {
|
for i, v := range nodes {
|
||||||
if i%2 == 0 && key == v.Value {
|
if i%2 == 0 && key == v.Value {
|
||||||
return v, nodes[i+1] // next node is what we need.
|
return v, nodes[i+1] // next node is what we need.
|
||||||
}
|
}
|
||||||
for x, j := range v.Content {
|
for x, j := range v.Content {
|
||||||
if key == j.Value {
|
if key == j.Value {
|
||||||
if IsNodeMap(v) {
|
if IsNodeMap(v) {
|
||||||
if x+1 == len(v.Content) {
|
if x+1 == len(v.Content) {
|
||||||
return v, v.Content[x]
|
return v, v.Content[x]
|
||||||
}
|
}
|
||||||
return v, v.Content[x+1] // next node is what we need.
|
return v, v.Content[x+1] // next node is what we need.
|
||||||
|
|
||||||
}
|
}
|
||||||
if IsNodeArray(v) {
|
if IsNodeArray(v) {
|
||||||
return v, v.Content[x]
|
return v, v.Content[x]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindKeyNodeFull(key string, nodes []*yaml.Node) (keyNode *yaml.Node, labelNode *yaml.Node, valueNode *yaml.Node) {
|
func FindKeyNodeFull(key string, nodes []*yaml.Node) (keyNode *yaml.Node, labelNode *yaml.Node, valueNode *yaml.Node) {
|
||||||
for i, v := range nodes {
|
for i, v := range nodes {
|
||||||
if i%2 == 0 && key == v.Value {
|
if i%2 == 0 && key == v.Value {
|
||||||
return v, nodes[i], nodes[i+1] // next node is what we need.
|
return v, nodes[i], nodes[i+1] // next node is what we need.
|
||||||
}
|
}
|
||||||
for x, j := range v.Content {
|
for x, j := range v.Content {
|
||||||
if key == j.Value {
|
if key == j.Value {
|
||||||
if IsNodeMap(v) {
|
if IsNodeMap(v) {
|
||||||
if x+1 == len(v.Content) {
|
if x+1 == len(v.Content) {
|
||||||
return v, v.Content[x], v.Content[x]
|
return v, v.Content[x], v.Content[x]
|
||||||
}
|
}
|
||||||
return v, v.Content[x], v.Content[x+1] // next node is what we need.
|
return v, v.Content[x], v.Content[x+1] // next node is what we need.
|
||||||
|
|
||||||
}
|
}
|
||||||
if IsNodeArray(v) {
|
if IsNodeArray(v) {
|
||||||
return v, v.Content[x], v.Content[x]
|
return v, v.Content[x], v.Content[x]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, nil, nil
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExtensionNode struct {
|
||||||
|
Key *yaml.Node
|
||||||
|
Value *yaml.Node
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindExtensionNodes(nodes []*yaml.Node) []*ExtensionNode {
|
||||||
|
var extensions []*ExtensionNode
|
||||||
|
for i, v := range nodes {
|
||||||
|
if i%2 == 0 && strings.HasPrefix(v.Value, "x-") {
|
||||||
|
if i+1 < len(nodes) {
|
||||||
|
extensions = append(extensions, &ExtensionNode{
|
||||||
|
Key: v,
|
||||||
|
Value: nodes[i+1],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return extensions
|
||||||
}
|
}
|
||||||
|
|
||||||
var ObjectLabel = "object"
|
var ObjectLabel = "object"
|
||||||
@@ -273,257 +293,257 @@ var SchemaSource = "https://json-schema.org/draft/2020-12/schema"
|
|||||||
var SchemaId = "https://quobix.com/api/vacuum"
|
var SchemaId = "https://quobix.com/api/vacuum"
|
||||||
|
|
||||||
func MakeTagReadable(node *yaml.Node) string {
|
func MakeTagReadable(node *yaml.Node) string {
|
||||||
switch node.Tag {
|
switch node.Tag {
|
||||||
case "!!map":
|
case "!!map":
|
||||||
return ObjectLabel
|
return ObjectLabel
|
||||||
case "!!seq":
|
case "!!seq":
|
||||||
return ArrayLabel
|
return ArrayLabel
|
||||||
case "!!str":
|
case "!!str":
|
||||||
return StringLabel
|
return StringLabel
|
||||||
case "!!int":
|
case "!!int":
|
||||||
return IntegerLabel
|
return IntegerLabel
|
||||||
case "!!float":
|
case "!!float":
|
||||||
return NumberLabel
|
return NumberLabel
|
||||||
case "!!bool":
|
case "!!bool":
|
||||||
return BooleanLabel
|
return BooleanLabel
|
||||||
}
|
}
|
||||||
return "unknown"
|
return "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNodeMap checks if the node is a map type
|
// IsNodeMap checks if the node is a map type
|
||||||
func IsNodeMap(node *yaml.Node) bool {
|
func IsNodeMap(node *yaml.Node) bool {
|
||||||
if node == nil {
|
if node == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return node.Tag == "!!map"
|
return node.Tag == "!!map"
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNodePolyMorphic will return true if the node contains polymorphic keys.
|
// IsNodePolyMorphic will return true if the node contains polymorphic keys.
|
||||||
func IsNodePolyMorphic(node *yaml.Node) bool {
|
func IsNodePolyMorphic(node *yaml.Node) bool {
|
||||||
for i, v := range node.Content {
|
for i, v := range node.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
if v.Value == "anyOf" || v.Value == "oneOf" || v.Value == "allOf" {
|
if v.Value == "anyOf" || v.Value == "oneOf" || v.Value == "allOf" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNodeArray checks if a node is an array type
|
// IsNodeArray checks if a node is an array type
|
||||||
func IsNodeArray(node *yaml.Node) bool {
|
func IsNodeArray(node *yaml.Node) bool {
|
||||||
if node == nil {
|
if node == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return node.Tag == "!!seq"
|
return node.Tag == "!!seq"
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNodeStringValue checks if a node is a string value
|
// IsNodeStringValue checks if a node is a string value
|
||||||
func IsNodeStringValue(node *yaml.Node) bool {
|
func IsNodeStringValue(node *yaml.Node) bool {
|
||||||
if node == nil {
|
if node == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return node.Tag == "!!str"
|
return node.Tag == "!!str"
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNodeIntValue will check if a node is an int value
|
// IsNodeIntValue will check if a node is an int value
|
||||||
func IsNodeIntValue(node *yaml.Node) bool {
|
func IsNodeIntValue(node *yaml.Node) bool {
|
||||||
if node == nil {
|
if node == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return node.Tag == "!!int"
|
return node.Tag == "!!int"
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNodeFloatValue will check is a node is a float value.
|
// IsNodeFloatValue will check is a node is a float value.
|
||||||
func IsNodeFloatValue(node *yaml.Node) bool {
|
func IsNodeFloatValue(node *yaml.Node) bool {
|
||||||
if node == nil {
|
if node == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return node.Tag == "!!float"
|
return node.Tag == "!!float"
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNodeBoolValue will check is a node is a bool
|
// IsNodeBoolValue will check is a node is a bool
|
||||||
func IsNodeBoolValue(node *yaml.Node) bool {
|
func IsNodeBoolValue(node *yaml.Node) bool {
|
||||||
if node == nil {
|
if node == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return node.Tag == "!!bool"
|
return node.Tag == "!!bool"
|
||||||
}
|
}
|
||||||
|
|
||||||
// FixContext will clean up a JSONpath string to be correctly traversable.
|
// FixContext will clean up a JSONpath string to be correctly traversable.
|
||||||
func FixContext(context string) string {
|
func FixContext(context string) string {
|
||||||
|
|
||||||
tokens := strings.Split(context, ".")
|
tokens := strings.Split(context, ".")
|
||||||
var cleaned = []string{}
|
var cleaned = []string{}
|
||||||
for i, t := range tokens {
|
for i, t := range tokens {
|
||||||
|
|
||||||
if v, err := strconv.Atoi(t); err == nil {
|
if v, err := strconv.Atoi(t); err == nil {
|
||||||
|
|
||||||
if v < 200 { // codes start here
|
if v < 200 { // codes start here
|
||||||
if cleaned[i-1] != "" {
|
if cleaned[i-1] != "" {
|
||||||
cleaned[i-1] += fmt.Sprintf("[%v]", t)
|
cleaned[i-1] += fmt.Sprintf("[%v]", t)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cleaned = append(cleaned, t)
|
cleaned = append(cleaned, t)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
cleaned = append(cleaned, strings.ReplaceAll(t, "(root)", "$"))
|
cleaned = append(cleaned, strings.ReplaceAll(t, "(root)", "$"))
|
||||||
|
|
||||||
}
|
}
|
||||||
return strings.Join(cleaned, ".")
|
return strings.Join(cleaned, ".")
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsJSON will tell you if a string is JSON or not.
|
// IsJSON will tell you if a string is JSON or not.
|
||||||
func IsJSON(testString string) bool {
|
func IsJSON(testString string) bool {
|
||||||
if testString == "" {
|
if testString == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
runes := []rune(strings.TrimSpace(testString))
|
runes := []rune(strings.TrimSpace(testString))
|
||||||
if runes[0] == '{' && runes[len(runes)-1] == '}' {
|
if runes[0] == '{' && runes[len(runes)-1] == '}' {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsYAML will tell you if a string is YAML or not.
|
// IsYAML will tell you if a string is YAML or not.
|
||||||
func IsYAML(testString string) bool {
|
func IsYAML(testString string) bool {
|
||||||
if testString == "" {
|
if testString == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if IsJSON(testString) {
|
if IsJSON(testString) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
var n interface{}
|
var n interface{}
|
||||||
err := yaml.Unmarshal([]byte(testString), &n)
|
err := yaml.Unmarshal([]byte(testString), &n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
_, err = yaml.Marshal(n)
|
_, err = yaml.Marshal(n)
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConvertYAMLtoJSON will do exactly what you think it will. It will deserialize YAML into serialized JSON.
|
// ConvertYAMLtoJSON will do exactly what you think it will. It will deserialize YAML into serialized JSON.
|
||||||
func ConvertYAMLtoJSON(yamlData []byte) ([]byte, error) {
|
func ConvertYAMLtoJSON(yamlData []byte) ([]byte, error) {
|
||||||
var decodedYaml map[string]interface{}
|
var decodedYaml map[string]interface{}
|
||||||
err := yaml.Unmarshal(yamlData, &decodedYaml)
|
err := yaml.Unmarshal(yamlData, &decodedYaml)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// if the data can be decoded, it can be encoded (that's my view anyway). no need for an error check.
|
// if the data can be decoded, it can be encoded (that's my view anyway). no need for an error check.
|
||||||
jsonData, _ := json.Marshal(decodedYaml)
|
jsonData, _ := json.Marshal(decodedYaml)
|
||||||
return jsonData, nil
|
return jsonData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsHttpVerb will check if an operation is valid or not.
|
// IsHttpVerb will check if an operation is valid or not.
|
||||||
func IsHttpVerb(verb string) bool {
|
func IsHttpVerb(verb string) bool {
|
||||||
verbs := []string{"get", "post", "put", "patch", "delete", "options", "trace", "head"}
|
verbs := []string{"get", "post", "put", "patch", "delete", "options", "trace", "head"}
|
||||||
for _, v := range verbs {
|
for _, v := range verbs {
|
||||||
if verb == v {
|
if verb == v {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConvertComponentIdIntoFriendlyPathSearch(id string) (string, string) {
|
func ConvertComponentIdIntoFriendlyPathSearch(id string) (string, string) {
|
||||||
segs := strings.Split(id, "/")
|
segs := strings.Split(id, "/")
|
||||||
name := segs[len(segs)-1]
|
name := segs[len(segs)-1]
|
||||||
|
|
||||||
replaced := strings.ReplaceAll(fmt.Sprintf("%s['%s']",
|
replaced := strings.ReplaceAll(fmt.Sprintf("%s['%s']",
|
||||||
strings.Join(segs[:len(segs)-1], "."), name), "#", "$")
|
strings.Join(segs[:len(segs)-1], "."), name), "#", "$")
|
||||||
|
|
||||||
if replaced[0] != '$' {
|
if replaced[0] != '$' {
|
||||||
replaced = fmt.Sprintf("$%s", replaced)
|
replaced = fmt.Sprintf("$%s", replaced)
|
||||||
}
|
}
|
||||||
return name, replaced
|
return name, replaced
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConvertComponentIdIntoPath(id string) (string, string) {
|
func ConvertComponentIdIntoPath(id string) (string, string) {
|
||||||
segs := strings.Split(id, "/")
|
segs := strings.Split(id, "/")
|
||||||
name := segs[len(segs)-1]
|
name := segs[len(segs)-1]
|
||||||
|
|
||||||
return name, strings.ReplaceAll(fmt.Sprintf("%s.%s",
|
return name, strings.ReplaceAll(fmt.Sprintf("%s.%s",
|
||||||
strings.Join(segs[:len(segs)-1], "."), name), "#", "$")
|
strings.Join(segs[:len(segs)-1], "."), name), "#", "$")
|
||||||
}
|
}
|
||||||
|
|
||||||
func RenderCodeSnippet(startNode *yaml.Node, specData []string, before, after int) string {
|
func RenderCodeSnippet(startNode *yaml.Node, specData []string, before, after int) string {
|
||||||
|
|
||||||
buf := new(strings.Builder)
|
buf := new(strings.Builder)
|
||||||
|
|
||||||
startLine := startNode.Line - before
|
startLine := startNode.Line - before
|
||||||
endLine := startNode.Line + after
|
endLine := startNode.Line + after
|
||||||
|
|
||||||
if startLine < 0 {
|
if startLine < 0 {
|
||||||
startLine = 0
|
startLine = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if endLine >= len(specData) {
|
if endLine >= len(specData) {
|
||||||
endLine = len(specData) - 1
|
endLine = len(specData) - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
delta := endLine - startLine
|
delta := endLine - startLine
|
||||||
|
|
||||||
for i := 0; i < delta; i++ {
|
for i := 0; i < delta; i++ {
|
||||||
l := startLine + i
|
l := startLine + i
|
||||||
if l < len(specData) {
|
if l < len(specData) {
|
||||||
line := specData[l]
|
line := specData[l]
|
||||||
buf.WriteString(fmt.Sprintf("%s\n", line))
|
buf.WriteString(fmt.Sprintf("%s\n", line))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConvertCase(input string, convert Case) string {
|
func ConvertCase(input string, convert Case) string {
|
||||||
if input == "" {
|
if input == "" {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
switch convert {
|
switch convert {
|
||||||
case PascalCase:
|
case PascalCase:
|
||||||
return strcase.ToCamel(input)
|
return strcase.ToCamel(input)
|
||||||
case CamelCase:
|
case CamelCase:
|
||||||
return strcase.ToLowerCamel(input)
|
return strcase.ToLowerCamel(input)
|
||||||
case ScreamingKebabCase:
|
case ScreamingKebabCase:
|
||||||
return strcase.ToScreamingKebab(input)
|
return strcase.ToScreamingKebab(input)
|
||||||
case ScreamingSnakeCase:
|
case ScreamingSnakeCase:
|
||||||
return strcase.ToScreamingSnake(input)
|
return strcase.ToScreamingSnake(input)
|
||||||
case SnakeCase:
|
case SnakeCase:
|
||||||
return strcase.ToSnake(input)
|
return strcase.ToSnake(input)
|
||||||
default:
|
default:
|
||||||
return input
|
return input
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func DetectCase(input string) Case {
|
func DetectCase(input string) Case {
|
||||||
trim := strings.TrimSpace(input)
|
trim := strings.TrimSpace(input)
|
||||||
if trim == "" {
|
if trim == "" {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
pascalCase := regexp.MustCompile("^[A-Z][a-z]+(?:[A-Z][a-z]+)*$")
|
pascalCase := regexp.MustCompile("^[A-Z][a-z]+(?:[A-Z][a-z]+)*$")
|
||||||
camelCase := regexp.MustCompile("^[a-z]+(?:[A-Z][a-z]+)*$")
|
camelCase := regexp.MustCompile("^[a-z]+(?:[A-Z][a-z]+)*$")
|
||||||
screamingSnakeCase := regexp.MustCompile("^[A-Z]+(_[A-Z]+)*$")
|
screamingSnakeCase := regexp.MustCompile("^[A-Z]+(_[A-Z]+)*$")
|
||||||
snakeCase := regexp.MustCompile("^[a-z]+(_[a-z]+)*$")
|
snakeCase := regexp.MustCompile("^[a-z]+(_[a-z]+)*$")
|
||||||
kebabCase := regexp.MustCompile("^[a-z]+(-[a-z]+)*$")
|
kebabCase := regexp.MustCompile("^[a-z]+(-[a-z]+)*$")
|
||||||
screamingKebabCase := regexp.MustCompile("^[A-Z]+(-[A-Z]+)*$")
|
screamingKebabCase := regexp.MustCompile("^[A-Z]+(-[A-Z]+)*$")
|
||||||
if pascalCase.MatchString(trim) {
|
if pascalCase.MatchString(trim) {
|
||||||
return PascalCase
|
return PascalCase
|
||||||
}
|
}
|
||||||
if camelCase.MatchString(trim) {
|
if camelCase.MatchString(trim) {
|
||||||
return CamelCase
|
return CamelCase
|
||||||
}
|
}
|
||||||
if screamingSnakeCase.MatchString(trim) {
|
if screamingSnakeCase.MatchString(trim) {
|
||||||
return ScreamingSnakeCase
|
return ScreamingSnakeCase
|
||||||
}
|
}
|
||||||
if snakeCase.MatchString(trim) {
|
if snakeCase.MatchString(trim) {
|
||||||
return SnakeCase
|
return SnakeCase
|
||||||
}
|
}
|
||||||
if kebabCase.MatchString(trim) {
|
if kebabCase.MatchString(trim) {
|
||||||
return KebabCase
|
return KebabCase
|
||||||
}
|
}
|
||||||
if screamingKebabCase.MatchString(trim) {
|
if screamingKebabCase.MatchString(trim) {
|
||||||
return ScreamingKebabCase
|
return ScreamingKebabCase
|
||||||
}
|
}
|
||||||
return RegularCase
|
return RegularCase
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user