Skip to content

Commit 1a05e7d

Browse files
committed
Responses and headers objects
1 parent 3fe1e6e commit 1a05e7d

File tree

5 files changed

+171
-8
lines changed

5 files changed

+171
-8
lines changed

openapi_core/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,9 @@ class InvalidContentType(OpenAPIBodyError):
6969
pass
7070

7171

72+
class InvalidResponse(OpenAPIMappingError):
73+
pass
74+
75+
7276
class InvalidValue(OpenAPIMappingError):
7377
pass

openapi_core/operations.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55

66
from six import iteritems
77

8+
from openapi_core.exceptions import InvalidResponse
89
from openapi_core.parameters import ParametersGenerator
910
from openapi_core.request_bodies import RequestBodyFactory
11+
from openapi_core.responses import ResponsesGenerator
1012

1113
log = logging.getLogger(__name__)
1214

@@ -15,10 +17,11 @@ class Operation(object):
1517
"""Represents an OpenAPI Operation."""
1618

1719
def __init__(
18-
self, http_method, path_name, parameters, request_body=None,
19-
deprecated=False, operation_id=None):
20+
self, http_method, path_name, responses, parameters,
21+
request_body=None, deprecated=False, operation_id=None):
2022
self.http_method = http_method
2123
self.path_name = path_name
24+
self.responses = dict(responses)
2225
self.parameters = dict(parameters)
2326
self.request_body = request_body
2427
self.deprecated = deprecated
@@ -27,6 +30,16 @@ def __init__(
2730
def __getitem__(self, name):
2831
return self.parameters[name]
2932

33+
def get_response(self, http_status='default'):
34+
try:
35+
return self.responses[http_status]
36+
except KeyError:
37+
if 'default' not in self.responses:
38+
raise InvalidResponse(
39+
"Unknown response http status {0}".format(http_status))
40+
41+
return self.responses['default']
42+
3043

3144
class OperationsGenerator(object):
3245
"""Represents an OpenAPI Operation in a service."""
@@ -42,9 +55,12 @@ def generate(self, path_name, path):
4255
continue
4356

4457
operation_deref = self.dereferencer.dereference(operation)
58+
responses_spec = operation_deref['responses']
59+
responses = self.responses_generator.generate(responses_spec)
4560
deprecated = operation_deref.get('deprecated', False)
4661
parameters_list = operation_deref.get('parameters', [])
47-
parameters = self.parameters_generator.generate(parameters_list)
62+
parameters = self.parameters_generator.generate_from_list(
63+
parameters_list)
4864

4965
request_body = None
5066
if 'requestBody' in operation_deref:
@@ -55,11 +71,16 @@ def generate(self, path_name, path):
5571
yield (
5672
http_method,
5773
Operation(
58-
http_method, path_name, list(parameters),
74+
http_method, path_name, responses, list(parameters),
5975
request_body=request_body, deprecated=deprecated,
6076
),
6177
)
6278

79+
@property
80+
@lru_cache()
81+
def responses_generator(self):
82+
return ResponsesGenerator(self.dereferencer, self.schemas_registry)
83+
6384
@property
6485
@lru_cache()
6586
def parameters_generator(self):

openapi_core/parameters.py

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import logging
33
import warnings
44

5+
from six import iteritems
6+
57
from openapi_core.exceptions import (
68
EmptyValue, InvalidValueType, InvalidParameterValue,
79
)
@@ -54,10 +56,36 @@ def __init__(self, dereferencer, schemas_registry):
5456
self.dereferencer = dereferencer
5557
self.schemas_registry = schemas_registry
5658

57-
def generate(self, paramters):
58-
for parameter in paramters:
59+
def generate(self, parameters):
60+
for parameter_name, parameter in iteritems(parameters):
61+
parameter_deref = self.dereferencer.dereference(parameter)
62+
63+
parameter_in = parameter_deref.get('in', 'header')
64+
65+
allow_empty_value = parameter_deref.get('allowEmptyValue')
66+
required = parameter_deref.get('required', False)
67+
68+
schema_spec = parameter_deref.get('schema', None)
69+
schema = None
70+
if schema_spec:
71+
schema, _ = self.schemas_registry.get_or_create(schema_spec)
72+
73+
yield (
74+
parameter_name,
75+
Parameter(
76+
parameter_name, parameter_in,
77+
schema=schema, required=required,
78+
allow_empty_value=allow_empty_value,
79+
),
80+
)
81+
82+
def generate_from_list(self, parameters_list):
83+
for parameter in parameters_list:
5984
parameter_deref = self.dereferencer.dereference(parameter)
6085

86+
parameter_name = parameter_deref['name']
87+
parameter_in = parameter_deref.get('in', 'header')
88+
6189
allow_empty_value = parameter_deref.get('allowEmptyValue')
6290
required = parameter_deref.get('required', False)
6391

@@ -67,9 +95,9 @@ def generate(self, paramters):
6795
schema, _ = self.schemas_registry.get_or_create(schema_spec)
6896

6997
yield (
70-
parameter_deref['name'],
98+
parameter_name,
7199
Parameter(
72-
parameter_deref['name'], parameter_deref['in'],
100+
parameter_name, parameter_in,
73101
schema=schema, required=required,
74102
allow_empty_value=allow_empty_value,
75103
),

openapi_core/responses.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""OpenAPI core responses module"""
2+
from functools import lru_cache
3+
4+
from six import iteritems
5+
6+
from openapi_core.media_types import MediaTypeGenerator
7+
from openapi_core.parameters import ParametersGenerator
8+
9+
10+
class Response(object):
11+
12+
def __init__(
13+
self, http_status, description, headers=None, content=None,
14+
links=None):
15+
self.http_status = http_status
16+
self.description = description
17+
self.headers = headers and dict(headers) or {}
18+
self.content = content and dict(content) or {}
19+
self.links = links and dict(links) or {}
20+
21+
22+
class ResponsesGenerator(object):
23+
24+
def __init__(self, dereferencer, schemas_registry):
25+
self.dereferencer = dereferencer
26+
self.schemas_registry = schemas_registry
27+
28+
def generate(self, responses):
29+
for http_status, response in iteritems(responses):
30+
description = response['description']
31+
headers = response.get('headers')
32+
content = response.get('content')
33+
34+
media_types = None
35+
if content:
36+
media_types = self.media_types_generator.generate(content)
37+
38+
parameters = None
39+
if headers:
40+
parameters = self.parameters_generator.generate(headers)
41+
42+
yield http_status, Response(
43+
http_status, description,
44+
content=media_types, headers=parameters)
45+
46+
@property
47+
@lru_cache()
48+
def media_types_generator(self):
49+
return MediaTypeGenerator(self.dereferencer, self.schemas_registry)
50+
51+
@property
52+
@lru_cache()
53+
def parameters_generator(self):
54+
return ParametersGenerator(self.dereferencer, self.schemas_registry)

tests/integration/test_petstore.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
)
1010
from openapi_core.media_types import MediaType
1111
from openapi_core.operations import Operation
12+
from openapi_core.parameters import Parameter
1213
from openapi_core.paths import Path
1314
from openapi_core.request_bodies import RequestBody
15+
from openapi_core.responses import Response
1416
from openapi_core.schemas import Schema
1517
from openapi_core.servers import Server, ServerVariable
1618
from openapi_core.shortcuts import create_spec
@@ -59,6 +61,60 @@ def test_spec(self, spec, spec_dict):
5961
assert operation.http_method == http_method
6062

6163
operation_spec = spec_dict['paths'][path_name][http_method]
64+
65+
responses_spec = operation_spec.get('responses')
66+
67+
for http_status, response in iteritems(operation.responses):
68+
assert type(response) == Response
69+
assert response.http_status == http_status
70+
71+
response_spec = responses_spec[http_status]
72+
description_spec = response_spec['description']
73+
74+
assert response.description == description_spec
75+
76+
for mimetype, media_type in iteritems(response.content):
77+
assert type(media_type) == MediaType
78+
assert media_type.mimetype == mimetype
79+
80+
content_spec = response_spec['content'][mimetype]
81+
schema_spec = content_spec.get('schema')
82+
assert bool(schema_spec) == bool(media_type.schema)
83+
84+
if not schema_spec:
85+
continue
86+
87+
# @todo: test with defererence
88+
if '$ref' in schema_spec:
89+
continue
90+
91+
assert type(media_type.schema) == Schema
92+
assert media_type.schema.type == schema_spec['type']
93+
assert media_type.schema.required == schema_spec.get(
94+
'required', False)
95+
96+
for parameter_name, parameter in iteritems(
97+
response.headers):
98+
assert type(parameter) == Parameter
99+
assert parameter.name == parameter_name
100+
101+
headers_spec = response_spec['headers']
102+
parameter_spec = headers_spec[parameter_name]
103+
schema_spec = parameter_spec.get('schema')
104+
assert bool(schema_spec) == bool(parameter.schema)
105+
106+
if not schema_spec:
107+
continue
108+
109+
# @todo: test with defererence
110+
if '$ref' in schema_spec:
111+
continue
112+
113+
assert type(parameter.schema) == Schema
114+
assert parameter.schema.type == schema_spec['type']
115+
assert parameter.schema.required == schema_spec.get(
116+
'required', False)
117+
62118
request_body_spec = operation_spec.get('requestBody')
63119

64120
assert bool(request_body_spec) == bool(operation.request_body)

0 commit comments

Comments
 (0)