Skip to content

Commit 7a2e41a

Browse files
committed
Added: untested support for query params in read_all API route (#288)
1 parent 3c26a30 commit 7a2e41a

File tree

3 files changed

+51
-10
lines changed

3 files changed

+51
-10
lines changed

libreforms_fastapi/app/__init__.py

+17-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import re, os, json, tempfile, logging, sys, asyncio, jwt, difflib, importlib, platform
1+
import re, os, json, tempfile, logging, sys
2+
import asyncio, jwt, difflib, importlib, platform
23
from datetime import datetime, timedelta
34
from contextlib import contextmanager
45
from functools import lru_cache
@@ -119,7 +120,6 @@
119120
UserRelationshipModel,
120121
FormConfigUpdateRequest,
121122
SiteConfig,
122-
FileUpload,
123123
get_user_model,
124124
get_form_model,
125125
get_form_names,
@@ -1080,6 +1080,7 @@ async def api_form_read_all(
10801080
set_length: int = 0,
10811081
newest_first: bool = False,
10821082
return_when_empty: bool = False,
1083+
query_params: Optional[str] = Query(None),
10831084
):
10841085
"""
10851086
Retrieves all documents of a specified form type, identified by the form name in the URL.
@@ -1095,7 +1096,10 @@ async def api_form_read_all(
10951096
the results. This applies to the created_at field, you can pair this option with the
10961097
sort_by_last_edited=True param to get the most recently modified forms at the top. If
10971098
you want the endpoint to return empty lists instead of raising an error, then pass
1098-
return_when_empty=true.
1099+
return_when_empty=true. You can pass query_params as a url-encoded dict to filter
1100+
data using the ==, !=, >, >=, <, <=, in, and nin operators. Example usage of this param:
1101+
{"data":{"age": {"operator": ">=", "value": 21},"name": {"operator": "==","value": "John"}}}.
1102+
'{"data": {"Fiscal_Year": {"condition": ">", "value": 2024}}}'
10991103
"""
11001104

11011105
if form_name not in get_form_names(config_path=config.FORM_CONFIG_PATH):
@@ -1119,6 +1123,15 @@ async def api_form_read_all(
11191123
except Exception as e:
11201124
limit_query_to = user.username
11211125

1126+
print("\n\n\n",query_params)
1127+
1128+
# Decode the JSON string to a dictionary
1129+
if query_params:
1130+
try:
1131+
query_params = json.loads(query_params)
1132+
except json.JSONDecodeError:
1133+
raise HTTPException(status_code=400, detail="Invalid query_params format. Must be a JSON string.")
1134+
11221135
documents = doc_db.get_all_documents(
11231136
form_name=form_name,
11241137
limit_users=limit_query_to,
@@ -1128,6 +1141,7 @@ async def api_form_read_all(
11281141
stringify_output=stringify_output,
11291142
sort_by_last_edited=sort_by_last_edited,
11301143
newest_first=newest_first,
1144+
query_params=query_params,
11311145
)
11321146

11331147
# Here we limit the length of the response based on the set_length parameter, see

libreforms_fastapi/utils/document_database.py

+34-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import os, shutil, json
1+
import os, shutil, json, operator
22
from bson import ObjectId
33
from fuzzywuzzy import fuzz
44
from datetime import datetime
@@ -27,6 +27,18 @@
2727
from libreforms_fastapi.utils.docs import sanitizer
2828

2929

30+
# Mapping of string operators to actual functions, used as query parameters
31+
# see https://github.com/signebedi/libreforms-fastapi/issues/288.
32+
OPERATORS = {
33+
'==': operator.eq,
34+
'!=': operator.ne,
35+
'>': operator.gt,
36+
'>=': operator.ge,
37+
'<': operator.lt,
38+
'<=': operator.le,
39+
'in': lambda a, b: a in b,
40+
'nin': lambda a, b: a not in b
41+
}
3042

3143
class CollectionDoesNotExist(Exception):
3244
"""Exception raised when attempting to access a collection that does not exist."""
@@ -947,6 +959,7 @@ def get_all_documents(
947959
stringify_output:bool=False,
948960
sort_by_last_edited:bool=False,
949961
newest_first:bool=False,
962+
query_params=None,
950963
):
951964

952965
"""Retrieves all entries from the specified form's database."""
@@ -963,13 +976,33 @@ def get_all_documents(
963976
if not documents or len(documents) == 0:
964977
return []
965978

979+
### Start with filtering
966980
if isinstance(limit_users, str):
967981
documents = [x for x in documents if x['metadata'][self.created_by_field] == limit_users]
968982

969983
if exclude_deleted:
970984
documents = [x for x in documents if x['metadata'][self.is_deleted_field] == False]
971985

972986

987+
if query_params:
988+
for field, condition in query_params.get('data', {}).items():
989+
operator_func = OPERATORS.get(condition['operator'])
990+
value = condition['value']
991+
992+
if operator_func:
993+
documents = [x for x in documents if operator_func(x['data'].get(field), value)]
994+
995+
if query_params:
996+
for field, condition in query_params.get('metadata', {}).items():
997+
operator_func = OPERATORS.get(condition['operator'])
998+
value = condition['value']
999+
1000+
if operator_func:
1001+
documents = [x for x in documents if operator_func(x['metadata'].get(field), value)]
1002+
1003+
1004+
### Now we move on to sorting and cleanup
1005+
9731006
# Conditionally sort documents by `last_modified` date, see
9741007
# https://github.com/signebedi/libreforms-fastapi/issues/265.
9751008
if sort_by_last_edited:

libreforms_fastapi/utils/pydantic_models.py

-6
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@
1616
SecretStr,
1717
)
1818

19-
from fastapi import (
20-
UploadFile,
21-
)
22-
2319
from pydantic.functional_validators import field_validator, model_validator
2420

2521
# We externalize the custom yaml constructors to create an easier entrypoint
@@ -920,5 +916,3 @@ class SiteConfig(BaseModel):
920916
"""This model will be used for validating site config changes through the admin API"""
921917
content: dict = Field(...)
922918

923-
class FileUpload(BaseModel):
924-
file: UploadFile

0 commit comments

Comments
 (0)