Added oAuth rendering, updated node builder.

This commit is contained in:
Dave Shanley
2023-03-08 10:49:47 -05:00
parent 3544ed0303
commit 38c46fc9dd
6 changed files with 148 additions and 49 deletions

View File

@@ -148,9 +148,13 @@ func (n *NodeBuilder) add(key string) {
value = reflect.ValueOf(fLow)
switch value.Kind() {
case reflect.Struct:
nb := value.Interface().(low.HasValueNodeUntyped).GetValueNode()
if nb != nil {
nodeEntry.Line = nb.Line
y := value.Interface()
if nb, ok := y.(low.HasValueNodeUntyped); ok {
if nb.GetValueNode() != nil {
nodeEntry.Line = nb.GetValueNode().Line
}
} else {
panic("not supported yet")
}
default:
// 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
if pr, ok := gh.(low.HasValueUnTyped); ok {
fg := reflect.ValueOf(pr.GetValueUntyped())
found := false
for _, ky := range fg.MapKeys() {
if we, wok := ky.Interface().(low.HasKeyNode); wok {
er := we.GetKeyNode().Value
if er == x {
found = true
orderedCollection = append(orderedCollection, &NodeEntry{
Tag: x,
Key: x,
@@ -278,6 +284,7 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any)
}
} else {
// this is a map, without any low level details available
found = true
orderedCollection = append(orderedCollection, &NodeEntry{
Tag: x,
Key: x,
@@ -286,10 +293,21 @@ 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 {
// this is a map, without an enclosing struct
// this is a map, but it may be wrapped still.
bj := reflect.ValueOf(gh)
for _, ky := range bj.MapKeys() {
yh := bj.Interface()
calc := func(iu reflect.Value) {
for _, ky := range iu.MapKeys() {
er := ky.Interface().(low.HasKeyNode).GetKeyNode().Value
if er == x {
orderedCollection = append(orderedCollection, &NodeEntry{
@@ -301,6 +319,13 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any)
}
}
}
if vg, jo := yh.(low.HasKeyNode); jo {
fv := reflect.ValueOf(vg.GetKeyNode())
calc(fv)
} else {
calc(bj)
}
}
} else {
// this is a map, without any low level details available (probably an extension map).
orderedCollection = append(orderedCollection, &NodeEntry{

View File

@@ -13,18 +13,18 @@ import (
// 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
type Header struct {
Description string
Required bool
Deprecated bool
AllowEmptyValue bool
Style string
Explode bool
AllowReserved bool
Schema *highbase.SchemaProxy
Example any
Examples map[string]*highbase.Example
Content map[string]*MediaType
Extensions map[string]any
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"`
Style string `json:"style,omitempty" yaml:"style,omitempty"`
Explode bool `json:"explode,omitempty" yaml:"explode,omitempty"`
AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"`
Schema *highbase.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"`
Example any `json:"example,omitempty" yaml:"example,omitempty"`
Examples map[string]*highbase.Example `json:"examples,omitempty" yaml:"examples,omitempty"`
Content map[string]*MediaType `json:"content,omitempty" yaml:"content,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"`
low *low.Header
}

View File

@@ -6,16 +6,17 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/high"
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.
// - https://spec.openapis.org/oas/v3.1.0#oauth-flows-object
type OAuthFlows struct {
Implicit *OAuthFlow
Password *OAuthFlow
ClientCredentials *OAuthFlow
AuthorizationCode *OAuthFlow
Extensions map[string]any
Implicit *OAuthFlow `json:"implicit,omitempty" yaml:"implicit,omitempty"`
Password *OAuthFlow `json:"password,omitempty" yaml:"password,omitempty"`
ClientCredentials *OAuthFlow `json:"clientCredentials,omitempty" yaml:"clientCredentials,omitempty"`
AuthorizationCode *OAuthFlow `json:"authorizationCode,omitempty" yaml:"authorizationCode,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"`
low *low.OAuthFlows
}
@@ -46,3 +47,18 @@ func NewOAuthFlows(flows *low.OAuthFlows) *OAuthFlows {
func (o *OAuthFlows) GoLow() *low.OAuthFlows {
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
}

View File

@@ -9,31 +9,32 @@ import (
"github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"strings"
"testing"
)
func TestNewOAuthFlows(t *testing.T) {
yml := `implicit:
authorizationUrl: https://pb33f.io/oauth
authorizationUrl: https://pb33f.io/oauth/implicit
scopes:
write:burgers: modify and add new burgers
write:burgers: modify and add new burgers implicitly
read:burgers: read all burgers
authorizationCode:
authorizationUrl: https://pb33f.io/oauth
authorizationUrl: https://pb33f.io/oauth/authCode
tokenUrl: https://api.pb33f.io/oauth/token
scopes:
write:burgers: modify burgers and stuff
write:burgers: modify burgers and stuff with a code
read:burgers: read all the burgers
password:
authorizationUrl: https://pb33f.io/oauth
authorizationUrl: https://pb33f.io/oauth/password
scopes:
write:burgers: modify and add new burgers
write:burgers: modify and add new burgers with a password
read:burgers: read all burgers
clientCredentials:
authorizationUrl: https://pb33f.io/oauth
authorizationUrl: https://pb33f.io/oauth/clientCreds
scopes:
write:burgers: modify burgers and stuff
write:burgers: modify burgers and stuff with creds
read:burgers: read all the burgers`
var idxNode yaml.Node
@@ -52,4 +53,36 @@ clientCredentials:
assert.Len(t, r.ClientCredentials.Scopes, 2)
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)))
}

View File

@@ -386,7 +386,32 @@ func SetField(field *reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) er
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]]{}):
if utils.IsNodeArray(valueNode) {

View File

@@ -90,7 +90,7 @@ type OAuthFlow struct {
AuthorizationUrl low.NodeReference[string]
TokenUrl 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]
}