mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 20:47:49 +00:00
Added oAuth rendering, updated node builder.
This commit is contained in:
@@ -148,9 +148,13 @@ func (n *NodeBuilder) add(key string) {
|
|||||||
value = reflect.ValueOf(fLow)
|
value = reflect.ValueOf(fLow)
|
||||||
switch value.Kind() {
|
switch value.Kind() {
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
nb := value.Interface().(low.HasValueNodeUntyped).GetValueNode()
|
y := value.Interface()
|
||||||
if nb != nil {
|
if nb, ok := y.(low.HasValueNodeUntyped); ok {
|
||||||
nodeEntry.Line = nb.Line
|
if nb.GetValueNode() != nil {
|
||||||
|
nodeEntry.Line = nb.GetValueNode().Line
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic("not supported yet")
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
// everything else, weight it to the bottom of the rendered object.
|
// everything else, weight it to the bottom of the rendered object.
|
||||||
@@ -265,10 +269,12 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any)
|
|||||||
// extract low level key line number
|
// extract low level key line number
|
||||||
if pr, ok := gh.(low.HasValueUnTyped); ok {
|
if pr, ok := gh.(low.HasValueUnTyped); ok {
|
||||||
fg := reflect.ValueOf(pr.GetValueUntyped())
|
fg := reflect.ValueOf(pr.GetValueUntyped())
|
||||||
|
found := false
|
||||||
for _, ky := range fg.MapKeys() {
|
for _, ky := range fg.MapKeys() {
|
||||||
if we, wok := ky.Interface().(low.HasKeyNode); wok {
|
if we, wok := ky.Interface().(low.HasKeyNode); wok {
|
||||||
er := we.GetKeyNode().Value
|
er := we.GetKeyNode().Value
|
||||||
if er == x {
|
if er == x {
|
||||||
|
found = true
|
||||||
orderedCollection = append(orderedCollection, &NodeEntry{
|
orderedCollection = append(orderedCollection, &NodeEntry{
|
||||||
Tag: x,
|
Tag: x,
|
||||||
Key: x,
|
Key: x,
|
||||||
@@ -278,6 +284,7 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// this is a map, without any low level details available
|
// this is a map, without any low level details available
|
||||||
|
found = true
|
||||||
orderedCollection = append(orderedCollection, &NodeEntry{
|
orderedCollection = append(orderedCollection, &NodeEntry{
|
||||||
Tag: x,
|
Tag: x,
|
||||||
Key: x,
|
Key: x,
|
||||||
@@ -286,20 +293,38 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if found != true {
|
||||||
|
// this is something new, add it.
|
||||||
|
orderedCollection = append(orderedCollection, &NodeEntry{
|
||||||
|
Tag: x,
|
||||||
|
Key: x,
|
||||||
|
Line: 9999,
|
||||||
|
Value: m.MapIndex(k).Interface(),
|
||||||
|
})
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// this is a map, without an enclosing struct
|
// this is a map, but it may be wrapped still.
|
||||||
bj := reflect.ValueOf(gh)
|
bj := reflect.ValueOf(gh)
|
||||||
for _, ky := range bj.MapKeys() {
|
yh := bj.Interface()
|
||||||
er := ky.Interface().(low.HasKeyNode).GetKeyNode().Value
|
calc := func(iu reflect.Value) {
|
||||||
if er == x {
|
for _, ky := range iu.MapKeys() {
|
||||||
orderedCollection = append(orderedCollection, &NodeEntry{
|
er := ky.Interface().(low.HasKeyNode).GetKeyNode().Value
|
||||||
Tag: x,
|
if er == x {
|
||||||
Key: x,
|
orderedCollection = append(orderedCollection, &NodeEntry{
|
||||||
Line: ky.Interface().(low.HasKeyNode).GetKeyNode().Line,
|
Tag: x,
|
||||||
Value: m.MapIndex(k).Interface(),
|
Key: x,
|
||||||
})
|
Line: ky.Interface().(low.HasKeyNode).GetKeyNode().Line,
|
||||||
|
Value: m.MapIndex(k).Interface(),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if vg, jo := yh.(low.HasKeyNode); jo {
|
||||||
|
fv := reflect.ValueOf(vg.GetKeyNode())
|
||||||
|
calc(fv)
|
||||||
|
} else {
|
||||||
|
calc(bj)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// this is a map, without any low level details available (probably an extension map).
|
// this is a map, without any low level details available (probably an extension map).
|
||||||
|
|||||||
@@ -13,18 +13,18 @@ import (
|
|||||||
// Header represents a high-level OpenAPI 3+ Header object that is backed by a low-level one.
|
// Header represents a high-level OpenAPI 3+ Header object that is backed by a low-level one.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#header-object
|
// - https://spec.openapis.org/oas/v3.1.0#header-object
|
||||||
type Header struct {
|
type Header struct {
|
||||||
Description string
|
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||||
Required bool
|
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
|
||||||
Deprecated bool
|
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
|
||||||
AllowEmptyValue bool
|
AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"`
|
||||||
Style string
|
Style string `json:"style,omitempty" yaml:"style,omitempty"`
|
||||||
Explode bool
|
Explode bool `json:"explode,omitempty" yaml:"explode,omitempty"`
|
||||||
AllowReserved bool
|
AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"`
|
||||||
Schema *highbase.SchemaProxy
|
Schema *highbase.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"`
|
||||||
Example any
|
Example any `json:"example,omitempty" yaml:"example,omitempty"`
|
||||||
Examples map[string]*highbase.Example
|
Examples map[string]*highbase.Example `json:"examples,omitempty" yaml:"examples,omitempty"`
|
||||||
Content map[string]*MediaType
|
Content map[string]*MediaType `json:"content,omitempty" yaml:"content,omitempty"`
|
||||||
Extensions map[string]any
|
Extensions map[string]any `json:"-" yaml:"-"`
|
||||||
low *low.Header
|
low *low.Header
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,16 +6,17 @@ package v3
|
|||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel/high"
|
"github.com/pb33f/libopenapi/datamodel/high"
|
||||||
low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OAuthFlows represents a high-level OpenAPI 3+ OAuthFlows object that is backed by a low-level one.
|
// OAuthFlows represents a high-level OpenAPI 3+ OAuthFlows object that is backed by a low-level one.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#oauth-flows-object
|
// - https://spec.openapis.org/oas/v3.1.0#oauth-flows-object
|
||||||
type OAuthFlows struct {
|
type OAuthFlows struct {
|
||||||
Implicit *OAuthFlow
|
Implicit *OAuthFlow `json:"implicit,omitempty" yaml:"implicit,omitempty"`
|
||||||
Password *OAuthFlow
|
Password *OAuthFlow `json:"password,omitempty" yaml:"password,omitempty"`
|
||||||
ClientCredentials *OAuthFlow
|
ClientCredentials *OAuthFlow `json:"clientCredentials,omitempty" yaml:"clientCredentials,omitempty"`
|
||||||
AuthorizationCode *OAuthFlow
|
AuthorizationCode *OAuthFlow `json:"authorizationCode,omitempty" yaml:"authorizationCode,omitempty"`
|
||||||
Extensions map[string]any
|
Extensions map[string]any `json:"-" yaml:"-"`
|
||||||
low *low.OAuthFlows
|
low *low.OAuthFlows
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,3 +47,18 @@ func NewOAuthFlows(flows *low.OAuthFlows) *OAuthFlows {
|
|||||||
func (o *OAuthFlows) GoLow() *low.OAuthFlows {
|
func (o *OAuthFlows) GoLow() *low.OAuthFlows {
|
||||||
return o.low
|
return o.low
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render will return a YAML representation of the OAuthFlows object as a byte slice.
|
||||||
|
func (o *OAuthFlows) Render() ([]byte, error) {
|
||||||
|
return yaml.Marshal(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalYAML will create a ready to render YAML representation of the OAuthFlows object.
|
||||||
|
func (o *OAuthFlows) MarshalYAML() (interface{}, error) {
|
||||||
|
if o == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
nb := high.NewNodeBuilder(o, o.low)
|
||||||
|
return nb.Render(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,32 +9,33 @@ import (
|
|||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewOAuthFlows(t *testing.T) {
|
func TestNewOAuthFlows(t *testing.T) {
|
||||||
|
|
||||||
yml := `implicit:
|
yml := `implicit:
|
||||||
authorizationUrl: https://pb33f.io/oauth
|
authorizationUrl: https://pb33f.io/oauth/implicit
|
||||||
scopes:
|
scopes:
|
||||||
write:burgers: modify and add new burgers
|
write:burgers: modify and add new burgers implicitly
|
||||||
read:burgers: read all burgers
|
read:burgers: read all burgers
|
||||||
authorizationCode:
|
authorizationCode:
|
||||||
authorizationUrl: https://pb33f.io/oauth
|
authorizationUrl: https://pb33f.io/oauth/authCode
|
||||||
tokenUrl: https://api.pb33f.io/oauth/token
|
tokenUrl: https://api.pb33f.io/oauth/token
|
||||||
scopes:
|
scopes:
|
||||||
write:burgers: modify burgers and stuff
|
write:burgers: modify burgers and stuff with a code
|
||||||
read:burgers: read all the burgers
|
read:burgers: read all the burgers
|
||||||
password:
|
password:
|
||||||
authorizationUrl: https://pb33f.io/oauth
|
authorizationUrl: https://pb33f.io/oauth/password
|
||||||
scopes:
|
scopes:
|
||||||
write:burgers: modify and add new burgers
|
write:burgers: modify and add new burgers with a password
|
||||||
read:burgers: read all burgers
|
read:burgers: read all burgers
|
||||||
clientCredentials:
|
clientCredentials:
|
||||||
authorizationUrl: https://pb33f.io/oauth
|
authorizationUrl: https://pb33f.io/oauth/clientCreds
|
||||||
scopes:
|
scopes:
|
||||||
write:burgers: modify burgers and stuff
|
write:burgers: modify burgers and stuff with creds
|
||||||
read:burgers: read all the burgers `
|
read:burgers: read all the burgers`
|
||||||
|
|
||||||
var idxNode yaml.Node
|
var idxNode yaml.Node
|
||||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
@@ -52,4 +53,36 @@ clientCredentials:
|
|||||||
assert.Len(t, r.ClientCredentials.Scopes, 2)
|
assert.Len(t, r.ClientCredentials.Scopes, 2)
|
||||||
assert.Equal(t, 2, r.GoLow().Implicit.Value.AuthorizationUrl.KeyNode.Line)
|
assert.Equal(t, 2, r.GoLow().Implicit.Value.AuthorizationUrl.KeyNode.Line)
|
||||||
|
|
||||||
|
// now render it back out, and it should be identical!
|
||||||
|
rBytes, _ := r.Render()
|
||||||
|
assert.Equal(t, yml, strings.TrimSpace(string(rBytes)))
|
||||||
|
|
||||||
|
modified := `implicit:
|
||||||
|
authorizationUrl: https://pb33f.io/oauth/implicit
|
||||||
|
scopes:
|
||||||
|
write:burgers: modify and add new burgers implicitly
|
||||||
|
read:burgers: read all burgers
|
||||||
|
authorizationCode:
|
||||||
|
authorizationUrl: https://pb33f.io/oauth/authCode
|
||||||
|
tokenUrl: https://api.pb33f.io/oauth/token
|
||||||
|
scopes:
|
||||||
|
write:burgers: modify burgers and stuff with a code
|
||||||
|
read:burgers: read all the burgers
|
||||||
|
password:
|
||||||
|
authorizationUrl: https://pb33f.io/oauth/password
|
||||||
|
scopes:
|
||||||
|
write:burgers: modify and add new burgers with a password
|
||||||
|
read:burgers: read all burgers
|
||||||
|
clientCredentials:
|
||||||
|
authorizationUrl: https://pb33f.io/oauth/clientCreds
|
||||||
|
scopes:
|
||||||
|
write:burgers: modify burgers and stuff with creds
|
||||||
|
read:burgers: read all the burgers
|
||||||
|
CHIP:CHOP: microwave a sock`
|
||||||
|
|
||||||
|
// now modify it and render it back out, and it should be identical!
|
||||||
|
r.ClientCredentials.Scopes["CHIP:CHOP"] = "microwave a sock"
|
||||||
|
rBytes, _ = r.Render()
|
||||||
|
assert.Equal(t, modified, strings.TrimSpace(string(rBytes)))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -386,7 +386,32 @@ func SetField(field *reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) er
|
|||||||
field.Set(reflect.ValueOf(ref))
|
field.Set(reflect.ValueOf(ref))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case reflect.TypeOf(NodeReference[map[KeyReference[string]]ValueReference[string]]{}):
|
||||||
|
if utils.IsNodeMap(valueNode) {
|
||||||
|
if field.CanSet() {
|
||||||
|
items := make(map[KeyReference[string]]ValueReference[string])
|
||||||
|
var cf *yaml.Node
|
||||||
|
for i, sliceItem := range valueNode.Content {
|
||||||
|
if i%2 == 0 {
|
||||||
|
cf = sliceItem
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
items[KeyReference[string]{
|
||||||
|
Value: cf.Value,
|
||||||
|
KeyNode: cf,
|
||||||
|
}] = ValueReference[string]{
|
||||||
|
Value: sliceItem.Value,
|
||||||
|
ValueNode: sliceItem,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ref := NodeReference[map[KeyReference[string]]ValueReference[string]]{
|
||||||
|
Value: items,
|
||||||
|
KeyNode: keyNode,
|
||||||
|
ValueNode: valueNode,
|
||||||
|
}
|
||||||
|
field.Set(reflect.ValueOf(ref))
|
||||||
|
}
|
||||||
|
}
|
||||||
case reflect.TypeOf(NodeReference[[]ValueReference[string]]{}):
|
case reflect.TypeOf(NodeReference[[]ValueReference[string]]{}):
|
||||||
|
|
||||||
if utils.IsNodeArray(valueNode) {
|
if utils.IsNodeArray(valueNode) {
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ type OAuthFlow struct {
|
|||||||
AuthorizationUrl low.NodeReference[string]
|
AuthorizationUrl low.NodeReference[string]
|
||||||
TokenUrl low.NodeReference[string]
|
TokenUrl low.NodeReference[string]
|
||||||
RefreshUrl low.NodeReference[string]
|
RefreshUrl low.NodeReference[string]
|
||||||
Scopes low.KeyReference[map[low.KeyReference[string]]low.ValueReference[string]]
|
Scopes low.NodeReference[map[low.KeyReference[string]]low.ValueReference[string]]
|
||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user