Skip to content

Commit

Permalink
site: api: Add a next_page field as the canonical next page
Browse files Browse the repository at this point in the history
Supply a next_page field which fills in the parameters automatically. This
will let us move away from limit/offset paging if I ever get around to
doing it. Undocument offset as the canonical way is now next_page. The
implementation itself is almost directly lifted from the existing
navigation macro.

Signed-off-by: Sean Anderson <[email protected]>
  • Loading branch information
Forty-Bot committed Feb 19, 2024
1 parent 7aed5b6 commit 927f7a2
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 27 deletions.
35 changes: 20 additions & 15 deletions test/site_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,9 +232,10 @@ def test_api_logs(client):
def get(**params):
resp = client.get("api/v1/logs", query_string=params)
assert resp.status_code == 200
return resp.json['logs']
resp = resp.json
return resp['logs'], resp['next_page']

logs = get()
logs, next_page = get()
valid_keys = {
'demoid',
'duplicate_of',
Expand Down Expand Up @@ -280,22 +281,26 @@ def get(**params):
updated_pivot = max(updated_pivot, log['updated'])

assert logs == sorted(logs, key=lambda log: log['logid'], reverse=True)
assert next_page is None

def paged(limit=10):
off = 0
while True:
logs = get(limit=limit, offset=off)
logs, next_page = get(limit=limit, offset=off)
assert len(logs) <= limit
yield from logs

if len(logs) < limit:
assert next_page is None
return

off += limit
assert next_page == f"/api/v1/logs?limit={limit}&offset={off}"

assert logs == list(paged())
assert get(offset=len(logs)) == []
assert get(offset=len(logs)) == ([], None)

for log in get(view='players'):
for log in get(view='players')[0]:
assert set(log.keys()) == valid_keys | { 'red', 'blue' }
for team in ('red', 'blue'):
team = log[team]
Expand All @@ -315,32 +320,32 @@ def paged(limit=10):
for by in ('logid', 'date', 'duration'):
key = lambda log: log['time' if by == 'date' else by]
for reverse in (True, False):
logs = get(sort=by, sort_dir='desc' if reverse else 'asc')
logs, _ = get(sort=by, sort_dir='desc' if reverse else 'asc')
assert logs == sorted(logs, key=key, reverse=reverse)

for log in get(league="etf2l"):
for log in get(league="etf2l")[0]:
assert log['league'] == "etf2l"

for log in get(format="sixes"):
for log in get(format="sixes")[0]:
assert log['format'] == "sixes"

for log in get(title="serveme"):
for log in get(title="serveme")[0]:
assert "serveme" in log['title']

for log in get(map="cp"):
for log in get(map="cp")[0]:
assert "cp" in log['map']

for log in get(date_from='2019-11-06', timezone='America/New_York'):
for log in get(date_from='2019-11-06', timezone='America/New_York')[0]:
assert log['time'] >= 1573016400

for log in get(date_to='2019-11-06', timezone='America/New_York'):
for log in get(date_to='2019-11-06', timezone='America/New_York')[0]:
assert log['time'] <= 1573016400

for log in get(time_from=1573016400):
for log in get(time_from=1573016400)[0]:
assert log['time'] >= 1573016400

for log in get(time_to=1573016400):
for log in get(time_to=1573016400)[0]:
assert log['time'] <= 1573016400

for log in get(updated_since=updated_pivot):
for log in get(updated_since=updated_pivot)[0]:
assert log['updated'] > updated_pivot
13 changes: 12 additions & 1 deletion trends/site/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,23 @@ def do_cache(resp):
resp.cache_control.max_age = 300
return resp

def next_page(rows):
args = flask.request.args.copy()
args.update(flask.request.view_args)

limit, offset = flask.g.page
args['limit'] = limit
if len(rows) == limit:
args['offset'] = offset + limit
return flask.url_for(flask.request.endpoint, **args)

@api.route('/logs')
def logs():
if resp := logs_last_modified():
return resp
view = flask.request.args.get('view', 'basic', str)
return flask.jsonify(logs=[dict(log) for log in get_logs(view)])
logs = [dict(log) for log in get_logs(view)]
return flask.jsonify(logs=logs, next_page=next_page(logs))

@api.route('/maps')
def maps():
Expand Down
20 changes: 9 additions & 11 deletions trends/site/templates/api.html
Original file line number Diff line number Diff line change
Expand Up @@ -171,15 +171,6 @@ <h2 id="params">Parameters</h2>
control-point logs</a>.
</p>

{{ paramdef("offset") }}
<p>
Skip results equal to the value of this parameter. For example, supplying
{{ pre("limit=10") }} and {{ pre("offset=20") }} to the {{ apiref('logs') }} endpoint would
<a href="{{ url_for('api.logs', limit=10, offset=20) }}">return 10 matches starting from the
21<sup>st</sup> match</a>. This may be used to request successive pages from an endpoint.
The default offset is 0.
</p>

{{ paramdef("sort") }}
<p>
This parameter specifies the key to use when sorting the output. The valid keys vary between
Expand Down Expand Up @@ -284,6 +275,13 @@ <h2 id="types">Members</h2>
assigned by their respective {{ fieldref('league') }}s.
</p>

{{ fielddef('next_page') }}
<p>
A relative URL to the next page of results. If the number of returned results is equal
to the {{ paramref('limit') }}, then this field may be non-{{ pre("null") }} even if the
next page is empty.
</p>

{{ fielddef('steamid64') }}
<p>
A unique number identifying a player. This is assigned by
Expand Down Expand Up @@ -323,7 +321,6 @@ <h4>Parameters</h4>
<li>{{ paramref("league") }}</li>
<li>{{ paramref("limit") }}</li>
<li>{{ paramref("map") }}</li>
<li>{{ paramref("offset") }}</li>
<li>
{{ paramref("sort") }}, with valid values being:
<dl>
Expand Down Expand Up @@ -431,7 +428,8 @@ <h4>Response</h4>
</dd>
</dl>
</p>
</dd>
<dt>{{ pre('next_page') }} : option(string)<dt>
<dd>The {{ fieldref('next_page') }} of logs, if any</dd>
</dl>
</div>
{% endblock %}

0 comments on commit 927f7a2

Please sign in to comment.