From 53e4052ebe57cc5240d25062847596dab7262be9 Mon Sep 17 00:00:00 2001 From: uclaros Date: Fri, 9 Sep 2022 17:53:48 +0300 Subject: [PATCH 01/11] add workspaces endpoint --- mergin/client.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mergin/client.py b/mergin/client.py index 4a497f74..dd24decc 100644 --- a/mergin/client.py +++ b/mergin/client.py @@ -322,6 +322,16 @@ def user_service(self): return response + def workspaces_list(self): + """ + Find all available workspaces + + :rtype: List[Dict] + """ + resp = self.get("/v1/workspaces") + workspaces = json.load(resp) + return workspaces + def create_project(self, project_name, is_public=False, namespace=None): """ Create new project repository in user namespace on Mergin Maps server. From a364067f6238121dc17018c4cb13e9f30517f693 Mon Sep 17 00:00:00 2001 From: uclaros Date: Mon, 19 Sep 2022 16:27:10 +0300 Subject: [PATCH 02/11] Add global_namespace() and server_type() methods --- mergin/client.py | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/mergin/client.py b/mergin/client.py index dd24decc..5796501f 100644 --- a/mergin/client.py +++ b/mergin/client.py @@ -70,6 +70,8 @@ def __init__(self, url=None, auth_token=None, login=None, password=None, plugin_ self._auth_params = None self._auth_session = None self._user_info = None + self._server_type = None + self._global_namespace = None self.client_version = "Python-client/" + __version__ if plugin_version is not None: # this could be e.g. "Plugin/2020.1 QGIS/3.14" self.client_version += " " + plugin_version @@ -322,6 +324,51 @@ def user_service(self): return response + def server_type(self): + """ + Returns the deployment type of the server: + ee - server supports workspaces + ce - server has a global workspace + old - server does not support workspaces + + The value is cached for self's lifetime + + :returns: Type of server deployment ("ee"/"ce"/"old") + rtype: str + """ + # todo: return enum + if not self._server_type: + try: + resp = self.get("/config") + config = json.load(resp) + if "user_workspaces_allowed" in config: + self._server_type = "ee" + if "global_namespace" in config: + self._server_type = "ce" + except ClientError as e: + self._server_type = "old" + + return self._server_type + + def global_namespace(self): + """" + Returns the name of the server's global namespace, if any. + (Applies to CE server types) + + The value is cached for self's lifetime + + :returns: server's global namespace name if exists, None otherwise + rtype: str + """ + if not self._global_namespace: + try: + resp = self.get("/config") + config = json.load(resp) + self._global_namespace = config.get("global_namespace", None) + except ClientError as e: + self._global_namespace = None + return self._global_namespace + def workspaces_list(self): """ Find all available workspaces From ea841973c0bc22552ac228a9ac8c8a68701425b5 Mon Sep 17 00:00:00 2001 From: uclaros Date: Tue, 20 Sep 2022 09:48:24 +0300 Subject: [PATCH 03/11] black --- mergin/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mergin/client.py b/mergin/client.py index 5796501f..df1692a9 100644 --- a/mergin/client.py +++ b/mergin/client.py @@ -351,7 +351,7 @@ def server_type(self): return self._server_type def global_namespace(self): - """" + """ Returns the name of the server's global namespace, if any. (Applies to CE server types) From 8b23a84c737d7ba11c954e061ce1f3389b3be57f Mon Sep 17 00:00:00 2001 From: uclaros Date: Tue, 25 Oct 2022 16:49:41 +0300 Subject: [PATCH 04/11] cache global namespace while checking server type --- mergin/client.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mergin/client.py b/mergin/client.py index df1692a9..ad6b75f3 100644 --- a/mergin/client.py +++ b/mergin/client.py @@ -345,6 +345,7 @@ def server_type(self): self._server_type = "ee" if "global_namespace" in config: self._server_type = "ce" + self._global_namespace = config["global_namespace"] except ClientError as e: self._server_type = "old" @@ -765,6 +766,10 @@ def project_status(self, directory): project_path = mp.metadata["name"] local_version = mp.metadata["version"] server_info = self.project_info(project_path, since=local_version) + + + + pull_changes = mp.get_pull_changes(server_info["files"]) push_changes = mp.get_push_changes() From 4fe96842d12a9c09f286b327fbf7feff13abfa50 Mon Sep 17 00:00:00 2001 From: uclaros Date: Tue, 25 Oct 2022 17:15:57 +0300 Subject: [PATCH 05/11] black --- mergin/client.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/mergin/client.py b/mergin/client.py index ad6b75f3..4d3175fd 100644 --- a/mergin/client.py +++ b/mergin/client.py @@ -767,9 +767,6 @@ def project_status(self, directory): local_version = mp.metadata["version"] server_info = self.project_info(project_path, since=local_version) - - - pull_changes = mp.get_pull_changes(server_info["files"]) push_changes = mp.get_push_changes() From 34f1d045ddc0576c37d8d85dbe4950e941a8e68d Mon Sep 17 00:00:00 2001 From: uclaros Date: Tue, 8 Nov 2022 12:28:17 +0200 Subject: [PATCH 06/11] Add only_namespace parameter to projects_list --- mergin/client.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/mergin/client.py b/mergin/client.py index 4d3175fd..adfe0524 100644 --- a/mergin/client.py +++ b/mergin/client.py @@ -424,7 +424,16 @@ def create_project_and_push(self, project_name, directory, is_public=False, name self.push_project(directory) def paginated_projects_list( - self, page=1, per_page=50, tags=None, user=None, flag=None, name=None, namespace=None, order_params=None + self, + page=1, + per_page=50, + tags=None, + user=None, + flag=None, + name=None, + only_namespace=None, + namespace=None, + order_params=None, ): """ Find all available Mergin Maps projects. @@ -441,6 +450,9 @@ def paginated_projects_list( :param name: Filter projects with name like name :type name: String + :param only_namespace: Filter projects with namespace exactly equal to namespace + :type namespace: String + :param namespace: Filter projects with namespace like namespace :type namespace: String @@ -466,7 +478,9 @@ def paginated_projects_list( params["flag"] = flag if name: params["name"] = name - if namespace: + if only_namespace: + params["only_namespace"] = only_namespace + elif namespace: params["namespace"] = namespace params["page"] = page params["per_page"] = per_page @@ -476,7 +490,9 @@ def paginated_projects_list( projects = json.load(resp) return projects - def projects_list(self, tags=None, user=None, flag=None, name=None, namespace=None, order_params=None): + def projects_list( + self, tags=None, user=None, flag=None, name=None, only_namespace=None, namespace=None, order_params=None + ): """ Find all available Mergin Maps projects. @@ -494,6 +510,9 @@ def projects_list(self, tags=None, user=None, flag=None, name=None, namespace=No :param name: Filter projects with name like name :type name: String + :param only_namespace: Filter projects with namespace exactly equal to namespace + :type namespace: String + :param namespace: Filter projects with namespace like namespace :type namespace: String @@ -515,6 +534,7 @@ def projects_list(self, tags=None, user=None, flag=None, name=None, namespace=No user=user, flag=flag, name=name, + only_namespace=only_namespace, namespace=namespace, order_params=order_params, ) From 422b2660b8e8e04267741d9ae0ac3bc90d648ada Mon Sep 17 00:00:00 2001 From: uclaros Date: Tue, 8 Nov 2022 12:38:42 +0200 Subject: [PATCH 07/11] switch to using /v1/user/profile for new servers --- mergin/client.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mergin/client.py b/mergin/client.py index adfe0524..1412d75d 100644 --- a/mergin/client.py +++ b/mergin/client.py @@ -635,7 +635,11 @@ def enough_storage_available(self, data): return True, free_space def user_info(self): - resp = self.get("/v1/user/" + self.username()) + server_type = self.server_type() + if server_type == "old": + resp = self.get("/v1/user/" + self.username()) + else: + resp = self.get("/v1/user/profile") return json.load(resp) def set_project_access(self, project_path, access): From a5c91a974d6bd1721b657b43c8af83e908e2d42a Mon Sep 17 00:00:00 2001 From: uclaros Date: Wed, 9 Nov 2022 13:56:19 +0200 Subject: [PATCH 08/11] remove unused method global_namespace --- mergin/client.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/mergin/client.py b/mergin/client.py index 1412d75d..41a89f64 100644 --- a/mergin/client.py +++ b/mergin/client.py @@ -351,25 +351,6 @@ def server_type(self): return self._server_type - def global_namespace(self): - """ - Returns the name of the server's global namespace, if any. - (Applies to CE server types) - - The value is cached for self's lifetime - - :returns: server's global namespace name if exists, None otherwise - rtype: str - """ - if not self._global_namespace: - try: - resp = self.get("/config") - config = json.load(resp) - self._global_namespace = config.get("global_namespace", None) - except ClientError as e: - self._global_namespace = None - return self._global_namespace - def workspaces_list(self): """ Find all available workspaces From 086c54403cf5da8d6b65e5c9d062f56e680494cd Mon Sep 17 00:00:00 2001 From: uclaros Date: Thu, 10 Nov 2022 14:18:48 +0200 Subject: [PATCH 09/11] use enum for ServerType and remove unused var --- mergin/client.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/mergin/client.py b/mergin/client.py index 41a89f64..961540d9 100644 --- a/mergin/client.py +++ b/mergin/client.py @@ -12,6 +12,7 @@ from datetime import datetime, timezone import dateutil.parser import ssl +from enum import Enum, auto import re from .common import ClientError, LoginError, InvalidProject @@ -37,6 +38,13 @@ class TokenError(Exception): pass +class ServerType(Enum): + OLD = auto() # Server is old and does not support workspaces + CE = auto() # Server is Community Edition + EE = auto() # Server is Enterprise Edition + SAAS = auto() # Server is SaaS + + def decode_token_data(token): token_prefix = "Bearer ." if not token.startswith(token_prefix): @@ -71,7 +79,6 @@ def __init__(self, url=None, auth_token=None, login=None, password=None, plugin_ self._auth_session = None self._user_info = None self._server_type = None - self._global_namespace = None self.client_version = "Python-client/" + __version__ if plugin_version is not None: # this could be e.g. "Plugin/2020.1 QGIS/3.14" self.client_version += " " + plugin_version @@ -326,28 +333,23 @@ def user_service(self): def server_type(self): """ - Returns the deployment type of the server: - ee - server supports workspaces - ce - server has a global workspace - old - server does not support workspaces + Returns the deployment type of the server The value is cached for self's lifetime - :returns: Type of server deployment ("ee"/"ce"/"old") - rtype: str + :returns: ServerType of server deployment + :rtype: ServerType """ - # todo: return enum if not self._server_type: try: resp = self.get("/config") config = json.load(resp) if "user_workspaces_allowed" in config: - self._server_type = "ee" + self._server_type = ServerType.EE if "global_namespace" in config: - self._server_type = "ce" - self._global_namespace = config["global_namespace"] + self._server_type = ServerType.CE except ClientError as e: - self._server_type = "old" + self._server_type = ServerType.OLD return self._server_type From 3e33c825c2bae6d07e8f5cf2e4323fdf8640c0af Mon Sep 17 00:00:00 2001 From: uclaros Date: Wed, 16 Nov 2022 11:47:51 +0200 Subject: [PATCH 10/11] rename global_namespace to global_workspace, fix blunder --- mergin/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mergin/client.py b/mergin/client.py index 961540d9..69006bbe 100644 --- a/mergin/client.py +++ b/mergin/client.py @@ -346,7 +346,7 @@ def server_type(self): config = json.load(resp) if "user_workspaces_allowed" in config: self._server_type = ServerType.EE - if "global_namespace" in config: + if "global_workspace" in config: self._server_type = ServerType.CE except ClientError as e: self._server_type = ServerType.OLD @@ -619,7 +619,7 @@ def enough_storage_available(self, data): def user_info(self): server_type = self.server_type() - if server_type == "old": + if server_type == ServerType.OLD: resp = self.get("/v1/user/" + self.username()) else: resp = self.get("/v1/user/profile") From 1a2ae1e72e05c1c448757f48d0e7ac15b9f2f708 Mon Sep 17 00:00:00 2001 From: uclaros Date: Wed, 7 Dec 2022 13:00:19 +0200 Subject: [PATCH 11/11] use new api for server_type and workspace services --- mergin/client.py | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/mergin/client.py b/mergin/client.py index 69006bbe..5df33023 100644 --- a/mergin/client.py +++ b/mergin/client.py @@ -319,6 +319,7 @@ def user_service(self): Requests information about user from /user/service endpoint if such exists in self.url server. Returns response from server as JSON dict or None if endpoint is not found + This can be removed once our SaaS server is upgraded to support workspaces """ try: @@ -331,6 +332,24 @@ def user_service(self): return response + def workspace_service(self, workspace_id): + """ + This Requests information about a workspace service from /workspace/{id}/service endpoint, + if such exists in self.url server. + + Returns response from server as JSON dict or None if endpoint is not found + """ + + try: + response = self.get(f"/v1/workspace/{workspace_id}/service") + except ClientError as e: + self.log.debug(f"Unable to query for /workspace/{workspace_id}/service endpoint") + return + + response = json.loads(response.read()) + + return response + def server_type(self): """ Returns the deployment type of the server @@ -344,11 +363,13 @@ def server_type(self): try: resp = self.get("/config") config = json.load(resp) - if "user_workspaces_allowed" in config: - self._server_type = ServerType.EE - if "global_workspace" in config: + if config["server_type"] == "ce": self._server_type = ServerType.CE - except ClientError as e: + elif config["server_type"] == "ee": + self._server_type = ServerType.EE + elif config["server_type"] == "saas": + self._server_type = ServerType.SAAS + except (ClientError, KeyError): self._server_type = ServerType.OLD return self._server_type