forked from ebhomengo/niki
139 lines
3.8 KiB
Go
139 lines
3.8 KiB
Go
package spec
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
const ExtensionPrefix = "x-"
|
|
|
|
// Extendable allows extensions to the OpenAPI Schema.
|
|
// The field name MUST begin with `x-`, for example, `x-internal-id`.
|
|
// Field names beginning `x-oai-` and `x-oas-` are reserved for uses defined by the OpenAPI Initiative.
|
|
// The value can be null, a primitive, an array or an object.
|
|
//
|
|
// https://spec.openapis.org/oas/v3.1.0#specification-extensions
|
|
//
|
|
// Example:
|
|
//
|
|
// openapi: 3.1.0
|
|
// info:
|
|
// title: Sample Pet Store App
|
|
// summary: A pet store manager.
|
|
// description: This is a sample server for a pet store.
|
|
// version: 1.0.1
|
|
// x-build-data: 2006-01-02T15:04:05Z07:00
|
|
// x-build-commit-id: dac33af14d0d4a5f1c226141042ca7cefc6aeb75
|
|
type Extendable[T any] struct {
|
|
Spec *T `json:"-" yaml:"-"`
|
|
Extensions map[string]any `json:"-" yaml:"-"`
|
|
}
|
|
|
|
// NewExtendable creates new Extendable object for given spec
|
|
func NewExtendable[T any](spec *T) *Extendable[T] {
|
|
ext := Extendable[T]{
|
|
Spec: spec,
|
|
Extensions: make(map[string]any),
|
|
}
|
|
return &ext
|
|
}
|
|
|
|
// MarshalJSON implements json.Marshaler interface.
|
|
func (o *Extendable[T]) MarshalJSON() ([]byte, error) {
|
|
var raw map[string]json.RawMessage
|
|
exts, err := json.Marshal(&o.Extensions)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%T.Extensions: %w", o.Spec, err)
|
|
}
|
|
if err := json.Unmarshal(exts, &raw); err != nil {
|
|
return nil, fmt.Errorf("%T(raw extensions): %w", o.Spec, err)
|
|
}
|
|
fields, err := json.Marshal(&o.Spec)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%T: %w", o.Spec, err)
|
|
}
|
|
if err := json.Unmarshal(fields, &raw); err != nil {
|
|
return nil, fmt.Errorf("%T(raw fields): %w", o.Spec, err)
|
|
}
|
|
data, err := json.Marshal(&raw)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%T(raw): %w", o.Spec, err)
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
// UnmarshalJSON implements json.Unmarshaler interface.
|
|
func (o *Extendable[T]) UnmarshalJSON(data []byte) error {
|
|
var raw map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &raw); err != nil {
|
|
return fmt.Errorf("%T: %w", o.Spec, err)
|
|
}
|
|
o.Extensions = make(map[string]any)
|
|
for name, value := range raw {
|
|
if strings.HasPrefix(name, ExtensionPrefix) {
|
|
var v any
|
|
if err := json.Unmarshal(value, &v); err != nil {
|
|
return fmt.Errorf("%T.Extensions.%s: %w", o.Spec, name, err)
|
|
}
|
|
o.Extensions[name] = v
|
|
delete(raw, name)
|
|
}
|
|
}
|
|
fields, err := json.Marshal(&raw)
|
|
if err != nil {
|
|
return fmt.Errorf("%T(raw): %w", o.Spec, err)
|
|
}
|
|
if err := json.Unmarshal(fields, &o.Spec); err != nil {
|
|
return fmt.Errorf("%T: %w", o.Spec, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// MarshalYAML implements yaml.Marshaler interface.
|
|
func (o *Extendable[T]) MarshalYAML() (any, error) {
|
|
var raw map[string]any
|
|
exts, err := yaml.Marshal(&o.Extensions)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%T.Extensions: %w", o.Spec, err)
|
|
}
|
|
if err := yaml.Unmarshal(exts, &raw); err != nil {
|
|
return nil, fmt.Errorf("%T(raw extensions): %w", o.Spec, err)
|
|
}
|
|
fields, err := yaml.Marshal(&o.Spec)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%T: %w", o.Spec, err)
|
|
}
|
|
if err := yaml.Unmarshal(fields, &raw); err != nil {
|
|
return nil, fmt.Errorf("%T(raw fields): %w", o.Spec, err)
|
|
}
|
|
return raw, nil
|
|
}
|
|
|
|
// UnmarshalYAML implements yaml.Unmarshaler interface.
|
|
func (o *Extendable[T]) UnmarshalYAML(node *yaml.Node) error {
|
|
var raw map[string]any
|
|
if err := node.Decode(&raw); err != nil {
|
|
return fmt.Errorf("%T: %w", o.Spec, err)
|
|
}
|
|
o.Extensions = make(map[string]any)
|
|
for name, value := range raw {
|
|
if strings.HasPrefix(name, ExtensionPrefix) {
|
|
o.Extensions[name] = value
|
|
delete(raw, name)
|
|
}
|
|
}
|
|
fields, err := yaml.Marshal(&raw)
|
|
if err != nil {
|
|
return fmt.Errorf("%T(raw): %w", o.Spec, err)
|
|
}
|
|
if err := yaml.Unmarshal(fields, &o.Spec); err != nil {
|
|
return fmt.Errorf("%T: %w", o.Spec, err)
|
|
}
|
|
|
|
return nil
|
|
}
|