mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-10 04:20:24 +00:00
Travis Newhouse pointed out that there is no way to get to the low level proxy, from the high level one!
75 lines
3.0 KiB
Go
75 lines
3.0 KiB
Go
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package base
|
|
|
|
import (
|
|
"github.com/pb33f/libopenapi/datamodel/low"
|
|
"github.com/pb33f/libopenapi/datamodel/low/base"
|
|
)
|
|
|
|
// SchemaProxy exists as a stub that will create a Schema once (and only once) the Schema() method is called. An
|
|
// underlying low-level SchemaProxy backs this high-level one.
|
|
//
|
|
// Why use a Proxy design?
|
|
//
|
|
// There are three reasons.
|
|
//
|
|
// 1. Circular References and Endless Loops.
|
|
//
|
|
// JSON Schema allows for references to be used. This means references can loop around and create infinite recursive
|
|
// structures, These 'Circular references' technically mean a schema can NEVER be resolved, not without breaking the
|
|
// loop somewhere along the chain.
|
|
//
|
|
// Polymorphism in the form of 'oneOf' and 'anyOf' in version 3+ only exacerbates the problem.
|
|
//
|
|
// These circular traps can be discovered using the resolver, however it's still not enough to stop endless loops and
|
|
// endless goroutine spawning. A proxy design means that resolving occurs on demand and runs down a single level only.
|
|
// preventing any run-away loops.
|
|
//
|
|
// 2. Performance
|
|
//
|
|
// Even without circular references, Polymorphism creates large additional resolving chains that take a long time
|
|
// and slow things down when building. By preventing recursion through every polymorphic item, building models is kept
|
|
// fast and snappy, which is desired for realtime processing of specs.
|
|
//
|
|
// - Q: Yeah, but, why not just use state to avoiding re-visiting seen polymorphic nodes?
|
|
// - A: It's slow, takes up memory and still has runaway potential in very, very long chains.
|
|
//
|
|
// 3. Short Circuit Errors.
|
|
//
|
|
// Schemas are where things can get messy, mainly because the Schema standard changes between versions, and
|
|
// it's not actually JSONSchema until 3.1, so lots of times a bad schema will break parsing. Errors are only found
|
|
// when a schema is needed, so the rest of the document is parsed and ready to use.
|
|
type SchemaProxy struct {
|
|
schema *low.NodeReference[*base.SchemaProxy]
|
|
buildError error
|
|
}
|
|
|
|
// NewSchemaProxy creates a new high-level SchemaProxy from a low-level one.
|
|
func NewSchemaProxy(schema *low.NodeReference[*base.SchemaProxy]) *SchemaProxy {
|
|
return &SchemaProxy{schema: schema}
|
|
}
|
|
|
|
// Schema will create a new Schema instance using NewSchema from the low-level SchemaProxy backing this high-level one.
|
|
// If there is a problem building the Schema, then this method will return nil. Use GetBuildError to gain access
|
|
// to that building error.
|
|
func (sp *SchemaProxy) Schema() *Schema {
|
|
s := sp.schema.Value.Schema()
|
|
if s == nil {
|
|
sp.buildError = sp.schema.Value.GetBuildError()
|
|
return nil
|
|
}
|
|
return NewSchema(s)
|
|
}
|
|
|
|
// GetBuildError returns any error that was thrown when calling Schema()
|
|
func (sp *SchemaProxy) GetBuildError() error {
|
|
return sp.buildError
|
|
}
|
|
|
|
// GoLow returns the low-level SchemaProxy that was used to create the high-level one.
|
|
func (sp *SchemaProxy) GoLow() *low.NodeReference[*base.SchemaProxy] {
|
|
return sp.schema
|
|
}
|