From 0c2c008902b5cbbec3e00074f3ad6bec15c9817c Mon Sep 17 00:00:00 2001 From: Dave Shanley Date: Fri, 19 Aug 2022 10:01:16 -0400 Subject: [PATCH] Building out highlevel model bit by bit, step by step. --- datamodel/high/3.0/components.go | 23 +++++- datamodel/high/3.0/discriminator.go | 12 +++ datamodel/high/3.0/document.go | 10 ++- datamodel/high/3.0/document_test.go | 59 +++++++++++++- datamodel/high/3.0/encoding.go | 11 +++ datamodel/high/3.0/example.go | 25 +++++- datamodel/high/3.0/header.go | 13 ++++ datamodel/high/3.0/link.go | 24 +++++- datamodel/high/3.0/media_type.go | 20 ++++- datamodel/high/3.0/operation.go | 17 ++++ datamodel/high/3.0/parameter.go | 22 +++++- datamodel/high/3.0/path_item.go | 13 +++- datamodel/high/3.0/schema.go | 90 +++++++++++++++++++++- datamodel/high/3.0/security_requirement.go | 19 +++++ datamodel/high/3.0/xml.go | 17 +++- datamodel/low/3.0/create_document.go | 2 + datamodel/low/3.0/document.go | 2 +- datamodel/low/3.0/security_scheme.go | 9 ++- test_specs/burgershop.openapi.yaml | 6 +- 19 files changed, 376 insertions(+), 18 deletions(-) diff --git a/datamodel/high/3.0/components.go b/datamodel/high/3.0/components.go index 277f803..0783ece 100644 --- a/datamodel/high/3.0/components.go +++ b/datamodel/high/3.0/components.go @@ -3,7 +3,10 @@ package v3 -import low "github.com/pb33f/libopenapi/datamodel/low/3.0" +import ( + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/3.0" +) type Components struct { Schemas map[string]*Schema @@ -19,6 +22,24 @@ type Components struct { low *low.Components } +func NewComponents(comp *low.Components) *Components { + c := new(Components) + c.low = comp + c.Extensions = high.ExtractExtensions(comp.Extensions) + callbacks := make(map[string]*Callback) + links := make(map[string]*Link) + + for k, v := range comp.Callbacks.Value { + callbacks[k.Value] = NewCallback(v.Value) + } + c.Callbacks = callbacks + for k, v := range comp.Links.Value { + links[k.Value] = NewLink(v.Value) + } + c.Links = links + return c +} + func (c *Components) GoLow() *low.Components { return c.low } diff --git a/datamodel/high/3.0/discriminator.go b/datamodel/high/3.0/discriminator.go index ac30bf1..5cede12 100644 --- a/datamodel/high/3.0/discriminator.go +++ b/datamodel/high/3.0/discriminator.go @@ -11,6 +11,18 @@ type Discriminator struct { low *low.Discriminator } +func NewDiscriminator(disc *low.Discriminator) *Discriminator { + d := new(Discriminator) + d.low = disc + d.PropertyName = disc.PropertyName.Value + mapping := make(map[string]string) + for k, v := range disc.Mapping { + mapping[k.Value] = v.Value + } + d.Mapping = mapping + return d +} + func (d *Discriminator) GoLow() *low.Discriminator { return d.low } diff --git a/datamodel/high/3.0/document.go b/datamodel/high/3.0/document.go index 03b31da..6363ef2 100644 --- a/datamodel/high/3.0/document.go +++ b/datamodel/high/3.0/document.go @@ -3,7 +3,10 @@ package v3 -import low "github.com/pb33f/libopenapi/datamodel/low/3.0" +import ( + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/3.0" +) type Document struct { Version string @@ -33,6 +36,11 @@ func NewDocument(document *low.Document) *Document { tags = append(tags, NewTag(tag.Value)) } d.Tags = tags + if !document.ExternalDocs.IsEmpty() { + d.ExternalDocs = NewExternalDoc(document.ExternalDocs.Value) + } + d.Extensions = high.ExtractExtensions(document.Extensions) + d.Components = NewComponents(document.Components.Value) return d } diff --git a/datamodel/high/3.0/document_test.go b/datamodel/high/3.0/document_test.go index de9531d..37b2b39 100644 --- a/datamodel/high/3.0/document_test.go +++ b/datamodel/high/3.0/document_test.go @@ -29,6 +29,16 @@ func BenchmarkNewDocument(b *testing.B) { } } +func TestNewDocument_Extensions(t *testing.T) { + h := NewDocument(doc) + assert.Equal(t, "darkside", h.Extensions["x-something-something"]) +} + +func TestNewDocument_ExternalDocs(t *testing.T) { + h := NewDocument(doc) + assert.Equal(t, "https://pb33f.io", h.ExternalDocs.URL) +} + func TestNewDocument_Info(t *testing.T) { highDoc := NewDocument(doc) assert.Equal(t, "3.0.1", highDoc.Version) @@ -45,6 +55,18 @@ func TestNewDocument_Info(t *testing.T) { assert.Equal(t, 1, wentLow.Version.ValueNode.Line) assert.Equal(t, 3, wentLow.Info.Value.Title.KeyNode.Line) + wentLower := highDoc.Info.Contact.GoLow() + assert.Equal(t, 8, wentLower.Name.ValueNode.Line) + assert.Equal(t, 11, wentLower.Name.ValueNode.Column) + + wentLowAgain := highDoc.Info.GoLow() + assert.Equal(t, 3, wentLowAgain.Title.ValueNode.Line) + assert.Equal(t, 10, wentLowAgain.Title.ValueNode.Column) + + wentOnceMore := highDoc.Info.License.GoLow() + assert.Equal(t, 12, wentOnceMore.Name.ValueNode.Line) + assert.Equal(t, 11, wentOnceMore.Name.ValueNode.Column) + } func TestNewDocument_Servers(t *testing.T) { @@ -67,10 +89,45 @@ func TestNewDocument_Servers(t *testing.T) { assert.Equal(t, 5, wentLow.Servers.Value[0].Value.Description.KeyNode.Column) assert.Equal(t, 45, wentLow.Servers.Value[0].Value.Description.ValueNode.Line) assert.Equal(t, 18, wentLow.Servers.Value[0].Value.Description.ValueNode.Column) - // holy shit! the perfect Golang OpenAPI Model! high and low! fuck yeah! + + wentLower := h.Servers[0].GoLow() + assert.Equal(t, 45, wentLower.Description.ValueNode.Line) + assert.Equal(t, 18, wentLower.Description.ValueNode.Column) + + wentLowest := h.Servers[0].Variables["scheme"].GoLow() + assert.Equal(t, 50, wentLowest.Description.ValueNode.Line) + assert.Equal(t, 22, wentLowest.Description.ValueNode.Column) + } func TestNewDocument_Tags(t *testing.T) { h := NewDocument(doc) assert.Len(t, h.Tags, 2) + assert.Equal(t, "Burgers", h.Tags[0].Name) + assert.Equal(t, "All kinds of yummy burgers.", h.Tags[0].Description) + assert.Equal(t, "Find out more", h.Tags[0].ExternalDocs.Description) + assert.Equal(t, "https://pb33f.io", h.Tags[0].ExternalDocs.URL) + assert.Equal(t, "somethingSpecial", h.Tags[0].Extensions["x-internal-ting"]) + assert.Equal(t, int64(1), h.Tags[0].Extensions["x-internal-tong"]) + assert.Equal(t, 1.2, h.Tags[0].Extensions["x-internal-tang"]) + assert.True(t, h.Tags[0].Extensions["x-internal-tung"].(bool)) + + wentLow := h.Tags[1].GoLow() + assert.Equal(t, 39, wentLow.Description.KeyNode.Line) + assert.Equal(t, 5, wentLow.Description.KeyNode.Column) + + wentLower := h.Tags[0].ExternalDocs.GoLow() + assert.Equal(t, 23, wentLower.Description.ValueNode.Line) + assert.Equal(t, 20, wentLower.Description.ValueNode.Column) +} + +func TestNewDocument_Components(t *testing.T) { + h := NewDocument(doc) + assert.Len(t, h.Components.Links, 2) + assert.Equal(t, "locateBurger", h.Components.Links["LocateBurger"].OperationId) + assert.Equal(t, "$response.body#/id", h.Components.Links["LocateBurger"].Parameters["burgerId"]) + assert.Len(t, h.Components.Callbacks, 1) + //assert.Equal(t, "Callback payload", + // h.Components.Callbacks["BurgerCallback"].Expression["{$request.query.queryUrl}"].Post.RequestBody.Description) + } diff --git a/datamodel/high/3.0/encoding.go b/datamodel/high/3.0/encoding.go index 59435e6..7f46ffa 100644 --- a/datamodel/high/3.0/encoding.go +++ b/datamodel/high/3.0/encoding.go @@ -14,6 +14,17 @@ type Encoding struct { low *low.Encoding } +func NewEncoding(encoding *low.Encoding) *Encoding { + e := new(Encoding) + e.low = encoding + e.ContentType = encoding.ContentType.Value + e.Style = encoding.Style.Value + e.Explode = encoding.Explode.Value + e.AllowReserved = encoding.AllowReserved.Value + //e.Headers + return e +} + func (e *Encoding) GoLow() *low.Encoding { return e.low } diff --git a/datamodel/high/3.0/example.go b/datamodel/high/3.0/example.go index 486377f..e7deee3 100644 --- a/datamodel/high/3.0/example.go +++ b/datamodel/high/3.0/example.go @@ -3,7 +3,11 @@ package v3 -import low "github.com/pb33f/libopenapi/datamodel/low/3.0" +import ( + "github.com/pb33f/libopenapi/datamodel/high" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + low "github.com/pb33f/libopenapi/datamodel/low/3.0" +) type Example struct { Summary string @@ -14,6 +18,25 @@ type Example struct { low *low.Example } +func NewExample(example *low.Example) *Example { + e := new(Example) + e.low = example + e.Summary = example.Summary.Value + e.Description = example.Description.Value + e.Value = example.Value + e.ExternalValue = example.ExternalValue.Value + e.Extensions = high.ExtractExtensions(example.Extensions) + return e +} + func (e *Example) GoLow() *low.Example { return e.low } + +func ExtractExamples(elements map[lowmodel.KeyReference[string]]lowmodel.ValueReference[*low.Example]) map[string]*Example { + extracted := make(map[string]*Example) + for k, v := range elements { + extracted[k.Value] = NewExample(v.Value) + } + return extracted +} diff --git a/datamodel/high/3.0/header.go b/datamodel/high/3.0/header.go index 0260eb1..787be38 100644 --- a/datamodel/high/3.0/header.go +++ b/datamodel/high/3.0/header.go @@ -21,6 +21,19 @@ type Header struct { low *low.Header } +func NewHeader(header *low.Header) *Header { + h := new(Header) + h.low = header + h.Description = header.Description.Value + h.Required = header.Required.Value + h.Deprecated = header.Deprecated.Value + h.AllowEmptyValue = header.AllowEmptyValue.Value + h.Style = header.Style.Value + + // TODO continue this. + return h +} + func (h *Header) GoLow() *low.Header { return h.low } diff --git a/datamodel/high/3.0/link.go b/datamodel/high/3.0/link.go index 37d60a4..1f9375e 100644 --- a/datamodel/high/3.0/link.go +++ b/datamodel/high/3.0/link.go @@ -3,7 +3,10 @@ package v3 -import low "github.com/pb33f/libopenapi/datamodel/low/3.0" +import ( + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/3.0" +) type Link struct { OperationRef string @@ -16,6 +19,25 @@ type Link struct { low *low.Link } +func NewLink(link *low.Link) *Link { + l := new(Link) + l.low = link + l.OperationRef = link.OperationRef.Value + l.OperationId = link.OperationId.Value + params := make(map[string]string) + for k, v := range link.Parameters.Value { + params[k.Value] = v.Value + } + l.Parameters = params + l.RequestBody = link.RequestBody.Value + l.Description = link.Description.Value + if link.Server.Value != nil { + l.Server = NewServer(link.Server.Value) + } + l.Extensions = high.ExtractExtensions(link.Extensions) + return l +} + func (l *Link) GoLow() *low.Link { return l.low } diff --git a/datamodel/high/3.0/media_type.go b/datamodel/high/3.0/media_type.go index 58f1133..f613536 100644 --- a/datamodel/high/3.0/media_type.go +++ b/datamodel/high/3.0/media_type.go @@ -3,7 +3,10 @@ package v3 -import low "github.com/pb33f/libopenapi/datamodel/low/3.0" +import ( + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/3.0" +) type MediaType struct { Schema *Schema @@ -14,6 +17,21 @@ type MediaType struct { low *low.MediaType } +func NewMediaType(mediaType *low.MediaType) *MediaType { + m := new(MediaType) + m.low = mediaType + if !mediaType.Schema.IsEmpty() { + m.Schema = NewSchema(mediaType.Schema.Value) + } + m.Example = mediaType.Example + m.Examples = ExtractExamples(mediaType.Examples.Value) + m.Extensions = high.ExtractExtensions(mediaType.Extensions) + + // TODO continue this. + + return m +} + func (m *MediaType) GoLow() *low.MediaType { return m.low } diff --git a/datamodel/high/3.0/operation.go b/datamodel/high/3.0/operation.go index 0b81882..dda1279 100644 --- a/datamodel/high/3.0/operation.go +++ b/datamodel/high/3.0/operation.go @@ -22,6 +22,23 @@ type Operation struct { low *low.Operation } +func NewOperation(operation *low.Operation) *Operation { + o := new(Operation) + o.low = operation + var tags []string + for i := range operation.Tags.Value { + tags = append(tags, operation.Tags.Value[i].Value) + } + o.Tags = tags + o.Summary = operation.Summary.Value + o.Description = operation.Description.Value + o.ExternalDocs = NewExternalDoc(operation.ExternalDocs.Value) + o.OperationId = operation.OperationId.Value + + // TODO: come back and finish. + return o +} + func (o *Operation) GoLow() *low.Operation { return o.low } diff --git a/datamodel/high/3.0/parameter.go b/datamodel/high/3.0/parameter.go index 4dd3340..ef83f28 100644 --- a/datamodel/high/3.0/parameter.go +++ b/datamodel/high/3.0/parameter.go @@ -3,7 +3,9 @@ package v3 -import low "github.com/pb33f/libopenapi/datamodel/low/3.0" +import ( + low "github.com/pb33f/libopenapi/datamodel/low/3.0" +) type Parameter struct { Name string @@ -23,6 +25,24 @@ type Parameter struct { low *low.Parameter } +func NewParameter(param *low.Parameter) *Parameter { + p := new(Parameter) + p.low = param + p.Name = param.Name.Value + p.In = param.In.Value + p.Description = param.Description.Value + p.Deprecated = param.Deprecated.Value + p.AllowEmptyValue = param.AllowEmptyValue.Value + p.Style = param.Style.Value + p.Explode = param.Explode.Value + p.AllowReserved = param.AllowReserved.Value + p.Schema = NewSchema(param.Schema.Value) + p.Example = param.Example.Value + p.Examples = ExtractExamples(param.Examples.Value) + + return p +} + func (p *Parameter) GoLow() *low.Parameter { return p.low } diff --git a/datamodel/high/3.0/path_item.go b/datamodel/high/3.0/path_item.go index b1fd912..a43f92d 100644 --- a/datamodel/high/3.0/path_item.go +++ b/datamodel/high/3.0/path_item.go @@ -22,10 +22,17 @@ type PathItem struct { low *low.PathItem } -func NewPathItem(lowPathItem *low.PathItem) *PathItem { +func NewPathItem(pathItem *low.PathItem) *PathItem { pi := new(PathItem) - pi.Description = lowPathItem.Description.Value - pi.Summary = lowPathItem.Summary.Value + pi.Description = pathItem.Description.Value + pi.Summary = pathItem.Summary.Value + + var servers []*Server + for _, ser := range pathItem.Servers.Value { + servers = append(servers, NewServer(ser.Value)) + } + pi.Servers = servers + return pi } diff --git a/datamodel/high/3.0/schema.go b/datamodel/high/3.0/schema.go index ef88133..70d45a2 100644 --- a/datamodel/high/3.0/schema.go +++ b/datamodel/high/3.0/schema.go @@ -3,7 +3,11 @@ package v3 -import low "github.com/pb33f/libopenapi/datamodel/low/3.0" +import ( + "github.com/pb33f/libopenapi/datamodel/high" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + low "github.com/pb33f/libopenapi/datamodel/low/3.0" +) type Schema struct { Title string @@ -45,6 +49,90 @@ type Schema struct { low *low.Schema } +func NewSchema(schema *low.Schema) *Schema { + s := new(Schema) + s.low = schema + s.Title = schema.Title.Value + s.MultipleOf = schema.MultipleOf.Value + s.Maximum = schema.Maximum.Value + s.ExclusiveMaximum = schema.ExclusiveMaximum.Value + s.Minimum = schema.Minimum.Value + s.ExclusiveMinimum = schema.ExclusiveMinimum.Value + s.MaxLength = schema.MaxLength.Value + s.MinLength = schema.MinLength.Value + s.Pattern = schema.Pattern.Value + s.Format = schema.Format.Value + s.MaxItems = schema.MaxItems.Value + s.MinItems = schema.MinItems.Value + s.MaxProperties = schema.MaxProperties.Value + s.MinProperties = schema.MinProperties.Value + s.Type = schema.Type.Value + s.AdditionalProperties = schema.AdditionalProperties.Value + s.Description = schema.Description.Value + s.Default = schema.Default.Value + s.Nullable = schema.Nullable.Value + s.ReadOnly = schema.ReadOnly.Value + s.WriteOnly = schema.WriteOnly.Value + s.Example = schema.Example.Value + s.Deprecated = schema.Deprecated.Value + s.Extensions = high.ExtractExtensions(schema.Extensions) + if !schema.Discriminator.IsEmpty() { + s.Discriminator = NewDiscriminator(schema.Discriminator.Value) + } + if !schema.XML.IsEmpty() { + s.XML = NewXML(schema.XML.Value) + } + if !schema.ExternalDocs.IsEmpty() { + s.ExternalDocs = NewExternalDoc(schema.ExternalDocs.Value) + } + var req []string + for i := range schema.Required.Value { + req = append(req, schema.Required.Value[i].Value) + } + s.Required = req + + var enum []string + for i := range schema.Enum.Value { + enum = append(req, schema.Enum.Value[i].Value) + } + s.Enum = enum + + totalItems := len(schema.AllOf.Value) + len(schema.OneOf.Value) + len(schema.AnyOf.Value) + len(schema.Not.Value) + + len(schema.Items.Value) + + completedChan := make(chan bool) + + buildOutSchema := func(schemas []lowmodel.NodeReference[*low.Schema], items *[]*Schema, doneChan chan bool) { + for v := range schemas { + *items = append(*items, NewSchema(schemas[v].Value)) + } + doneChan <- true + } + + allOf := make([]*Schema, len(schema.AllOf.Value)) + oneOf := make([]*Schema, len(schema.OneOf.Value)) + anyOf := make([]*Schema, len(schema.AnyOf.Value)) + not := make([]*Schema, len(schema.Not.Value)) + items := make([]*Schema, len(schema.Items.Value)) + + go buildOutSchema(schema.AllOf.Value, &allOf, completedChan) + go buildOutSchema(schema.AnyOf.Value, &anyOf, completedChan) + go buildOutSchema(schema.OneOf.Value, &oneOf, completedChan) + go buildOutSchema(schema.Not.Value, ¬, completedChan) + go buildOutSchema(schema.Items.Value, &items, completedChan) + + complete := 0 + for complete < totalItems { + select { + case <-completedChan: + complete++ + } + } + + return s + +} + func (s *Schema) GoLow() *low.Schema { return s.low } diff --git a/datamodel/high/3.0/security_requirement.go b/datamodel/high/3.0/security_requirement.go index 2c615b9..c6578d6 100644 --- a/datamodel/high/3.0/security_requirement.go +++ b/datamodel/high/3.0/security_requirement.go @@ -10,6 +10,25 @@ type SecurityRequirement struct { low *low.SecurityRequirement } +func NewSecurityRequirement(req *low.SecurityRequirement) *SecurityRequirement { + r := new(SecurityRequirement) + r.low = req + values := make([]map[string][]string, len(req.ValueRequirements)) + for i := range req.ValueRequirements { + valmap := make(map[string][]string) + for k, v := range req.ValueRequirements[i].Value { + var mItems []string + for h := range v { + mItems = append(mItems, v[h].Value) + } + valmap[k.Value] = mItems + } + values = append(values, valmap) + } + r.ValueRequirements = values + return r +} + func (s *SecurityRequirement) GoLow() *low.SecurityRequirement { return s.low } diff --git a/datamodel/high/3.0/xml.go b/datamodel/high/3.0/xml.go index 9c51e02..7f38b99 100644 --- a/datamodel/high/3.0/xml.go +++ b/datamodel/high/3.0/xml.go @@ -3,7 +3,10 @@ package v3 -import low "github.com/pb33f/libopenapi/datamodel/low/3.0" +import ( + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/3.0" +) type XML struct { Name string @@ -15,6 +18,18 @@ type XML struct { low *low.XML } +func NewXML(xml *low.XML) *XML { + x := new(XML) + x.low = xml + x.Name = xml.Name.Value + x.Namespace = xml.Namespace.Value + x.Prefix = xml.Namespace.Value + x.Attribute = xml.Attribute.Value + x.Wrapped = xml.Wrapped.Value + x.Extensions = high.ExtractExtensions(xml.Extensions) + return x +} + func (x *XML) GoLow() *low.XML { return x.low } diff --git a/datamodel/low/3.0/create_document.go b/datamodel/low/3.0/create_document.go index 152c10b..eb44769 100644 --- a/datamodel/low/3.0/create_document.go +++ b/datamodel/low/3.0/create_document.go @@ -19,6 +19,8 @@ func CreateDocument(info *datamodel.SpecInfo) (*Document, []error) { var wg sync.WaitGroup var errors []error + doc.Extensions = low.ExtractExtensions(info.RootNode.Content[0]) + var runExtraction = func(info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex, runFunc func(i *datamodel.SpecInfo, d *Document, idx *index.SpecIndex) error, ers *[]error, diff --git a/datamodel/low/3.0/document.go b/datamodel/low/3.0/document.go index 8e30359..fa6a39a 100644 --- a/datamodel/low/3.0/document.go +++ b/datamodel/low/3.0/document.go @@ -17,6 +17,6 @@ type Document struct { Security low.NodeReference[*SecurityRequirement] Tags low.NodeReference[[]low.ValueReference[*Tag]] ExternalDocs low.NodeReference[*ExternalDoc] - Extensions map[low.NodeReference[string]]low.NodeReference[any] + Extensions map[low.KeyReference[string]]low.ValueReference[any] Index *index.SpecIndex } diff --git a/datamodel/low/3.0/security_scheme.go b/datamodel/low/3.0/security_scheme.go index 38980a9..bc21b7e 100644 --- a/datamodel/low/3.0/security_scheme.go +++ b/datamodel/low/3.0/security_scheme.go @@ -90,10 +90,11 @@ func (sr *SecurityRequirement) Build(root *yaml.Node, idx *index.SpecIndex) erro Value: currSec.Value, KeyNode: currSec, }] = dat - requirements = append(requirements, low.ValueReference[map[low.KeyReference[string]][]low.ValueReference[string]]{ - Value: res, - ValueNode: n, - }) + requirements = append(requirements, + low.ValueReference[map[low.KeyReference[string]][]low.ValueReference[string]]{ + Value: res, + ValueNode: n, + }) } } sr.ValueRequirements = requirements diff --git a/test_specs/burgershop.openapi.yaml b/test_specs/burgershop.openapi.yaml index 55d1f77..4262f69 100644 --- a/test_specs/burgershop.openapi.yaml +++ b/test_specs/burgershop.openapi.yaml @@ -510,4 +510,8 @@ components: drink: some value SomePayload: type: string - description: some kind of payload for something. \ No newline at end of file + description: some kind of payload for something. +x-something-something: darkside +externalDocs: + description: "Find out more information about our products)" + url: "https://pb33f.io" \ No newline at end of file