mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-11 04:20:24 +00:00
Building out spec model
this one will take a few tests, there is significant recursion and threading going on.
This commit is contained in:
@@ -72,7 +72,7 @@ func (o *Operation) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
KeyNode: paramLabel,
|
||||
})
|
||||
}
|
||||
|
||||
o.Parameters = params
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -4,9 +4,14 @@ import (
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const (
|
||||
SchemaLabel = "schema"
|
||||
)
|
||||
|
||||
type Parameter struct {
|
||||
Name low.NodeReference[string]
|
||||
In low.NodeReference[string]
|
||||
@@ -32,7 +37,27 @@ func (p *Parameter) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
}
|
||||
p.Extensions = extensionMap
|
||||
|
||||
// deal with schema
|
||||
// handle schema
|
||||
_, schLabel, schNode := utils.FindKeyNodeFull(SchemaLabel, root.Content)
|
||||
if schNode != nil {
|
||||
// deal with schema flat props
|
||||
var schema Schema
|
||||
err = datamodel.BuildModel(schNode, &schema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// now comes the part where things may get hairy, schemas are recursive.
|
||||
// which means we could be here forever if our resolver has some unknown bug in it.
|
||||
// in order to prevent this from happening, we will add a counter that tracks the depth
|
||||
// and will hard stop once we reach 50 levels. That's too deep for any data structure IMHO.
|
||||
err = schema.Build(schNode, idx, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Schema = low.NodeReference[*Schema]{Value: &schema, KeyNode: schLabel, ValueNode: schNode}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,12 +1,25 @@
|
||||
package v3
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
PropertiesLabel = "properties"
|
||||
ItemsLabel = "items"
|
||||
AllOfLabel = "allOf"
|
||||
AnyOfLabel = "anyOf"
|
||||
OneOfLabel = "oneOf"
|
||||
NotLabel = "not"
|
||||
DiscriminatorLabel = "discriminator"
|
||||
)
|
||||
|
||||
type Schema struct {
|
||||
Node *yaml.Node
|
||||
Title low.NodeReference[string]
|
||||
MultipleOf low.NodeReference[int]
|
||||
Maximum low.NodeReference[int]
|
||||
@@ -24,22 +37,128 @@ type Schema struct {
|
||||
Required []low.NodeReference[string]
|
||||
Enum []low.NodeReference[string]
|
||||
Type low.NodeReference[string]
|
||||
AllOf *Schema
|
||||
OneOf *Schema
|
||||
AnyOf *Schema
|
||||
Not *Schema
|
||||
Items *Schema
|
||||
Properties map[string]*Schema
|
||||
AdditionalProperties low.ObjectReference
|
||||
AllOf *low.NodeReference[*Schema]
|
||||
OneOf *low.NodeReference[*Schema]
|
||||
AnyOf *low.NodeReference[*Schema]
|
||||
Not *low.NodeReference[*Schema]
|
||||
Items *low.NodeReference[*Schema]
|
||||
Properties map[low.NodeReference[string]]*low.NodeReference[*Schema]
|
||||
AdditionalProperties low.NodeReference[any]
|
||||
Description low.NodeReference[string]
|
||||
Default low.ObjectReference
|
||||
Default low.NodeReference[any]
|
||||
Nullable low.NodeReference[bool]
|
||||
Discriminator Discriminator
|
||||
Discriminator *low.NodeReference[*Discriminator]
|
||||
ReadOnly low.NodeReference[bool]
|
||||
WriteOnly low.NodeReference[bool]
|
||||
XML XML
|
||||
ExternalDocs ExternalDoc
|
||||
Example low.ObjectReference
|
||||
XML *low.NodeReference[*XML]
|
||||
ExternalDocs *low.NodeReference[*ExternalDoc]
|
||||
Example low.NodeReference[any]
|
||||
Deprecated low.NodeReference[bool]
|
||||
Extensions map[string]low.ObjectReference
|
||||
Extensions map[low.NodeReference[string]]low.NodeReference[any]
|
||||
}
|
||||
|
||||
func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex, level int) error {
|
||||
level++
|
||||
if level > 50 {
|
||||
return nil // we done, son! too fricken deep.
|
||||
}
|
||||
|
||||
extensionMap, err := datamodel.ExtractExtensions(root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Extensions = extensionMap
|
||||
|
||||
// handle properties
|
||||
_, propLabel, propsNode := utils.FindKeyNodeFull(PropertiesLabel, root.Content)
|
||||
if propsNode != nil {
|
||||
propertyMap := make(map[low.NodeReference[string]]*low.NodeReference[*Schema])
|
||||
var currentProp *yaml.Node
|
||||
for i, prop := range propsNode.Content {
|
||||
if i%2 == 0 {
|
||||
currentProp = prop
|
||||
continue
|
||||
}
|
||||
|
||||
var property Schema
|
||||
err = datamodel.BuildModel(prop, &property)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = property.Build(prop, idx, level)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
propertyMap[low.NodeReference[string]{
|
||||
Value: currentProp.Value,
|
||||
KeyNode: propLabel,
|
||||
ValueNode: propsNode,
|
||||
}] = &low.NodeReference[*Schema]{
|
||||
Value: &property,
|
||||
KeyNode: currentProp,
|
||||
ValueNode: prop,
|
||||
}
|
||||
}
|
||||
s.Properties = propertyMap
|
||||
|
||||
// extract all sub-schemas
|
||||
var errors []error
|
||||
var wg sync.WaitGroup
|
||||
|
||||
var allOf, anyOf, oneOf, not, items low.NodeReference[*Schema]
|
||||
allOf = low.NodeReference[*Schema]{Value: &Schema{}}
|
||||
anyOf = low.NodeReference[*Schema]{Value: &Schema{}}
|
||||
oneOf = low.NodeReference[*Schema]{Value: &Schema{}}
|
||||
not = low.NodeReference[*Schema]{Value: &Schema{}}
|
||||
items = low.NodeReference[*Schema]{Value: &Schema{}}
|
||||
go buildSchema(&allOf, AllOfLabel, idx, root, level, &errors, &wg)
|
||||
go buildSchema(&anyOf, AnyOfLabel, idx, root, level, &errors, &wg)
|
||||
go buildSchema(&oneOf, OneOfLabel, idx, root, level, &errors, &wg)
|
||||
go buildSchema(¬, NotLabel, idx, root, level, &errors, &wg)
|
||||
go buildSchema(&items, ItemsLabel, idx, root, level, &errors, &wg)
|
||||
wg.Wait()
|
||||
|
||||
if len(errors) > 0 {
|
||||
// todo fix this
|
||||
return errors[0]
|
||||
}
|
||||
if anyOf.KeyNode != nil {
|
||||
s.AnyOf = &anyOf
|
||||
}
|
||||
if oneOf.KeyNode != nil {
|
||||
s.OneOf = &oneOf
|
||||
}
|
||||
if allOf.KeyNode != nil {
|
||||
s.AllOf = &allOf
|
||||
}
|
||||
if not.KeyNode != nil {
|
||||
s.Not = ¬
|
||||
}
|
||||
if items.KeyNode != nil {
|
||||
s.Items = &items
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildSchema(schema *low.NodeReference[*Schema], attribute string, idx *index.SpecIndex, rootNode *yaml.Node, level int, errors *[]error, wg *sync.WaitGroup) {
|
||||
_, labelNode, valueNode := utils.FindKeyNodeFull(attribute, rootNode.Content)
|
||||
if valueNode != nil {
|
||||
wg.Add(1)
|
||||
err := datamodel.BuildModel(valueNode, &schema.Value)
|
||||
if err != nil {
|
||||
*errors = append(*errors, err)
|
||||
return
|
||||
}
|
||||
err = schema.Value.Build(rootNode, idx, level)
|
||||
if err != nil {
|
||||
*errors = append(*errors, err)
|
||||
return
|
||||
}
|
||||
schema.KeyNode = labelNode
|
||||
schema.ValueNode = valueNode
|
||||
wg.Done()
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package openapi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
v3 "github.com/pb33f/libopenapi/datamodel/low/3.0"
|
||||
@@ -52,7 +51,6 @@ func CreateDocument(info *datamodel.SpecInfo) (*v3.Document, error) {
|
||||
if len(errors) > 0 {
|
||||
return &doc, errors[0]
|
||||
}
|
||||
fmt.Sprint(idx)
|
||||
return &doc, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -141,13 +141,8 @@ paths:
|
||||
summary: Search a burger by ID - returns the burger with that identifier
|
||||
description: Look up a tasty burger take it and enjoy it
|
||||
parameters:
|
||||
- in: path
|
||||
name: burgerId
|
||||
schema:
|
||||
type: string
|
||||
example: big-mac
|
||||
description: the name of the burger. use this to order your food
|
||||
required: true
|
||||
- $ref: '#/components/parameters/BurgerId'
|
||||
- $ref: '#/components/parameters/BurgerHeader'
|
||||
responses:
|
||||
"200":
|
||||
description: A tasty burger for you to eat. Wide variety of products to choose from
|
||||
@@ -311,6 +306,29 @@ paths:
|
||||
example:
|
||||
message: "failed looking up all dressings, something went wrong."
|
||||
components:
|
||||
parameters:
|
||||
BurgerHeader:
|
||||
in: header
|
||||
name: burgerHeader
|
||||
schema:
|
||||
properties:
|
||||
burgerTheme:
|
||||
type: string
|
||||
description: something about a theme?
|
||||
burgerTime:
|
||||
type: number
|
||||
description: number of burgers ordered this year.
|
||||
example: big-mac
|
||||
description: the name of the burger. use this to order your food
|
||||
required: true
|
||||
BurgerId:
|
||||
in: path
|
||||
name: burgerId
|
||||
schema:
|
||||
type: string
|
||||
example: big-mac
|
||||
description: the name of the burger. use this to order your food
|
||||
required: true
|
||||
schemas:
|
||||
Error:
|
||||
type: object
|
||||
|
||||
Reference in New Issue
Block a user