Files
libopenapi/datamodel/high/3.0/components.go
Dave Shanley c34c4f668c Massive refactor on how the library handles schemas.
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>
2022-08-27 09:47:37 -04:00

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
}