mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 12:37:49 +00:00
Schemas are now rendered on demand. There is no reasonable way to navigate the mayhem that is circular dependencies through multiple inheritance and polymorphism. So now using a msuch simpler design (and MUCH faster), there is a `SchemaProxy` for every schema reference. This holds a reference to the low model and index, that renders the schema on demand. Once rendered, it's done. Any children can also be rendered on demand, and so down the rabbit hole you do (if you want). All circular dependencies are know by the index, so you can decide when you want to stop, or just keep going for ever, however it's now a choice, not something decided for you. Signed-off-by: Dave Shanley <dave@quobix.com>
188 lines
5.6 KiB
Go
188 lines
5.6 KiB
Go
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package v3
|
|
|
|
import (
|
|
"github.com/pb33f/libopenapi/datamodel/high"
|
|
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
|
|
low "github.com/pb33f/libopenapi/datamodel/low/3.0"
|
|
"sync"
|
|
)
|
|
|
|
const (
|
|
responses = iota
|
|
parameters
|
|
examples
|
|
requestBodies
|
|
headers
|
|
securitySchemes
|
|
links
|
|
callbacks
|
|
)
|
|
|
|
var seenSchemas map[string]*Schema
|
|
|
|
func init() {
|
|
clearSchemas()
|
|
}
|
|
|
|
func clearSchemas() {
|
|
seenSchemas = make(map[string]*Schema)
|
|
}
|
|
|
|
var seenSchemaLock sync.RWMutex
|
|
|
|
func addSeenSchema(key string, schema *Schema) {
|
|
defer seenSchemaLock.Unlock()
|
|
seenSchemaLock.Lock()
|
|
if seenSchemas[key] == nil {
|
|
seenSchemas[key] = schema
|
|
}
|
|
}
|
|
func getSeenSchema(key string) *Schema {
|
|
defer seenSchemaLock.Unlock()
|
|
seenSchemaLock.Lock()
|
|
return seenSchemas[key]
|
|
}
|
|
|
|
type Components struct {
|
|
Schemas map[string]*SchemaProxy
|
|
Responses map[string]*Response
|
|
Parameters map[string]*Parameter
|
|
Examples map[string]*Example
|
|
RequestBodies map[string]*RequestBody
|
|
Headers map[string]*Header
|
|
SecuritySchemes map[string]*SecurityScheme
|
|
Links map[string]*Link
|
|
Callbacks map[string]*Callback
|
|
Extensions map[string]any
|
|
low *low.Components
|
|
}
|
|
|
|
func NewComponents(comp *low.Components) *Components {
|
|
c := new(Components)
|
|
c.low = comp
|
|
c.Extensions = high.ExtractExtensions(comp.Extensions)
|
|
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]*SchemaProxy)
|
|
schemaChan := make(chan componentResult[*SchemaProxy])
|
|
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 {
|
|
go buildComponent[*Callback, *low.Callback](callbacks, k.Value, v.Value, cbChan, NewCallback)
|
|
}
|
|
for k, v := range comp.Links.Value {
|
|
go buildComponent[*Link, *low.Link](links, k.Value, v.Value, linkChan, NewLink)
|
|
}
|
|
for k, v := range comp.Responses.Value {
|
|
go buildComponent[*Response, *low.Response](responses, k.Value, v.Value, responseChan, NewResponse)
|
|
}
|
|
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)
|
|
}
|
|
|
|
for k, v := range comp.Schemas.Value {
|
|
go buildSchema(k, v, schemaChan)
|
|
}
|
|
|
|
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 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 lowmodel.ValueReference[*low.SchemaProxy], c chan componentResult[*SchemaProxy]) {
|
|
var sch *SchemaProxy
|
|
sch = &SchemaProxy{schema: &lowmodel.NodeReference[*low.SchemaProxy]{
|
|
Value: orig.Value,
|
|
ValueNode: orig.ValueNode,
|
|
}}
|
|
c <- componentResult[*SchemaProxy]{res: sch, key: key.Value}
|
|
}
|
|
|
|
func (c *Components) GoLow() *low.Components {
|
|
return c.low
|
|
}
|