diff --git a/datamodel/high/base/security_requirement.go b/datamodel/high/base/security_requirement.go index 39cae3f..4952d44 100644 --- a/datamodel/high/base/security_requirement.go +++ b/datamodel/high/base/security_requirement.go @@ -4,7 +4,9 @@ package base import ( + "github.com/pb33f/libopenapi/datamodel/high" "github.com/pb33f/libopenapi/datamodel/low/base" + "gopkg.in/yaml.v3" ) // SecurityRequirement is a high-level representation of a Swagger / OpenAPI 2 SecurityRequirement object. @@ -40,3 +42,17 @@ func NewSecurityRequirement(req *base.SecurityRequirement) *SecurityRequirement func (s *SecurityRequirement) GoLow() *base.SecurityRequirement { return s.low } + +// Render will return a YAML representation of the SecurityRequirement object as a byte slice. +func (s *SecurityRequirement) Render() ([]byte, error) { + return yaml.Marshal(s) +} + +// MarshalYAML will create a ready to render YAML representation of the SecurityRequirement object. +func (s *SecurityRequirement) MarshalYAML() (interface{}, error) { + if s == nil { + return nil, nil + } + nb := high.NewNodeBuilder(s, s.low) + return nb.Render(), nil +} diff --git a/datamodel/high/base/security_requirement_test.go b/datamodel/high/base/security_requirement_test.go index e056255..1033615 100644 --- a/datamodel/high/base/security_requirement_test.go +++ b/datamodel/high/base/security_requirement_test.go @@ -8,6 +8,7 @@ import ( lowbase "github.com/pb33f/libopenapi/datamodel/low/base" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" + "strings" "testing" ) @@ -16,8 +17,8 @@ func TestNewSecurityRequirement(t *testing.T) { var cNode yaml.Node yml := `pizza: - - cheese - - tomato` + - cheese + - tomato` _ = yaml.Unmarshal([]byte(yml), &cNode) @@ -32,4 +33,8 @@ func TestNewSecurityRequirement(t *testing.T) { wentLow := highExt.GoLow() assert.Len(t, wentLow.Requirements.Value, 1) + + // render the high-level object as YAML + highBytes, _ := highExt.Render() + assert.Equal(t, strings.TrimSpace(string(highBytes)), yml) } diff --git a/datamodel/high/base/tag.go b/datamodel/high/base/tag.go index 68cc842..f08f120 100644 --- a/datamodel/high/base/tag.go +++ b/datamodel/high/base/tag.go @@ -6,6 +6,7 @@ package base import ( "github.com/pb33f/libopenapi/datamodel/high" low "github.com/pb33f/libopenapi/datamodel/low/base" + "gopkg.in/yaml.v3" ) // Tag represents a high-level Tag instance that is backed by a low-level one. @@ -15,9 +16,9 @@ import ( // - v2: https://swagger.io/specification/v2/#tagObject // - v3: https://swagger.io/specification/#tag-object type Tag struct { - Name string - Description string - ExternalDocs *ExternalDoc + Name string `json:"name,omitempty" yaml:"name,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + ExternalDocs *ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` Extensions map[string]any low *low.Tag } @@ -44,6 +45,20 @@ func (t *Tag) GoLow() *low.Tag { return t.low } +// Render will return a YAML representation of the Info object as a byte slice. +func (t *Tag) Render() ([]byte, error) { + return yaml.Marshal(t) +} + +// MarshalYAML will create a ready to render YAML representation of the Info object. +func (t *Tag) MarshalYAML() (interface{}, error) { + if t == nil { + return nil, nil + } + nb := high.NewNodeBuilder(t, t.low) + return nb.Render(), nil +} + // Experimental mutation API. //func (t *Tag) SetName(value string) { // t.GoLow().Name.ValueNode.Value = value diff --git a/datamodel/high/base/tag_test.go b/datamodel/high/base/tag_test.go index dcecd90..4a20ca5 100644 --- a/datamodel/high/base/tag_test.go +++ b/datamodel/high/base/tag_test.go @@ -9,6 +9,7 @@ import ( lowbase "github.com/pb33f/libopenapi/datamodel/low/base" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" + "strings" "testing" ) @@ -19,7 +20,7 @@ func TestNewTag(t *testing.T) { yml := `name: chicken description: nuggets externalDocs: - url: https://pb33f.io + url: https://pb33f.io x-hack: code` _ = yaml.Unmarshal([]byte(yml), &cNode) @@ -38,6 +39,10 @@ x-hack: code` wentLow := highTag.GoLow() assert.Equal(t, 5, wentLow.FindExtension("x-hack").ValueNode.Line) + // render the tag as YAML + highTagBytes, _ := highTag.Render() + assert.Equal(t, strings.TrimSpace(string(highTagBytes)), yml) + } func ExampleNewTag() { diff --git a/datamodel/high/base/xml.go b/datamodel/high/base/xml.go index 7720496..b82c037 100644 --- a/datamodel/high/base/xml.go +++ b/datamodel/high/base/xml.go @@ -6,6 +6,7 @@ package base import ( "github.com/pb33f/libopenapi/datamodel/high" low "github.com/pb33f/libopenapi/datamodel/low/base" + "gopkg.in/yaml.v3" ) // XML represents a high-level representation of an XML object defined by all versions of OpenAPI and backed by @@ -18,11 +19,11 @@ import ( // v2 - https://swagger.io/specification/v2/#xmlObject // v3 - https://swagger.io/specification/#xml-object type XML struct { - Name string - Namespace string - Prefix string - Attribute bool - Wrapped bool + Name string `json:"name,omitempty" yaml:"name,omitempty"` + Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` + Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"` + Attribute bool `json:"attribute,omitempty" yaml:"attribute,omitempty"` + Wrapped bool `json:"wrapped,omitempty" yaml:"wrapped,omitempty"` Extensions map[string]any low *low.XML } @@ -33,7 +34,7 @@ func NewXML(xml *low.XML) *XML { x.low = xml x.Name = xml.Name.Value x.Namespace = xml.Namespace.Value - x.Prefix = xml.Namespace.Value + x.Prefix = xml.Prefix.Value x.Attribute = xml.Attribute.Value x.Wrapped = xml.Wrapped.Value x.Extensions = high.ExtractExtensions(xml.Extensions) @@ -44,3 +45,18 @@ func NewXML(xml *low.XML) *XML { func (x *XML) GoLow() *low.XML { return x.low } + +// Render will return a YAML representation of the XML object as a byte slice. +func (x *XML) Render() ([]byte, error) { + return yaml.Marshal(x) +} + +// MarshalYAML will create a ready to render YAML representation of the XML object. +func (x *XML) MarshalYAML() (interface{}, error) { + if x == nil { + return nil, nil + } + nb := high.NewNodeBuilder(x, x.low) + return nb.Render(), nil +} + diff --git a/datamodel/high/base/xml_test.go b/datamodel/high/base/xml_test.go index 01d2051..9c2c7e7 100644 --- a/datamodel/high/base/xml_test.go +++ b/datamodel/high/base/xml_test.go @@ -7,7 +7,10 @@ import ( "fmt" lowmodel "github.com/pb33f/libopenapi/datamodel/low" lowbase "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" + "strings" + "testing" ) func ExampleNewXML() { @@ -16,7 +19,10 @@ func ExampleNewXML() { // this can be either JSON or YAML. yml := ` namespace: https://pb33f.io/schema -prefix: sample` +name: something +attribute: true +prefix: sample +wrapped: true` // unmarshal raw bytes var node yaml.Node @@ -34,3 +40,35 @@ prefix: sample` fmt.Print(highXML.Namespace) // Output: https://pb33f.io/schema } + +func TestContact_Render(t *testing.T) { + + // create an example schema object + // this can be either JSON or YAML. + yml := `namespace: https://pb33f.io/schema +name: something +attribute: true +prefix: sample +wrapped: true` + + // unmarshal raw bytes + var node yaml.Node + _ = yaml.Unmarshal([]byte(yml), &node) + + // build out the low-level model + var lowXML lowbase.XML + _ = lowmodel.BuildModel(node.Content[0], &lowXML) + _ = lowXML.Build(node.Content[0], nil) + + // build the high level tag + highXML := NewXML(&lowXML) + + // print out the XML doc + highXMLBytes, _ := highXML.Render() + assert.Equal(t, yml, strings.TrimSpace(string(highXMLBytes))) + + highXML.Attribute = false + highXMLBytes, _ = highXML.Render() + assert.NotEqual(t, yml, strings.TrimSpace(string(highXMLBytes))) + +} \ No newline at end of file diff --git a/datamodel/high/shared.go b/datamodel/high/shared.go index 7394f23..274a1fb 100644 --- a/datamodel/high/shared.go +++ b/datamodel/high/shared.go @@ -167,10 +167,14 @@ func (n *NodeBuilder) add(key string) { nodeEntry.Value = strconv.FormatInt(value.Int(), 10) case reflect.String: nodeEntry.Value = value.String() + case reflect.Bool: + nodeEntry.Value = value.Bool() case reflect.Ptr: nodeEntry.Value = f case reflect.Map: nodeEntry.Value = f + default: + panic("not supported yet") } // if there is no low level object, then we cannot extract line numbers, @@ -216,26 +220,14 @@ func AddYAMLNode(parent *yaml.Node, key string, value any) *yaml.Node { // check the type t := reflect.TypeOf(value) - l := CreateStringNode(key) + var l *yaml.Node + if key != "" { + l = CreateStringNode(key) + } var valueNode *yaml.Node switch t.Kind() { - case reflect.String: - if value.(string) == "" { - return parent - } - valueNode = CreateStringNode(value.(string)) - case reflect.Int: - valueNode = CreateIntNode(value.(int)) case reflect.Struct: panic("no way dude, why?") - case reflect.Map: - var rawNode yaml.Node - err := rawNode.Encode(value) - if err != nil { - return parent - } else { - valueNode = &rawNode - } case reflect.Ptr: rawRender, _ := value.(Renderable).MarshalYAML() if rawRender != nil { @@ -244,9 +236,20 @@ func AddYAMLNode(parent *yaml.Node, key string, value any) *yaml.Node { return parent } default: - panic("not supported yet") + var rawNode yaml.Node + err := rawNode.Encode(value) + if err != nil { + return parent + } else { + valueNode = &rawNode + } } - parent.Content = append(parent.Content, l, valueNode) + if l != nil { + parent.Content = append(parent.Content, l, valueNode) + } else { + parent.Content = valueNode.Content + } + return parent }