diff --git a/forms-flow-api-utils/src/formsflow_api_utils/utils/enums.py b/forms-flow-api-utils/src/formsflow_api_utils/utils/enums.py index 36bad2ef09..8ffe663e9f 100644 --- a/forms-flow-api-utils/src/formsflow_api_utils/utils/enums.py +++ b/forms-flow-api-utils/src/formsflow_api_utils/utils/enums.py @@ -28,6 +28,8 @@ class ApplicationSortingParameters: # pylint: disable=too-few-public-methods FormName = "formName" visibility= "visibility" is_anonymous= "is_anonymous" + type = "type" + is_draft = "is_draft" @unique diff --git a/forms-flow-api-utils/src/formsflow_api_utils/utils/util.py b/forms-flow-api-utils/src/formsflow_api_utils/utils/util.py index 9208851e72..50299387b4 100644 --- a/forms-flow-api-utils/src/formsflow_api_utils/utils/util.py +++ b/forms-flow-api-utils/src/formsflow_api_utils/utils/util.py @@ -67,6 +67,7 @@ def validate_sort_order_and_order_by(order_by: str, sort_order: str) -> bool: ApplicationSortingParameters.FormStatus, ApplicationSortingParameters.FormName, ApplicationSortingParameters.visibility, + ApplicationSortingParameters.type, DraftSortingParameters.Name, ProcessSortingParameters.Name, ProcessSortingParameters.Created, @@ -79,6 +80,12 @@ def validate_sort_order_and_order_by(order_by: str, sort_order: str) -> bool: order_by = ApplicationSortingParameters.FormName if order_by == ApplicationSortingParameters.visibility: order_by = ApplicationSortingParameters.is_anonymous + if order_by == ApplicationSortingParameters.type: + # If the sort type is 'type', sort by the 'is_draft' column + order_by = ApplicationSortingParameters.is_draft + # - By default, sorting boolean values in ascending order places `False` (submissions) first. + # - To ensure drafts (True) come first in ascending order, we invert the sort order. + sort_order = "asc" if sort_order == "desc" else "desc" order_by = camel_to_snake(order_by) if sort_order not in ["asc", "desc"]: sort_order = None diff --git a/forms-flow-api/requirements.txt b/forms-flow-api/requirements.txt index b2942b60ce..49a208b894 100644 --- a/forms-flow-api/requirements.txt +++ b/forms-flow-api/requirements.txt @@ -26,7 +26,7 @@ ecdsa==0.19.0 flask-jwt-oidc==0.7.0 flask-marshmallow==1.2.1 flask-restx==1.3.0 -formsflow_api_utils @ git+https://github.com/auslin-aot/forms-flow-ai.git@feature/fwf-4315-update-api-docs#subdirectory=forms-flow-api-utils +formsflow_api_utils @ git+https://github.com/auslin-aot/forms-flow-ai.git@feature/fwf-4429-application-screen-backend-updates#subdirectory=forms-flow-api-utils gunicorn==23.0.0 h11==0.14.0 h2==4.1.0 diff --git a/forms-flow-api/requirements/prod.txt b/forms-flow-api/requirements/prod.txt index 4ef46d5573..5b3f1e2e6c 100644 --- a/forms-flow-api/requirements/prod.txt +++ b/forms-flow-api/requirements/prod.txt @@ -17,4 +17,4 @@ markupsafe PyJWT redis lxml -git+https://github.com/auslin-aot/forms-flow-ai.git@feature/fwf-4315-update-api-docs#subdirectory=forms-flow-api-utils +git+https://github.com/auslin-aot/forms-flow-ai.git@feature/fwf-4429-application-screen-backend-updates#subdirectory=forms-flow-api-utils diff --git a/forms-flow-api/src/formsflow_api/resources/application.py b/forms-flow-api/src/formsflow_api/resources/application.py index a5157877c5..ec5f2d2211 100644 --- a/forms-flow-api/src/formsflow_api/resources/application.py +++ b/forms-flow-api/src/formsflow_api/resources/application.py @@ -229,8 +229,14 @@ def get(): # pylint:disable=too-many-locals include_drafts = dict_data.get("include_drafts", False) only_drafts = dict_data.get("only_drafts", False) created_user_submissions = dict_data.get("created_user_submissions", False) - - if auth.has_role([VIEW_TASKS, MANAGE_TASKS]) and not created_user_submissions: + form_name = ApplicationService.fetch_latest_form_name_by_parent_form_id( + common_filters["parent_form_id"] + ) + # Check if the application_id is not a valid integer, return an empty response + application_id = dict_data.get("application_id") + if application_id and not application_id.isdigit(): + application_schema_dump, application_count = [], 0 + elif auth.has_role([VIEW_TASKS, MANAGE_TASKS]) and not created_user_submissions: ( application_schema_dump, application_count, @@ -253,6 +259,7 @@ def get(): # pylint:disable=too-many-locals "totalCount": application_count, "limit": common_filters["limit"], "pageNo": common_filters["page_no"], + "formName": form_name, } ), HTTPStatus.OK, diff --git a/forms-flow-api/src/formsflow_api/schemas/application.py b/forms-flow-api/src/formsflow_api/schemas/application.py index 48bb8a549e..895ddfcf56 100644 --- a/forms-flow-api/src/formsflow_api/schemas/application.py +++ b/forms-flow-api/src/formsflow_api/schemas/application.py @@ -21,7 +21,7 @@ class ApplicationListRequestSchema(ApplicationListReqSchema): """This class manages application list request schema.""" order_by = fields.Str(data_key="sortBy", required=False) - application_id = fields.Int(data_key="Id", required=False) + application_id = fields.Str(data_key="Id", required=False) application_name = fields.Str(data_key="applicationName", required=False) application_status = fields.Str(data_key="applicationStatus", required=False) created_by = fields.Str(data_key="createdBy", required=False) diff --git a/forms-flow-api/src/formsflow_api/services/application.py b/forms-flow-api/src/formsflow_api/services/application.py index dc330c504c..3d651e817e 100644 --- a/forms-flow-api/src/formsflow_api/services/application.py +++ b/forms-flow-api/src/formsflow_api/services/application.py @@ -598,3 +598,13 @@ def delete_draft_application(cls, application_id: int, **kwargs): application.delete() else: raise BusinessException(BusinessErrorCode.DRAFT_APPLICATION_NOT_FOUND) + + @classmethod + def fetch_latest_form_name_by_parent_form_id(cls, parent_form_id): + """Get latest form name by parent_form_id.""" + current_app.logger.info("Fetching form name by parent id..") + if parent_form_id and ( + mapper := FormProcessMapper.get_latest_by_parent_form_id(parent_form_id) + ): + return mapper.form_name + return None diff --git a/forms-flow-api/tests/unit/api/test_application.py b/forms-flow-api/tests/unit/api/test_application.py index 75aef8b6af..f98f99fc62 100644 --- a/forms-flow-api/tests/unit/api/test_application.py +++ b/forms-flow-api/tests/unit/api/test_application.py @@ -169,6 +169,32 @@ def test_application_list_include_drafts_and_only_drafts( response = client.get("/application?onlyDrafts=true", headers=headers) assert response.status_code == 200 assert len(response.json["applications"]) == 1 + # Assert sortBy=type with sortOrder=asc returns Draft(isDraft=True) first + response = client.get("/application?includeDrafts=true&createdUserSubmissions=true&parentFormId=1234&sortBy=type&sortOrder=asc", headers=headers) + assert response.status_code == 200 + assert len(response.json["applications"]) == 2 + assert response.json["applications"][0]["isDraft"] is True + assert response.json["formName"] == "Sample form" + # Assert sortBy=type with sortOrder=desc returns Submissions(isDraft=False first + response = client.get("/application?includeDrafts=true&createdUserSubmissions=true&parentFormId=1234&sortBy=type&sortOrder=desc", headers=headers) + assert response.status_code == 200 + assert len(response.json["applications"]) == 2 + assert response.json["applications"][0]["isDraft"] is False + assert response.json["formName"] == "Sample form" + # Assert if parentFormId not provided returns formName as null + response = client.get("/application?includeDrafts=true&createdUserSubmissions=true", headers=headers) + assert response.status_code == 200 + assert response.json["formName"] is None + # Assert search by Id with string value returns empty result instead of error + response = client.get("/application?pageNo=1&limit=5&includeDrafts=true&createdUserSubmissions=true&parentFormId=1234&Id=a", headers=headers) + assert response.status_code == 200 + assert response.json == { + "applications": [], + "totalCount": 0, + "limit": 5, + "pageNo": 1, + "formName": "Sample form" + } class TestApplicationDetailView: