V2 swagger model in place

tests being written to validate and flush out bugs
This commit is contained in:
Dave Shanley
2022-09-03 13:30:53 -04:00
parent 8929db5868
commit 1814ac58eb
17 changed files with 1161 additions and 90 deletions

View File

@@ -0,0 +1,10 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package v3
const (
NameLabel = "name"
DescriptionLabel = "description"
ExternalDocsLabel = "externalDocs"
)

View File

@@ -35,3 +35,20 @@ func NewTag(tag *low.Tag) *Tag {
func (t *Tag) GoLow() *low.Tag {
return t.low
}
//func (t *Tag) MarshalYAML() (interface{}, error) {
// m := make(map[string]interface{})
// for i := range t.Extensions {
// m[i] = t.Extensions[i]
// }
// if t.Name != "" {
// m[NameLabel] = t.Name
// }
// if t.Description != "" {
// m[DescriptionLabel] = t.Description
// }
// if t.ExternalDocs != nil {
// m[ExternalDocsLabel] = t.ExternalDocs
// }
// return m, nil
//}

View File

@@ -11,7 +11,8 @@ import (
)
const (
DefinitionsLabel = "definitions"
DefinitionsLabel = "definitions"
SecurityDefinitionsLabel = "securityDefinitions"
)
type ParameterDefinitions struct {
@@ -42,13 +43,13 @@ func (r *ResponsesDefinitions) FindResponse(schema string) *low.ValueReference[*
return low.FindItemInMap[*Response](schema, r.Definitions)
}
func (s *SecurityDefinitions) FindSecurityScheme(schema string) *low.ValueReference[*SecurityScheme] {
func (s *SecurityDefinitions) FindSecurityDefinition(schema string) *low.ValueReference[*SecurityScheme] {
return low.FindItemInMap[*SecurityScheme](schema, s.Definitions)
}
func (d *Definitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
errorChan := make(chan error)
resultChan := make(chan definitionResult)
resultChan := make(chan definitionResult[*base.SchemaProxy])
var defLabel *yaml.Node
totalDefinitions := 0
for i := range root.Content {
@@ -57,12 +58,12 @@ func (d *Definitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
continue
}
totalDefinitions++
var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex, r chan definitionResult, e chan error) {
var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex, r chan definitionResult[*base.SchemaProxy], e chan error) {
obj, err := low.ExtractObjectRaw[*base.SchemaProxy](value, idx)
if err != nil {
e <- err
}
r <- definitionResult{k: label, v: low.ValueReference[any]{Value: obj, ValueNode: value}}
r <- definitionResult[*base.SchemaProxy]{k: label, v: low.ValueReference[*base.SchemaProxy]{Value: obj, ValueNode: value}}
}
go buildFunc(defLabel, root.Content[i], idx, resultChan, errorChan)
}
@@ -74,10 +75,11 @@ func (d *Definitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
case err := <-errorChan:
return err
case sch := <-resultChan:
completedDefs++
results[low.KeyReference[string]{
Value: sch.k.Value,
KeyNode: sch.k,
}] = sch.v.(low.ValueReference[*base.SchemaProxy])
}] = sch.v
}
}
d.Schemas = results
@@ -86,7 +88,7 @@ func (d *Definitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
func (pd *ParameterDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
errorChan := make(chan error)
resultChan := make(chan definitionResult)
resultChan := make(chan definitionResult[*Parameter])
var defLabel *yaml.Node
totalDefinitions := 0
for i := range root.Content {
@@ -95,12 +97,12 @@ func (pd *ParameterDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) err
continue
}
totalDefinitions++
var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex, r chan definitionResult, e chan error) {
var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex, r chan definitionResult[*Parameter], e chan error) {
obj, err := low.ExtractObjectRaw[*Parameter](value, idx)
if err != nil {
e <- err
}
r <- definitionResult{k: label, v: low.ValueReference[any]{Value: obj, ValueNode: value}}
r <- definitionResult[*Parameter]{k: label, v: low.ValueReference[*Parameter]{Value: obj, ValueNode: value}}
}
go buildFunc(defLabel, root.Content[i], idx, resultChan, errorChan)
}
@@ -112,24 +114,25 @@ func (pd *ParameterDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) err
case err := <-errorChan:
return err
case sch := <-resultChan:
completedDefs++
results[low.KeyReference[string]{
Value: sch.k.Value,
KeyNode: sch.k,
}] = sch.v.(low.ValueReference[*Parameter])
}] = sch.v
}
}
pd.Definitions = results
return nil
}
type definitionResult struct {
type definitionResult[T any] struct {
k *yaml.Node
v any
v low.ValueReference[T]
}
func (r *ResponsesDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
errorChan := make(chan error)
resultChan := make(chan definitionResult)
resultChan := make(chan definitionResult[*Response])
var defLabel *yaml.Node
totalDefinitions := 0
for i := range root.Content {
@@ -138,12 +141,12 @@ func (r *ResponsesDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) erro
continue
}
totalDefinitions++
var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex, r chan definitionResult, e chan error) {
var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex, r chan definitionResult[*Response], e chan error) {
obj, err := low.ExtractObjectRaw[*Response](value, idx)
if err != nil {
e <- err
}
r <- definitionResult{k: label, v: low.ValueReference[any]{Value: obj, ValueNode: value}}
r <- definitionResult[*Response]{k: label, v: low.ValueReference[*Response]{Value: obj, ValueNode: value}}
}
go buildFunc(defLabel, root.Content[i], idx, resultChan, errorChan)
}
@@ -155,10 +158,11 @@ func (r *ResponsesDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) erro
case err := <-errorChan:
return err
case sch := <-resultChan:
completedDefs++
results[low.KeyReference[string]{
Value: sch.k.Value,
KeyNode: sch.k,
}] = sch.v.(low.ValueReference[*Response])
}] = sch.v
}
}
r.Definitions = results
@@ -167,7 +171,7 @@ func (r *ResponsesDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) erro
func (s *SecurityDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
errorChan := make(chan error)
resultChan := make(chan definitionResult)
resultChan := make(chan definitionResult[*SecurityScheme])
var defLabel *yaml.Node
totalDefinitions := 0
for i := range root.Content {
@@ -176,12 +180,12 @@ func (s *SecurityDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) error
continue
}
totalDefinitions++
var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex, r chan definitionResult, e chan error) {
var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex, r chan definitionResult[*SecurityScheme], e chan error) {
obj, err := low.ExtractObjectRaw[*SecurityScheme](value, idx)
if err != nil {
e <- err
}
r <- definitionResult{k: label, v: low.ValueReference[any]{Value: obj, ValueNode: value}}
r <- definitionResult[*SecurityScheme]{k: label, v: low.ValueReference[*SecurityScheme]{Value: obj, ValueNode: value}}
}
go buildFunc(defLabel, root.Content[i], idx, resultChan, errorChan)
}
@@ -193,10 +197,11 @@ func (s *SecurityDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) error
case err := <-errorChan:
return err
case sch := <-resultChan:
completedDefs++
results[low.KeyReference[string]{
Value: sch.k.Value,
KeyNode: sch.k,
}] = sch.v.(low.ValueReference[*SecurityScheme])
}] = sch.v
}
}
s.Definitions = results

