mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 12:37:49 +00:00
Operartion model is now complete!
That should be the largest bulk of complexity, now onto components.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user