diff --git a/datamodel/low/3.0/callback.go b/datamodel/low/3.0/callback.go index 726acfc..6bdc27c 100644 --- a/datamodel/low/3.0/callback.go +++ b/datamodel/low/3.0/callback.go @@ -1,12 +1,12 @@ package v3 import ( - "github.com/pb33f/libopenapi/datamodel/low" - "gopkg.in/yaml.v3" + "github.com/pb33f/libopenapi/datamodel/low" + "gopkg.in/yaml.v3" ) type Callback struct { - Node *yaml.Node - Expression map[string]Path - Extensions map[string]low.ObjectReference + Node *yaml.Node + Expression map[string]PathItem + Extensions map[string]low.ObjectReference } diff --git a/datamodel/low/3.0/encoding.go b/datamodel/low/3.0/encoding.go index 9e02917..ecfd6fd 100644 --- a/datamodel/low/3.0/encoding.go +++ b/datamodel/low/3.0/encoding.go @@ -1,15 +1,31 @@ package v3 import ( - "github.com/pb33f/libopenapi/datamodel/low" - "gopkg.in/yaml.v3" + "github.com/pb33f/libopenapi/datamodel/low" + "gopkg.in/yaml.v3" +) + +const ( + EncodingLabel = "encoding" ) type Encoding struct { - Node *yaml.Node - ContentType low.NodeReference[string] - Headers map[string]Parameter - Style low.NodeReference[string] - Explode low.NodeReference[bool] - AllowReserved low.NodeReference[bool] + ContentType low.NodeReference[string] + Headers map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[*Header] + Style low.NodeReference[string] + Explode low.NodeReference[bool] + AllowReserved low.NodeReference[bool] +} + +func (en Encoding) Build(root *yaml.Node) error { + + headers, err := ExtractMap[*Header](HeadersLabel, root) + if err != nil { + return err + } + if headers != nil { + en.Headers = headers + } + + return nil } diff --git a/datamodel/low/3.0/example.go b/datamodel/low/3.0/example.go index 4a93622..edc9c86 100644 --- a/datamodel/low/3.0/example.go +++ b/datamodel/low/3.0/example.go @@ -1,15 +1,29 @@ package v3 import ( - "github.com/pb33f/libopenapi/datamodel/low" - "gopkg.in/yaml.v3" + "github.com/pb33f/libopenapi/datamodel/low" + "gopkg.in/yaml.v3" +) + +const ( + ExamplesLabel = "examples" + ExampleLabel = "example" ) type Example struct { - Node *yaml.Node - Summary low.NodeReference[string] - Description low.NodeReference[string] - Value low.ObjectReference - ExternalValue low.NodeReference[string] - Extensions map[string]low.ObjectReference + Summary low.NodeReference[string] + Description low.NodeReference[string] + Value low.NodeReference[any] + ExternalValue low.NodeReference[string] + Extensions map[low.KeyReference[string]]low.ValueReference[any] +} + +func (ex Example) Build(root *yaml.Node) error { + // extract extensions + extensionMap, err := ExtractExtensions(root) + if err != nil { + return err + } + ex.Extensions = extensionMap + return nil } diff --git a/datamodel/low/3.0/external_doc.go b/datamodel/low/3.0/external_doc.go index 97222e6..5dd4d32 100644 --- a/datamodel/low/3.0/external_doc.go +++ b/datamodel/low/3.0/external_doc.go @@ -2,10 +2,21 @@ package v3 import ( "github.com/pb33f/libopenapi/datamodel/low" + "gopkg.in/yaml.v3" ) type ExternalDoc struct { Description low.NodeReference[string] URL low.NodeReference[string] - Extensions map[string]low.ObjectReference + Extensions map[low.KeyReference[string]]low.ValueReference[any] +} + +func (ex *ExternalDoc) Build(root *yaml.Node) error { + // extract extensions + extensionMap, err := ExtractExtensions(root) + if err != nil { + return err + } + ex.Extensions = extensionMap + return nil } diff --git a/datamodel/low/3.0/extraction_functions.go b/datamodel/low/3.0/extraction_functions.go new file mode 100644 index 0000000..9eb7647 --- /dev/null +++ b/datamodel/low/3.0/extraction_functions.go @@ -0,0 +1,123 @@ +package v3 + +import ( + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/utils" + "gopkg.in/yaml.v3" + "strconv" + "sync" +) + +func ExtractSchema(root *yaml.Node) (*low.NodeReference[*Schema], error) { + _, schLabel, schNode := utils.FindKeyNodeFull(SchemaLabel, root.Content) + if schNode != nil { + var schema Schema + err := BuildModel(schNode, &schema) + if err != nil { + return nil, err + } + err = schema.Build(schNode, 0) + if err != nil { + return nil, err + } + return &low.NodeReference[*Schema]{Value: &schema, KeyNode: schLabel, ValueNode: schNode}, nil + } + return nil, nil +} + +var mapLock sync.Mutex + +func ExtractMap[PT low.Buildable[N], N any](label string, root *yaml.Node) (map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[PT], error) { + _, labelNode, valueNode := utils.FindKeyNodeFull(label, root.Content) + if valueNode != nil { + var currentLabelNode *yaml.Node + valueMap := make(map[low.KeyReference[string]]low.ValueReference[PT]) + for i, en := range valueNode.Content { + if i%2 == 0 { + currentLabelNode = en + continue + } + var n PT = new(N) + err := BuildModel(valueNode, n) + if err != nil { + return nil, err + } + berr := n.Build(valueNode) + if berr != nil { + return nil, berr + } + valueMap[low.KeyReference[string]{ + Value: currentLabelNode.Value, + KeyNode: currentLabelNode, + }] = low.ValueReference[PT]{ + Value: n, + ValueNode: en, + } + } + + resMap := make(map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[PT]) + resMap[low.KeyReference[string]{ + Value: labelNode.Value, + KeyNode: labelNode, + }] = valueMap + return resMap, nil + } + return nil, nil +} + +func ExtractExtensions(root *yaml.Node) (map[low.KeyReference[string]]low.ValueReference[any], error) { + extensions := utils.FindExtensionNodes(root.Content) + extensionMap := make(map[low.KeyReference[string]]low.ValueReference[any]) + for _, ext := range extensions { + if utils.IsNodeMap(ext.Value) { + var v interface{} + err := ext.Value.Decode(&v) + if err != nil { + return nil, err + } + extensionMap[low.KeyReference[string]{ + Value: ext.Key.Value, + KeyNode: ext.Key, + }] = low.ValueReference[any]{Value: v, ValueNode: ext.Value} + } + if utils.IsNodeStringValue(ext.Value) { + extensionMap[low.KeyReference[string]{ + Value: ext.Key.Value, + KeyNode: ext.Key, + }] = low.ValueReference[any]{Value: ext.Value.Value, ValueNode: ext.Value} + } + if utils.IsNodeFloatValue(ext.Value) { + fv, _ := strconv.ParseFloat(ext.Value.Value, 64) + extensionMap[low.KeyReference[string]{ + Value: ext.Key.Value, + KeyNode: ext.Key, + }] = low.ValueReference[any]{Value: fv, ValueNode: ext.Value} + } + if utils.IsNodeIntValue(ext.Value) { + iv, _ := strconv.ParseInt(ext.Value.Value, 10, 64) + extensionMap[low.KeyReference[string]{ + Value: ext.Key.Value, + KeyNode: ext.Key, + }] = low.ValueReference[any]{Value: iv, ValueNode: ext.Value} + } + if utils.IsNodeBoolValue(ext.Value) { + bv, _ := strconv.ParseBool(ext.Value.Value) + extensionMap[low.KeyReference[string]{ + Value: ext.Key.Value, + KeyNode: ext.Key, + }] = low.ValueReference[any]{Value: bv, ValueNode: ext.Value} + } + if utils.IsNodeArray(ext.Value) { + var v []interface{} + err := ext.Value.Decode(&v) + if err != nil { + return nil, err + } + extensionMap[low.KeyReference[string]{ + Value: ext.Key.Value, + KeyNode: ext.Key, + }] = low.ValueReference[any]{Value: v, ValueNode: ext.Value} + } + } + return extensionMap, nil +} diff --git a/datamodel/low/3.0/header.go b/datamodel/low/3.0/header.go new file mode 100644 index 0000000..0324cac --- /dev/null +++ b/datamodel/low/3.0/header.go @@ -0,0 +1,61 @@ +package v3 + +import ( + "github.com/pb33f/libopenapi/datamodel/low" + "gopkg.in/yaml.v3" +) + +const ( + HeadersLabel = "headers" +) + +type Header struct { + Description low.NodeReference[string] + Required low.NodeReference[bool] + Deprecated low.NodeReference[bool] + AllowEmptyValue low.NodeReference[bool] + Style low.NodeReference[string] + Explode low.NodeReference[bool] + AllowReserved low.NodeReference[bool] + Schema low.NodeReference[*Schema] + Example low.NodeReference[any] + Examples map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[*Example] + Content map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[*MediaType] + Extensions map[low.KeyReference[string]]low.ValueReference[any] +} + +func (h *Header) Build(root *yaml.Node) error { + // extract extensions + extensionMap, err := ExtractExtensions(root) + if err != nil { + return err + } + h.Extensions = extensionMap + + // handle examples if set. + exps, eErr := ExtractMap[*Example](ExamplesLabel, root) + if eErr != nil { + return eErr + } + if exps != nil { + h.Examples = exps + } + + // handle schema + sch, sErr := ExtractSchema(root) + if sErr != nil { + return nil + } + if sch != nil { + h.Schema = *sch + } + + // handle content, if set. + con, cErr := ExtractMap[*MediaType](ContentLabel, root) + if cErr != nil { + return cErr + } + h.Content = con + + return nil +} diff --git a/datamodel/low/3.0/media_type.go b/datamodel/low/3.0/media_type.go index 523c384..3a3ed04 100644 --- a/datamodel/low/3.0/media_type.go +++ b/datamodel/low/3.0/media_type.go @@ -1,14 +1,59 @@ package v3 import ( - "github.com/pb33f/libopenapi/datamodel/low" - "gopkg.in/yaml.v3" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/utils" + "gopkg.in/yaml.v3" ) type MediaType struct { - Node *yaml.Node - Schema Schema - Example low.ObjectReference - Examples map[string]Example - Encoding map[string]Encoding + Schema low.NodeReference[*Schema] + Example low.NodeReference[any] + Examples map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[*Example] + Encoding map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[*Encoding] + Extensions map[low.KeyReference[string]]low.ValueReference[any] +} + +func (mt *MediaType) Build(root *yaml.Node) error { + + // extract extensions + extensionMap, err := ExtractExtensions(root) + if err != nil { + return err + } + mt.Extensions = extensionMap + + // handle example if set. + _, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content) + if expNode != nil { + mt.Example = low.NodeReference[any]{Value: expNode.Value, KeyNode: expLabel, ValueNode: expNode} + } + + // handle schema + sch, sErr := ExtractSchema(root) + if sErr != nil { + return nil + } + if sch != nil { + mt.Schema = *sch + } + + // handle examples if set. + exps, eErr := ExtractMap[*Example](ExamplesLabel, root) + if eErr != nil { + return eErr + } + if exps != nil { + mt.Examples = exps + } + + // handle encoding + encs, encErr := ExtractMap[*Encoding](EncodingLabel, root) + if encErr != nil { + return err + } + if encs != nil { + mt.Encoding = encs + } + return nil } diff --git a/datamodel/low/3.0/model_builder.go b/datamodel/low/3.0/model_builder.go index 317c48c..9bf4506 100644 --- a/datamodel/low/3.0/model_builder.go +++ b/datamodel/low/3.0/model_builder.go @@ -377,60 +377,3 @@ func BuildModelAsync(n *yaml.Node, model interface{}, lwg *sync.WaitGroup, error } lwg.Done() } - -func ExtractExtensions(root *yaml.Node) (map[low.NodeReference[string]]low.NodeReference[any], error) { - extensions := utils.FindExtensionNodes(root.Content) - extensionMap := make(map[low.NodeReference[string]]low.NodeReference[any]) - for _, ext := range extensions { - if utils.IsNodeMap(ext.Value) { - var v interface{} - err := ext.Value.Decode(&v) - if err != nil { - return nil, err - } - extensionMap[low.NodeReference[string]{ - Value: ext.Key.Value, - KeyNode: ext.Key, - }] = low.NodeReference[any]{Value: v, KeyNode: ext.Key} - } - if utils.IsNodeStringValue(ext.Value) { - extensionMap[low.NodeReference[string]{ - Value: ext.Key.Value, - KeyNode: ext.Key, - }] = low.NodeReference[any]{Value: ext.Value.Value, ValueNode: ext.Value} - } - if utils.IsNodeFloatValue(ext.Value) { - fv, _ := strconv.ParseFloat(ext.Value.Value, 64) - extensionMap[low.NodeReference[string]{ - Value: ext.Key.Value, - KeyNode: ext.Key, - }] = low.NodeReference[any]{Value: fv, ValueNode: ext.Value} - } - if utils.IsNodeIntValue(ext.Value) { - iv, _ := strconv.ParseInt(ext.Value.Value, 10, 64) - extensionMap[low.NodeReference[string]{ - Value: ext.Key.Value, - KeyNode: ext.Key, - }] = low.NodeReference[any]{Value: iv, ValueNode: ext.Value} - } - if utils.IsNodeBoolValue(ext.Value) { - bv, _ := strconv.ParseBool(ext.Value.Value) - extensionMap[low.NodeReference[string]{ - Value: ext.Key.Value, - KeyNode: ext.Key, - }] = low.NodeReference[any]{Value: bv, ValueNode: ext.Value} - } - if utils.IsNodeArray(ext.Value) { - var v []interface{} - err := ext.Value.Decode(&v) - if err != nil { - return nil, err - } - extensionMap[low.NodeReference[string]{ - Value: ext.Key.Value, - KeyNode: ext.Key, - }] = low.NodeReference[any]{Value: v, ValueNode: ext.Value} - } - } - return extensionMap, nil -} diff --git a/datamodel/low/3.0/operation.go b/datamodel/low/3.0/operation.go index 7050b97..e13dd6b 100644 --- a/datamodel/low/3.0/operation.go +++ b/datamodel/low/3.0/operation.go @@ -2,7 +2,6 @@ package v3 import ( "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/utils" "gopkg.in/yaml.v3" ) @@ -15,19 +14,19 @@ type Operation struct { Tags []low.NodeReference[string] Summary low.NodeReference[string] Description low.NodeReference[string] - ExternalDocs *low.NodeReference[*ExternalDoc] + ExternalDocs low.NodeReference[*ExternalDoc] OperationId low.NodeReference[string] Parameters []low.NodeReference[*Parameter] - RequestBody *low.NodeReference[*RequestBody] - Responses *low.NodeReference[*Responses] - Callbacks map[low.NodeReference[string]]low.NodeReference[*Callback] - Deprecated *low.NodeReference[bool] + RequestBody low.NodeReference[*RequestBody] + Responses low.NodeReference[*Responses] + Callbacks map[low.KeyReference[string]]low.ValueReference[*Callback] + Deprecated low.NodeReference[bool] Security []low.NodeReference[*SecurityRequirement] Servers []low.NodeReference[*Server] - Extensions map[low.NodeReference[string]]low.NodeReference[any] + Extensions map[low.KeyReference[string]]low.ValueReference[any] } -func (o *Operation) Build(root *yaml.Node, idx *index.SpecIndex) error { +func (o *Operation) Build(root *yaml.Node) error { extensionMap, err := ExtractExtensions(root) if err != nil { @@ -43,7 +42,7 @@ func (o *Operation) Build(root *yaml.Node, idx *index.SpecIndex) error { if err != nil { return err } - o.ExternalDocs = &low.NodeReference[*ExternalDoc]{ + o.ExternalDocs = low.NodeReference[*ExternalDoc]{ Value: &externalDoc, KeyNode: ln, ValueNode: exDocs, @@ -61,7 +60,7 @@ func (o *Operation) Build(root *yaml.Node, idx *index.SpecIndex) error { if err != nil { return err } - err = param.Build(pN, idx) + err = param.Build(pN) if err != nil { return err } diff --git a/datamodel/low/3.0/parameter.go b/datamodel/low/3.0/parameter.go index 1f7285f..f36771d 100644 --- a/datamodel/low/3.0/parameter.go +++ b/datamodel/low/3.0/parameter.go @@ -2,13 +2,13 @@ package v3 import ( "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/utils" "gopkg.in/yaml.v3" ) const ( - SchemaLabel = "schema" + SchemaLabel = "schema" + ContentLabel = "content" ) type Parameter struct { @@ -23,11 +23,23 @@ type Parameter struct { AllowReserved low.NodeReference[bool] Schema low.NodeReference[*Schema] Example low.NodeReference[any] - Examples map[low.NodeReference[string]]low.NodeReference[*Example] - Extensions map[low.NodeReference[string]]low.NodeReference[any] + Examples map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[*Example] + Content map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[*MediaType] + Extensions map[low.KeyReference[string]]low.ValueReference[any] } -func (p *Parameter) Build(root *yaml.Node, idx *index.SpecIndex) error { +func (p *Parameter) GetContent(cType string) *low.ValueReference[*MediaType] { + for _, c := range p.Content { + for n, o := range c { + if n.Value == cType { + return &o + } + } + } + return nil +} + +func (p *Parameter) Build(root *yaml.Node) error { // extract extensions extensionMap, err := ExtractExtensions(root) @@ -36,27 +48,24 @@ func (p *Parameter) Build(root *yaml.Node, idx *index.SpecIndex) error { } p.Extensions = extensionMap - // handle schema - _, schLabel, schNode := utils.FindKeyNodeFull(SchemaLabel, root.Content) - if schNode != nil { - // deal with schema flat props - var schema Schema - err = 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} + // handle example if set. + _, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content) + if expNode != nil { + p.Example = low.NodeReference[any]{Value: expNode.Value, KeyNode: expLabel, ValueNode: expNode} } + // handle schema + sch, sErr := ExtractSchema(root) + if sErr != nil { + return sErr + } + p.Schema = *sch + + // handle content, if set. + con, cErr := ExtractMap[*MediaType](ContentLabel, root) + if cErr != nil { + return cErr + } + p.Content = con return nil } diff --git a/datamodel/low/3.0/path.go b/datamodel/low/3.0/path.go index d384b52..771e6b6 100644 --- a/datamodel/low/3.0/path.go +++ b/datamodel/low/3.0/path.go @@ -2,7 +2,6 @@ package v3 import ( "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/index" "gopkg.in/yaml.v3" "strings" "sync" @@ -21,19 +20,20 @@ const ( ) type Paths struct { - Paths map[low.NodeReference[string]]low.NodeReference[*Path] - Extensions map[low.NodeReference[string]]low.NodeReference[any] + PathItems map[low.KeyReference[string]]low.ValueReference[*PathItem] + Extensions map[low.KeyReference[string]]low.ValueReference[any] } -func (p *Paths) GetPathMap() map[string]*Path { - pMap := make(map[string]*Path) - for i, pv := range p.Paths { - pMap[i.Value] = pv.Value +func (p *Paths) GetPath(path string) *low.ValueReference[*PathItem] { + for k, p := range p.PathItems { + if k.Value == path { + return &p + } } - return pMap + return nil } -func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error { +func (p *Paths) Build(root *yaml.Node) error { // extract extensions extensionMap, err := ExtractExtensions(root) @@ -44,7 +44,7 @@ func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error { skip := false var currentNode *yaml.Node - pathsMap := make(map[low.NodeReference[string]]low.NodeReference[*Path]) + pathsMap := make(map[low.KeyReference[string]]low.ValueReference[*PathItem]) for i, pathNode := range root.Content { if strings.HasPrefix(strings.ToLower(pathNode.Value), "x-") { @@ -59,32 +59,32 @@ func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error { currentNode = pathNode continue } - var path = Path{} + var path = PathItem{} err = BuildModel(pathNode, &path) if err != nil { } - err = path.Build(pathNode, idx) + err = path.Build(pathNode) if err != nil { return err } // add bulk here - pathsMap[low.NodeReference[string]{ + pathsMap[low.KeyReference[string]{ Value: currentNode.Value, KeyNode: currentNode, - }] = low.NodeReference[*Path]{ + }] = low.ValueReference[*PathItem]{ Value: &path, ValueNode: pathNode, } } - p.Paths = pathsMap + p.PathItems = pathsMap return nil } -type Path struct { +type PathItem struct { Description low.NodeReference[string] Summary low.NodeReference[string] Get *low.NodeReference[*Operation] @@ -97,10 +97,10 @@ type Path struct { Trace *low.NodeReference[*Operation] Servers []*low.NodeReference[*Server] Parameters []*low.NodeReference[*Parameter] - Extensions map[low.NodeReference[string]]low.NodeReference[any] + Extensions map[low.KeyReference[string]]low.ValueReference[any] } -func (p *Path) Build(root *yaml.Node, idx *index.SpecIndex) error { +func (p *PathItem) Build(root *yaml.Node) error { extensionMap, err := ExtractExtensions(root) if err != nil { return err @@ -171,7 +171,7 @@ func (p *Path) Build(root *yaml.Node, idx *index.SpecIndex) error { var buildOpFunc = func(op low.NodeReference[*Operation], ch chan<- bool, errCh chan<- error) { // build out the operation. - er := op.Value.Build(op.ValueNode, idx) + er := op.Value.Build(op.ValueNode) if err != nil { errCh <- er } diff --git a/datamodel/low/3.0/schema.go b/datamodel/low/3.0/schema.go index e2e3dc9..8a722fb 100644 --- a/datamodel/low/3.0/schema.go +++ b/datamodel/low/3.0/schema.go @@ -2,7 +2,6 @@ package v3 import ( "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/utils" "gopkg.in/yaml.v3" "strconv" @@ -12,7 +11,7 @@ import ( const ( PropertiesLabel = "properties" AdditionalPropertiesLabel = "additionalProperties" - ExampleLabel = "example" + XMLLabel = "xml" ItemsLabel = "items" AllOfLabel = "allOf" AnyOfLabel = "anyOf" @@ -44,7 +43,7 @@ type Schema struct { AnyOf []low.NodeReference[*Schema] Not []low.NodeReference[*Schema] Items []low.NodeReference[*Schema] - Properties map[low.NodeReference[string]]*low.NodeReference[*Schema] + Properties map[low.KeyReference[string]]*low.ValueReference[*Schema] AdditionalProperties low.NodeReference[any] Description low.NodeReference[string] Default low.NodeReference[any] @@ -52,14 +51,14 @@ type Schema struct { Discriminator low.NodeReference[*Discriminator] ReadOnly low.NodeReference[bool] WriteOnly low.NodeReference[bool] - XML *low.NodeReference[*XML] - ExternalDocs *low.NodeReference[*ExternalDoc] + XML low.NodeReference[*XML] + ExternalDocs low.NodeReference[*ExternalDoc] Example low.NodeReference[any] Deprecated low.NodeReference[bool] - Extensions map[low.NodeReference[string]]low.NodeReference[any] + Extensions map[low.KeyReference[string]]low.ValueReference[any] } -func (s *Schema) FindProperty(name string) *low.NodeReference[*Schema] { +func (s *Schema) FindProperty(name string) *low.ValueReference[*Schema] { for k, v := range s.Properties { if k.Value == name { return v @@ -68,17 +67,16 @@ func (s *Schema) FindProperty(name string) *low.NodeReference[*Schema] { return nil } -func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex, level int) error { +func (s *Schema) Build(root *yaml.Node, level int) error { level++ if level > 50 { return nil // we're done, son! too fricken deep. } - extensionMap, err := ExtractExtensions(root) + err := s.extractExtensions(root) if err != nil { return err } - s.Extensions = extensionMap // handle example if set. _, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content) @@ -90,7 +88,10 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex, level int) error { if addPNode != nil { if utils.IsNodeMap(addPNode) { var props map[string]interface{} - addPNode.Decode(&props) + err = addPNode.Decode(&props) + if err != nil { + return err + } s.AdditionalProperties = low.NodeReference[any]{Value: props, KeyNode: addPLabel, ValueNode: addPNode} } @@ -111,11 +112,43 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex, level int) error { s.Discriminator = low.NodeReference[*Discriminator]{Value: &discriminator, KeyNode: discLabel, ValueNode: discNode} } + // handle externalDocs if set. + _, extDocLabel, extDocNode := utils.FindKeyNodeFull(ExternalDocsLabel, root.Content) + if extDocNode != nil { + var exDoc ExternalDoc + err = BuildModel(extDocNode, &exDoc) + if err != nil { + return err + } + err = exDoc.Build(extDocNode) + if err != nil { + return err + } + s.ExternalDocs = low.NodeReference[*ExternalDoc]{Value: &exDoc, KeyNode: extDocLabel, ValueNode: extDocNode} + } + + // handle xml if set. + _, xmlLabel, xmlNode := utils.FindKeyNodeFull(XMLLabel, root.Content) + if xmlNode != nil { + var xml XML + err = BuildModel(xmlNode, &xml) + if err != nil { + return err + } + // extract extensions if set. + err = xml.Build(xmlNode) + if err != nil { + return err + } + s.XML = low.NodeReference[*XML]{Value: &xml, KeyNode: xmlLabel, ValueNode: xmlNode} + } + // handle properties _, propLabel, propsNode := utils.FindKeyNodeFull(PropertiesLabel, root.Content) if propsNode != nil { - propertyMap := make(map[low.NodeReference[string]]*low.NodeReference[*Schema]) + propertyMap := make(map[low.KeyReference[string]]*low.ValueReference[*Schema]) var currentProp *yaml.Node + var wg sync.WaitGroup for i, prop := range propsNode.Content { if i%2 == 0 { currentProp = prop @@ -127,17 +160,15 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex, level int) error { if err != nil { return err } - err = property.Build(prop, idx, level) + err = property.Build(prop, level) if err != nil { return err } - propertyMap[low.NodeReference[string]{ - Value: currentProp.Value, - KeyNode: propLabel, - ValueNode: propsNode, - }] = &low.NodeReference[*Schema]{ + propertyMap[low.KeyReference[string]{ + Value: currentProp.Value, + KeyNode: propLabel, + }] = &low.ValueReference[*Schema]{ Value: &property, - KeyNode: currentProp, ValueNode: prop, } } @@ -145,16 +176,15 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex, level int) error { // extract all sub-schemas var errors []error - var wg sync.WaitGroup var allOf, anyOf, oneOf, not, items []low.NodeReference[*Schema] // make this async at some point to speed things up. - buildSchema(&allOf, AllOfLabel, idx, root, level, &errors, &wg) - buildSchema(&anyOf, AnyOfLabel, idx, root, level, &errors, &wg) - buildSchema(&oneOf, OneOfLabel, idx, root, level, &errors, &wg) - buildSchema(¬, NotLabel, idx, root, level, &errors, &wg) - buildSchema(&items, ItemsLabel, idx, root, level, &errors, &wg) + buildSchema(&allOf, AllOfLabel, root, level, &errors, &wg) + buildSchema(&anyOf, AnyOfLabel, root, level, &errors, &wg) + buildSchema(&oneOf, OneOfLabel, root, level, &errors, &wg) + buildSchema(¬, NotLabel, root, level, &errors, &wg) + buildSchema(&items, ItemsLabel, root, level, &errors, &wg) //wg.Wait() if len(errors) > 0 { @@ -182,7 +212,16 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex, level int) error { return nil } -func buildSchema(schemas *[]low.NodeReference[*Schema], attribute string, idx *index.SpecIndex, rootNode *yaml.Node, level int, errors *[]error, wg *sync.WaitGroup) { +func (s *Schema) extractExtensions(root *yaml.Node) error { + extensionMap, err := ExtractExtensions(root) + if err != nil { + return err + } + s.Extensions = extensionMap + return err +} + +func buildSchema(schemas *[]low.NodeReference[*Schema], attribute string, rootNode *yaml.Node, level int, errors *[]error, wg *sync.WaitGroup) { _, labelNode, valueNode := utils.FindKeyNodeFull(attribute, rootNode.Content) //wg.Add(1) if valueNode != nil { @@ -194,7 +233,7 @@ func buildSchema(schemas *[]low.NodeReference[*Schema], attribute string, idx *i *errors = append(*errors, err) return nil } - err = schema.Build(vn, idx, level) + err = schema.Build(vn, level) if err != nil { *errors = append(*errors, err) return nil diff --git a/datamodel/low/3.0/schema_test.go b/datamodel/low/3.0/schema_test.go index 0b7ea52..e349dc2 100644 --- a/datamodel/low/3.0/schema_test.go +++ b/datamodel/low/3.0/schema_test.go @@ -83,15 +83,25 @@ properties: somethingB: type: object description: an object + externalDocs: + description: the best docs + url: https://pb33f.io properties: somethingBProp: type: string description: something b subprop example: picnics are nice. + xml: + name: an xml thing + namespace: an xml namespace + prefix: a prefix + attribute: true + wrapped: false + x-pizza: love additionalProperties: why: yes thatIs: true -additionalProperties: true` +additionalProperties: true ` var rootNode yaml.Node mErr := yaml.Unmarshal([]byte(testSpec), &rootNode) @@ -101,13 +111,26 @@ additionalProperties: true` mbErr := BuildModel(&rootNode, &sch) assert.NoError(t, mbErr) - schErr := sch.Build(rootNode.Content[0], nil, 0) + schErr := sch.Build(rootNode.Content[0], 0) assert.NoError(t, schErr) assert.Equal(t, "something object", sch.Description.Value) assert.True(t, sch.AdditionalProperties.Value.(bool)) assert.Len(t, sch.Properties, 2) v := sch.FindProperty("somethingB") + + assert.Equal(t, "https://pb33f.io", v.Value.ExternalDocs.Value.URL.Value) + assert.Equal(t, "the best docs", v.Value.ExternalDocs.Value.Description.Value) + + j := v.Value.FindProperty("somethingBProp") + assert.NotNil(t, j.Value) + assert.NotNil(t, j.Value.XML.Value) + assert.Equal(t, "an xml thing", j.Value.XML.Value.Name.Value) + assert.Equal(t, "an xml namespace", j.Value.XML.Value.Namespace.Value) + assert.Equal(t, "a prefix", j.Value.XML.Value.Prefix.Value) + assert.Equal(t, true, j.Value.XML.Value.Attribute.Value) + assert.Len(t, j.Value.XML.Value.Extensions, 1) + assert.NotNil(t, v.Value.AdditionalProperties.Value) var addProps map[string]interface{} diff --git a/datamodel/low/3.0/tag.go b/datamodel/low/3.0/tag.go index d4ab991..a4ce3b4 100644 --- a/datamodel/low/3.0/tag.go +++ b/datamodel/low/3.0/tag.go @@ -15,7 +15,7 @@ type Tag struct { Name low.NodeReference[string] Description low.NodeReference[string] ExternalDocs low.NodeReference[*ExternalDoc] - Extensions map[low.NodeReference[string]]low.NodeReference[any] + Extensions map[low.KeyReference[string]]low.ValueReference[any] } func (t *Tag) Build(root *yaml.Node) error { diff --git a/datamodel/low/3.0/xml.go b/datamodel/low/3.0/xml.go index 8acb071..dfe3512 100644 --- a/datamodel/low/3.0/xml.go +++ b/datamodel/low/3.0/xml.go @@ -1,16 +1,24 @@ package v3 import ( - "github.com/pb33f/libopenapi/datamodel/low" - "gopkg.in/yaml.v3" + "github.com/pb33f/libopenapi/datamodel/low" + "gopkg.in/yaml.v3" ) type XML struct { - Node *yaml.Node - Name low.NodeReference[string] - Namespace low.NodeReference[string] - Prefix low.NodeReference[string] - Attribute low.NodeReference[string] - Wrapped low.NodeReference[bool] - Extensions map[string]low.ObjectReference + Name low.NodeReference[string] + Namespace low.NodeReference[string] + Prefix low.NodeReference[string] + Attribute low.NodeReference[bool] + Wrapped low.NodeReference[bool] + Extensions map[low.KeyReference[string]]low.ValueReference[any] +} + +func (x *XML) Build(root *yaml.Node) error { + extensionMap, err := ExtractExtensions(root) + if err != nil { + return err + } + x.Extensions = extensionMap + return nil } diff --git a/datamodel/low/reference.go b/datamodel/low/reference.go index a8d860a..aee2fbb 100644 --- a/datamodel/low/reference.go +++ b/datamodel/low/reference.go @@ -6,8 +6,9 @@ type HasNode interface { GetNode() *yaml.Node } -type Buildable interface { +type Buildable[T any] interface { Build(node *yaml.Node) error + *T } type NodeReference[T any] struct { @@ -16,6 +17,16 @@ type NodeReference[T any] struct { KeyNode *yaml.Node } +type KeyReference[T any] struct { + Value T + KeyNode *yaml.Node +} + +type ValueReference[T any] struct { + Value T + ValueNode *yaml.Node +} + type ObjectReference struct { Value interface{} ValueNode *yaml.Node @@ -25,3 +36,11 @@ type ObjectReference struct { func (n NodeReference[T]) IsEmpty() bool { return n.KeyNode == nil && n.ValueNode == nil } + +func (n NodeReference[T]) IsMapKeyNode() bool { + return n.KeyNode != nil && n.ValueNode == nil +} + +func (n NodeReference[T]) IsMapValueNode() bool { + return n.KeyNode == nil && n.ValueNode != nil +} diff --git a/openapi/create_document.go b/openapi/create_document.go index f87b6e0..dad34ec 100644 --- a/openapi/create_document.go +++ b/openapi/create_document.go @@ -15,8 +15,7 @@ func CreateDocument(info *datamodel.SpecInfo) (*v3.Document, error) { doc := v3.Document{Version: low.NodeReference[string]{Value: info.Version, ValueNode: info.RootNode}} // build an index - idx := index.NewSpecIndex(info.RootNode) - rsolvr := resolver.NewResolver(idx) + rsolvr := resolver.NewResolver(index.NewSpecIndex(info.RootNode)) // todo handle errors rsolvr.Resolve() @@ -24,18 +23,18 @@ func CreateDocument(info *datamodel.SpecInfo) (*v3.Document, error) { var wg sync.WaitGroup var errors []error var runExtraction = func(info *datamodel.SpecInfo, doc *v3.Document, - runFunc func(i *datamodel.SpecInfo, d *v3.Document, idx *index.SpecIndex) error, + runFunc func(i *datamodel.SpecInfo, d *v3.Document) error, ers *[]error, wg *sync.WaitGroup) { - if er := runFunc(info, doc, idx); er != nil { + if er := runFunc(info, doc); er != nil { *ers = append(*ers, er) } wg.Done() } - extractionFuncs := []func(i *datamodel.SpecInfo, d *v3.Document, idx *index.SpecIndex) error{ + extractionFuncs := []func(i *datamodel.SpecInfo, d *v3.Document) error{ extractInfo, extractServers, extractTags, @@ -54,7 +53,7 @@ func CreateDocument(info *datamodel.SpecInfo) (*v3.Document, error) { return &doc, nil } -func extractInfo(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecIndex) error { +func extractInfo(info *datamodel.SpecInfo, doc *v3.Document) error { _, ln, vn := utils.FindKeyNodeFull(v3.InfoLabel, info.RootNode.Content) if vn != nil { ir := v3.Info{} @@ -69,7 +68,7 @@ func extractInfo(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecInde return nil } -func extractServers(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecIndex) error { +func extractServers(info *datamodel.SpecInfo, doc *v3.Document) error { _, ln, vn := utils.FindKeyNodeFull(v3.ServersLabel, info.RootNode.Content) if vn != nil { if utils.IsNodeArray(vn) { @@ -95,7 +94,7 @@ func extractServers(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecI return nil } -func extractTags(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecIndex) error { +func extractTags(info *datamodel.SpecInfo, doc *v3.Document) error { _, ln, vn := utils.FindKeyNodeFull(v3.TagsLabel, info.RootNode.Content) if vn != nil { if utils.IsNodeArray(vn) { @@ -121,11 +120,11 @@ func extractTags(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecInde return nil } -func extractPaths(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecIndex) error { +func extractPaths(info *datamodel.SpecInfo, doc *v3.Document) error { _, ln, vn := utils.FindKeyNodeFull(v3.PathsLabel, info.RootNode.Content) if vn != nil { ir := v3.Paths{} - err := ir.Build(vn, idx) + err := ir.Build(vn) if err != nil { return err } diff --git a/openapi/create_document_test.go b/openapi/create_document_test.go index 76f7003..16d37b9 100644 --- a/openapi/create_document_test.go +++ b/openapi/create_document_test.go @@ -1,109 +1,126 @@ package openapi import ( - "github.com/pb33f/libopenapi/datamodel" - v3 "github.com/pb33f/libopenapi/datamodel/low/3.0" - "github.com/stretchr/testify/assert" - "io/ioutil" - "testing" + "github.com/pb33f/libopenapi/datamodel" + v3 "github.com/pb33f/libopenapi/datamodel/low/3.0" + "github.com/stretchr/testify/assert" + "io/ioutil" + "testing" ) var doc *v3.Document func init() { - data, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml") - info, _ := datamodel.ExtractSpecInfo(data) - doc, _ = CreateDocument(info) + data, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml") + info, _ := datamodel.ExtractSpecInfo(data) + doc, _ = CreateDocument(info) } func BenchmarkCreateDocument(b *testing.B) { - data, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml") - info, _ := datamodel.ExtractSpecInfo(data) - for i := 0; i < b.N; i++ { - doc, _ = CreateDocument(info) - } + data, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml") + info, _ := datamodel.ExtractSpecInfo(data) + for i := 0; i < b.N; i++ { + doc, _ = CreateDocument(info) + } } func TestCreateDocument(t *testing.T) { - assert.Equal(t, "3.0.1", doc.Version.Value) - assert.Equal(t, "Burger Shop", doc.Info.Value.Title.Value) - assert.NotEmpty(t, doc.Info.Value.Title.Value) + assert.Equal(t, "3.0.1", doc.Version.Value) + assert.Equal(t, "Burger Shop", doc.Info.Value.Title.Value) + assert.NotEmpty(t, doc.Info.Value.Title.Value) } func TestCreateDocument_Info(t *testing.T) { - assert.Equal(t, "https://pb33f.io", doc.Info.Value.TermsOfService.Value) - assert.Equal(t, "pb33f", doc.Info.Value.Contact.Value.Name.Value) - assert.Equal(t, "buckaroo@pb33f.io", doc.Info.Value.Contact.Value.Email.Value) - assert.Equal(t, "https://pb33f.io", doc.Info.Value.Contact.Value.URL.Value) - assert.Equal(t, "pb33f", doc.Info.Value.License.Value.Name.Value) - assert.Equal(t, "https://pb33f.io/made-up", doc.Info.Value.License.Value.URL.Value) + assert.Equal(t, "https://pb33f.io", doc.Info.Value.TermsOfService.Value) + assert.Equal(t, "pb33f", doc.Info.Value.Contact.Value.Name.Value) + assert.Equal(t, "buckaroo@pb33f.io", doc.Info.Value.Contact.Value.Email.Value) + assert.Equal(t, "https://pb33f.io", doc.Info.Value.Contact.Value.URL.Value) + assert.Equal(t, "pb33f", doc.Info.Value.License.Value.Name.Value) + assert.Equal(t, "https://pb33f.io/made-up", doc.Info.Value.License.Value.URL.Value) } func TestCreateDocument_Servers(t *testing.T) { - assert.Len(t, doc.Servers, 2) - server1 := doc.Servers[0] - server2 := doc.Servers[1] + assert.Len(t, doc.Servers, 2) + server1 := doc.Servers[0] + server2 := doc.Servers[1] - // server 1 - assert.Equal(t, "{scheme}://api.pb33f.io", server1.Value.URL.Value) - assert.NotEmpty(t, server1.Value.Description.Value) - assert.Len(t, server1.Value.Variables.Value, 1) - assert.Len(t, server1.Value.Variables.Value["scheme"].Value.Enum, 2) - assert.Equal(t, server1.Value.Variables.Value["scheme"].Value.Default.Value, "https") - assert.NotEmpty(t, server1.Value.Variables.Value["scheme"].Value.Description.Value) + // server 1 + assert.Equal(t, "{scheme}://api.pb33f.io", server1.Value.URL.Value) + assert.NotEmpty(t, server1.Value.Description.Value) + assert.Len(t, server1.Value.Variables.Value, 1) + assert.Len(t, server1.Value.Variables.Value["scheme"].Value.Enum, 2) + assert.Equal(t, server1.Value.Variables.Value["scheme"].Value.Default.Value, "https") + assert.NotEmpty(t, server1.Value.Variables.Value["scheme"].Value.Description.Value) - // server 2 - assert.Equal(t, "https://{domain}.{host}.com", server2.Value.URL.Value) - assert.NotEmpty(t, server2.Value.Description.Value) - assert.Len(t, server2.Value.Variables.Value, 2) - assert.Equal(t, server2.Value.Variables.Value["domain"].Value.Default.Value, "api") - assert.NotEmpty(t, server2.Value.Variables.Value["domain"].Value.Description.Value) - assert.NotEmpty(t, server2.Value.Variables.Value["host"].Value.Description.Value) - assert.Equal(t, server2.Value.Variables.Value["host"].Value.Default.Value, "pb33f.io") - assert.Equal(t, "1.2", doc.Info.Value.Version.Value) + // server 2 + assert.Equal(t, "https://{domain}.{host}.com", server2.Value.URL.Value) + assert.NotEmpty(t, server2.Value.Description.Value) + assert.Len(t, server2.Value.Variables.Value, 2) + assert.Equal(t, server2.Value.Variables.Value["domain"].Value.Default.Value, "api") + assert.NotEmpty(t, server2.Value.Variables.Value["domain"].Value.Description.Value) + assert.NotEmpty(t, server2.Value.Variables.Value["host"].Value.Description.Value) + assert.Equal(t, server2.Value.Variables.Value["host"].Value.Default.Value, "pb33f.io") + assert.Equal(t, "1.2", doc.Info.Value.Version.Value) } func TestCreateDocument_Tags(t *testing.T) { - assert.Len(t, doc.Tags, 2) + assert.Len(t, doc.Tags, 2) - // tag1 - assert.Equal(t, "Burgers", doc.Tags[0].Value.Name.Value) - assert.NotEmpty(t, doc.Tags[0].Value.Description.Value) - assert.NotNil(t, doc.Tags[0].Value.ExternalDocs.Value) - assert.Equal(t, "https://pb33f.io", doc.Tags[0].Value.ExternalDocs.Value.URL.Value) - assert.NotEmpty(t, doc.Tags[0].Value.ExternalDocs.Value.URL.Value) - assert.Len(t, doc.Tags[0].Value.Extensions, 7) + // tag1 + assert.Equal(t, "Burgers", doc.Tags[0].Value.Name.Value) + assert.NotEmpty(t, doc.Tags[0].Value.Description.Value) + assert.NotNil(t, doc.Tags[0].Value.ExternalDocs.Value) + assert.Equal(t, "https://pb33f.io", doc.Tags[0].Value.ExternalDocs.Value.URL.Value) + assert.NotEmpty(t, doc.Tags[0].Value.ExternalDocs.Value.URL.Value) + assert.Len(t, doc.Tags[0].Value.Extensions, 7) - for key, extension := range doc.Tags[0].Value.Extensions { - switch key.Value { - case "x-internal-ting": - assert.Equal(t, "somethingSpecial", extension.Value) - case "x-internal-tong": - assert.Equal(t, int64(1), extension.Value) - case "x-internal-tang": - assert.Equal(t, 1.2, extension.Value) - case "x-internal-tung": - assert.Equal(t, true, extension.Value) - case "x-internal-arr": - assert.Len(t, extension.Value, 2) - assert.Equal(t, "one", extension.Value.([]interface{})[0].(string)) - case "x-internal-arrmap": - assert.Len(t, extension.Value, 2) - assert.Equal(t, "now", extension.Value.([]interface{})[0].(map[string]interface{})["what"]) - case "x-something-else": - // crazy times in the upside down. this API should be avoided for the higher up use cases. - // this is why we will need a higher level API to this model, this looks cool and all, but dude. - assert.Equal(t, "now?", extension.Value.(map[string]interface{})["ok"].([]interface{})[0].(map[string]interface{})["what"]) - } + for key, extension := range doc.Tags[0].Value.Extensions { + switch key.Value { + case "x-internal-ting": + assert.Equal(t, "somethingSpecial", extension.Value) + case "x-internal-tong": + assert.Equal(t, int64(1), extension.Value) + case "x-internal-tang": + assert.Equal(t, 1.2, extension.Value) + case "x-internal-tung": + assert.Equal(t, true, extension.Value) + case "x-internal-arr": + assert.Len(t, extension.Value, 2) + assert.Equal(t, "one", extension.Value.([]interface{})[0].(string)) + case "x-internal-arrmap": + assert.Len(t, extension.Value, 2) + assert.Equal(t, "now", extension.Value.([]interface{})[0].(map[string]interface{})["what"]) + case "x-something-else": + // crazy times in the upside down. this API should be avoided for the higher up use cases. + // this is why we will need a higher level API to this model, this looks cool and all, but dude. + assert.Equal(t, "now?", extension.Value.(map[string]interface{})["ok"].([]interface{})[0].(map[string]interface{})["what"]) + } - } + } - /// tag2 - assert.Equal(t, "Dressing", doc.Tags[1].Value.Name.Value) - assert.NotEmpty(t, doc.Tags[1].Value.Description.Value) - assert.NotNil(t, doc.Tags[1].Value.ExternalDocs.Value) - assert.Equal(t, "https://pb33f.io", doc.Tags[1].Value.ExternalDocs.Value.URL.Value) - assert.NotEmpty(t, doc.Tags[1].Value.ExternalDocs.Value.URL.Value) - assert.Len(t, doc.Tags[1].Value.Extensions, 0) + /// tag2 + assert.Equal(t, "Dressing", doc.Tags[1].Value.Name.Value) + assert.NotEmpty(t, doc.Tags[1].Value.Description.Value) + assert.NotNil(t, doc.Tags[1].Value.ExternalDocs.Value) + assert.Equal(t, "https://pb33f.io", doc.Tags[1].Value.ExternalDocs.Value.URL.Value) + assert.NotEmpty(t, doc.Tags[1].Value.ExternalDocs.Value.URL.Value) + assert.Len(t, doc.Tags[1].Value.Extensions, 0) + +} + +func TestCreateDocument_Paths(t *testing.T) { + assert.Len(t, doc.Paths.Value.PathItems, 6) + burgerId := doc.Paths.Value.GetPath("/burgers/{burgerId}") + assert.NotNil(t, burgerId) + assert.Len(t, burgerId.Value.Get.Value.Parameters, 2) + param := burgerId.Value.Get.Value.Parameters[1] + assert.Equal(t, "burgerHeader", param.Value.Name.Value) + prop := param.Value.Schema.Value.FindProperty("burgerTheme") + assert.Equal(t, "something about a theme?", prop.Value.Description.Value) + assert.Equal(t, "big-mac", param.Value.Example.Value) + + // check content + pContent := param.Value.GetContent("application/json") + assert.Equal(t, "somethingNice", pContent.Value.Example.Value) } diff --git a/test_specs/burgershop.openapi.yaml b/test_specs/burgershop.openapi.yaml index 176e152..191dd8e 100644 --- a/test_specs/burgershop.openapi.yaml +++ b/test_specs/burgershop.openapi.yaml @@ -321,6 +321,27 @@ components: example: big-mac description: the name of the burger. use this to order your food required: true + content: + application/json: + example: somethingNice + encoding: + burgerTheme: + contentType: text/plain + headers: + someHeader: + description: this is a header + schema: + type: string + schema: + type: object + required: [burgerTheme, burgerTime] + properties: + burgerTheme: + type: string + description: something about a theme? + burgerTime: + type: number + description: number of burgers ordered this year. BurgerId: in: path name: burgerId