From 8afbd0695d038a214409c5cd4d1a69e646e7dc12 Mon Sep 17 00:00:00 2001 From: Martin Dobias Date: Thu, 23 Apr 2020 14:16:45 +0200 Subject: [PATCH] API changes to better support project creation + async upload - create_project() only creates empty remote project, no uploads - create_project_and_push() creates a project and does non-async upload - username() new convenience method to get user name - SyncError exception removed - we already have ClientError --- mergin/client.py | 30 +++++++++++++++++++----------- mergin/client_push.py | 4 ++-- mergin/common.py | 6 ------ mergin/test/test_client.py | 16 ++++++++-------- 4 files changed, 29 insertions(+), 27 deletions(-) diff --git a/mergin/client.py b/mergin/client.py index 95be389c..9a1ef8d5 100644 --- a/mergin/client.py +++ b/mergin/client.py @@ -12,7 +12,7 @@ from pip._vendor import distro -from .common import ClientError, SyncError +from .common import ClientError from .merginproject import MerginProject from .client_pull import download_project_async, download_project_wait, download_project_finalize from .client_pull import pull_project_async, pull_project_wait, pull_project_finalize @@ -218,7 +218,13 @@ def login(self, login, password): } return session - def create_project(self, project_name, directory=None, is_public=False): + def username(self): + """ Returns user name used in this session or None if not authenticated """ + if not self._user_info: + return None # not authenticated + return self._user_info["username"] + + def create_project(self, project_name, is_public=False): """ Create new project repository in user namespace on Mergin server. Optionally initialized from given local directory. @@ -226,22 +232,17 @@ def create_project(self, project_name, directory=None, is_public=False): :param project_name: Project name :type project_name: String - :param directory: Local project directory, defaults to None - :type directory: String - :param is_public: Flag for public/private project, defaults to False :type directory: Boolean """ if not self._user_info: raise Exception("Authentication required") - if directory and not os.path.exists(directory): - raise Exception("Project directory does not exists") params = { "name": project_name, "public": is_public } - namespace = self._user_info["username"] + namespace = self.username() try: self.post("/v1/project/%s" % namespace, params, {"Content-Type": "application/json"}) data = { @@ -251,10 +252,17 @@ def create_project(self, project_name, directory=None, is_public=False): } except Exception as e: detail = f"Namespace: {namespace}, project name: {project_name}" - raise SyncError(str(e), detail) + raise ClientError(str(e), detail) + + def create_project_and_push(self, project_name, directory, is_public=False): + """ + Convenience method to create project and push the initial version right after that. + """ + self.create_project(project_name, is_public) if directory: mp = MerginProject(directory) - mp.metadata = data + full_project_name = "{}/{}".format(self.username(), project_name) + mp.metadata = {"name": full_project_name, "version": "v0", "files": []} if mp.inspect_files(): self.push_project(directory) @@ -330,7 +338,7 @@ def download_project(self, project_path, directory): download_project_finalize(job) def enough_storage_available(self, data): - user_name = self._user_info["username"] + user_name = self.username() resp = self.get('/v1/user/' + user_name) info = json.load(resp) free_space = int(info["storage_limit"]) - int(info["disk_usage"]) diff --git a/mergin/client_push.py b/mergin/client_push.py index 2c3b35f2..715258e0 100644 --- a/mergin/client_push.py +++ b/mergin/client_push.py @@ -14,7 +14,7 @@ import pprint import concurrent.futures -from .common import UPLOAD_CHUNK_SIZE, ClientError, SyncError +from .common import UPLOAD_CHUNK_SIZE, ClientError from .merginproject import MerginProject from .utils import generate_checksum, do_sqlite_checkpoint @@ -99,7 +99,7 @@ def push_project_async(mc, directory): if not enough_free_space: freespace = int(freespace/(1024*1024)) mp.log.error(f"--- push {project_path} - not enough space") - raise SyncError("Storage limit has been reached. Only " + str(freespace) + "MB left") + raise ClientError("Storage limit has been reached. Only " + str(freespace) + "MB left") if not sum(len(v) for v in changes.values()): mp.log.info(f"--- push {project_path} - nothing to do") diff --git a/mergin/common.py b/mergin/common.py index 720a9b83..c0510f95 100644 --- a/mergin/common.py +++ b/mergin/common.py @@ -15,12 +15,6 @@ class ClientError(Exception): pass -class SyncError(Exception): - def __init__(self, msg, detail=""): - super().__init__(msg) - self.detail = detail - - try: import dateutil.parser from dateutil.tz import tzlocal diff --git a/mergin/test/test_client.py b/mergin/test/test_client.py index bb7fccc8..442de4c1 100644 --- a/mergin/test/test_client.py +++ b/mergin/test/test_client.py @@ -5,7 +5,7 @@ import pytest import pytz -from ..client import MerginClient, ClientError, MerginProject, SyncError, LoginError +from ..client import MerginClient, ClientError, MerginProject, LoginError from ..utils import generate_checksum SERVER_URL = os.environ.get('TEST_MERGIN_URL') @@ -56,7 +56,7 @@ def test_create_delete_project(mc): assert any(p for p in projects if p['name'] == test_project and p['namespace'] == API_USER) # try again - with pytest.raises(SyncError, match=f'Project {test_project} already exists'): + with pytest.raises(ClientError, match=f'Project {test_project} already exists'): mc.create_project(test_project) # remove project @@ -80,7 +80,7 @@ def test_create_remote_project_from_local(mc): shutil.copytree(TEST_DATA_DIR, project_dir) # create remote project - mc.create_project(test_project, directory=project_dir) + mc.create_project_and_push(test_project, directory=project_dir) # check basic metadata about created project project_info = mc.project_info(project) @@ -119,7 +119,7 @@ def test_push_pull_changes(mc): cleanup(mc, project, [project_dir, project_dir_2]) # create remote project shutil.copytree(TEST_DATA_DIR, project_dir) - mc.create_project(test_project, project_dir) + mc.create_project_and_push(test_project, project_dir) # make sure we have v1 also in concurrent project dir mc.download_project(project, project_dir_2) @@ -196,7 +196,7 @@ def test_ignore_files(mc): # create remote project shutil.copytree(TEST_DATA_DIR, project_dir) shutil.copy(os.path.join(project_dir, 'test.qgs'), os.path.join(project_dir, 'test.qgs~')) - mc.create_project(test_project, project_dir) + mc.create_project_and_push(test_project, project_dir) project_info = mc.project_info(project) assert not next((f for f in project_info['files'] if f['path'] == 'test.qgs~'), None) @@ -228,7 +228,7 @@ def test_sync_diff(mc, push_geodiff_enabled, pull_geodiff_enabled): # create remote project toggle_geodiff(push_geodiff_enabled) shutil.copytree(TEST_DATA_DIR, project_dir) - mc.create_project(test_project, project_dir) + mc.create_project_and_push(test_project, project_dir) # make sure we have v1 also in concurrent project dirs toggle_geodiff(pull_geodiff_enabled) @@ -318,7 +318,7 @@ def test_list_of_push_changes(mc): cleanup(mc, project, [project_dir]) shutil.copytree(TEST_DATA_DIR, project_dir) toggle_geodiff(True) - mc.create_project(test_project, project_dir) + mc.create_project_and_push(test_project, project_dir) f_updated = 'base.gpkg' mp = MerginProject(project_dir) @@ -342,7 +342,7 @@ def test_force_gpkg_update(mc): cleanup(mc, project, [project_dir]) # create remote project shutil.copytree(TEST_DATA_DIR, project_dir) - mc.create_project(test_project, project_dir) + mc.create_project_and_push(test_project, project_dir) # test push changes with force gpkg file upload: mp = MerginProject(project_dir)