Skip to content

Commit ff40647

Browse files
committed
api errors: determine success by http status, handle filter error
1 parent a1d2909 commit ff40647

File tree

3 files changed

+32
-32
lines changed

3 files changed

+32
-32
lines changed

frontend/src/lib/fetch.ts

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,33 +22,20 @@ export async function fetch(
2222
* Handles JSON content for a Promise returned by fetch, also handling an HTTP
2323
* error status.
2424
*/
25-
async function handleJSON(response: Response): Promise<unknown> {
25+
async function handleJSON(
26+
response: Response,
27+
): Promise<Record<string, unknown>> {
28+
const data: unknown = await response.json().catch(() => null);
2629
if (!response.ok) {
27-
try {
28-
const data: unknown = await response.json();
29-
if (isJsonObject(data)) {
30-
throw new FetchError(
31-
typeof data.error === "string"
32-
? data.error
33-
: "Invalid response: missing error",
34-
);
35-
}
36-
throw new FetchError(response.statusText);
37-
} catch {
38-
throw new FetchError(response.statusText);
39-
}
40-
}
41-
const data: unknown = await response.json();
42-
if (!isJsonObject(data)) {
43-
throw new FetchError("Invalid response: not an object");
44-
}
45-
if (data.success !== true) {
4630
throw new FetchError(
47-
typeof data.error === "string"
31+
isJsonObject(data) && typeof data.error === "string"
4832
? data.error
49-
: "Invalid response: missing error",
33+
: response.statusText,
5034
);
5135
}
36+
if (!isJsonObject(data)) {
37+
throw new FetchError("Invalid response: not a valid JSON object");
38+
}
5239
return data;
5340
}
5441

@@ -79,8 +66,8 @@ export async function fetchJSON(
7966
*/
8067
export async function handleText(response: Response): Promise<string> {
8168
if (!response.ok) {
82-
const msg = await response.text();
83-
throw new Error(msg || response.statusText);
69+
const msg = await response.text().catch(() => response.statusText);
70+
throw new FetchError(msg);
8471
}
8572
return response.text();
8673
}

src/fava/json_api.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from fava.context import g
3131
from fava.core.documents import filepath_in_document_folder
3232
from fava.core.documents import is_document_or_import_file
33+
from fava.core.filters import FilterError
3334
from fava.core.ingest import filepath_in_primary_imports_folder
3435
from fava.core.misc import align
3536
from fava.helpers import FavaAPIError
@@ -81,15 +82,15 @@ def __init__(self, param: str, expected: type) -> None:
8182

8283
def json_err(msg: str, status: HTTPStatus) -> Response:
8384
"""Jsonify the error message."""
84-
res = jsonify({"success": False, "error": msg})
85+
res = jsonify({"error": msg})
8586
res.status = status # type: ignore[assignment]
8687
return res
8788

8889

8990
def json_success(data: Any) -> Response:
9091
"""Jsonify the response."""
9192
return jsonify(
92-
{"success": True, "data": data, "mtime": str(g.ledger.mtime)},
93+
{"data": data, "mtime": str(g.ledger.mtime)},
9394
)
9495

9596

@@ -157,24 +158,29 @@ def __init__(self, filename: str) -> None:
157158

158159

159160
@json_api.errorhandler(FavaAPIError)
160-
def _json_api_fava_api_error(error: FavaAPIError) -> Response:
161+
def _(error: FavaAPIError) -> Response:
161162
log.error("Encountered FavaAPIError.", exc_info=error)
162163
return json_err(error.message, HTTPStatus.INTERNAL_SERVER_ERROR)
163164

164165

165166
@json_api.errorhandler(FavaJSONAPIError)
166-
def _json_api_fava_json_api(error: FavaJSONAPIError) -> Response:
167+
def _(error: FavaJSONAPIError) -> Response:
167168
return json_err(error.message, error.status)
168169

169170

171+
@json_api.errorhandler(FilterError)
172+
def _(error: FilterError) -> Response:
173+
return json_err(error.message, HTTPStatus.BAD_REQUEST)
174+
175+
170176
@json_api.errorhandler(OSError)
171-
def _json_api_oserror(error: OSError) -> Response: # pragma: no cover
177+
def _(error: OSError) -> Response: # pragma: no cover
172178
log.error("Encountered OSError.", exc_info=error)
173179
return json_err(error.strerror, HTTPStatus.INTERNAL_SERVER_ERROR)
174180

175181

176182
@json_api.errorhandler(ValidationError)
177-
def _json_api_validation_error(error: ValidationError) -> Response:
183+
def _(error: ValidationError) -> Response:
178184
return json_err(f"Invalid API request: {error!s}", HTTPStatus.BAD_REQUEST)
179185

180186

tests/test_json_api.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ def assert_api_error(
5252
"""Asserts that the response errored and contains the message."""
5353
assert response.status_code == status.value
5454
assert response.json
55-
assert not response.json["success"], response.json
5655
err_msg = response.json["error"]
5756
assert isinstance(err_msg, str)
5857
if msg:
@@ -64,7 +63,6 @@ def assert_api_success(response: TestResponse, data: Any | None = None) -> Any:
6463
"""Asserts that the request was successful and contains the data."""
6564
assert response.status_code == HTTPStatus.OK.value
6665
assert response.json
67-
assert response.json["success"], response.json
6866
if data is not None:
6967
assert data == response.json["data"]
7068
return response.json["data"]
@@ -654,6 +652,15 @@ def test_api_commodities_empty(
654652
assert not data
655653

656654

655+
def test_api_filter_error(
656+
test_client: FlaskClient,
657+
) -> None:
658+
response = test_client.get(
659+
"/long-example/api/commodities?time=20",
660+
)
661+
assert_api_error(response, status=HTTPStatus.BAD_REQUEST)
662+
663+
657664
@pytest.mark.parametrize(
658665
("name", "url"),
659666
[

0 commit comments

Comments
 (0)