package openapi3
import (
"context"
"errors"
"fmt"
"github.com/getkin/kin-openapi/jsoninfo"
)
// Parameters is specified by OpenAPI/Swagger 3.0 standard.
type Parameters []*ParameterRef
func NewParameters() Parameters {
return make(Parameters, 0, 4)
}
func (parameters Parameters) GetByInAndName(in string, name string) *Parameter {
for _, item := range parameters {
if v := item.Value; v != nil {
if v.Name == name && v.In == in {
return v
}
}
}
return nil
}
func (parameters Parameters) Validate(c context.Context) error {
dupes := make(map[string]struct{})
for _, item := range parameters {
if v := item.Value; v != nil {
key := v.In + ":" + v.Name
if _, ok := dupes[key]; ok {
return fmt.Errorf("more than one %q parameter has name %q", v.In, v.Name)
}
dupes[key] = struct{}{}
}
if err := item.Validate(c); err != nil {
return err
}
}
return nil
}
// Parameter is specified by OpenAPI/Swagger 3.0 standard.
type Parameter struct {
ExtensionProps
Name string `json:"name,omitempty" yaml:"name,omitempty"`
In string `json:"in,omitempty" yaml:"in,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Style string `json:"style,omitempty" yaml:"style,omitempty"`
Explode *bool `json:"explode,omitempty" yaml:"explode,omitempty"`
AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"`
AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"`
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
Schema *SchemaRef `json:"schema,omitempty" yaml:"schema,omitempty"`
Example interface{} `json:"example,omitempty" yaml:"example,omitempty"`
Examples map[string]*ExampleRef `json:"examples,omitempty" yaml:"examples,omitempty"`
Content Content `json:"content,omitempty" yaml:"content,omitempty"`
}
const (
ParameterInPath = "path"
ParameterInQuery = "query"
ParameterInHeader = "header"
ParameterInCookie = "cookie"
)
func NewPathParameter(name string) *Parameter {
return &Parameter{
Name: name,
In: ParameterInPath,
Required: true,
}
}
func NewQueryParameter(name string) *Parameter {
return &Parameter{
Name: name,
In: ParameterInQuery,
}
}
func NewHeaderParameter(name string) *Parameter {
return &Parameter{
Name: name,
In: ParameterInHeader,
}
}
func NewCookieParameter(name string) *Parameter {
return &Parameter{
Name: name,
In: ParameterInCookie,
}
}
func (parameter *Parameter) WithDescription(value string) *Parameter {
parameter.Description = value
return parameter
}
func (parameter *Parameter) WithRequired(value bool) *Parameter {
parameter.Required = value
return parameter
}
func (parameter *Parameter) WithSchema(value *Schema) *Parameter {
if value == nil {
parameter.Schema = nil
} else {
parameter.Schema = &SchemaRef{
Value: value,
}
}
return parameter
}
func (parameter *Parameter) MarshalJSON() ([]byte, error) {
return jsoninfo.MarshalStrictStruct(parameter)
}
func (parameter *Parameter) UnmarshalJSON(data []byte) error {
return jsoninfo.UnmarshalStrictStruct(data, parameter)
}
// SerializationMethod returns a parameter's serialization method.
// When a parameter's serialization method is not defined the method returns
// the default serialization method corresponding to a parameter's location.
func (parameter *Parameter) SerializationMethod() (*SerializationMethod, error) {
switch parameter.In {
case ParameterInPath, ParameterInHeader:
style := parameter.Style
if style == "" {
style = SerializationSimple
}
explode := false
if parameter.Explode != nil {
explode = *parameter.Explode
}
return &SerializationMethod{Style: style, Explode: explode}, nil
case ParameterInQuery, ParameterInCookie:
style := parameter.Style
if style == "" {
style = SerializationForm
}
explode := true
if parameter.Explode != nil {
explode = *parameter.Explode
}
return &SerializationMethod{Style: style, Explode: explode}, nil
default:
return nil, fmt.Errorf("unexpected parameter's 'in': %q", parameter.In)
}
}
func (parameter *Parameter) Validate(c context.Context) error {
if parameter.Name == "" {
return errors.New("parameter name can't be blank")
}
in := parameter.In
switch in {
case
ParameterInPath,
ParameterInQuery,
ParameterInHeader,
ParameterInCookie:
default:
return fmt.Errorf("parameter can't have 'in' value %q", parameter.In)
}
// Validate a parameter's serialization method.
sm, err := parameter.SerializationMethod()
if err != nil {
return err
}
var smSupported bool
switch {
case parameter.In == ParameterInPath && sm.Style == SerializationSimple && !sm.Explode,
parameter.In == ParameterInPath && sm.Style == SerializationSimple && sm.Explode,
parameter.In == ParameterInPath && sm.Style == SerializationLabel && !sm.Explode,
parameter.In == ParameterInPath && sm.Style == SerializationLabel && sm.Explode,
parameter.In == ParameterInPath && sm.Style == SerializationMatrix && !sm.Explode,
parameter.In == ParameterInPath && sm.Style == SerializationMatrix && sm.Explode,
parameter.In == ParameterInQuery && sm.Style == SerializationForm && sm.Explode,
parameter.In == ParameterInQuery && sm.Style == SerializationForm && !sm.Explode,
parameter.In == ParameterInQuery && sm.Style == SerializationSpaceDelimited && sm.Explode,
parameter.In == ParameterInQuery && sm.Style == SerializationSpaceDelimited && !sm.Explode,
parameter.In == ParameterInQuery && sm.Style == SerializationPipeDelimited && sm.Explode,
parameter.In == ParameterInQuery && sm.Style == SerializationPipeDelimited && !sm.Explode,
parameter.In == ParameterInQuery && sm.Style == SerializationDeepObject && sm.Explode,
parameter.In == ParameterInHeader && sm.Style == SerializationSimple && !sm.Explode,
parameter.In == ParameterInHeader && sm.Style == SerializationSimple && sm.Explode,
parameter.In == ParameterInCookie && sm.Style == SerializationForm && !sm.Explode,
parameter.In == ParameterInCookie && sm.Style == SerializationForm && sm.Explode:
smSupported = true
}
if !smSupported {
e := fmt.Errorf("serialization method with style=%q and explode=%v is not supported by a %s parameter", sm.Style, sm.Explode, in)
return fmt.Errorf("parameter %q schema is invalid: %v", parameter.Name, e)
}
if (parameter.Schema == nil) == (parameter.Content == nil) {
e := errors.New("parameter must contain exactly one of content and schema")
return fmt.Errorf("parameter %q schema is invalid: %v", parameter.Name, e)
}
if schema := parameter.Schema; schema != nil {
if err := schema.Validate(c); err != nil {
return fmt.Errorf("parameter %q schema is invalid: %v", parameter.Name, err)
}
}
if content := parameter.Content; content != nil {
if err := content.Validate(c); err != nil {
return fmt.Errorf("parameter %q content is invalid: %v", parameter.Name, err)
}
}
return nil
}