Skip to content

Commit 56338d2

Browse files
authored
Have Header Object follow the structure of the Parameter Object (#355)
1 parent 7be9302 commit 56338d2

File tree

3 files changed

+89
-34
lines changed

3 files changed

+89
-34
lines changed

openapi3/header.go

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package openapi3
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67

78
"github.com/getkin/kin-openapi/jsoninfo"
@@ -24,17 +25,10 @@ func (h Headers) JSONLookup(token string) (interface{}, error) {
2425
return ref.Value, nil
2526
}
2627

28+
// Header is specified by OpenAPI/Swagger 3.0 standard.
29+
// See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#headerObject
2730
type Header struct {
28-
ExtensionProps
29-
30-
// Optional description. Should use CommonMark syntax.
31-
Description string `json:"description,omitempty" yaml:"description,omitempty"`
32-
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
33-
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
34-
Schema *SchemaRef `json:"schema,omitempty" yaml:"schema,omitempty"`
35-
Example interface{} `json:"example,omitempty" yaml:"example,omitempty"`
36-
Examples Examples `json:"examples,omitempty" yaml:"examples,omitempty"`
37-
Content Content `json:"content,omitempty" yaml:"content,omitempty"`
31+
Parameter
3832
}
3933

4034
var _ jsonpointer.JSONPointable = (*Header)(nil)
@@ -43,10 +37,52 @@ func (value *Header) UnmarshalJSON(data []byte) error {
4337
return jsoninfo.UnmarshalStrictStruct(data, value)
4438
}
4539

40+
// SerializationMethod returns a header's serialization method.
41+
func (value *Header) SerializationMethod() (*SerializationMethod, error) {
42+
style := value.Style
43+
if style == "" {
44+
style = SerializationSimple
45+
}
46+
explode := false
47+
if value.Explode != nil {
48+
explode = *value.Explode
49+
}
50+
return &SerializationMethod{Style: style, Explode: explode}, nil
51+
}
52+
4653
func (value *Header) Validate(ctx context.Context) error {
47-
if v := value.Schema; v != nil {
48-
if err := v.Validate(ctx); err != nil {
49-
return err
54+
if value.Name != "" {
55+
return errors.New("header 'name' MUST NOT be specified, it is given in the corresponding headers map")
56+
}
57+
if value.In != "" {
58+
return errors.New("header 'in' MUST NOT be specified, it is implicitly in header")
59+
}
60+
61+
// Validate a parameter's serialization method.
62+
sm, err := value.SerializationMethod()
63+
if err != nil {
64+
return err
65+
}
66+
if smSupported := false ||
67+
sm.Style == SerializationSimple && !sm.Explode ||
68+
sm.Style == SerializationSimple && sm.Explode; !smSupported {
69+
e := fmt.Errorf("serialization method with style=%q and explode=%v is not supported by a header parameter", sm.Style, sm.Explode)
70+
return fmt.Errorf("header schema is invalid: %v", e)
71+
}
72+
73+
if (value.Schema == nil) == (value.Content == nil) {
74+
e := fmt.Errorf("parameter must contain exactly one of content and schema: %v", value)
75+
return fmt.Errorf("header schema is invalid: %v", e)
76+
}
77+
if schema := value.Schema; schema != nil {
78+
if err := schema.Validate(ctx); err != nil {
79+
return fmt.Errorf("header schema is invalid: %v", err)
80+
}
81+
}
82+
83+
if content := value.Content; content != nil {
84+
if err := content.Validate(ctx); err != nil {
85+
return fmt.Errorf("header content is invalid: %v", err)
5086
}
5187
}
5288
return nil
@@ -61,8 +97,20 @@ func (value Header) JSONLookup(token string) (interface{}, error) {
6197
}
6298
return value.Schema.Value, nil
6399
}
100+
case "name":
101+
return value.Name, nil
102+
case "in":
103+
return value.In, nil
64104
case "description":
65105
return value.Description, nil
106+
case "style":
107+
return value.Style, nil
108+
case "explode":
109+
return value.Explode, nil
110+
case "allowEmptyValue":
111+
return value.AllowEmptyValue, nil
112+
case "allowReserved":
113+
return value.AllowReserved, nil
66114
case "deprecated":
67115
return value.Deprecated, nil
68116
case "required":

openapi3/openapi3_test.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ components:
130130
someSchema:
131131
description: Some schema
132132
headers:
133-
otherHeader: {}
133+
otherHeader:
134+
schema: {type: string}
134135
someHeader:
135136
"$ref": "#/components/headers/otherHeader"
136137
examples:
@@ -207,7 +208,11 @@ var specJSON = []byte(`
207208
}
208209
},
209210
"headers": {
210-
"otherHeader": {},
211+
"otherHeader": {
212+
"schema": {
213+
"type": "string"
214+
}
215+
},
211216
"someHeader": {
212217
"$ref": "#/components/headers/otherHeader"
213218
}
@@ -317,7 +322,7 @@ func spec() *T {
317322
Ref: "#/components/headers/otherHeader",
318323
},
319324
"otherHeader": {
320-
Value: &Header{},
325+
Value: &Header{Parameter{Schema: &SchemaRef{Value: NewStringSchema()}}},
321326
},
322327
},
323328
Examples: map[string]*ExampleRef{

openapi3/parameter.go

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ func (value Parameters) Validate(ctx context.Context) error {
8383
}
8484

8585
// Parameter is specified by OpenAPI/Swagger 3.0 standard.
86+
// See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#parameterObject
8687
type Parameter struct {
8788
ExtensionProps
8889
Name string `json:"name,omitempty" yaml:"name,omitempty"`
@@ -167,42 +168,42 @@ func (parameter *Parameter) UnmarshalJSON(data []byte) error {
167168
return jsoninfo.UnmarshalStrictStruct(data, parameter)
168169
}
169170

170-
func (parameter Parameter) JSONLookup(token string) (interface{}, error) {
171+
func (value Parameter) JSONLookup(token string) (interface{}, error) {
171172
switch token {
172173
case "schema":
173-
if parameter.Schema != nil {
174-
if parameter.Schema.Ref != "" {
175-
return &Ref{Ref: parameter.Schema.Ref}, nil
174+
if value.Schema != nil {
175+
if value.Schema.Ref != "" {
176+
return &Ref{Ref: value.Schema.Ref}, nil
176177
}
177-
return parameter.Schema.Value, nil
178+
return value.Schema.Value, nil
178179
}
179180
case "name":
180-
return parameter.Name, nil
181+
return value.Name, nil
181182
case "in":
182-
return parameter.In, nil
183+
return value.In, nil
183184
case "description":
184-
return parameter.Description, nil
185+
return value.Description, nil
185186
case "style":
186-
return parameter.Style, nil
187+
return value.Style, nil
187188
case "explode":
188-
return parameter.Explode, nil
189+
return value.Explode, nil
189190
case "allowEmptyValue":
190-
return parameter.AllowEmptyValue, nil
191+
return value.AllowEmptyValue, nil
191192
case "allowReserved":
192-
return parameter.AllowReserved, nil
193+
return value.AllowReserved, nil
193194
case "deprecated":
194-
return parameter.Deprecated, nil
195+
return value.Deprecated, nil
195196
case "required":
196-
return parameter.Required, nil
197+
return value.Required, nil
197198
case "example":
198-
return parameter.Example, nil
199+
return value.Example, nil
199200
case "examples":
200-
return parameter.Examples, nil
201+
return value.Examples, nil
201202
case "content":
202-
return parameter.Content, nil
203+
return value.Content, nil
203204
}
204205

205-
v, _, err := jsonpointer.GetForToken(parameter.ExtensionProps, token)
206+
v, _, err := jsonpointer.GetForToken(value.ExtensionProps, token)
206207
return v, err
207208
}
208209

@@ -294,6 +295,7 @@ func (value *Parameter) Validate(ctx context.Context) error {
294295
return fmt.Errorf("parameter %q schema is invalid: %v", value.Name, err)
295296
}
296297
}
298+
297299
if content := value.Content; content != nil {
298300
if err := content.Validate(ctx); err != nil {
299301
return fmt.Errorf("parameter %q content is invalid: %v", value.Name, err)

0 commit comments

Comments
 (0)