From 8187b3f0d84a0a9aea1dad8ea644f234b1821c8f Mon Sep 17 00:00:00 2001 From: Rob Marsal Date: Thu, 23 Jul 2026 11:27:42 +0100 Subject: [PATCH] refactor(PLU-325): use v3 collections endpoint for search --- pyproject.toml | 2 +- .../app/components/dialogs/matching_dialog.py | 20 +++++++-------- .../app/services/matching/matching_service.py | 25 ++++++++----------- tests/unit/matching/test_matching_service.py | 23 +++++++++++------ tests/unit/matching/test_sdk_schemas.py | 4 +-- uv.lock | 8 +++--- 6 files changed, 42 insertions(+), 40 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 858fb24..27ce0c6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ dependencies = [ "requests>=2.32", "loguru>=0.7", "pydantic", - "revengai>=3.114.2", + "revengai>=3.123.0", "libbs>=2.16.5", ] diff --git a/reai_toolkit/app/components/dialogs/matching_dialog.py b/reai_toolkit/app/components/dialogs/matching_dialog.py index f739205..1e121d8 100644 --- a/reai_toolkit/app/components/dialogs/matching_dialog.py +++ b/reai_toolkit/app/components/dialogs/matching_dialog.py @@ -5,7 +5,7 @@ from revengai.models import ( BinarySearchResult, - CollectionSearchResult, + CollectionListItemBody, FunctionMatch, MatchedFunction, ) @@ -154,8 +154,8 @@ def __init__( self.ui.searchFunctions.textEdited.connect(self._update_functions_table) # ----------------- Collections state ----------------- - # Internal multi-selection: key -> (name, scope, owner, model, created) - self._selected_collections: dict[str, CollectionSearchResult] = {} + # Internal multi-selection: key -> (name, scope, owner, size, created) + self._selected_collections: dict[str, CollectionListItemBody] = {} self._collectionsDebounce = QtCore.QTimer(self) self._collectionsDebounce.setSingleShot(True) self._collectionsDebounce.timeout.connect(self._performCollectionsSearch) @@ -390,7 +390,7 @@ def _onCollectionsEdited(self, _text: str): def _performCollectionsSearch(self): query = self.ui.editCollections.text().strip() - response: GenericApiReturn[List[CollectionSearchResult]] = ( + response: GenericApiReturn[List[CollectionListItemBody]] = ( self.matching_service.search_collections(text_input=query) ) @@ -405,7 +405,7 @@ def _performCollectionsSearch(self): self._fillCollectionsPopup(query=query, rows=response.data) def _initCollectionsHeader(self): - labels = ["Select", "Collection", "Scope", "Owner", "Model", "Created"] + labels = ["Select", "Collection", "Scope", "Owner", "Size", "Created"] view = self.ui.collectionsPopupView view.setHeaderHidden(False) @@ -423,7 +423,7 @@ def _initCollectionsHeader(self): else: hdr.setSectionResizeMode(i, QtWidgets.QHeaderView.Stretch) - def _fillCollectionsPopup(self, query: str, rows: list[CollectionSearchResult]): + def _fillCollectionsPopup(self, query: str, rows: list[CollectionListItemBody]): view = self.ui.collectionsPopupView view.blockSignals(True) view.clear() @@ -449,10 +449,10 @@ def _fillCollectionsPopup(self, query: str, rows: list[CollectionSearchResult]): [ "", tup.collection_name, - tup.scope, - tup.owned_by, - tup.model_name, - tup.created_at.isoformat(), + tup.collection_scope, + tup.collection_owner, + str(tup.collection_size), + tup.creation.isoformat(), ] ) it.setFlags( diff --git a/reai_toolkit/app/services/matching/matching_service.py b/reai_toolkit/app/services/matching/matching_service.py index 908f295..e04fd9c 100644 --- a/reai_toolkit/app/services/matching/matching_service.py +++ b/reai_toolkit/app/services/matching/matching_service.py @@ -10,7 +10,8 @@ from loguru import logger from revengai import ( BinarySearchResult, - CollectionSearchResult, + CollectionListItemBody, + CollectionsApi, Configuration, FunctionMapping, FunctionMatch, @@ -57,27 +58,21 @@ def function_id_to_local_name(self, function_id: int) -> Optional[str]: name = self.demangle(idc.get_func_name(vaddr)) return name - def _search_collections(self, text_input: str) -> List[CollectionSearchResult]: + def _search_collections(self, text_input: str) -> List[CollectionListItemBody]: with self.yield_api_client(sdk_config=self.sdk_config) as api_client: - search_client = SearchApi(api_client) - - return_list: List[CollectionSearchResult] = [] + collections_client = CollectionsApi(api_client) - response = search_client.search_collections( - model_name=self.netstore_service.get_model_name(), - partial_collection_name=text_input, - page_size=10, - page=1, + response = collections_client.v3_list_collections( + search_term=text_input or None, + limit=50, + offset=0, ) - return_list.extend(response.data.results) - - return return_list - pass + return response.results or [] def search_collections( self, text_input: str - ) -> GenericApiReturn[List[CollectionSearchResult]]: + ) -> GenericApiReturn[List[CollectionListItemBody]]: response = self.api_request_returning( lambda: self._search_collections(text_input) ) diff --git a/tests/unit/matching/test_matching_service.py b/tests/unit/matching/test_matching_service.py index dad27b1..d5c88db 100644 --- a/tests/unit/matching/test_matching_service.py +++ b/tests/unit/matching/test_matching_service.py @@ -45,6 +45,15 @@ def search_api(mocker): return api_inst +@pytest.fixture +def collections_api(mocker): + mocker.patch.object(MatchingService, "yield_api_client") + api_class = mocker.patch.object(svc_mod, "CollectionsApi") + api_inst = MagicMock() + api_class.return_value = api_inst + return api_inst + + @pytest.fixture def core_api(mocker): mocker.patch.object(MatchingService, "yield_api_client") @@ -78,23 +87,21 @@ def _matches(matches=None) -> GetMatchesOutputBody: ) -def test_search_collections_returns_results(service, search_api): +def test_search_collections_returns_results(service, collections_api): results = [MagicMock(), MagicMock()] - search_api.search_collections.return_value = MagicMock( - data=MagicMock(results=results) - ) + collections_api.v3_list_collections.return_value = MagicMock(results=results) out = service.search_collections("libc") assert out.success is True assert out.data == results - search_api.search_collections.assert_called_once_with( - model_name="binnet-0.1", partial_collection_name="libc", page_size=10, page=1 + collections_api.v3_list_collections.assert_called_once_with( + search_term="libc", limit=50, offset=0 ) -def test_search_collections_failure_returns_empty_success(service, search_api): - search_api.search_collections.side_effect = RuntimeError("boom") +def test_search_collections_failure_returns_empty_success(service, collections_api): + collections_api.v3_list_collections.side_effect = RuntimeError("boom") out = service.search_collections("libc") diff --git a/tests/unit/matching/test_sdk_schemas.py b/tests/unit/matching/test_sdk_schemas.py index d2e7efe..ba929b2 100644 --- a/tests/unit/matching/test_sdk_schemas.py +++ b/tests/unit/matching/test_sdk_schemas.py @@ -1,6 +1,6 @@ from revengai import ( BinarySearchResult, - CollectionSearchResult, + CollectionListItemBody, FunctionMapping, FunctionMatch, GetMatchesOutputBody, @@ -64,7 +64,7 @@ def test_search_result_fields(): BinarySearchResult.model_fields ) assert {"collection_id", "collection_name"} <= set( - CollectionSearchResult.model_fields + CollectionListItemBody.model_fields ) diff --git a/uv.lock b/uv.lock index aa7d84f..83a1f97 100644 --- a/uv.lock +++ b/uv.lock @@ -820,7 +820,7 @@ requires-dist = [ { name = "loguru", specifier = ">=0.7" }, { name = "pydantic" }, { name = "requests", specifier = ">=2.32" }, - { name = "revengai", specifier = ">=3.114.2" }, + { name = "revengai", specifier = ">=3.123.0" }, ] [package.metadata.requires-dev] @@ -855,7 +855,7 @@ wheels = [ [[package]] name = "revengai" -version = "3.114.2" +version = "3.123.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "lazy-imports" }, @@ -864,9 +864,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/db/4c/12366df3fd9c7619dfb761498a052849aba1c78c00aafe53f396ceb85e4f/revengai-3.114.2.tar.gz", hash = "sha256:7f45c4ff9bda8455ca7329dd7aa9fb9c6588f29e8c3fddfe2508f71bf917a5d0", size = 380007, upload-time = "2026-07-14T17:36:07.172Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/11/c263645fd280bb0d4c0a3cb3057880570bd8d6581b71be0f688f7adc6dee/revengai-3.123.0.tar.gz", hash = "sha256:871199b75675ac54543005179e7f92b845cfceba79ee49c8f11db0d77613aa7f", size = 369727, upload-time = "2026-07-23T08:49:18.527Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/58/e7afb09db756556f00ba4c328f7aee1dfc5fe8ce7c10a413bf39c0bd0f2d/revengai-3.114.2-py3-none-any.whl", hash = "sha256:945817973b1435c6d749ca1d14eef87d5c22cf97a0c3fda356ee5fe683b09068", size = 987242, upload-time = "2026-07-14T17:36:05.146Z" }, + { url = "https://files.pythonhosted.org/packages/91/e1/5a01283b4eb34f54879ce7a91a2db81d27f987c0c1a5e33ce2e81c6ae297/revengai-3.123.0-py3-none-any.whl", hash = "sha256:f488e653bd88b9c8d432d062d11417710502b9bb10966c88c2c77f69e588fdb1", size = 966561, upload-time = "2026-07-23T08:49:17.023Z" }, ] [[package]]