Skip to content

Commit

Permalink
Response binary format support
Browse files Browse the repository at this point in the history
  • Loading branch information
p1c2u committed Nov 1, 2023
1 parent ffb17a7 commit 9925e1b
Show file tree
Hide file tree
Showing 19 changed files with 67 additions and 71 deletions.
8 changes: 4 additions & 4 deletions openapi_core/contrib/aiohttp/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ def __init__(self, response: web.Response):
self.response = response

@property
def data(self) -> str:
def data(self) -> bytes:
if self.response.body is None:
return ""
return b""
if isinstance(self.response.body, bytes):
return self.response.body.decode("utf-8")
return self.response.body
assert isinstance(self.response.body, str)
return self.response.body
return self.response.body.encode("utf-8")

@property
def status_code(self) -> int:
Expand Down
16 changes: 12 additions & 4 deletions openapi_core/contrib/django/responses.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
"""OpenAPI core contrib django responses module"""
from itertools import tee

from django.http.response import HttpResponse
from django.http.response import StreamingHttpResponse
from werkzeug.datastructures import Headers


class DjangoOpenAPIResponse:
def __init__(self, response: HttpResponse):
if not isinstance(response, HttpResponse):
if not isinstance(response, (HttpResponse, StreamingHttpResponse)):
raise TypeError(
f"'response' argument is not type of {HttpResponse}"
f"'response' argument is not type of {HttpResponse} or {StreamingHttpResponse}"
)
self.response = response

@property
def data(self) -> str:
def data(self) -> bytes:
if isinstance(self.response, StreamingHttpResponse):
resp_iter1, resp_iter2 = tee(self.response._iterator)
self.response.streaming_content = resp_iter1
content = b"".join(map(self.response.make_bytes, resp_iter2))
return content
assert isinstance(self.response.content, bytes)
return self.response.content.decode("utf-8")
return self.response.content

@property
def status_code(self) -> int:
Expand Down
6 changes: 3 additions & 3 deletions openapi_core/contrib/falcon/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ def __init__(self, response: Response):
self.response = response

@property
def data(self) -> str:
def data(self) -> bytes:
if self.response.text is None:
return ""
return b""
assert isinstance(self.response.text, str)
return self.response.text
return self.response.text.encode("utf-8")

@property
def status_code(self) -> int:
Expand Down
4 changes: 2 additions & 2 deletions openapi_core/contrib/requests/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ def __init__(self, response: Response):
self.response = response

@property
def data(self) -> str:
def data(self) -> bytes:
assert isinstance(self.response.content, bytes)
return self.response.content.decode("utf-8")
return self.response.content

@property
def status_code(self) -> int:
Expand Down
6 changes: 3 additions & 3 deletions openapi_core/contrib/starlette/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ def __init__(self, response: Response):
self.response = response

@property
def data(self) -> str:
def data(self) -> bytes:
if isinstance(self.response.body, bytes):
return self.response.body.decode("utf-8")
return self.response.body
assert isinstance(self.response.body, str)
return self.response.body
return self.response.body.encode("utf-8")

@property
def status_code(self) -> int:
Expand Down
4 changes: 2 additions & 2 deletions openapi_core/contrib/werkzeug/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ def __init__(self, response: Response):
self.response = response

@property
def data(self) -> str:
return self.response.get_data(as_text=True)
def data(self) -> bytes:
return self.response.get_data(as_text=False)

