diff --git a/doc/20.api.md b/doc/20.api.md index dab560f..f8950af 100644 --- a/doc/20.api.md +++ b/doc/20.api.md @@ -8,3 +8,70 @@ API token (see the doc about deployment for more infos). A helper script is available under `scripts/api_doc.py` to export a documentation of the available API endpoints and usage in the current version of the code. + + +### Pagination + +All the API endpoints support pagination. By default, no pagination is done. +You can force pagination specifying a `page[size]` GET parameter to specify a +number of items per page and a `page[number]` GET parameter to specify the +page to return. Pages are numbered starting from zero. + +For instance, + +``` +> GET /api/v1/reports?page[size]=10&page[number]=0 +``` + +will return the ten first reports of the first page, which are the ten first +reports from the database. + + +### Filtering + +#### Basic filtering + +Filtering is possible in the API through the use of a `filter` GET parameter. + +You can filter on a given field value using the parameter value +`filter[FIELD]=VALUE`. For instance, + +``` +> GET /api/v1/reports?filter[id]=1 +``` + +will return the reports with `id` 1. + +#### Combining filters + +All provided filters must be filled for an item to be returned. That is, if +you want to return all the reports of type `interrupt` with no `upvotes`, you +can use + +``` +> GET /api/v1/reports?filter[type]=interrupt&filter[upvotes]=0 +``` + +If you want to make an `OR` condition, satisfying either one of the filters, +you should make two different API calls. + + +#### Commplex filtering + +You can also use more complex elaborations through the syntax +`filter[FIELD][OPERATION]=VALUE`. The available operations are `eq` (equal, +default operation), `ne` (not equal), `gt` (greater than), `ge` (greater or +equal), `lt` (lower than) and `le` (lower or equal). For instance, + +``` +> GET /api/v1/reports?filter[id][ne]=1 +``` + +will return all the reports except the one with `id` 1. + +With the filters combination capability, you can use this to get all the +reports in a geographical bounding box: + +``` +> GET /api/v1/reports?filter[lat][gt]=LAT_MIN&filter[lat][lt]=LAT_MAX&filter[lng][gt]=LNG_MIN&filter[lng][lt]=LNG_MAX +``` diff --git a/server/jsonapi.py b/server/jsonapi.py index f866fe6..77d7b21 100644 --- a/server/jsonapi.py +++ b/server/jsonapi.py @@ -10,7 +10,7 @@ import re import bottle -FILTER_RE = re.compile(r"filter\[([A-z0-9_]+)\]") +FILTER_RE = re.compile(r"filter\[([A-z0-9_]+?)\](\[([A-z0-9_]+)\])?") class DateAwareJSONEncoder(json.JSONEncoder): @@ -73,8 +73,23 @@ def JsonApiParseQuery(query, model, default_sorting=None): if not filter_match: continue field = getattr(model, filter_match.group(1)) - value = query[filter_match.group(0)] - filters.append(field == value) + for value in query.getall(param): + # Handle operation + operation = filter_match.group(3) + if operation == 'eq' or operation is None: + filters.append(field == value) + elif operation == 'ne': + filters.append(field != value) + elif operation == 'gt': + filters.append(field > value) + elif operation == 'ge': + filters.append(field >= value) + elif operation == 'lt': + filters.append(field < value) + elif operation == 'le': + filters.append(field <= value) + else: + raise ValueError("Invalid filtering operator provided.") # Handle pagination according to JSON API spec page_number, page_size = 0, None