diff --git a/datamodel/high/base/discriminator.go b/datamodel/high/base/discriminator.go index 0e18c6f..c1d472a 100644 --- a/datamodel/high/base/discriminator.go +++ b/datamodel/high/base/discriminator.go @@ -4,7 +4,9 @@ package base import ( + "github.com/pb33f/libopenapi/datamodel/high" low "github.com/pb33f/libopenapi/datamodel/low/base" + "gopkg.in/yaml.v3" ) // Discriminator is only used by OpenAPI 3+ documents, it represents a polymorphic discriminator used for schemas @@ -16,8 +18,8 @@ import ( // When using the discriminator, inline schemas will not be considered. // v3 - https://spec.openapis.org/oas/v3.1.0#discriminator-object type Discriminator struct { - PropertyName string - Mapping map[string]string + PropertyName string `json:"propertyName,omitempty" yaml:"propertyName,omitempty"` + Mapping map[string]string `json:"mapping,omitempty" yaml:"mapping,omitempty"` low *low.Discriminator } @@ -38,3 +40,17 @@ func NewDiscriminator(disc *low.Discriminator) *Discriminator { func (d *Discriminator) GoLow() *low.Discriminator { return d.low } + +// Render will return a YAML representation of the Discriminator object as a byte slice. +func (d *Discriminator) Render() ([]byte, error) { + return yaml.Marshal(d) +} + +// MarshalYAML will create a ready to render YAML representation of the Discriminator object. +func (d *Discriminator) MarshalYAML() (interface{}, error) { + if d == nil { + return nil, nil + } + nb := high.NewNodeBuilder(d, d.low) + return nb.Render(), nil +} diff --git a/datamodel/high/base/discriminator_test.go b/datamodel/high/base/discriminator_test.go index ec29a10..85dde25 100644 --- a/datamodel/high/base/discriminator_test.go +++ b/datamodel/high/base/discriminator_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" ) @@ -18,7 +19,7 @@ func TestNewDiscriminator(t *testing.T) { yml := `propertyName: coffee mapping: - fogCleaner: in the morning` + fogCleaner: in the morning` _ = yaml.Unmarshal([]byte(yml), &cNode) @@ -33,6 +34,10 @@ mapping: assert.Equal(t, "in the morning", highDiscriminator.Mapping["fogCleaner"]) assert.Equal(t, 3, highDiscriminator.GoLow().FindMappingValue("fogCleaner").ValueNode.Line) + // render the example as YAML + rendered, _ := highDiscriminator.Render() + assert.Equal(t, strings.TrimSpace(string(rendered)), yml) + } func ExampleNewDiscriminator() { diff --git a/datamodel/high/base/example.go b/datamodel/high/base/example.go index cf191d0..71136b4 100644 --- a/datamodel/high/base/example.go +++ b/datamodel/high/base/example.go @@ -7,16 +7,17 @@ import ( "github.com/pb33f/libopenapi/datamodel/high" lowmodel "github.com/pb33f/libopenapi/datamodel/low" low "github.com/pb33f/libopenapi/datamodel/low/base" + "gopkg.in/yaml.v3" ) // Example represents a high-level Example object as defined by OpenAPI 3+ // v3 - https://spec.openapis.org/oas/v3.1.0#example-object type Example struct { - Summary string - Description string - Value any - ExternalValue string - Extensions map[string]any + Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Value any `json:"value,omitempty" yaml:"value,omitempty"` + ExternalValue string `json:"externalValue,omitempty" yaml:"externalValue,omitempty"` + Extensions map[string]any `json:"-" yaml:"-"` low *low.Example } @@ -37,6 +38,20 @@ func (e *Example) GoLow() *low.Example { return e.low } +// Render will return a YAML representation of the Example object as a byte slice. +func (e *Example) Render() ([]byte, error) { + return yaml.Marshal(e) +} + +// MarshalYAML will create a ready to render YAML representation of the Example object. +func (e *Example) MarshalYAML() (interface{}, error) { + if e == nil { + return nil, nil + } + nb := high.NewNodeBuilder(e, e.low) + return nb.Render(), nil +} + // ExtractExamples will convert a low-level example map, into a high level one that is simple to navigate. // no fidelity is lost, everything is still available via GoLow() func ExtractExamples(elements map[lowmodel.KeyReference[string]]lowmodel.ValueReference[*low.Example]) map[string]*Example { diff --git a/datamodel/high/base/example_test.go b/datamodel/high/base/example_test.go index 0c32518..d0e5c97 100644 --- a/datamodel/high/base/example_test.go +++ b/datamodel/high/base/example_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" ) @@ -39,6 +40,11 @@ x-hack: code` assert.Equal(t, "code", highExample.Extensions["x-hack"]) assert.Equal(t, "a thing", highExample.Value) assert.Equal(t, 4, highExample.GoLow().ExternalValue.ValueNode.Line) + + // render the example as YAML + rendered, _ := highExample.Render() + assert.Equal(t, strings.TrimSpace(string(rendered)), yml) + } func TestExtractExamples(t *testing.T) { diff --git a/datamodel/high/base/external_doc.go b/datamodel/high/base/external_doc.go index 55eae68..aee3e37 100644 --- a/datamodel/high/base/external_doc.go +++ b/datamodel/high/base/external_doc.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" ) // ExternalDoc represents a high-level External Documentation object as defined by OpenAPI 2 and 3 @@ -14,9 +15,9 @@ import ( // v2 - https://swagger.io/specification/v2/#externalDocumentationObject // v3 - https://spec.openapis.org/oas/v3.1.0#external-documentation-object type ExternalDoc struct { - Description string - URL string - Extensions map[string]any + Description string `json:"description,omitempty" yaml:"description,omitempty"` + URL string `json:"url,omitempty" yaml:"url,omitempty"` + Extensions map[string]any `json:"-" yaml:"-"` low *low.ExternalDoc } @@ -41,4 +42,18 @@ func (e *ExternalDoc) GoLow() *low.ExternalDoc { func (e *ExternalDoc) GetExtensions() map[string]any { return e.Extensions -} \ No newline at end of file +} + +// Render will return a YAML representation of the ExternalDoc object as a byte slice. +func (e *ExternalDoc) Render() ([]byte, error) { + return yaml.Marshal(e) +} + +// MarshalYAML will create a ready to render YAML representation of the ExternalDoc object. +func (e *ExternalDoc) MarshalYAML() (interface{}, error) { + if e == nil { + return nil, nil + } + nb := high.NewNodeBuilder(e, e.low) + return nb.Render(), nil +} diff --git a/datamodel/high/base/external_doc_test.go b/datamodel/high/base/external_doc_test.go index fcef1ce..59ec209 100644 --- a/datamodel/high/base/external_doc_test.go +++ b/datamodel/high/base/external_doc_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" ) @@ -36,6 +37,11 @@ x-hack: code` wentLow := highExt.GoLow() assert.Equal(t, 2, wentLow.URL.ValueNode.Line) assert.Len(t, highExt.GetExtensions(), 1) + + // render the high-level object as YAML + rendered, _ := highExt.Render() + assert.Equal(t, strings.TrimSpace(string(rendered)), yml) + } func ExampleNewExternalDoc() { diff --git a/datamodel/high/shared.go b/datamodel/high/shared.go index 53ebee5..7394f23 100644 --- a/datamodel/high/shared.go +++ b/datamodel/high/shared.go @@ -14,7 +14,6 @@ package high import ( - "fmt" "github.com/pb33f/libopenapi/datamodel/low" "gopkg.in/yaml.v3" "reflect" @@ -118,11 +117,7 @@ func (n *NodeBuilder) add(key string) { // if the key is 'Extensions' then we need to extract the keys from the map // and add them to the node builder. if key == "Extensions" { - extensions := reflect.ValueOf(n.High).Elem().FieldByName(key) - //for _, k := range extensions.MapKeys() { - // n.add(k.String()) - //} for _, e := range extensions.MapKeys() { v := extensions.MapIndex(e) @@ -135,17 +130,6 @@ func (n *NodeBuilder) add(key string) { f := fieldValue.Interface() value := reflect.ValueOf(f) switch value.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - panic("not supported yet") - case reflect.String: - panic("not supported yet") - case reflect.Ptr: - panic("not supported yet") - case reflect.Struct: - nb := f.(low.HasValueNodeUntyped).GetValueNode() - if nb != nil { - nodeEntry.Line = nb.Line - } case reflect.Map: if j, ok := n.Low.(low.HasExtensionsUntyped); ok { originalExtensions := j.GetExtensions() @@ -156,9 +140,6 @@ func (n *NodeBuilder) add(key string) { } } default: - if j, ok := n.Low.(low.HasExtensionsUntyped); ok { - fmt.Print(j) - } panic("not supported yet") } } @@ -189,30 +170,26 @@ func (n *NodeBuilder) add(key string) { case reflect.Ptr: nodeEntry.Value = f case reflect.Map: - nodeEntry.Value = value + nodeEntry.Value = f } // if there is no low level object, then we cannot extract line numbers, // so skip and default to 0, which means a new entry to the spec. // this will place new content and the top of the rendered object. if !reflect.ValueOf(n.Low).IsZero() { - fieldValue = reflect.ValueOf(n.Low).Elem().FieldByName(key) - f = fieldValue.Interface() - value = reflect.ValueOf(f) + lowFieldValue := reflect.ValueOf(n.Low).Elem().FieldByName(key) + fLow := lowFieldValue.Interface() + value = reflect.ValueOf(fLow) switch value.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - panic("not supported yet") - case reflect.String: - panic("not supported yet") - case reflect.Ptr: - panic("not supported yet") case reflect.Struct: nb := value.Interface().(low.HasValueNodeUntyped).GetValueNode() if nb != nil { nodeEntry.Line = nb.Line } default: - panic("not supported yet") + // everything else, weight it to the bottom of the rendered object. + // this is things that we have no way of knowing where they should be placed. + nodeEntry.Line = 9999 } } n.Nodes = append(n.Nodes, nodeEntry)