From b6a5ac29c5b97fe3507cdee743c7518fc31e86b6 Mon Sep 17 00:00:00 2001 From: atif09 Date: Sat, 17 Jan 2026 19:03:42 +0530 Subject: [PATCH] feat(api): add pagination support to /rest/v1/tags endpoint --- application/database/db.py | 28 +++++++++++++++++ application/tests/db_test.py | 48 ++++++++++++++++++++++++++++++ application/tests/web_main_test.py | 6 +++- application/web/web_main.py | 20 +++++++++---- 4 files changed, 96 insertions(+), 6 deletions(-) diff --git a/application/database/db.py b/application/database/db.py index 86a09b6f3..c1cdfa187 100644 --- a/application/database/db.py +++ b/application/database/db.py @@ -884,6 +884,34 @@ def get_by_tags(self, tags: List[str]) -> List[cre_defs.Document]: ) return documents + def get_by_tags_with_pagination( + self, tags: List[str], page: int = 1, items_per_page: int = 20 + ) -> Tuple[Optional[int], List[cre_defs.Document]]: + + if not tags: + return 0, [] + + # Get all documents matching tags (reuse existing logic) + all_documents = self.get_by_tags(tags) + + # Apply manual pagination since we have mixed types + total_items = len(all_documents) + total_pages = ( + total_items + items_per_page - 1 + ) // items_per_page # Ceiling division + + if total_pages == 0: + return 0, [] + + # Calculate slice indices + start_idx = (page - 1) * items_per_page + end_idx = start_idx + items_per_page + + # Return paginated slice + paginated_documents = all_documents[start_idx:end_idx] + + return total_pages, paginated_documents + def get_nodes_with_pagination( self, name: str, diff --git a/application/tests/db_test.py b/application/tests/db_test.py index 1d13bd0be..aa5e54e28 100644 --- a/application/tests/db_test.py +++ b/application/tests/db_test.py @@ -125,6 +125,54 @@ def test_get_by_tags(self) -> None: self.assertEqual(self.collection.get_by_tags([]), []) self.assertEqual(self.collection.get_by_tags(["this should not be a tag"]), []) + def test_get_by_tags_with_pagination(self) -> None: + + for i in range(5): + dbcre = db.CRE( + description=f"Pagiation CRE {i}", + name=f"PaginationCRE{i}", + tags="pagination-test", + external_id=f"500-{i:03d}", + ) + self.collection.session.add(dbcre) + self.collection.session.commit() + + total_pages, docs = self.collection.get_by_tags_with_pagination( + ["pagination-test"], page=1, items_per_page=10 + ) + self.assertEqual(len(docs), 5) + self.assertEqual(total_pages, 1) + + total_pages, docs = self.collection.get_by_tags_with_pagination( + ["pagination-test"], page=1, items_per_page=2 + ) + self.assertEqual(len(docs), 2) + self.assertEqual(total_pages, 3) + + total_pages, docs = self.collection.get_by_tags_with_pagination( + ["pagination-test"], page=2, items_per_page=2 + ) + self.assertEqual(len(docs), 2) + self.assertEqual(total_pages, 3) + + total_pages, docs = self.collection.get_by_tags_with_pagination( + ["pagination-test"], page=3, items_per_page=2 + ) + self.assertEqual(len(docs), 1) + self.assertEqual(total_pages, 3) + + total_pages, docs = self.collection.get_by_tags_with_pagination( + [], page=1, items_per_page=1 + ) + self.assertEqual(total_pages, 0) + self.assertEqual(docs, []) + + total_pages, docs = self.collection.get_by_tags_with_pagination( + ["non-existent-tag"], page=1, items_per_page=10 + ) + self.assertEqual(total_pages, 0) + self.assertEqual(docs, []) + def test_get_standards_names(self) -> None: result = self.collection.get_node_names() expected = [("Standard", "BarStand"), ("Standard", "Unlinked")] diff --git a/application/tests/web_main_test.py b/application/tests/web_main_test.py index 3f78a5e2f..d472cb829 100644 --- a/application/tests/web_main_test.py +++ b/application/tests/web_main_test.py @@ -400,7 +400,11 @@ def test_find_document_by_tag(self) -> None: response = client.get(f"/rest/v1/tags?tag=CW") self.assertEqual(404, response.status_code) - expected = {"data": [cres["ca"].todict(), cres["cb"].todict()]} + expected = { + "data": [cres["ca"].todict(), cres["cb"].todict()], + "total_pages": 1, + "page": 1, + } response = client.get(f"/rest/v1/tags?tag=ta") self.assertEqual(200, response.status_code) diff --git a/application/web/web_main.py b/application/web/web_main.py index fa7a82efb..f38f62783 100644 --- a/application/web/web_main.py +++ b/application/web/web_main.py @@ -242,16 +242,26 @@ def find_node_by_name( @app.route("/rest/v1/tags", methods=["GET"]) def find_document_by_tag() -> Any: tags = request.args.getlist("tag") + opt_format = request.args.get("format") + + page = 1 + if request.args.get("page") is not None and int(request.args.get("page")) > 0: + page = int(request.args.get("page")) + + items_per_page = int(request.args.get("items_per_page") or ITEMS_PER_PAGE) + if posthog: - posthog.capture(f"find_document_by_tag", f"tags:{tags}") + posthog.capture(f"find_document_by_tag", f"tags:{tags};page:{page}") database = db.Node_collection() - # opt_osib = request.args.get("osib") - opt_format = request.args.get("format") - documents = database.get_by_tags(tags) + total_pages, documents = database.get_by_tags_with_pagination( + tags, page=page, items_per_page=items_per_page + ) + if documents: res = [doc.todict() for doc in documents] - result = {"data": res} + result = {"data": res, "total_pages": total_pages, "page": page} + # if opt_osib: # result["osib"] = odefs.cre2osib(documents).todict() if opt_format == SupportedFormats.Markdown.value: