Skip to content

Commit dcad71f

Browse files
fix: return 400 instead of 500 for malformed timeperiods in /api/0/query/
Previously, a malformed timeperiod (non-ISO8601 string, or missing slash separator) caused an uncaught iso8601.ParseError or IndexError, surfacing as HTTP 500 Internal Server Error. Now catches both cases and raises QueryException, which the endpoint already maps to HTTP 400 Bad Request. Adds three regression tests for: - Non-ISO8601 timeperiod string - Single datetime (no slash separator) - Valid timeperiod (asserts existing path still works)
1 parent 26be7f2 commit dcad71f

2 files changed

Lines changed: 53 additions & 2 deletions

File tree

aw_server/api.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from aw_core.log import get_log_file_path
1818
from aw_core.models import Event
1919
from aw_query import query2
20+
from aw_query.exceptions import QueryException
2021
from aw_transform import heartbeat_merge
2122

2223
from .__about__ import __version__
@@ -342,8 +343,18 @@ def query2(self, name, query, timeperiods, cache):
342343
period = timeperiod.split("/")[
343344
:2
344345
] # iso8601 timeperiods are separated by a slash
345-
starttime = iso8601.parse_date(period[0])
346-
endtime = iso8601.parse_date(period[1])
346+
if len(period) != 2:
347+
raise QueryException(
348+
f"Invalid timeperiod '{timeperiod}': expected two ISO8601 "
349+
"datetimes separated by a slash (start/end)"
350+
)
351+
try:
352+
starttime = iso8601.parse_date(period[0])
353+
endtime = iso8601.parse_date(period[1])
354+
except iso8601.ParseError as e:
355+
raise QueryException(
356+
f"Invalid timeperiod '{timeperiod}': {e}"
357+
)
347358
query = "".join(query)
348359
result.append(query2.query(name, query, starttime, endtime, self.db))
349360
return result

tests/test_server.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,43 @@ def get_events():
8989

9090

9191
# TODO: Add benchmark for basic AFK-filtering query
92+
93+
94+
def test_query_invalid_timeperiod(flask_client):
95+
"""Malformed timeperiods must yield 400 (client error), not 500.
96+
97+
Regression test: a non-ISO8601 timeperiod previously raised an uncaught
98+
iso8601.ParseError, surfacing as an Internal Server Error.
99+
"""
100+
r = flask_client.post(
101+
"/api/0/query/",
102+
json={"query": ["RETURN = 1;"], "timeperiods": ["not-a-valid-period"]},
103+
)
104+
assert r.status_code == 400
105+
assert "not-a-valid-period" in r.json["message"]
106+
107+
108+
def test_query_timeperiod_missing_slash(flask_client):
109+
"""A timeperiod without a start/end slash separator must yield 400, not 500.
110+
111+
Regression test: a single ISO8601 datetime (no slash) previously raised an
112+
uncaught IndexError when indexing the split result.
113+
"""
114+
r = flask_client.post(
115+
"/api/0/query/",
116+
json={"query": ["RETURN = 1;"], "timeperiods": ["2024-01-01T00:00:00+00:00"]},
117+
)
118+
assert r.status_code == 400
119+
120+
121+
def test_query_valid_timeperiod(flask_client):
122+
"""A well-formed query with a valid timeperiod still succeeds."""
123+
r = flask_client.post(
124+
"/api/0/query/",
125+
json={
126+
"query": ["RETURN = 1;"],
127+
"timeperiods": ["2024-01-01T00:00:00+00:00/2024-01-02T00:00:00+00:00"],
128+
},
129+
)
130+
assert r.status_code == 200
131+
assert r.json == [1]

0 commit comments

Comments
 (0)