diff --git a/datamodel/high/base/dynamic_value.go b/datamodel/high/base/dynamic_value.go index 0d7bd0d..26b8038 100644 --- a/datamodel/high/base/dynamic_value.go +++ b/datamodel/high/base/dynamic_value.go @@ -4,9 +4,9 @@ package base import ( - "github.com/pb33f/libopenapi/datamodel/high" - "gopkg.in/yaml.v3" - "reflect" + "github.com/pb33f/libopenapi/datamodel/high" + "gopkg.in/yaml.v3" + "reflect" ) // DynamicValue is used to hold multiple possible values for a schema property. There are two values, a left @@ -18,61 +18,101 @@ import ( // The N value is a bit to make it each to know which value (A or B) is used, this prevents having to // if/else on the value to determine which one is set. type DynamicValue[A any, B any] struct { - N int // 0 == A, 1 == B - A A - B B + N int // 0 == A, 1 == B + A A + B B } // IsA will return true if the 'A' or left value is set. (OpenAPI 3) func (d *DynamicValue[A, B]) IsA() bool { - return d.N == 0 + return d.N == 0 } // IsB will return true if the 'B' or right value is set (OpenAPI 3.1) func (d *DynamicValue[A, B]) IsB() bool { - return d.N == 1 + return d.N == 1 } func (d *DynamicValue[A, B]) Render() ([]byte, error) { - return yaml.Marshal(d) + return yaml.Marshal(d) } // MarshalYAML will create a ready to render YAML representation of the DynamicValue object. func (d *DynamicValue[A, B]) MarshalYAML() (interface{}, error) { - // this is a custom renderer, we can't use the NodeBuilder out of the gate. - var n yaml.Node - var err error - var value any + // this is a custom renderer, we can't use the NodeBuilder out of the gate. + var n yaml.Node + var err error + var value any - if d.IsA() { - value = d.A - } - if d.IsB() { - value = d.B - } - to := reflect.TypeOf(value) - switch to.Kind() { - case reflect.Ptr: - if r, ok := value.(high.Renderable); ok { - return r.MarshalYAML() - } else { - _ = n.Encode(value) - } - case reflect.Bool: - _ = n.Encode(value.(bool)) - case reflect.Int: - _ = n.Encode(value.(int)) - case reflect.String: - _ = n.Encode(value.(string)) - case reflect.Int64: - _ = n.Encode(value.(int64)) - case reflect.Float64: - _ = n.Encode(value.(float64)) - case reflect.Float32: - _ = n.Encode(value.(float32)) - case reflect.Int32: - _ = n.Encode(value.(int32)) + if d.IsA() { + value = d.A + } + if d.IsB() { + value = d.B + } + to := reflect.TypeOf(value) + switch to.Kind() { + case reflect.Ptr: + if r, ok := value.(high.Renderable); ok { + return r.MarshalYAML() + } else { + _ = n.Encode(value) + } + case reflect.Bool: + _ = n.Encode(value.(bool)) + case reflect.Int: + _ = n.Encode(value.(int)) + case reflect.String: + _ = n.Encode(value.(string)) + case reflect.Int64: + _ = n.Encode(value.(int64)) + case reflect.Float64: + _ = n.Encode(value.(float64)) + case reflect.Float32: + _ = n.Encode(value.(float32)) + case reflect.Int32: + _ = n.Encode(value.(int32)) - } - return &n, err + } + return &n, err +} + +// MarshalYAML will create a ready to render YAML representation of the DynamicValue object. +func (d *DynamicValue[A, B]) MarshalYAMLInline() (interface{}, error) { + // this is a custom renderer, we can't use the NodeBuilder out of the gate. + var n yaml.Node + var err error + var value any + + if d.IsA() { + value = d.A + } + if d.IsB() { + value = d.B + } + to := reflect.TypeOf(value) + switch to.Kind() { + case reflect.Ptr: + if r, ok := value.(high.RenderableInline); ok { + return r.MarshalYAMLInline() + } else { + _ = n.Encode(value) + } + case reflect.Bool: + _ = n.Encode(value.(bool)) + case reflect.Int: + _ = n.Encode(value.(int)) + case reflect.String: + _ = n.Encode(value.(string)) + case reflect.Int64: + _ = n.Encode(value.(int64)) + case reflect.Float64: + _ = n.Encode(value.(float64)) + case reflect.Float32: + _ = n.Encode(value.(float32)) + case reflect.Int32: + _ = n.Encode(value.(int32)) + + } + return &n, err } diff --git a/datamodel/high/v3/responses.go b/datamodel/high/v3/responses.go index f064a27..b7a481f 100644 --- a/datamodel/high/v3/responses.go +++ b/datamodel/high/v3/responses.go @@ -4,12 +4,12 @@ package v3 import ( - "fmt" - "github.com/pb33f/libopenapi/datamodel/high" - low "github.com/pb33f/libopenapi/datamodel/low/v3" - "github.com/pb33f/libopenapi/utils" - "gopkg.in/yaml.v3" - "sort" + "fmt" + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/utils" + "gopkg.in/yaml.v3" + "sort" ) // Responses represents a high-level OpenAPI 3+ Responses object that is backed by a low-level one. @@ -27,123 +27,128 @@ import ( // be the response for a successful operation call. // - https://spec.openapis.org/oas/v3.1.0#responses-object type Responses struct { - Codes map[string]*Response `json:"-" yaml:"-"` - Default *Response `json:"default,omitempty" yaml:"default,omitempty"` - Extensions map[string]any `json:"-" yaml:"-"` - low *low.Responses + Codes map[string]*Response `json:"-" yaml:"-"` + Default *Response `json:"default,omitempty" yaml:"default,omitempty"` + Extensions map[string]any `json:"-" yaml:"-"` + low *low.Responses } // NewResponses will create a new high-level Responses instance from a low-level one. It operates asynchronously // internally, as each response may be considerable in complexity. func NewResponses(responses *low.Responses) *Responses { - r := new(Responses) - r.low = responses - r.Extensions = high.ExtractExtensions(responses.Extensions) - if !responses.Default.IsEmpty() { - r.Default = NewResponse(responses.Default.Value) - } - codes := make(map[string]*Response) + r := new(Responses) + r.low = responses + r.Extensions = high.ExtractExtensions(responses.Extensions) + if !responses.Default.IsEmpty() { + r.Default = NewResponse(responses.Default.Value) + } + codes := make(map[string]*Response) - // struct to hold response and code sent over chan. - type respRes struct { - code string - resp *Response - } + // struct to hold response and code sent over chan. + type respRes struct { + code string + resp *Response + } - // build each response async for speed - rChan := make(chan respRes) - var buildResponse = func(code string, resp *low.Response, c chan respRes) { - c <- respRes{code: code, resp: NewResponse(resp)} - } - for k, v := range responses.Codes { - go buildResponse(k.Value, v.Value, rChan) - } - totalCodes := len(responses.Codes) - codesParsed := 0 - for codesParsed < totalCodes { - select { - case re := <-rChan: - codesParsed++ - codes[re.code] = re.resp - } - } - r.Codes = codes - return r + // build each response async for speed + rChan := make(chan respRes) + var buildResponse = func(code string, resp *low.Response, c chan respRes) { + c <- respRes{code: code, resp: NewResponse(resp)} + } + for k, v := range responses.Codes { + go buildResponse(k.Value, v.Value, rChan) + } + totalCodes := len(responses.Codes) + codesParsed := 0 + for codesParsed < totalCodes { + select { + case re := <-rChan: + codesParsed++ + codes[re.code] = re.resp + } + } + r.Codes = codes + return r } // FindResponseByCode is a shortcut for looking up code by an integer vs. a string func (r *Responses) FindResponseByCode(code int) *Response { - return r.Codes[fmt.Sprintf("%d", code)] + return r.Codes[fmt.Sprintf("%d", code)] +} + +// GetDefaultResponse will +func (r *Responses) GetDefaultResponse(code int) *Response { + return r.Codes[fmt.Sprintf("%d", code)] } // GoLow returns the low-level Response object used to create the high-level one. func (r *Responses) GoLow() *low.Responses { - return r.low + return r.low } // GoLowUntyped will return the low-level Responses instance that was used to create the high-level one, with no type func (r *Responses) GoLowUntyped() any { - return r.low + return r.low } // Render will return a YAML representation of the Responses object as a byte slice. func (r *Responses) Render() ([]byte, error) { - return yaml.Marshal(r) + return yaml.Marshal(r) } // MarshalYAML will create a ready to render YAML representation of the Responses object. func (r *Responses) MarshalYAML() (interface{}, error) { - // map keys correctly. - m := utils.CreateEmptyMapNode() - type responseItem struct { - resp *Response - code string - line int - ext *yaml.Node - } - var mapped []*responseItem + // map keys correctly. + m := utils.CreateEmptyMapNode() + type responseItem struct { + resp *Response + code string + line int + ext *yaml.Node + } + var mapped []*responseItem - for k, re := range r.Codes { - ln := 9999 // default to a high value to weight new content to the bottom. - if r.low != nil { - for lKey := range r.low.Codes { - if lKey.Value == k { - ln = lKey.KeyNode.Line - } - } - } - mapped = append(mapped, &responseItem{re, k, ln, nil}) - } + for k, re := range r.Codes { + ln := 9999 // default to a high value to weight new content to the bottom. + if r.low != nil { + for lKey := range r.low.Codes { + if lKey.Value == k { + ln = lKey.KeyNode.Line + } + } + } + mapped = append(mapped, &responseItem{re, k, ln, nil}) + } - // extract extensions - nb := high.NewNodeBuilder(r, r.low) - extNode := nb.Render() - if extNode != nil && extNode.Content != nil { - var label string - for u := range extNode.Content { - if u%2 == 0 { - label = extNode.Content[u].Value - continue - } - mapped = append(mapped, &responseItem{nil, label, - extNode.Content[u].Line, extNode.Content[u]}) - } - } + // extract extensions + nb := high.NewNodeBuilder(r, r.low) + extNode := nb.Render() + if extNode != nil && extNode.Content != nil { + var label string + for u := range extNode.Content { + if u%2 == 0 { + label = extNode.Content[u].Value + continue + } + mapped = append(mapped, &responseItem{nil, label, + extNode.Content[u].Line, extNode.Content[u]}) + } + } - sort.Slice(mapped, func(i, j int) bool { - return mapped[i].line < mapped[j].line - }) - for j := range mapped { - if mapped[j].resp != nil { - rendered, _ := mapped[j].resp.MarshalYAML() - m.Content = append(m.Content, utils.CreateStringNode(mapped[j].code)) - m.Content = append(m.Content, rendered.(*yaml.Node)) - } - if mapped[j].ext != nil { - m.Content = append(m.Content, utils.CreateStringNode(mapped[j].code)) - m.Content = append(m.Content, mapped[j].ext) - } + sort.Slice(mapped, func(i, j int) bool { + return mapped[i].line < mapped[j].line + }) + for j := range mapped { + if mapped[j].resp != nil { + rendered, _ := mapped[j].resp.MarshalYAML() + m.Content = append(m.Content, utils.CreateStringNode(mapped[j].code)) + m.Content = append(m.Content, rendered.(*yaml.Node)) + } + if mapped[j].ext != nil { + m.Content = append(m.Content, utils.CreateStringNode(mapped[j].code)) + m.Content = append(m.Content, mapped[j].ext) + } - } - return m, nil + } + return m, nil }