Skip to content

Commit

Permalink
Check presence of media before attempting to parse it (#296)
Browse files Browse the repository at this point in the history
* Check presence of media before attempting to parse it

In the falcon plugin, if we use `.data` or `.text`, then the plugin still tries to parse our response from media. Even though `.media` is not present. This PR changes the approach of parsing, we only try to parse the response model if either `.data` or `.text` is not set and `.media` is.

* Update pytest snapshot
  • Loading branch information
CucumisSativus committed Mar 26, 2023
1 parent ebdb30d commit 6af3ce7
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 0 deletions.
9 changes: 9 additions & 0 deletions spectree/plugins/falcon_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ def validate(
_resp.media = _resp.media.dict()
skip_validation = True

if self._data_set_manually(_resp):
skip_validation = True

if model and not skip_validation:
try:
model.parse_obj(_resp.media)
Expand All @@ -242,6 +245,9 @@ def validate(

after(_req, _resp, resp_validation_error, _self)

def _data_set_manually(self, resp):
return (resp.text is not None or resp.data is not None) and resp.media is None

def bypass(self, func, method):
if isinstance(func, partial):
return True
Expand Down Expand Up @@ -328,6 +334,9 @@ async def validate(
_resp.media = _resp.media.dict()
skip_validation = True

if self._data_set_manually(_resp):
skip_validation = True

model = resp.find_model(_resp.status[:3])
if model and not skip_validation:
try:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,66 @@
},
"openapi": "3.0.3",
"paths": {
"/api/custom_serializer": {
"get": {
"description": "",
"operationId": "get__api_custom_serializer",
"parameters": [],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Resp.7068f62"
}
}
},
"description": "OK"
},
"422": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ValidationError.6a07bef"
}
}
},
"description": "Unprocessable Entity"
}
},
"summary": "on_get <GET>",
"tags": []
},
"post": {
"description": "",
"operationId": "post__api_custom_serializer",
"parameters": [],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Resp.7068f62"
}
}
},
"description": "OK"
},
"422": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ValidationError.6a07bef"
}
}
},
"description": "Unprocessable Entity"
}
},
"summary": "on_post <POST>",
"tags": []
}
},
"/api/file_upload": {
"post": {
"description": "",
Expand Down
33 changes: 33 additions & 0 deletions tests/test_plugin_falcon.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,22 @@ def on_post(self, req, resp, json: ListJSON):
pass


class ViewWithCustomSerializer:
name = "view with custom serializer"

@api.validate(
resp=Response(HTTP_200=Resp),
)
def on_get(self, req, resp):
resp.data = Resp(name="falcon", score=[1, 2, 3]).json().encode("utf-8")

@api.validate(
resp=Response(HTTP_200=Resp),
)
def on_post(self, req, resp):
resp.text = Resp(name="falcon", score=[1, 2, 3]).json()


app = App()
app.add_route("/ping", Ping())
app.add_route("/api/user/{name}", UserScore())
Expand All @@ -206,6 +222,7 @@ def on_post(self, req, resp, json: ListJSON):
app.add_route("/api/no_response", NoResponseView())
app.add_route("/api/file_upload", FileUploadView())
app.add_route("/api/list_json", ListJsonView())
app.add_route("/api/custom_serializer", ViewWithCustomSerializer())
api.register(app)


Expand Down Expand Up @@ -426,3 +443,19 @@ def test_falcon_file_upload_sync(client):
assert resp.status_code == 200, resp.text
assert resp.headers["content-type"] == "application/json"
assert resp.json["file"] == file_content


def test_falcon_custom_serializer(client):
resp = client.simulate_get(
"/api/custom_serializer",
)
assert resp.status_code == 200
assert resp.json["name"] == "falcon"
assert resp.json["score"] == [1, 2, 3]

resp = client.simulate_post(
"/api/custom_serializer",
)
assert resp.status_code == 200
assert resp.json["name"] == "falcon"
assert resp.json["score"] == [1, 2, 3]
33 changes: 33 additions & 0 deletions tests/test_plugin_falcon_asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,30 @@ async def on_post(self, req, resp, form: FormFileUpload):
resp.media = {"file": file_content.decode("utf-8")}


class ViewWithCustomSerializer:
name = "view with custom serializer"

@api.validate(
resp=Response(HTTP_200=Resp),
)
async def on_get(self, req, resp):
resp.data = Resp(name="falcon", score=[1, 2, 3]).json().encode("utf-8")

@api.validate(
resp=Response(HTTP_200=Resp),
)
async def on_post(self, req, resp):
resp.text = Resp(name="falcon", score=[1, 2, 3]).json()


app = App()
app.add_route("/ping", Ping())
app.add_route("/api/user/{name}", UserScore())
app.add_route("/api/user_annotated/{name}", UserScoreAnnotated())
app.add_route("/api/no_response", NoResponseView())
app.add_route("/api/file_upload", FileUploadView())
app.add_route("/api/list_json", ListJsonView())
app.add_route("/api/custom_serializer", ViewWithCustomSerializer())
api.register(app)


Expand Down Expand Up @@ -335,3 +352,19 @@ def test_falcon_file_upload_async(client):
)
assert resp.status_code == 200, resp.text
assert resp.json["file"] == file_content


def test_falcon_custom_serializer(client):
resp = client.simulate_get(
"/api/custom_serializer",
)
assert resp.status_code == 200
assert resp.json["name"] == "falcon"
assert resp.json["score"] == [1, 2, 3]

resp = client.simulate_post(
"/api/custom_serializer",
)
assert resp.status_code == 200
assert resp.json["name"] == "falcon"
assert resp.json["score"] == [1, 2, 3]

0 comments on commit 6af3ce7

Please sign in to comment.