diff --git a/mergin/cli.py b/mergin/cli.py index f342e361..cad1385e 100755 --- a/mergin/cli.py +++ b/mergin/cli.py @@ -137,7 +137,8 @@ def list_projects(flag): c = _init_client() if c is None: return - projects_list = c.projects_list(flag=flag) + resp = c.paginated_projects_list(flag=flag) + projects_list = resp["projects"] for project in projects_list: full_name = "{} / {}".format(project["namespace"], project["name"]) click.echo(" {:40}\t{:6.1f} MB\t{}".format(full_name, project["disk_usage"]/(1024*1024), project['version'])) diff --git a/mergin/client.py b/mergin/client.py index 4f86dc8f..25d6abcb 100644 --- a/mergin/client.py +++ b/mergin/client.py @@ -287,7 +287,8 @@ def create_project_and_push(self, project_name, directory, is_public=False): if mp.inspect_files(): self.push_project(directory) - def projects_list(self, tags=None, user=None, flag=None, q=None): + def paginated_projects_list(self, page=1, per_page=50, tags=None, user=None, flag=None, name=None, + namespace=None, order_params=None): """ Find all available mergin projects. @@ -300,6 +301,58 @@ def projects_list(self, tags=None, user=None, flag=None, q=None): :param flag: Predefined filter flag ('created', 'shared') :type flag: String + :param name: Filter projects with name like name + :type name: String + + :param namespace: Filter projects with namespace like namespace + :type namespace: String + + :param page: Page number for paginated projects list + :type page: Integer + + :param per_page: Number of projects fetched per page, max 100 (restriction set by server) + :type per_page: Integer + + :param order_params: optional attributes for sorting the list. It should be a comma separated attribute names + with _asc or _desc appended for sorting direction. For example: "namespace_asc,disk_usage_desc". + Available attrs: namespace, name, created, updated, disk_usage, creator + :type order_params: String + + :rtype: List[Dict] + """ + params = {} + if tags: + params["tags"] = ",".join(tags) + if user: + params["user"] = user + if flag: + params["flag"] = flag + if name: + params["name"] = name + if namespace: + params["namespace"] = namespace + params["page"] = page + params["per_page"] = per_page + if order_params is not None: + params["order_params"] = order_params + resp = self.get("/v1/project/paginated", params) + projects = json.load(resp) + return projects + + def projects_list(self, tags=None, user=None, flag=None, q=None): + """ + Find all available Mergin projects. It will always retrieve max 100 projects. + Consider using the paginated_projects_list instead. + + :param tags: Filter projects by tags ('valid_qgis', 'mappin_use', input_use') + :type tags: List + + :param user: Username for 'flag' filter. If not provided, it means user executing request. + :type user: String + + :param flag: Predefined filter flag ('created', 'shared') + :type flag: String + :param q: Search query string :type q: String diff --git a/mergin/test/test_client.py b/mergin/test/test_client.py index 47eb78d7..936125f4 100644 --- a/mergin/test/test_client.py +++ b/mergin/test/test_client.py @@ -26,6 +26,7 @@ def toggle_geodiff(enabled): def mc(): return create_client(API_USER, USER_PWD) + @pytest.fixture(scope='function') def mc2(): return create_client(API_USER2, USER_PWD2) @@ -64,8 +65,13 @@ def test_login(mc): def test_create_delete_project(mc): - # create new (empty) project on server test_project = 'test_create_delete' + project = API_USER + '/' + test_project + project_dir = os.path.join(TMP_DIR, test_project) + download_dir = os.path.join(TMP_DIR, 'download', test_project) + + cleanup(mc, project, [project_dir, download_dir]) + # create new (empty) project on server mc.create_project(test_project) projects = mc.projects_list(flag='created') assert any(p for p in projects if p['name'] == test_project and p['namespace'] == API_USER) @@ -698,3 +704,35 @@ def test_download_versions(mc): # try to download not-existing version with pytest.raises(ClientError): mc.download_project(project, project_dir_v3, 'v3') + + +def test_paginated_project_list(mc): + """Test the new endpoint for projects list with pagination, ordering etc.""" + test_projects = dict() + for symb in "ABCDEF": + name = f"test_paginated_{symb}" + test_projects[name] = f"{API_USER}/{name}" + + for name, full_name in test_projects.items(): + cleanup(mc, full_name, []) + mc.create_project(name) + + sorted_test_names = [n for n in sorted(test_projects.keys())] + + resp = mc.paginated_projects_list( + flag='created', name="test_paginated", page=1, per_page=10, order_params="name_asc" + ) + projects = resp["projects"] + count = resp["count"] + assert count == len(test_projects) + assert len(projects) == len(test_projects) + for i, project in enumerate(projects): + assert project["name"] == sorted_test_names[i] + + resp = mc.paginated_projects_list( + flag='created', name="test_paginated", page=2, per_page=2, order_params="name_asc" + ) + projects = resp["projects"] + assert len(projects) == 2 + for i, project in enumerate(projects): + assert project["name"] == sorted_test_names[i+2]