View File

@@ -6,6 +6,7 @@ package v2
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
)
@@ -41,8 +42,44 @@ func (h *Header) FindExtension(ext string) *low.ValueReference[any] {
func (h *Header) Build(root *yaml.Node, idx *index.SpecIndex) error {
h.Extensions = low.ExtractExtensions(root)
items, err := low.ExtractObject[*Items](ItemsLabel, root, idx)
if err != nil {
return err
}
h.Items = items
// TODO: build items.
_, ln, vn := utils.FindKeyNodeFull(DefaultLabel, root.Content)
if vn != nil {
var n map[string]interface{}
err = vn.Decode(&n)
if err != nil {
// if not a map, then try an array
var k []interface{}
err = vn.Decode(&k)
if err != nil {
// lets just default to interface
var j interface{}
_ = vn.Decode(&j)
h.Default = low.NodeReference[any]{
Value: j,
KeyNode: ln,
ValueNode: vn,
}
return nil
}
h.Default = low.NodeReference[any]{
Value: k,
KeyNode: ln,
ValueNode: vn,
}
return nil
}
h.Default.Value = low.NodeReference[any]{
Value: n,
KeyNode: ln,
ValueNode: vn,
}
return nil
}
return nil
}

View File

@@ -12,13 +12,14 @@ import (
const (
DefaultLabel = "default"
ItemsLabel = "items"
)
type Items struct {
Type low.NodeReference[string]
Format low.NodeReference[string]
CollectionFormat low.NodeReference[string]
Items low.NodeReference[string]
Items low.NodeReference[*Items]
Default low.NodeReference[any]
Maximum low.NodeReference[int]
ExclusiveMaximum low.NodeReference[bool]
@@ -35,6 +36,12 @@ type Items struct {
}
func (i *Items) Build(root *yaml.Node, idx *index.SpecIndex) error {
items, iErr := low.ExtractObject[*Items](ItemsLabel, root, idx)
if iErr != nil {
return iErr
}
i.Items = items
_, ln, vn := utils.FindKeyNodeFull(DefaultLabel, root.Content)
if vn != nil {
var n map[string]interface{}

View File

@@ -62,6 +62,19 @@ func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
skip = true
continue
}
// because (for some reason) the spec for swagger docs allows for a '$ref' property for path items.
// this is kinda nuts, because '$ref' is a reserved keyword for JSON references, which is ALSO used
// in swagger. Why this choice was made, I do not know.
if strings.Contains(strings.ToLower(pathNode.Value), "$ref") {
rn := root.Content[i+1]
p.Ref = low.NodeReference[string]{
Value: rn.Value,
ValueNode: rn,
KeyNode: pathNode,
}
skip = true
continue
}
if skip {
skip = false
continue

View File

@@ -36,7 +36,9 @@ func (r *Response) Build(root *yaml.Node, idx *index.SpecIndex) error {
if err != nil {
return err
}
r.Schema = *s
if s != nil {
r.Schema = *s
}
//extract headers
headers, lN, kN, err := low.ExtractMapFlat[*Header](HeadersLabel, root, idx)

View File

@@ -3,9 +3,38 @@
package v2
import "github.com/pb33f/libopenapi/datamodel/low"
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
)
type Scopes struct {
Values low.NodeReference[map[low.KeyReference[string]]low.ValueReference[string]]
Values map[low.KeyReference[string]]low.ValueReference[string]
Extensions map[low.KeyReference[string]]low.ValueReference[any]
}
func (s *Scopes) FindScope(scope string) *low.ValueReference[string] {
return low.FindItemInMap[string](scope, s.Values)
}
func (s *Scopes) Build(root *yaml.Node, idx *index.SpecIndex) error {
s.Extensions = low.ExtractExtensions(root)
valueMap := make(map[low.KeyReference[string]]low.ValueReference[string])
if utils.IsNodeMap(root) {
for k := range root.Content {
if k%2 == 0 {
valueMap[low.KeyReference[string]{
Value: root.Content[k].Value,
KeyNode: root.Content[k],
}] = low.ValueReference[string]{
Value: root.Content[k+1].Value,
ValueNode: root.Content[k+1],
}
}
}
s.Values = valueMap
}
return nil
}

View File

@@ -14,10 +14,35 @@ const (
)
type SecurityRequirement struct {
Values low.NodeReference[[]low.ValueReference[string]]
Values low.ValueReference[map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]]
}
func (s *SecurityRequirement) Build(_ *yaml.Node, _ *index.SpecIndex) error {
// not implemented.
func (s *SecurityRequirement) Build(root *yaml.Node, _ *index.SpecIndex) error {
var labelNode *yaml.Node
var arr []low.ValueReference[string]
valueMap := make(map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]])
for i := range root.Content {
if i%2 == 0 {
labelNode = root.Content[i]
continue
}
for j := range root.Content[i].Content {
arr = append(arr, low.ValueReference[string]{
Value: root.Content[i].Content[j].Value,
ValueNode: root.Content[i].Content[j],
})
}
valueMap[low.KeyReference[string]{
Value: labelNode.Value,
KeyNode: labelNode,
}] = low.ValueReference[[]low.ValueReference[string]]{
Value: arr,
ValueNode: root.Content[i],
}
}
s.Values = low.ValueReference[map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]]{
Value: valueMap,
ValueNode: root,
}
return nil
}

View File

@@ -4,26 +4,34 @@
package v2
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"gopkg.in/yaml.v3"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"gopkg.in/yaml.v3"
)
const (
ScopesLabel = "scopes"
)
type SecurityScheme struct {
Type low.NodeReference[string]
Description low.NodeReference[string]
Name low.NodeReference[string]
In low.NodeReference[string]
Flow low.NodeReference[string]
AuthorizationUrl low.NodeReference[string]
TokenUrl low.NodeReference[string]
Scopes low.NodeReference[*Scopes]
Extensions map[low.KeyReference[string]]low.ValueReference[any]
Type low.NodeReference[string]
Description low.NodeReference[string]
Name low.NodeReference[string]
In low.NodeReference[string]
Flow low.NodeReference[string]
AuthorizationUrl low.NodeReference[string]
TokenUrl low.NodeReference[string]
Scopes low.NodeReference[*Scopes]
Extensions map[low.KeyReference[string]]low.ValueReference[any]
}
func (ss *SecurityScheme) Build(root *yaml.Node, idx *index.SpecIndex) error {
ss.Extensions = low.ExtractExtensions(root)
ss.Extensions = low.ExtractExtensions(root)
// TODO: scopes
return nil
scopes, sErr := low.ExtractObject[*Scopes](ScopesLabel, root, idx)
if sErr != nil {
return sErr
}
ss.Scopes = scopes
return nil
}

View File

@@ -15,24 +15,28 @@ import (
type documentFunction func(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error)
type Swagger struct {
Swagger low.ValueReference[string]
Info low.NodeReference[*base.Info]
Host low.NodeReference[string]
BasePath low.NodeReference[string]
Schemes low.NodeReference[[]low.ValueReference[string]]
Consumes low.NodeReference[[]low.ValueReference[string]]
Produces low.NodeReference[[]low.ValueReference[string]]
Paths low.NodeReference[*Paths]
Definitions low.NodeReference[*Definitions]
SecurityDefinitions low.NodeReference[*SecurityDefinitions]
Parameters low.NodeReference[*ParameterDefinitions]
ResponsesDefinitions low.NodeReference[*ResponsesDefinitions]
Responses low.NodeReference[*Responses]
Security low.NodeReference[[]low.ValueReference[*SecurityRequirement]]
Tags low.NodeReference[[]low.ValueReference[*base.Tag]]
ExternalDocs low.NodeReference[*base.ExternalDoc]
Extensions map[low.KeyReference[string]]low.ValueReference[any]
Index *index.SpecIndex
Swagger low.ValueReference[string]
Info low.NodeReference[*base.Info]
Host low.NodeReference[string]
BasePath low.NodeReference[string]
Schemes low.NodeReference[[]low.ValueReference[string]]
Consumes low.NodeReference[[]low.ValueReference[string]]
Produces low.NodeReference[[]low.ValueReference[string]]
Paths low.NodeReference[*Paths]
Definitions low.NodeReference[*Definitions]
SecurityDefinitions low.NodeReference[*SecurityDefinitions]
Parameters low.NodeReference[*ParameterDefinitions]
Responses low.NodeReference[*ResponsesDefinitions]
Security low.NodeReference[[]low.ValueReference[*SecurityRequirement]]
Tags low.NodeReference[[]low.ValueReference[*base.Tag]]
ExternalDocs low.NodeReference[*base.ExternalDoc]
Extensions map[low.KeyReference[string]]low.ValueReference[any]
Index *index.SpecIndex
SpecInfo *datamodel.SpecInfo
}
func (s *Swagger) FindExtension(ext string) *low.ValueReference[any] {
return low.FindItemInMap[any](ext, s.Extensions)
}
func CreateDocument(info *datamodel.SpecInfo) (*Swagger, []error) {
@@ -43,13 +47,28 @@ func CreateDocument(info *datamodel.SpecInfo) (*Swagger, []error) {
// build an index
idx := index.NewSpecIndex(info.RootNode)
doc.Index = idx
doc.SpecInfo = info
var errors []error
// build out swagger scalar variables.
bErr := low.BuildModel(info.RootNode, &doc)
if bErr != nil {
errors = append(errors, bErr)
}
// extract externalDocs
extDocs, err := low.ExtractObject[*base.ExternalDoc](base.ExternalDocsLabel, info.RootNode, idx)
if err != nil {
errors = append(errors, err)
}
doc.ExternalDocs = extDocs
// create resolver and check for circular references.
resolve := resolver.NewResolver(idx)
_ = resolve.CheckForCircularReferences()
var errors []error
extractionFuncs := []documentFunction{
extractInfo,
extractPaths,
@@ -57,6 +76,8 @@ func CreateDocument(info *datamodel.SpecInfo) (*Swagger, []error) {
extractParamDefinitions,
extractResponsesDefinitions,
extractSecurityDefinitions,
extractTags,
extractSecurity,
}
doneChan := make(chan bool)
errChan := make(chan error)
@@ -120,12 +141,12 @@ func extractResponsesDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecI
e <- err
return
}
doc.ResponsesDefinitions = resp
doc.Responses = resp
c <- true
}
func extractSecurityDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
sec, err := low.ExtractObject[*SecurityDefinitions](SecurityLabel, root, idx)
sec, err := low.ExtractObject[*SecurityDefinitions](SecurityDefinitionsLabel, root, idx)
if err != nil {
e <- err
return
@@ -133,3 +154,31 @@ func extractSecurityDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIn
doc.SecurityDefinitions = sec
c <- true
}
func extractTags(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
tags, ln, vn, err := low.ExtractArray[*base.Tag](base.TagsLabel, root, idx)
if err != nil {
e <- err
return
}
doc.Tags = low.NodeReference[[]low.ValueReference[*base.Tag]]{
Value: tags,
KeyNode: ln,
ValueNode: vn,
}
c <- true
}
func extractSecurity(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
sec, ln, vn, err := low.ExtractArray[*SecurityRequirement](SecurityLabel, root, idx)
if err != nil {
e <- err
return
}
doc.Security = low.NodeReference[[]low.ValueReference[*SecurityRequirement]]{
Value: sec,
KeyNode: ln,
ValueNode: vn,
}
c <- true
}

View File

@@ -0,0 +1,93 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package v2
import (
"github.com/pb33f/libopenapi/datamodel"
"github.com/stretchr/testify/assert"
"io/ioutil"
"testing"
)
var doc *Swagger
func initTest() {
if doc != nil {
return
}
data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml")
info, _ := datamodel.ExtractSpecInfo(data)
var err []error
doc, err = CreateDocument(info)
wait := true
for wait {
select {
case <-info.JsonParsingChannel:
wait = false
}
}
if err != nil {
panic("broken something")
}
}
func BenchmarkCreateDocument(b *testing.B) {
data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml")
info, _ := datamodel.ExtractSpecInfo(data)
for i := 0; i < b.N; i++ {
doc, _ = CreateDocument(info)
}
}
func TestCreateDocument(t *testing.T) {
initTest()
assert.Equal(t, "2.0", doc.SpecInfo.Version)
assert.Equal(t, "1.0.6", doc.Info.Value.Version.Value)
assert.Equal(t, "petstore.swagger.io", doc.Host.Value)
assert.Equal(t, "/v2", doc.BasePath.Value)
assert.Len(t, doc.Parameters.Value.Definitions, 1)
assert.Len(t, doc.Tags.Value, 3)
assert.Len(t, doc.Schemes.Value, 2)
assert.Len(t, doc.Definitions.Value.Schemas, 6)
assert.Len(t, doc.SecurityDefinitions.Value.Definitions, 2)
assert.Len(t, doc.Paths.Value.PathItems, 14)
assert.Len(t, doc.Responses.Value.Definitions, 2)
assert.Equal(t, "http://swagger.io", doc.ExternalDocs.Value.URL.Value)
assert.Equal(t, true, doc.FindExtension("x-pet").Value)
assert.Equal(t, true, doc.FindExtension("X-Pet").Value)
}
func TestCreateDocument_Info(t *testing.T) {
initTest()
assert.Equal(t, "Swagger Petstore", doc.Info.Value.Title.Value)
assert.Equal(t, "apiteam@swagger.io", doc.Info.Value.Contact.Value.Email.Value)
assert.Equal(t, "Apache 2.0", doc.Info.Value.License.Value.Name.Value)
}
func TestCreateDocument_Parameters(t *testing.T) {
initTest()
simpleParam := doc.Parameters.Value.FindParameter("simpleParam")
assert.NotNil(t, simpleParam)
assert.Equal(t, "simple", simpleParam.Value.Name.Value)
}
func TestCreateDocument_Tags(t *testing.T) {
initTest()
assert.Equal(t, "pet", doc.Tags.Value[0].Value.Name.Value)
assert.Equal(t, "http://swagger.io", doc.Tags.Value[0].Value.ExternalDocs.Value.URL.Value)
assert.Equal(t, "store", doc.Tags.Value[1].Value.Name.Value)
assert.Equal(t, "user", doc.Tags.Value[2].Value.Name.Value)
assert.Equal(t, "http://swagger.io", doc.Tags.Value[2].Value.ExternalDocs.Value.URL.Value)
}
func TestCreateDocument_SecurityDefinitions(t *testing.T) {
initTest()
apiKey := doc.SecurityDefinitions.Value.FindSecurityDefinition("api_key")
assert.Equal(t, "apiKey", apiKey.Value.Type.Value)
petStoreAuth := doc.SecurityDefinitions.Value.FindSecurityDefinition("petstore_auth")
assert.Equal(t, "oauth2", petStoreAuth.Value.Type.Value)
assert.Equal(t, "implicit", petStoreAuth.Value.Flow.Value)
assert.Len(t, petStoreAuth.Value.Scopes.Value.Values, 2)
assert.Equal(t, "read your pets", petStoreAuth.Value.Scopes.Value.FindScope("read:pets").Value)
}

View File

@@ -19,6 +19,9 @@ func FindItemInMap[T any](item string, collection map[KeyReference[string]]Value
if n.Value == item {
return &o
}
if strings.ToLower(n.Value) == strings.ToLower(item) {
return &o
}
}
return nil
}

View File

@@ -35,7 +35,7 @@ func BuildModel(node *yaml.Node, model interface{}) error {
}
// we need to find a matching field in the YAML, the cases may be off, so take no chances.
cases := []utils.Case{utils.PascalCase, utils.CamelCase, utils.ScreamingSnakeCase,
cases := []utils.Case{utils.CamelCase, utils.PascalCase, utils.ScreamingSnakeCase,
utils.SnakeCase, utils.KebabCase, utils.RegularCase}
var vn, kn *yaml.Node

View File

@@ -1,30 +1,51 @@
package main
import (
"fmt"
"github.com/pb33f/libopenapi/datamodel"
high "github.com/pb33f/libopenapi/datamodel/high/3.0"
low "github.com/pb33f/libopenapi/datamodel/low/3.0"
"gopkg.in/yaml.v3"
"io/ioutil"
"os"
)
func main() {
// testData := `openapi: 3.0.1
//info:
// title: this is a title
// description: this is a description`
//
// data := []byte(testData)
// _ = ioutil.WriteFile("sample.yaml", data, 0664)
//
// info, _ := datamodel.ExtractSpecInfo(data)
// lowDoc, err := low.CreateDocument(info)
// if len(err) > 0 {
// for e := range err {
// fmt.Printf("%e\n", err[e])
// }
// return
// }
// highDoc := high.NewDocument(lowDoc)
// fmt.Println(highDoc.Info.Title)
// highDoc.Info.GoLow().Title.ValueNode.Value = "let's hack this shizzle"
//
// modified, _ := yaml.Marshal(info.RootNode)
// fmt.Println(string(modified))
testData := `openapi: 3.0.1
info:
title: this is a title
description: this is a description
tags:
- name: Tag A
description: cake
x-hack: true
- name: Tag B
description: coffee
x-code: hack`
//os.Remove("sample.yaml")
data := []byte(testData)
_ = ioutil.WriteFile("sample.yaml", data, 0664)
info, _ := datamodel.ExtractSpecInfo(data)
lowDoc, err := low.CreateDocument(info)
if len(err) > 0 {
for e := range err {
fmt.Printf("%e\n", err[e])
}
return
}
highDoc := high.NewDocument(lowDoc)
fmt.Println(highDoc.Info.Title)
highDoc.Info.GoLow().Title.ValueNode.Value = "let's hack this shizzle"
modified, _ := yaml.Marshal(info.RootNode)
fmt.Println(string(modified))
d, _ := yaml.Marshal(highDoc.Tags[0])
fmt.Println(d)
os.Remove("sample.yaml")
}

11
sample.yaml Normal file
View File

@@ -0,0 +1,11 @@
openapi: 3.0.1
info:
title: this is a title
description: this is a description
tags:
- name: Tag A
description: cake
x-hack: true
- name: Tag B
description: coffee
x-code: hack

View File

@@ -0,0 +1,741 @@
---
swagger: '2.0'
x-pet: true
info:
description: 'This is a sample server Petstore server. You can find out more about
Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For
this sample, you can use the api key `special-key` to test the authorization filters.'
version: 1.0.6
title: Swagger Petstore
termsOfService: http://swagger.io/terms/
contact:
email: apiteam@swagger.io
license:
name: Apache 2.0
url: http://www.apache.org/licenses/LICENSE-2.0.html
host: petstore.swagger.io
basePath: "/v2"
parameters:
simpleParam:
in: query
name: simple
type: string
tags:
- name: pet
description: Everything about your Pets
externalDocs:
description: Find out more
url: http://swagger.io
- name: store
description: Access to Petstore orders
- name: user
description: Operations about user
externalDocs:
description: Find out more about our store
url: http://swagger.io
schemes:
- https
- http
paths:
"/pet/{petId}/uploadImage":
post:
tags:
- pet
summary: uploads an image
description: ''
operationId: uploadFile
consumes:
- multipart/form-data
produces:
- application/json
parameters:
- name: petId
in: path
description: ID of pet to update
required: true
type: integer
format: int64
- name: additionalMetadata
in: formData
description: Additional data to pass to server
required: false
type: string
- name: file
in: formData
description: file to upload
required: false
type: file
responses:
'200':
headers:
myHeader:
type: array
description: myHeader
items:
type: string
collectionFormat: csv
default: [ one, two, three ]
enum: [99,100,101]
description: successful operation
schema:
"$ref": "#/definitions/ApiResponse"
security:
- petstore_auth:
- write:pets
- read:pets
"/pet":
post:
tags:
- pet
summary: Add a new pet to the store
description: ''
operationId: addPet
consumes:
- application/json
- application/xml
produces:
- application/json
- application/xml
parameters:
- in: body
name: body
description: Pet object that needs to be added to the store
required: true
schema:
"$ref": "#/definitions/Pet"
responses:
'405':
description: Invalid input
security:
- petstore_auth:
- write:pets
- read:pets
put:
tags:
- pet
summary: Update an existing pet
description: ''
operationId: updatePet
consumes:
- application/json
- application/xml
produces:
- application/json
- application/xml
parameters:
- in: body
name: body
description: Pet object that needs to be added to the store
required: true
schema:
"$ref": "#/definitions/Pet"
responses:
'400':
description: Invalid ID supplied
'404':
description: Pet not found
'405':
description: Validation exception
security:
- petstore_auth:
- write:pets
- read:pets
"/pet/findByStatus":
get:
tags:
- pet
summary: Finds Pets by status
description: Multiple status values can be provided with comma separated strings
operationId: findPetsByStatus
produces:
- application/json
- application/xml
parameters:
- name: status
in: query
description: Status values that need to be considered for filter
required: true
type: array
items:
type: string
enum:
- available
- pending
- sold
default: available
collectionFormat: multi
responses:
'200':
description: successful operation
schema:
type: array
items:
"$ref": "#/definitions/Pet"
'400':
description: Invalid status value
security:
- petstore_auth:
- write:pets
- read:pets
"/pet/findByTags":
get:
tags:
- pet
summary: Finds Pets by tags
description: Multiple tags can be provided with comma separated strings. Use
tag1, tag2, tag3 for testing.
operationId: findPetsByTags
produces:
- application/json
- application/xml
parameters:
- name: tags
in: query
description: Tags to filter by
required: true
type: array
items:
type: string
collectionFormat: multi
responses:
'200':
description: successful operation
schema:
type: array
items:
"$ref": "#/definitions/Pet"
'400':
description: Invalid tag value
security:
- petstore_auth:
- write:pets
- read:pets
deprecated: true
"/pet/{petId}":
get:
tags:
- pet
summary: Find pet by ID
description: Returns a single pet
operationId: getPetById
produces:
- application/json
- application/xml
parameters:
- name: petId
in: path
description: ID of pet to return
required: true
type: integer
format: int64
responses:
'200':
description: successful operation
schema:
"$ref": "#/definitions/Pet"
'400':
description: Invalid ID supplied
'404':
description: Pet not found
security:
- api_key: []
post:
tags:
- pet
summary: Updates a pet in the store with form data
description: ''
operationId: updatePetWithForm
consumes:
- application/x-www-form-urlencoded
produces:
- application/json
- application/xml
parameters:
- name: petId
in: path
description: ID of pet that needs to be updated
required: true
type: integer
format: int64
- name: name
in: formData
description: Updated name of the pet
required: false
type: string
- name: status
in: formData
description: Updated status of the pet
required: false
type: string
responses:
'405':
description: Invalid input
security:
- petstore_auth:
- write:pets
- read:pets
delete:
tags:
- pet
summary: Deletes a pet
description: ''
operationId: deletePet
produces:
- application/json
- application/xml
parameters:
- name: api_key
in: header
required: false
type: string
- name: petId
in: path
description: Pet id to delete
required: true
type: integer
format: int64
responses:
'400':
description: Invalid ID supplied
'404':
description: Pet not found
security:
- petstore_auth:
- write:pets
- read:pets
"/store/order":
post:
tags:
- store
summary: Place an order for a pet
description: ''
operationId: placeOrder
consumes:
- application/json
produces:
- application/json
- application/xml
parameters:
- in: body
name: body
description: order placed for purchasing the pet
required: true
schema:
"$ref": "#/definitions/Order"
responses:
'200':
description: successful operation
schema:
"$ref": "#/definitions/Order"
'400':
description: Invalid Order
"/store/order/{orderId}":
get:
tags:
- store
summary: Find purchase order by ID
description: For valid response try integer IDs with value >= 1 and <= 10. Other
values will generated exceptions
operationId: getOrderById
produces:
- application/json
- application/xml
parameters:
- name: orderId
in: path
description: ID of pet that needs to be fetched
required: true
type: integer
maximum: 10
minimum: 1
format: int64
responses:
'200':
description: successful operation
schema:
"$ref": "#/definitions/Order"
'400':
description: Invalid ID supplied
'404':
description: Order not found
delete:
tags:
- store
summary: Delete purchase order by ID
description: For valid response try integer IDs with positive integer value.
Negative or non-integer values will generate API errors
operationId: deleteOrder
produces:
- application/json
- application/xml
parameters:
- name: orderId
in: path
description: ID of the order that needs to be deleted
required: true
type: integer
minimum: 1
format: int64
responses:
'400':
description: Invalid ID supplied
'404':
description: Order not found
"/store/inventory":
get:
tags:
- store
summary: Returns pet inventories by status
description: Returns a map of status codes to quantities
operationId: getInventory
produces:
- application/json
parameters: []
responses:
'200':
description: successful operation
schema:
type: object
additionalProperties:
type: integer
format: int32
security:
- api_key: []
"/user/createWithArray":
post:
tags:
- user
summary: Creates list of users with given input array
description: ''
operationId: createUsersWithArrayInput
consumes:
- application/json
produces:
- application/json
- application/xml
parameters:
- in: body
name: body
description: List of user object
required: true
schema:
type: array
items:
"$ref": "#/definitions/User"
responses:
default:
description: successful operation
"/user/createWithList":
post:
tags:
- user
summary: Creates list of users with given input array
description: ''
operationId: createUsersWithListInput
consumes:
- application/json
produces:
- application/json
- application/xml
parameters:
- in: body
name: body
description: List of user object
required: true
schema:
type: array
items:
"$ref": "#/definitions/User"
responses:
default:
description: successful operation
"/user/{username}":
get:
tags:
- user
summary: Get user by user name
description: ''
operationId: getUserByName
produces:
- application/json
- application/xml
parameters:
- name: username
in: path
description: 'The name that needs to be fetched. Use user1 for testing. '
required: true
type: string
responses:
'200':
description: successful operation
schema:
"$ref": "#/definitions/User"
'400':
description: Invalid username supplied
'404':
description: User not found
put:
tags:
- user
summary: Updated user
description: This can only be done by the logged in user.
operationId: updateUser
consumes:
- application/json
produces:
- application/json
- application/xml
parameters:
- name: username
in: path
description: name that need to be updated
required: true
type: string
- in: body
name: body
description: Updated user object
required: true
schema:
"$ref": "#/definitions/User"
responses:
'400':
description: Invalid user supplied
'404':
description: User not found
delete:
tags:
- user
summary: Delete user
description: This can only be done by the logged in user.
operationId: deleteUser
produces:
- application/json
- application/xml
parameters:
- name: username
in: path
description: The name that needs to be deleted
required: true
type: string
responses:
'400':
description: Invalid username supplied
'404':
description: User not found
"/user/login":
get:
tags:
- user
summary: Logs user into the system
description: ''
operationId: loginUser
produces:
- application/json
- application/xml
parameters:
- name: username
in: query
description: The user name for login
required: true
type: string
- name: password
in: query
description: The password for login in clear text
required: true
type: string
responses:
'200':
description: successful operation
headers:
X-Expires-After:
type: string
format: date-time
description: date in UTC when token expires
X-Rate-Limit:
type: integer
format: int32
description: calls per hour allowed by the user
schema:
type: string
'400':
description: Invalid username/password supplied
"/user/logout":
get:
tags:
- user
summary: Logs out current logged in user session
description: ''
operationId: logoutUser
produces:
- application/json
- application/xml
parameters: []
responses:
default:
description: successful operation
"/user":
post:
tags:
- user
summary: Create user
description: This can only be done by the logged in user.
operationId: createUser
consumes:
- application/json
produces:
- application/json
- application/xml
parameters:
- in: body
name: body
description: Created user object
required: true
schema:
"$ref": "#/definitions/User"
responses:
default:
description: successful operation
securityDefinitions:
api_key:
type: apiKey
name: api_key
in: header
petstore_auth:
type: oauth2
authorizationUrl: https://petstore.swagger.io/oauth/authorize
flow: implicit
scopes:
read:pets: read your pets
write:pets: modify pets in your account
responses:
"200":
description: OK
schema:
$ref: '#/definitions/ApiResponse'
"500":
description: Error
schema:
$ref: '#/definitions/ApiResponse'
definitions:
ApiResponse:
type: object
properties:
code:
type: integer
format: int32
type:
type: string
message:
type: string
Category:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
xml:
name: Category
Pet:
type: object
required:
- name
- photoUrls
properties:
id:
type: integer
format: int64
category:
"$ref": "#/definitions/Category"
name:
type: string
example: doggie
photoUrls:
type: array
xml:
wrapped: true
items:
type: string
xml:
name: photoUrl
tags:
type: array
xml:
wrapped: true
items:
xml:
name: tag
"$ref": "#/definitions/Tag"
status:
type: string
description: pet status in the store
enum:
- available
- pending
- sold
xml:
name: Pet
Tag:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
xml:
name: Tag
Order:
type: object
properties:
id:
type: integer
format: int64
petId:
type: integer
format: int64
quantity:
type: integer
format: int32
shipDate:
type: string
format: date-time
status:
type: string
description: Order Status
enum:
- placed
- approved
- delivered
complete:
type: boolean
xml:
name: Order
User:
type: object
properties:
id:
type: integer
format: int64
username:
type: string
firstName:
type: string
lastName:
type: string
email:
type: string
password:
type: string
phone:
type: string
userStatus:
type: integer
format: int32
description: User Status
xml:
name: User
externalDocs:
description: Find out more about Swagger
url: http://swagger.io