mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-11 04:20:24 +00:00
components are in,
this should actually be the full model, the whole graph is in - now to optimize full model and check for circular deps. then, it's time to move back in history and rebuild the swagger model.
This commit is contained in:
@@ -10,6 +10,17 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
responses = iota
|
||||
parameters
|
||||
examples
|
||||
requestBodies
|
||||
headers
|
||||
securitySchemes
|
||||
links
|
||||
callbacks
|
||||
)
|
||||
|
||||
var seenSchemas map[string]*Schema
|
||||
|
||||
func init() {
|
||||
@@ -49,50 +60,125 @@ func NewComponents(comp *low.Components) *Components {
|
||||
c := new(Components)
|
||||
c.low = comp
|
||||
c.Extensions = high.ExtractExtensions(comp.Extensions)
|
||||
callbacks := make(map[string]*Callback)
|
||||
links := make(map[string]*Link)
|
||||
cbMap := make(map[string]*Callback)
|
||||
linkMap := make(map[string]*Link)
|
||||
responseMap := make(map[string]*Response)
|
||||
parameterMap := make(map[string]*Parameter)
|
||||
exampleMap := make(map[string]*Example)
|
||||
requestBodyMap := make(map[string]*RequestBody)
|
||||
headerMap := make(map[string]*Header)
|
||||
securitySchemeMap := make(map[string]*SecurityScheme)
|
||||
schemas := make(map[string]*Schema)
|
||||
schemaChan := make(chan componentResult[*Schema])
|
||||
cbChan := make(chan componentResult[*Callback])
|
||||
linkChan := make(chan componentResult[*Link])
|
||||
responseChan := make(chan componentResult[*Response])
|
||||
paramChan := make(chan componentResult[*Parameter])
|
||||
exampleChan := make(chan componentResult[*Example])
|
||||
requestBodyChan := make(chan componentResult[*RequestBody])
|
||||
headerChan := make(chan componentResult[*Header])
|
||||
securitySchemeChan := make(chan componentResult[*SecurityScheme])
|
||||
|
||||
// build all components asynchronously.
|
||||
for k, v := range comp.Callbacks.Value {
|
||||
callbacks[k.Value] = NewCallback(v.Value)
|
||||
go buildComponent[*Callback, *low.Callback](callbacks, k.Value, v.Value, cbChan, NewCallback)
|
||||
}
|
||||
c.Callbacks = callbacks
|
||||
for k, v := range comp.Links.Value {
|
||||
links[k.Value] = NewLink(v.Value)
|
||||
go buildComponent[*Link, *low.Link](links, k.Value, v.Value, linkChan, NewLink)
|
||||
}
|
||||
c.Links = links
|
||||
|
||||
sLock := sync.RWMutex{}
|
||||
buildOutSchema := func(k lowmodel.KeyReference[string],
|
||||
schema lowmodel.ValueReference[*low.Schema], doneChan chan bool, schemas map[string]*Schema) {
|
||||
var sch *Schema
|
||||
if ss := getSeenSchema(schema.GenerateMapKey()); ss != nil {
|
||||
sch = ss
|
||||
} else {
|
||||
sch = NewSchema(schema.Value)
|
||||
for k, v := range comp.Responses.Value {
|
||||
go buildComponent[*Response, *low.Response](responses, k.Value, v.Value, responseChan, NewResponse)
|
||||
}
|
||||
defer sLock.Unlock()
|
||||
sLock.Lock()
|
||||
schemas[k.Value] = sch
|
||||
addSeenSchema(schema.GenerateMapKey(), sch)
|
||||
doneChan <- true
|
||||
for k, v := range comp.Parameters.Value {
|
||||
go buildComponent[*Parameter, *low.Parameter](parameters, k.Value, v.Value, paramChan, NewParameter)
|
||||
}
|
||||
for k, v := range comp.Examples.Value {
|
||||
go buildComponent[*Example, *low.Example](parameters, k.Value, v.Value, exampleChan, NewExample)
|
||||
}
|
||||
for k, v := range comp.RequestBodies.Value {
|
||||
go buildComponent[*RequestBody, *low.RequestBody](requestBodies, k.Value, v.Value,
|
||||
requestBodyChan, NewRequestBody)
|
||||
}
|
||||
for k, v := range comp.Headers.Value {
|
||||
go buildComponent[*Header, *low.Header](headers, k.Value, v.Value, headerChan, NewHeader)
|
||||
}
|
||||
for k, v := range comp.SecuritySchemes.Value {
|
||||
go buildComponent[*SecurityScheme, *low.SecurityScheme](securitySchemes, k.Value, v.Value,
|
||||
securitySchemeChan, NewSecurityScheme)
|
||||
}
|
||||
|
||||
doneChan := make(chan bool)
|
||||
for k, v := range comp.Schemas.Value {
|
||||
go buildOutSchema(k, v, doneChan, schemas)
|
||||
go buildSchema(k, v.Value, schemaChan)
|
||||
}
|
||||
k := 0
|
||||
for k < len(comp.Schemas.Value) {
|
||||
|
||||
totalComponents := len(comp.Callbacks.Value) + len(comp.Links.Value) + len(comp.Responses.Value) +
|
||||
len(comp.Parameters.Value) + len(comp.Examples.Value) + len(comp.RequestBodies.Value) +
|
||||
len(comp.Headers.Value) + len(comp.SecuritySchemes.Value) + len(comp.Schemas.Value)
|
||||
|
||||
processedComponents := 0
|
||||
for processedComponents < totalComponents {
|
||||
select {
|
||||
case <-doneChan:
|
||||
k++
|
||||
case sRes := <-schemaChan:
|
||||
processedComponents++
|
||||
schemas[sRes.key] = sRes.res
|
||||
case cbRes := <-cbChan:
|
||||
processedComponents++
|
||||
cbMap[cbRes.key] = cbRes.res
|
||||
case lRes := <-linkChan:
|
||||
processedComponents++
|
||||
linkMap[lRes.key] = lRes.res
|
||||
case respRes := <-responseChan:
|
||||
processedComponents++
|
||||
responseMap[respRes.key] = respRes.res
|
||||
case pRes := <-paramChan:
|
||||
processedComponents++
|
||||
parameterMap[pRes.key] = pRes.res
|
||||
case eRes := <-exampleChan:
|
||||
processedComponents++
|
||||
exampleMap[eRes.key] = eRes.res
|
||||
case rbRes := <-requestBodyChan:
|
||||
processedComponents++
|
||||
requestBodyMap[rbRes.key] = rbRes.res
|
||||
case hRes := <-headerChan:
|
||||
processedComponents++
|
||||
headerMap[hRes.key] = hRes.res
|
||||
case ssRes := <-securitySchemeChan:
|
||||
processedComponents++
|
||||
securitySchemeMap[ssRes.key] = ssRes.res
|
||||
}
|
||||
}
|
||||
c.Schemas = schemas
|
||||
c.Callbacks = cbMap
|
||||
c.Links = linkMap
|
||||
c.Parameters = parameterMap
|
||||
c.Headers = headerMap
|
||||
c.Responses = responseMap
|
||||
c.RequestBodies = requestBodyMap
|
||||
c.Examples = exampleMap
|
||||
c.SecuritySchemes = securitySchemeMap
|
||||
return c
|
||||
}
|
||||
|
||||
type componentResult[T any] struct {
|
||||
res T
|
||||
key string
|
||||
comp int
|
||||
}
|
||||
|
||||
func buildComponent[N any, O any](comp int, key string, orig O, c chan componentResult[N], f func(O) N) {
|
||||
c <- componentResult[N]{comp: comp, res: f(orig), key: key}
|
||||
}
|
||||
|
||||
func buildSchema(key lowmodel.KeyReference[string], orig *low.Schema, c chan componentResult[*Schema]) {
|
||||
var sch *Schema
|
||||
if ss := getSeenSchema(key.GenerateMapKey()); ss != nil {
|
||||
sch = ss
|
||||
} else {
|
||||
sch = NewSchema(orig)
|
||||
}
|
||||
c <- componentResult[*Schema]{res: sch, key: key.Value}
|
||||
}
|
||||
|
||||
func (c *Components) GoLow() *low.Components {
|
||||
return c.low
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ func TestNewDocument_Components(t *testing.T) {
|
||||
assert.Equal(t, "locateBurger", h.Components.Links["LocateBurger"].OperationId)
|
||||
assert.Equal(t, "$response.body#/id", h.Components.Links["LocateBurger"].Parameters["burgerId"])
|
||||
assert.Len(t, h.Components.Callbacks, 1)
|
||||
//assert.Equal(t, "Callback payload",
|
||||
// h.Components.Callbacks["BurgerCallback"].Expression["{$request.query.queryUrl}"].Post.RequestBody.Description)
|
||||
assert.Equal(t, "Callback payload",
|
||||
h.Components.Callbacks["BurgerCallback"].Expression["{$request.query.queryUrl}"].Post.RequestBody.Description)
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
|
||||
package v3
|
||||
|
||||
import low "github.com/pb33f/libopenapi/datamodel/low/3.0"
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/3.0"
|
||||
)
|
||||
|
||||
type OAuthFlow struct {
|
||||
AuthorizationUrl string
|
||||
@@ -14,6 +17,21 @@ type OAuthFlow struct {
|
||||
low *low.OAuthFlow
|
||||
}
|
||||
|
||||
func NewOAuthFlow(flow *low.OAuthFlow) *OAuthFlow {
|
||||
o := new(OAuthFlow)
|
||||
o.low = flow
|
||||
o.TokenUrl = flow.TokenUrl.Value
|
||||
o.AuthorizationUrl = flow.AuthorizationUrl.Value
|
||||
o.RefreshUrl = flow.RefreshUrl.Value
|
||||
scopes := make(map[string]string)
|
||||
for k, v := range flow.Scopes.Value {
|
||||
scopes[k.Value] = v.Value
|
||||
}
|
||||
o.Scopes = scopes
|
||||
o.Extensions = high.ExtractExtensions(flow.Extensions)
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *OAuthFlow) GoLow() *low.OAuthFlow {
|
||||
return o.low
|
||||
}
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
|
||||
package v3
|
||||
|
||||
import low "github.com/pb33f/libopenapi/datamodel/low/3.0"
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/3.0"
|
||||
)
|
||||
|
||||
type OAuthFlows struct {
|
||||
Implicit *OAuthFlow
|
||||
@@ -14,6 +17,28 @@ type OAuthFlows struct {
|
||||
low *low.OAuthFlows
|
||||
}
|
||||
|
||||
func NewOAuthFlows(flows *low.OAuthFlows) *OAuthFlows {
|
||||
o := new(OAuthFlows)
|
||||
o.low = flows
|
||||
if !flows.Implicit.IsEmpty() {
|
||||
o.Implicit = NewOAuthFlow(flows.Implicit.Value)
|
||||
}
|
||||
if !flows.Password.IsEmpty() {
|
||||
o.Password = NewOAuthFlow(flows.Password.Value)
|
||||
}
|
||||
if !flows.ClientCredentials.IsEmpty() {
|
||||
o.ClientCredentials = NewOAuthFlow(flows.ClientCredentials.Value)
|
||||
}
|
||||
if !flows.AuthorizationCode.IsEmpty() {
|
||||
o.AuthorizationCode = NewOAuthFlow(flows.AuthorizationCode.Value)
|
||||
}
|
||||
if !flows.Implicit.IsEmpty() {
|
||||
o.Implicit = NewOAuthFlow(flows.Implicit.Value)
|
||||
}
|
||||
o.Extensions = high.ExtractExtensions(flows.Extensions)
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *OAuthFlows) GoLow() *low.OAuthFlows {
|
||||
return o.low
|
||||
}
|
||||
|
||||
@@ -26,16 +26,31 @@ func NewOperation(operation *low.Operation) *Operation {
|
||||
o := new(Operation)
|
||||
o.low = operation
|
||||
var tags []string
|
||||
if !operation.Tags.IsEmpty() {
|
||||
for i := range operation.Tags.Value {
|
||||
tags = append(tags, operation.Tags.Value[i].Value)
|
||||
}
|
||||
}
|
||||
o.Tags = tags
|
||||
o.Summary = operation.Summary.Value
|
||||
o.Description = operation.Description.Value
|
||||
if !operation.ExternalDocs.IsEmpty() {
|
||||
o.ExternalDocs = NewExternalDoc(operation.ExternalDocs.Value)
|
||||
}
|
||||
o.OperationId = operation.OperationId.Value
|
||||
|
||||
// TODO: come back and finish.
|
||||
if !operation.Parameters.IsEmpty() {
|
||||
params := make([]*Parameter, len(operation.Parameters.Value))
|
||||
for i := range operation.Parameters.Value {
|
||||
params[i] = NewParameter(operation.Parameters.Value[i].Value)
|
||||
}
|
||||
o.Parameters = params
|
||||
}
|
||||
if !operation.RequestBody.IsEmpty() {
|
||||
o.RequestBody = NewRequestBody(operation.RequestBody.Value)
|
||||
}
|
||||
if !operation.Responses.IsEmpty() {
|
||||
o.Responses = NewResponses(operation.Responses.Value)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,17 @@ package v3
|
||||
|
||||
import low "github.com/pb33f/libopenapi/datamodel/low/3.0"
|
||||
|
||||
const (
|
||||
get = iota
|
||||
put
|
||||
post
|
||||
del
|
||||
options
|
||||
head
|
||||
patch
|
||||
trace
|
||||
)
|
||||
|
||||
type PathItem struct {
|
||||
Description string
|
||||
Summary string
|
||||
@@ -33,6 +44,66 @@ func NewPathItem(pathItem *low.PathItem) *PathItem {
|
||||
}
|
||||
pi.Servers = servers
|
||||
|
||||
// build operation async
|
||||
type opResult struct {
|
||||
method int
|
||||
op *Operation
|
||||
}
|
||||
opChan := make(chan opResult)
|
||||
var buildOperation = func(method int, op *low.Operation, c chan opResult) {
|
||||
if op == nil {
|
||||
c <- opResult{method: method, op: nil}
|
||||
return
|
||||
}
|
||||
c <- opResult{method: method, op: NewOperation(op)}
|
||||
}
|
||||
// build out operations async.
|
||||
go buildOperation(get, pathItem.Get.Value, opChan)
|
||||
go buildOperation(put, pathItem.Put.Value, opChan)
|
||||
go buildOperation(post, pathItem.Post.Value, opChan)
|
||||
go buildOperation(del, pathItem.Delete.Value, opChan)
|
||||
go buildOperation(options, pathItem.Options.Value, opChan)
|
||||
go buildOperation(head, pathItem.Head.Value, opChan)
|
||||
go buildOperation(patch, pathItem.Patch.Value, opChan)
|
||||
go buildOperation(trace, pathItem.Trace.Value, opChan)
|
||||
|
||||
if !pathItem.Parameters.IsEmpty() {
|
||||
params := make([]*Parameter, len(pathItem.Parameters.Value))
|
||||
for i := range pathItem.Parameters.Value {
|
||||
params[i] = NewParameter(pathItem.Parameters.Value[i].Value)
|
||||
}
|
||||
pi.Parameters = params
|
||||
}
|
||||
|
||||
complete := false
|
||||
opCount := 0
|
||||
for !complete {
|
||||
select {
|
||||
case opRes := <-opChan:
|
||||
switch opRes.method {
|
||||
case get:
|
||||
pi.Get = opRes.op
|
||||
case put:
|
||||
pi.Put = opRes.op
|
||||
case post:
|
||||
pi.Post = opRes.op
|
||||
case del:
|
||||
pi.Delete = opRes.op
|
||||
case options:
|
||||
pi.Options = opRes.op
|
||||
case head:
|
||||
pi.Head = opRes.op
|
||||
case patch:
|
||||
pi.Patch = opRes.op
|
||||
case trace:
|
||||
pi.Trace = opRes.op
|
||||
}
|
||||
}
|
||||
opCount++
|
||||
if opCount == 8 {
|
||||
complete = true
|
||||
}
|
||||
}
|
||||
return pi
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
|
||||
package v3
|
||||
|
||||
import low "github.com/pb33f/libopenapi/datamodel/low/3.0"
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/3.0"
|
||||
)
|
||||
|
||||
type RequestBody struct {
|
||||
Description string
|
||||
@@ -13,6 +16,16 @@ type RequestBody struct {
|
||||
low *low.RequestBody
|
||||
}
|
||||
|
||||
func NewRequestBody(rb *low.RequestBody) *RequestBody {
|
||||
r := new(RequestBody)
|
||||
r.low = rb
|
||||
r.Description = rb.Description.Value
|
||||
r.Required = rb.Required.Value
|
||||
r.Extensions = high.ExtractExtensions(rb.Extensions)
|
||||
r.Content = ExtractContent(rb.Content.Value)
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *RequestBody) GoLow() *low.RequestBody {
|
||||
return r.low
|
||||
}
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
|
||||
package v3
|
||||
|
||||
import low "github.com/pb33f/libopenapi/datamodel/low/3.0"
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/3.0"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
Description string
|
||||
@@ -14,6 +17,27 @@ type Response struct {
|
||||
low *low.Response
|
||||
}
|
||||
|
||||
func NewResponse(response *low.Response) *Response {
|
||||
r := new(Response)
|
||||
r.low = response
|
||||
r.Description = response.Description.Value
|
||||
if !response.Headers.IsEmpty() {
|
||||
r.Headers = ExtractHeaders(response.Headers.Value)
|
||||
}
|
||||
r.Extensions = high.ExtractExtensions(response.Extensions)
|
||||
if !response.Content.IsEmpty() {
|
||||
r.Content = ExtractContent(response.Content.Value)
|
||||
}
|
||||
if !response.Links.IsEmpty() {
|
||||
links := make(map[string]*Link)
|
||||
for k, v := range response.Links.Value {
|
||||
links[k.Value] = NewLink(v.Value)
|
||||
}
|
||||
r.Links = links
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Response) GoLow() *low.Response {
|
||||
return r.low
|
||||
}
|
||||
|
||||
@@ -11,6 +11,42 @@ type Responses struct {
|
||||
low *low.Responses
|
||||
}
|
||||
|
||||
func NewResponses(response *low.Responses) *Responses {
|
||||
r := new(Responses)
|
||||
r.low = response
|
||||
if !response.Default.IsEmpty() {
|
||||
r.Default = NewResponse(response.Default.Value)
|
||||
}
|
||||
codes := make(map[string]*Response)
|
||||
|
||||
// struct to hold response and code sent over chan.
|
||||
type respRes struct {
|
||||
code string
|
||||
resp *Response
|
||||
}
|
||||
|
||||
// build each response async for speed
|
||||
rChan := make(chan respRes)
|
||||
var buildResponse = func(code string, resp *low.Response, c chan respRes) {
|
||||
c <- respRes{code: code, resp: NewResponse(resp)}
|
||||
}
|
||||
for k, v := range response.Codes {
|
||||
go buildResponse(k.Value, v.Value, rChan)
|
||||
}
|
||||
totalCodes := len(response.Codes)
|
||||
codesParsed := 0
|
||||
for codesParsed < totalCodes {
|
||||
select {
|
||||
case re := <-rChan:
|
||||
codesParsed++
|
||||
codes[re.code] = re.resp
|
||||
}
|
||||
}
|
||||
r.Codes = codes
|
||||
return r
|
||||
|
||||
}
|
||||
|
||||
func (r *Responses) GoLow() *low.Responses {
|
||||
return r.low
|
||||
}
|
||||
|
||||
@@ -140,13 +140,14 @@ func NewSchema(schema *low.Schema) *Schema {
|
||||
var buildProps = func(k lowmodel.KeyReference[string], v lowmodel.ValueReference[*low.Schema], c chan bool,
|
||||
props map[string]*Schema) {
|
||||
if ss := getSeenSchema(v.GenerateMapKey()); ss != nil {
|
||||
defer plock.Unlock()
|
||||
plock.Lock()
|
||||
props[k.Value] = ss
|
||||
plock.Unlock()
|
||||
|
||||
} else {
|
||||
defer plock.Unlock()
|
||||
plock.Lock()
|
||||
props[k.Value] = NewSchema(v.Value)
|
||||
plock.Unlock()
|
||||
addSeenSchema(k.GenerateMapKey(), props[k.Value])
|
||||
}
|
||||
s.Properties = props
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
|
||||
package v3
|
||||
|
||||
import low "github.com/pb33f/libopenapi/datamodel/low/3.0"
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/3.0"
|
||||
)
|
||||
|
||||
type SecurityScheme struct {
|
||||
Type string
|
||||
@@ -18,6 +21,23 @@ type SecurityScheme struct {
|
||||
low *low.SecurityScheme
|
||||
}
|
||||
|
||||
func NewSecurityScheme(ss *low.SecurityScheme) *SecurityScheme {
|
||||
s := new(SecurityScheme)
|
||||
s.low = ss
|
||||
s.Type = ss.Type.Value
|
||||
s.Description = ss.Description.Value
|
||||
s.Name = ss.Name.Value
|
||||
s.Scheme = ss.Scheme.Value
|
||||
s.In = ss.In.Value
|
||||
s.BearerFormat = ss.BearerFormat.Value
|
||||
s.OpenIdConnectUrl = ss.OpenIdConnectUrl.Value
|
||||
s.Extensions = high.ExtractExtensions(ss.Extensions)
|
||||
if !ss.Flows.IsEmpty() {
|
||||
s.Flows = NewOAuthFlows(ss.Flows.Value)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *SecurityScheme) GoLow() *low.SecurityScheme {
|
||||
return s.low
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user