Operartion model is now complete!

That should be the largest bulk of complexity, now onto components.
This commit is contained in:
Dave Shanley
2022-08-09 07:59:22 -04:00
parent e8bb52bf3d
commit ec87ddf8cc
11 changed files with 271 additions and 41 deletions

View File

@@ -7,9 +7,9 @@ import (
type Document struct {
Version low.NodeReference[string]
Info low.NodeReference[*Info]
Servers []low.NodeReference[*Server]
Servers low.NodeReference[[]low.ValueReference[*Server]]
Paths low.NodeReference[*Paths]
Components *Components
Components low.NodeReference[*Components]
Security []*SecurityRequirement
Tags []low.NodeReference[*Tag]
ExternalDocs *ExternalDoc

View File

@@ -64,11 +64,14 @@ func ExtractObjectRaw[T low.Buildable[N], N any](root *yaml.Node) (T, error) {
func ExtractObject[T low.Buildable[N], N any](label string, root *yaml.Node) (low.NodeReference[T], error) {
_, ln, vn := utils.FindKeyNodeFull(label, root.Content)
var n T = new(N)
err := BuildModel(root, n)
err := BuildModel(vn, n)
if err != nil {
return low.NodeReference[T]{}, err
}
err = n.Build(root)
if ln == nil {
return low.NodeReference[T]{}, nil
}
err = n.Build(vn)
if err != nil {
return low.NodeReference[T]{}, err
}
@@ -79,30 +82,59 @@ func ExtractObject[T low.Buildable[N], N any](label string, root *yaml.Node) (lo
}, nil
}
func ExtractArray[T low.Buildable[N], N any](label string, root *yaml.Node) ([]low.NodeReference[T], *yaml.Node, *yaml.Node, error) {
func ExtractArray[T low.Buildable[N], N any](label string, root *yaml.Node) ([]low.ValueReference[T], *yaml.Node, *yaml.Node, error) {
_, ln, vn := utils.FindKeyNodeFull(label, root.Content)
var items []low.NodeReference[T]
var items []low.ValueReference[T]
if vn != nil && ln != nil {
for _, node := range vn.Content {
var n T = new(N)
err := BuildModel(node, n)
if err != nil {
return []low.NodeReference[T]{}, ln, vn, err
return []low.ValueReference[T]{}, ln, vn, err
}
berr := n.Build(node)
if berr != nil {
return nil, ln, vn, berr
}
items = append(items, low.NodeReference[T]{
items = append(items, low.ValueReference[T]{
Value: n,
ValueNode: node,
KeyNode: ln,
})
}
}
return items, ln, vn, nil
}
func ExtractMapFlatNoLookup[PT low.Buildable[N], N any](root *yaml.Node) (map[low.KeyReference[string]]low.ValueReference[PT], error) {
valueMap := make(map[low.KeyReference[string]]low.ValueReference[PT])
if utils.IsNodeMap(root) {
var currentKey *yaml.Node
for i, node := range root.Content {
if i%2 == 0 {
currentKey = node
continue
}
var n PT = new(N)
err := BuildModel(node, n)
if err != nil {
return nil, err
}
berr := n.Build(node)
if berr != nil {
return nil, berr
}
valueMap[low.KeyReference[string]{
Value: currentKey.Value,
KeyNode: currentKey,
}] = low.ValueReference[PT]{
Value: n,
ValueNode: node,
}
}
}
return valueMap, nil
}
func ExtractMapFlat[PT low.Buildable[N], N any](label string, root *yaml.Node) (map[low.KeyReference[string]]low.ValueReference[PT], *yaml.Node, *yaml.Node, error) {
_, labelNode, valueNode := utils.FindKeyNodeFull(label, root.Content)
if valueNode != nil {

View File

@@ -11,6 +11,9 @@ import (
)
func BuildModel(node *yaml.Node, model interface{}) error {
if node == nil {
return nil
}
if reflect.ValueOf(model).Type().Kind() != reflect.Pointer {
return fmt.Errorf("cannot build model on non-pointer: %v", reflect.ValueOf(model).Type().Kind())

View File

@@ -5,6 +5,13 @@ import (
"gopkg.in/yaml.v3"
)
const (
ImplicitLabel = "implicit"
PasswordLabel = "password"
ClientCredentialsLabel = "clientCredentials"
AuthorizationCodeLabel = "authorizationCode"
)
type OAuthFlows struct {
Implicit low.NodeReference[*OAuthFlow]
Password low.NodeReference[*OAuthFlow]
@@ -19,7 +26,32 @@ func (o *OAuthFlows) Build(root *yaml.Node) error {
return err
}
o.Extensions = extensionMap
v, vErr := ExtractObject[*OAuthFlow](ImplicitLabel, root)
if vErr != nil {
return vErr
}
o.Implicit = v
v, vErr = ExtractObject[*OAuthFlow](PasswordLabel, root)
if vErr != nil {
return vErr
}
o.Password = v
v, vErr = ExtractObject[*OAuthFlow](ClientCredentialsLabel, root)
if vErr != nil {
return vErr
}
o.ClientCredentials = v
v, vErr = ExtractObject[*OAuthFlow](AuthorizationCodeLabel, root)
if vErr != nil {
return vErr
}
o.AuthorizationCode = v
return nil
}
type OAuthFlow struct {

View File

@@ -18,13 +18,13 @@ type Operation struct {
Description low.NodeReference[string]
ExternalDocs low.NodeReference[*ExternalDoc]
OperationId low.NodeReference[string]
Parameters low.NodeReference[[]low.NodeReference[*Parameter]]
Parameters low.NodeReference[[]low.ValueReference[*Parameter]]
RequestBody low.NodeReference[*RequestBody]
Responses low.NodeReference[*Responses]
Callbacks low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Callback]]
Deprecated low.NodeReference[bool]
Security []low.NodeReference[*SecurityRequirement]
Servers []low.NodeReference[*Server]
Security low.NodeReference[*SecurityRequirement]
Servers low.NodeReference[[]low.ValueReference[*Server]]
Extensions map[low.KeyReference[string]]low.ValueReference[any]
}
@@ -48,7 +48,7 @@ func (o *Operation) Build(root *yaml.Node) error {
return pErr
}
if params != nil {
o.Parameters = low.NodeReference[[]low.NodeReference[*Parameter]]{
o.Parameters = low.NodeReference[[]low.ValueReference[*Parameter]]{
Value: params,
KeyNode: ln,
ValueNode: vn,
@@ -82,5 +82,24 @@ func (o *Operation) Build(root *yaml.Node) error {
}
}
// extract security
sec, sErr := ExtractObject[*SecurityRequirement](SecurityLabel, root)
if sErr != nil {
return sErr
}
o.Security = sec
// extract servers
servers, sl, sn, serErr := ExtractArray[*Server](ServersLabel, root)
if serErr != nil {
return serErr
}
if servers != nil {
o.Servers = low.NodeReference[[]low.ValueReference[*Server]]{
Value: servers,
KeyNode: sl,
ValueNode: sn,
}
}
return nil
}

View File

@@ -2,6 +2,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
)
@@ -15,13 +16,15 @@ type Responses struct {
}
func (r *Responses) Build(root *yaml.Node) error {
codes, _, _, err := ExtractMapFlat[*Response](ResponsesLabel, root)
if utils.IsNodeMap(root) {
codes, err := ExtractMapFlatNoLookup[*Response](root)
if err != nil {
return err
}
if codes != nil {
r.Codes = codes
}
}
return nil
}

View File

@@ -2,9 +2,14 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
)
const (
SecurityLabel = "security"
)
type SecurityScheme struct {
Type low.NodeReference[string]
Description low.NodeReference[string]
@@ -27,5 +32,89 @@ func (ss *SecurityScheme) Build(root *yaml.Node) error {
}
type SecurityRequirement struct {
Value low.NodeReference[[]low.ValueReference[string]]
Value []low.ValueReference[map[low.KeyReference[string]][]low.ValueReference[string]]
}
func (sr *SecurityRequirement) FindRequirement(name string) []low.ValueReference[string] {
for _, r := range sr.Value {
for k, v := range r.Value {
if k.Value == name {
return v
}
}
}
return nil
}
func (sr *SecurityRequirement) Build(root *yaml.Node) error {
//if utils.IsNodeArray(root) {
// var currSec *yaml.Node
// var requirements []low.ValueReference[map[low.KeyReference[string]][]low.ValueReference[string]]
// for i, n := range root.Content {
// if i%2 == 0 {
// currSec = n
// continue
// }
// if utils.IsNodeArray(n) {
// res := make(map[low.KeyReference[string]][]low.ValueReference[string])
// var dat []low.ValueReference[string]
// for _, r := range n.Content {
// dat = append(dat, low.ValueReference[string]{
// Value: r.Value,
// ValueNode: r,
// })
// }
// res[low.KeyReference[string]{
// Value: currSec.Value,
// KeyNode: currSec,
// }] = dat
// requirements = append(requirements, low.ValueReference[map[low.KeyReference[string]][]low.ValueReference[string]]{
// Value: res,
// ValueNode: n,
// })
// }
// }
// sr.Value = requirements
//}
if utils.IsNodeArray(root) {
var requirements []low.ValueReference[map[low.KeyReference[string]][]low.ValueReference[string]]
for _, n := range root.Content {
var currSec *yaml.Node
if utils.IsNodeMap(n) {
res := make(map[low.KeyReference[string]][]low.ValueReference[string])
var dat []low.ValueReference[string]
for i, r := range n.Content {
if i%2 == 0 {
currSec = r
continue
}
if utils.IsNodeArray(r) {
// value (should be) an array of strings
var keyValues []low.ValueReference[string]
for _, strN := range r.Content {
keyValues = append(keyValues, low.ValueReference[string]{
Value: strN.Value,
ValueNode: strN,
})
}
dat = keyValues
}
}
res[low.KeyReference[string]{
Value: currSec.Value,
KeyNode: currSec,
}] = dat
requirements = append(requirements, low.ValueReference[map[low.KeyReference[string]][]low.ValueReference[string]]{
Value: res,
ValueNode: n,
})
}
}
sr.Value = requirements
}
return nil
}

View File

@@ -221,7 +221,7 @@ func TestSpecIndex_BurgerShop(t *testing.T) {
assert.NotNil(t, index.GetDiscoveredReferences())
assert.Equal(t, 0, len(index.GetPolyReferences()))
assert.NotNil(t, index.GetOperationParameterReferences())
assert.Equal(t, 0, len(index.GetAllSecuritySchemes()))
assert.Equal(t, 3, len(index.GetAllSecuritySchemes()))
assert.Equal(t, 2, len(index.GetAllParameters()))
assert.Equal(t, 0, len(index.GetAllResponses()))
assert.Equal(t, 2, len(index.GetInlineOperationDuplicateParameters()))

View File

@@ -72,7 +72,7 @@ func extractServers(info *datamodel.SpecInfo, doc *v3.Document) error {
_, ln, vn := utils.FindKeyNodeFull(v3.ServersLabel, info.RootNode.Content)
if vn != nil {
if utils.IsNodeArray(vn) {
var servers []low.NodeReference[*v3.Server]
var servers []low.ValueReference[*v3.Server]
for _, srvN := range vn.Content {
if utils.IsNodeMap(srvN) {
srvr := v3.Server{}
@@ -81,14 +81,17 @@ func extractServers(info *datamodel.SpecInfo, doc *v3.Document) error {
return err
}
srvr.Build(srvN)
servers = append(servers, low.NodeReference[*v3.Server]{
servers = append(servers, low.ValueReference[*v3.Server]{
Value: &srvr,
ValueNode: srvN,
KeyNode: ln,
})
}
}
doc.Servers = servers
doc.Servers = low.NodeReference[[]low.ValueReference[*v3.Server]]{
Value: servers,
KeyNode: ln,
ValueNode: vn,
}
}
}
return nil

View File

@@ -40,26 +40,26 @@ func TestCreateDocument_Info(t *testing.T) {
}
func TestCreateDocument_Servers(t *testing.T) {
assert.Len(t, doc.Servers, 2)
server1 := doc.Servers[0]
server2 := doc.Servers[1]
assert.Len(t, doc.Servers.Value, 2)
server1 := doc.Servers.Value[0].Value
server2 := doc.Servers.Value[1].Value
// 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)
assert.Equal(t, "{scheme}://api.pb33f.io", server1.URL.Value)
assert.NotEmpty(t, server1.Description.Value)
assert.Len(t, server1.Variables.Value, 1)
assert.Len(t, server1.Variables.Value["scheme"].Value.Enum, 2)
assert.Equal(t, server1.Variables.Value["scheme"].Value.Default.Value, "https")
assert.NotEmpty(t, server1.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, "https://{domain}.{host}.com", server2.URL.Value)
assert.NotEmpty(t, server2.Description.Value)
assert.Len(t, server2.Variables.Value, 2)
assert.Equal(t, server2.Variables.Value["domain"].Value.Default.Value, "api")
assert.NotEmpty(t, server2.Variables.Value["domain"].Value.Description.Value)
assert.NotEmpty(t, server2.Variables.Value["host"].Value.Description.Value)
assert.Equal(t, server2.Variables.Value["host"].Value.Default.Value, "pb33f.io")
assert.Equal(t, "1.2", doc.Info.Value.Version.Value)
}
@@ -220,4 +220,18 @@ func TestCreateDocument_Paths(t *testing.T) {
assert.NotNil(t, burgerIdParam)
assert.Equal(t, "$response.body#/id", burgerIdParam.Value)
// check security requirements
security := burgersPost.Security.Value
assert.NotNil(t, security)
assert.Len(t, security.Value, 1)
oAuthReq := security.FindRequirement("OAuthScheme")
assert.Len(t, oAuthReq, 2)
assert.Equal(t, "read:burgers", oAuthReq[0].Value)
servers := burgersPost.Servers.Value
assert.NotNil(t, servers)
assert.Len(t, servers, 1)
assert.Equal(t, "https://pb33f.io", servers[0].Value.URL.Value)
}

View File

@@ -138,6 +138,13 @@ paths:
summary: invalid request
value:
message: unable to accept this request, looks bad, missing something.
security:
- OAuthScheme:
- read:burgers
- write:burgers
servers:
- url: https://pb33f.io
description: this is an alternative server for this operation.
/burgers/{burgerId}:
get:
callbacks:
@@ -324,6 +331,34 @@ paths:
example:
message: "failed looking up all dressings, something went wrong."
components:
securitySchemes:
APIKeyScheme:
type: apiKey
description: an apiKey security scheme
name: apiKeyScheme
in: query
JWTScheme:
type: http
description: an JWT security scheme
name: aJWTThing
scheme: bearer
bearerFormat: JWT
OAuthScheme:
type: oauth2
description: an oAuth security scheme
name: oAuthy
flows:
implicit:
authorizationUrl: https://pb33f.io/oauth
scopes:
write:burgers: modify and add new burgers
read:burgers: read all burgers
authorizationCode:
authorizationUrl: https://pb33f.io/oauth
tokenUrl: https://api.pb33f.io/oauth/token
scopes:
write:burgers: modify burgers and stuff
read:burgers: read all the burgers
parameters:
BurgerHeader:
in: header