Skip to content

Commit a86c49d

Browse files
committed
Parameter with required and allow empty value support
1 parent 279ace9 commit a86c49d

File tree

7 files changed

+88
-20
lines changed

7 files changed

+88
-20
lines changed

openapi_core/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,9 @@ class InvalidValue(OpenAPIMappingError):
3333
pass
3434

3535

36+
class EmptyValue(OpenAPIMappingError):
37+
pass
38+
39+
3640
class UndefinedSchemaProperty(OpenAPIMappingError):
3741
pass

openapi_core/parameters.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
"""OpenAPI core parameters module"""
22
import logging
3+
import warnings
4+
5+
from openapi_core.exceptions import EmptyValue
36

47
log = logging.getLogger(__name__)
58

@@ -8,20 +11,32 @@ class Parameter(object):
811
"""Represents an OpenAPI operation Parameter."""
912

1013
def __init__(
11-
self, name, location, schema=None, default=None,
12-
required=False, deprecated=False, allow_empty_value=False,
14+
self, name, location, schema=None, required=False,
15+
deprecated=False, allow_empty_value=False,
1316
items=None, collection_format=None):
1417
self.name = name
1518
self.location = location
1619
self.schema = schema
17-
self.default = default
18-
self.required = required
20+
self.required = True if self.location == "path" else required
1921
self.deprecated = deprecated
20-
self.allow_empty_value = allow_empty_value
22+
self.allow_empty_value = (
23+
allow_empty_value if self.location == "query" else False
24+
)
2125
self.items = items
2226
self.collection_format = collection_format
2327

2428
def unmarshal(self, value):
29+
if self.deprecated:
30+
warnings.warn(
31+
"{0} parameter is deprecated".format(self.name),
32+
DeprecationWarning,
33+
)
34+
35+
if (self.location == "query" and value == "" and
36+
not self.allow_empty_value):
37+
raise EmptyValue(
38+
"Value of {0} parameter cannot be empty.".format(self.name))
39+
2540
if not self.schema:
2641
return value
2742

@@ -38,7 +53,7 @@ def generate(self, paramters):
3853
for parameter in paramters:
3954
parameter_deref = self.dereferencer.dereference(parameter)
4055

41-
default = parameter_deref.get('default')
56+
allow_empty_value = parameter_deref.get('allowEmptyValue')
4257
required = parameter_deref.get('required', False)
4358

4459
schema_spec = parameter_deref.get('schema', None)
@@ -50,6 +65,7 @@ def generate(self, paramters):
5065
parameter_deref['name'],
5166
Parameter(
5267
parameter_deref['name'], parameter_deref['in'],
53-
schema=schema, default=default, required=required,
68+
schema=schema, required=required,
69+
allow_empty_value=allow_empty_value,
5470
),
5571
)

openapi_core/schemas.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ def create(self, schema_spec):
149149
schema_type = schema_deref['type']
150150
model = schema_deref.get('x-model', None)
151151
required = schema_deref.get('required', False)
152+
default = schema_deref.get('default', None)
152153
properties_spec = schema_deref.get('properties', None)
153154
items_spec = schema_deref.get('items', None)
154155
nullable = schema_deref.get('nullable', False)
@@ -165,7 +166,7 @@ def create(self, schema_spec):
165166

166167
return Schema(
167168
schema_type, model=model, properties=properties, items=items,
168-
required=required, nullable=nullable, enum=enum,
169+
required=required, default=default, nullable=nullable, enum=enum,
169170
deprecated=deprecated,
170171
)
171172

openapi_core/wrappers.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,27 +64,30 @@ def create(self, request, spec):
6464
params = RequestParameters()
6565
for param_name, param in iteritems(operation.parameters):
6666
try:
67-
value = self._unmarshal_param(request, param)
67+
raw_value = self._get_raw_value(request, param)
6868
except MissingParameterError:
6969
if param.required:
7070
raise
71-
continue
71+
72+
if not param.schema or not param.schema.default:
73+
continue
74+
raw_value = param.schema.default
75+
76+
value = param.unmarshal(raw_value)
7277

7378
params[param.location][param_name] = value
7479
return params
7580

76-
def _unmarshal_param(self, request, param):
81+
def _get_raw_value(self, request, param):
7782
request_location = self.attr_mapping[param.location]
7883
request_attr = getattr(request, request_location)
7984

8085
try:
81-
raw_value = request_attr[param.name]
86+
return request_attr[param.name]
8287
except KeyError:
8388
raise MissingParameterError(
8489
"Missing required `{0}` parameter".format(param.name))
8590

86-
return param.unmarshal(raw_value)
87-
8891

8992
class RequestBodyFactory(BaseRequestFactory):
9093

tests/integration/data/v3.0/petstore.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ paths:
2020
tags:
2121
- pets
2222
parameters:
23+
- name: page
24+
in: query
25+
schema:
26+
type: integer
27+
format: int32
28+
default: 1
2329
- name: limit
2430
in: query
2531
description: How many items to return at one time (max 100)

tests/integration/test_petstore.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from openapi_core.exceptions import (
66
MissingParameterError, InvalidContentTypeError, InvalidServerError,
77
InvalidValueType, UndefinedSchemaProperty, MissingPropertyError,
8+
EmptyValue,
89
)
910
from openapi_core.media_types import MediaType
1011
from openapi_core.operations import Operation
@@ -135,6 +136,7 @@ def test_get_pets(self, spec):
135136
assert parameters == {
136137
'query': {
137138
'limit': 20,
139+
'page': 1,
138140
'ids': [12, 13],
139141
}
140142
}
@@ -186,14 +188,10 @@ def test_get_pets_empty_value(self, spec):
186188
path_pattern=path_pattern, args=query_params,
187189
)
188190

189-
parameters = request.get_parameters(spec)
191+
with pytest.raises(EmptyValue):
192+
request.get_parameters(spec)
190193
body = request.get_body(spec)
191194

192-
assert parameters == {
193-
'query': {
194-
'limit': None,
195-
}
196-
}
197195
assert body is None
198196

199197
def test_get_pets_none_value(self, spec):
@@ -213,6 +211,7 @@ def test_get_pets_none_value(self, spec):
213211
assert parameters == {
214212
'query': {
215213
'limit': None,
214+
'page': 1,
216215
}
217216
}
218217

tests/unit/test_paramters.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import pytest
2+
3+
from openapi_core.exceptions import EmptyValue
4+
from openapi_core.parameters import Parameter
5+
6+
7+
class TestParameterUnmarshal(object):
8+
9+
def test_deprecated(self):
10+
param = Parameter('param', 'query', deprecated=True)
11+
value = 'test'
12+
13+
with pytest.warns(DeprecationWarning):
14+
result = param.unmarshal(value)
15+
16+
assert result == value
17+
18+
def test_query_valid(self):
19+
param = Parameter('param', 'query')
20+
value = 'test'
21+
22+
result = param.unmarshal(value)
23+
24+
assert result == value
25+
26+
def test_query_empty(self):
27+
param = Parameter('param', 'query')
28+
value = ''
29+
30+
with pytest.raises(EmptyValue):
31+
param.unmarshal(value)
32+
33+
def test_query_allow_empty_value(self):
34+
param = Parameter('param', 'query', allow_empty_value=True)
35+
value = ''
36+
37+
result = param.unmarshal(value)
38+
39+
assert result == value

0 commit comments

Comments
 (0)