@property
def status_code(self) -> int:
Expand Down
2 changes: 1 addition & 1 deletion openapi_core/protocols.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ class Response(Protocol):
"""

@property
def data(self) -> str:
def data(self) -> Optional[bytes]:
...

@property
Expand Down
2 changes: 1 addition & 1 deletion openapi_core/testing/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
class MockResponse:
def __init__(
self,
data: str,
data: bytes,
status_code: int = 200,
headers: Optional[Dict[str, Any]] = None,
content_type: str = "application/json",
Expand Down
4 changes: 0 additions & 4 deletions tests/integration/contrib/aiohttp/test_aiohttp_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,6 @@ def api_key_encoded(self):


class TestPetPhotoView(BaseTestPetstore):
@pytest.mark.xfail(
reason="response binary format not supported",
strict=True,
)
async def test_get_valid(self, client, data_gif):
headers = {
"Authorization": "Basic testuser",
Expand Down
4 changes: 0 additions & 4 deletions tests/integration/contrib/django/test_django_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,10 +398,6 @@ def test_get_skip_response_validation(self, client):


class TestPetPhotoView(BaseTestDjangoProject):
@pytest.mark.xfail(
reason="response binary format not supported",
strict=True,
)
def test_get_valid(self, client, data_gif):
headers = {
"HTTP_AUTHORIZATION": "Basic testuser",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,6 @@ def request_unmarshaller(self, spec):
def response_unmarshaller(self, spec):
return V30ResponseUnmarshaller(spec)

@pytest.mark.xfail(
reason="response binary format not supported",
strict=True,
)
@responses.activate
def test_response_binary_valid(self, response_unmarshaller, data_gif):
responses.add(
Expand Down
28 changes: 14 additions & 14 deletions tests/integration/test_petstore.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def test_get_pets(self, spec):
data_json = {
"data": [],
}
data = json.dumps(data_json)
data = json.dumps(data_json).encode()
headers = {
"Content-Type": "application/json",
"x-next": "next-url",
Expand Down Expand Up @@ -185,7 +185,7 @@ def test_get_pets_response(self, spec):
}
],
}
data = json.dumps(data_json)
data = json.dumps(data_json).encode()
response = MockResponse(data)

response_result = unmarshal_response(request, response, spec=spec)
Expand Down Expand Up @@ -286,7 +286,7 @@ def test_get_pets_invalid_response(self, spec, response_unmarshaller):
}
],
}
response_data = json.dumps(response_data_json)
response_data = json.dumps(response_data_json).encode()
response = MockResponse(response_data)

with pytest.raises(InvalidData) as exc_info:
Expand Down Expand Up @@ -349,7 +349,7 @@ def test_get_pets_ids_param(self, spec):
data_json = {
"data": [],
}
data = json.dumps(data_json)
data = json.dumps(data_json).encode()
response = MockResponse(data)

response_result = unmarshal_response(request, response, spec=spec)
Expand Down Expand Up @@ -398,7 +398,7 @@ def test_get_pets_tags_param(self, spec):
data_json = {
"data": [],
}
data = json.dumps(data_json)
data = json.dumps(data_json).encode()
response = MockResponse(data)

response_result = unmarshal_response(request, response, spec=spec)
Expand Down Expand Up @@ -1267,7 +1267,7 @@ def test_post_pets_raises_invalid_server_error(self, spec):
},
},
}
data = json.dumps(data_json)
data = json.dumps(data_json).encode()
response = MockResponse(data)

with pytest.raises(ServerNotFound):
Expand Down Expand Up @@ -1362,7 +1362,7 @@ def test_get_pet(self, spec):
},
},
}
data = json.dumps(data_json)
data = json.dumps(data_json).encode()
response = MockResponse(data)

response_result = unmarshal_response(request, response, spec=spec)
Expand Down Expand Up @@ -1413,7 +1413,7 @@ def test_get_pet_not_found(self, spec):
"message": message,
"rootCause": rootCause,
}
data = json.dumps(data_json)
data = json.dumps(data_json).encode()
response = MockResponse(data, status_code=404)

response_result = unmarshal_response(request, response, spec=spec)
Expand Down Expand Up @@ -1492,7 +1492,7 @@ def test_get_tags(self, spec):
assert result.body is None

data_json = ["cats", "birds"]
data = json.dumps(data_json)
data = json.dumps(data_json).encode()
response = MockResponse(data)

response_result = unmarshal_response(request, response, spec=spec)
Expand Down Expand Up @@ -1637,7 +1637,7 @@ def test_post_tags_additional_properties(self, spec):
"rootCause": rootCause,
"additionalinfo": additionalinfo,
}
data = json.dumps(data_json)
data = json.dumps(data_json).encode()
response = MockResponse(data, status_code=404)

response_result = unmarshal_response(request, response, spec=spec)
Expand Down Expand Up @@ -1694,7 +1694,7 @@ def test_post_tags_created_now(self, spec):
"rootCause": "Tag already exist",
"additionalinfo": "Tag Dog already exist",
}
data = json.dumps(data_json)
data = json.dumps(data_json).encode()
response = MockResponse(data, status_code=404)

response_result = unmarshal_response(request, response, spec=spec)
Expand Down Expand Up @@ -1753,7 +1753,7 @@ def test_post_tags_created_datetime(self, spec):
"rootCause": rootCause,
"additionalinfo": additionalinfo,
}
response_data = json.dumps(response_data_json)
response_data = json.dumps(response_data_json).encode()
response = MockResponse(response_data, status_code=404)

result = unmarshal_response(
Expand Down Expand Up @@ -1827,7 +1827,7 @@ def test_post_tags_urlencoded(self, spec):
"rootCause": rootCause,
"additionalinfo": additionalinfo,
}
response_data = json.dumps(response_data_json)
response_data = json.dumps(response_data_json).encode()
response = MockResponse(response_data, status_code=404)

result = unmarshal_response(
Expand Down Expand Up @@ -1898,7 +1898,7 @@ def test_post_tags_created_invalid_type(self, spec):
"rootCause": rootCause,
"additionalinfo": additionalinfo,
}
data = json.dumps(data_json)
data = json.dumps(data_json).encode()
response = MockResponse(data, status_code=404)

response_result = unmarshal_response(request, response, spec=spec)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def test_read_only_property_response(self, response_unmarshaller):
"id": 10,
"name": "Pedro",
}
)
).encode()

request = MockRequest(host_url="", method="POST", path="/users")

Expand Down
18 changes: 9 additions & 9 deletions tests/integration/unmarshalling/test_response_unmarshaller.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def response_unmarshaller(self, spec):

def test_invalid_server(self, response_unmarshaller):
request = MockRequest("http://petstore.invalid.net/v1", "get", "/")
response = MockResponse("Not Found", status_code=404)
response = MockResponse(b"Not Found", status_code=404)

result = response_unmarshaller.unmarshal(request, response)

Expand All @@ -50,7 +50,7 @@ def test_invalid_server(self, response_unmarshaller):

def test_invalid_operation(self, response_unmarshaller):
request = MockRequest(self.host_url, "patch", "/v1/pets")
response = MockResponse("Not Found", status_code=404)
response = MockResponse(b"Not Found", status_code=404)

result = response_unmarshaller.unmarshal(request, response)

Expand All @@ -61,7 +61,7 @@ def test_invalid_operation(self, response_unmarshaller):

def test_invalid_response(self, response_unmarshaller):
request = MockRequest(self.host_url, "get", "/v1/pets")
response = MockResponse("Not Found", status_code=409)
response = MockResponse(b"Not Found", status_code=409)

result = response_unmarshaller.unmarshal(request, response)

Expand All @@ -72,7 +72,7 @@ def test_invalid_response(self, response_unmarshaller):

def test_invalid_content_type(self, response_unmarshaller):
request = MockRequest(self.host_url, "get", "/v1/pets")
response = MockResponse("Not Found", content_type="text/csv")
response = MockResponse(b"Not Found", content_type="text/csv")

result = response_unmarshaller.unmarshal(request, response)

Expand All @@ -93,20 +93,20 @@ def test_missing_body(self, response_unmarshaller):

def test_invalid_media_type(self, response_unmarshaller):
request = MockRequest(self.host_url, "get", "/v1/pets")
response = MockResponse("abcde")
response = MockResponse(b"abcde")

result = response_unmarshaller.unmarshal(request, response)

assert result.errors == [DataValidationError()]
assert result.errors[0].__cause__ == MediaTypeDeserializeError(
mimetype="application/json", value="abcde"
mimetype="application/json", value=b"abcde"
)
assert result.data is None
assert result.headers == {}

def test_invalid_media_type_value(self, response_unmarshaller):
request = MockRequest(self.host_url, "get", "/v1/pets")
response = MockResponse("{}")
response = MockResponse(b"{}")

result = response_unmarshaller.unmarshal(request, response)

Expand Down Expand Up @@ -154,7 +154,7 @@ def test_invalid_header(self, response_unmarshaller):
},
],
}
response_data = json.dumps(response_json)
response_data = json.dumps(response_json).encode()
headers = {
"x-delete-confirm": "true",
"x-delete-date": "today",
Expand All @@ -181,7 +181,7 @@ def test_get_pets(self, response_unmarshaller):
},
],
}
response_data = json.dumps(response_json)
response_data = json.dumps(response_json).encode()
response = MockResponse(response_data)

result = response_unmarshaller.unmarshal(request, response)
Expand Down
Loading

0 comments on commit 9925e1b

Please sign in to comment.