diff --git a/arlas/cli/cli.py b/arlas/cli/cli.py index 9db7703..fe3fb68 100644 --- a/arlas/cli/cli.py +++ b/arlas/cli/cli.py @@ -1,3 +1,4 @@ +import requests import typer import os import sys @@ -62,6 +63,18 @@ def init( def main(): + try: + json = requests.get('https://pypi.org/pypi/arlas.cli/json').json() + if json: + version = json.get("info", {}).get("version", None) + if version: + if not version == arlas_cli_version: + print("WARNING: You are not using the latest version of arlas_cli! Please update with:", file=sys.stderr) + print("pip3.10 install arlas_cli==" + version, file=sys.stderr) + else: + print("WARNING: Can not identify arlas.cli version.", file=sys.stderr) + except Exception: + print("WARNING: Can not contact pypi.org to identify if this arlas_cli is the latest version.", file=sys.stderr) app.add_typer(collections, name="collections") app.add_typer(indices, name="indices") app.add_typer(persist, name="persist") diff --git a/arlas/cli/collections.py b/arlas/cli/collections.py index 2eeac96..5de687c 100644 --- a/arlas/cli/collections.py +++ b/arlas/cli/collections.py @@ -52,6 +52,54 @@ def describe( print(tab) +@collections.command(help="Set collection visibility to public") +def public( + collection: str = typer.Argument(help="Collection's name") +): + config = variables["arlas"] + ispublic = Service.set_collection_visibility(config, collection, public=True) + print("{} is {}".format(collection, "public" if ispublic else "private")) + + +@collections.command(help="Set collection visibility to private") +def private( + collection: str = typer.Argument(help="Collection's name") +): + config = variables["arlas"] + ispublic = Service.set_collection_visibility(config, collection, public=False) + print("{} is {}".format(collection, "public" if ispublic else "private")) + + +@collections.command(help="Share the collection with the organisation") +def share( + collection: str = typer.Argument(help="Collection's name"), + organisation: str = typer.Argument(help="Organisation's name") +): + config = variables["arlas"] + shared = Service.share_with(config, collection, organisation) + print("{} is shared with {}".format(collection, ", ".join(shared))) + + +@collections.command(help="Share the collection with the organisation") +def unshare( + collection: str = typer.Argument(help="Collection's name"), + organisation: str = typer.Argument(help="Organisation's name") +): + config = variables["arlas"] + shared = Service.unshare_with(config, collection, organisation) + print("{} is shared with {}".format(collection, ", ".join(shared))) + + +@collections.command(help="Set the collection display name", name="name") +def set_display_name( + collection: str = typer.Argument(help="Collection's name"), + name: str = typer.Argument(help="The display name") +): + config = variables["arlas"] + name = Service.set_collection_display_name(config, collection, name) + print("{} display name is {}".format(collection, name)) + + @collections.command(help="Display a sample of a collection") def sample( collection: str = typer.Argument(help="Collection's name"), diff --git a/arlas/cli/service.py b/arlas/cli/service.py index 129c9e9..0565688 100644 --- a/arlas/cli/service.py +++ b/arlas/cli/service.py @@ -147,6 +147,46 @@ def list_indices(arlas: str) -> list[list[str]]: ]) return table + def set_collection_visibility(arlas: str, collection: str, public: bool): + description = Service.__arlas__(arlas, "/".join(["explore", collection, "_describe"])) + doc = { + "shared": description.get("params", {}).get("organisations", {}).get("shared", []), + "public": public + } + return Service.__arlas__(arlas, "/".join(["collections", collection, "organisations"]), patch=json.dumps(doc)).get("params", {}).get("organisations", {}).get("public") + + def set_collection_display_name(arlas: str, collection: str, name: str): + doc = name + return Service.__arlas__(arlas, "/".join(["collections", collection, "display_names"]), patch=json.dumps(doc)).get("params", {}).get("display_names", {}).get("collection") + + def share_with(arlas: str, collection: str, organisation: str): + description = Service.__arlas__(arlas, "/".join(["explore", collection, "_describe"])) + orgs = description.get("params", {}).get("organisations", {}).get("shared", []) + if organisation not in orgs: + orgs.append(organisation) + doc = { + "organisations": { + "shared": orgs, + "public": description.get("params", {}).get("organisations", {}).get("public", False) + } + } + return Service.__arlas__(arlas, "/".join(["collections", collection, "organisations"]), patch=json.dumps(doc)).get("params", {}).get("organisations", {}).get("shared") + + def unshare_with(arlas: str, collection: str, organisation: str): + description = Service.__arlas__(arlas, "/".join(["explore", collection, "_describe"])) + orgs: list = description.get("params", {}).get("organisations", {}).get("shared", []) + if organisation in orgs: + orgs.remove(organisation) + else: + print("Warning: {} not shared with {}".format(collection, organisation)) + doc = { + "organisations": { + "shared": orgs, + "public": description.get("params", {}).get("organisations", {}).get("public", False) + } + } + return Service.__arlas__(arlas, "/".join(["collections", collection, "organisations"]), patch=json.dumps(doc)).get("params", {}).get("organisations", {}).get("shared") + def describe_collection(arlas: str, collection: str) -> list[list[str]]: description = Service.__arlas__(arlas, "/".join(["explore", collection, "_describe"])) table = [["field name", "type"]] @@ -339,7 +379,7 @@ def __get_fields__(origin: list[str], properties: dict[str:dict]): fields.append([".".join(o), type]) return fields - def __arlas__(arlas: str, suffix, post=None, put=None, delete=None, service=Services.arlas_server): + def __arlas__(arlas: str, suffix, post=None, put=None, patch=None, delete=None, service=Services.arlas_server): configuration: ARLAS = Configuration.settings.arlas.get(arlas, None) if configuration is None: print("Error: arlas configuration ({}) not found among [{}] for {}.".format(arlas, ", ".join(Configuration.settings.arlas.keys()), service.name), file=sys.stderr) @@ -347,15 +387,13 @@ def __arlas__(arlas: str, suffix, post=None, put=None, delete=None, service=Serv if service == Services.arlas_server: __headers__ = configuration.server.headers.copy() endpoint: Resource = configuration.server - else: - if service == Services.persistence_server: - __headers__ = configuration.persistence.headers.copy() - endpoint: Resource = configuration.persistence - else: - if service == Services.iam: - __headers__ = configuration.authorization.token_url.headers.copy() - endpoint: Resource = configuration.authorization.token_url.model_copy() - endpoint.location = endpoint.location.rsplit('/', 1)[0] + elif service == Services.persistence_server: + __headers__ = configuration.persistence.headers.copy() + endpoint: Resource = configuration.persistence + elif service == Services.iam: + __headers__ = configuration.authorization.token_url.headers.copy() + endpoint: Resource = configuration.authorization.token_url.model_copy() + endpoint.location = endpoint.location.rsplit('/', 1)[0] if Configuration.settings.arlas.get(arlas).authorization is not None: __headers__["Authorization"] = "Bearer " + Service.__get_token__(arlas) @@ -366,6 +404,9 @@ def __arlas__(arlas: str, suffix, post=None, put=None, delete=None, service=Serv if post: data = post method = "POST" + if patch: + data = patch + method = "PATCH" if put: data = put method = "PUT" @@ -408,13 +449,12 @@ def __es__(arlas: str, suffix, post=None, put=None, delete=None, exit_on_failure r = Service.__request__(url, method, data, __headers, auth) if r.ok: return r.content + elif exit_on_failure: + print("Error: request failed with status {}: {}".format(str(r.status_code), r.content), file=sys.stderr) + print(" url: {}".format(url), file=sys.stderr) + exit(1) else: - if exit_on_failure: - print("Error: request failed with status {}: {}".format(str(r.status_code), r.content), file=sys.stderr) - print(" url: {}".format(url), file=sys.stderr) - exit(1) - else: - raise RequestException(r.status_code, r.content) + raise RequestException(r.status_code, r.content) def __request__(url: str, method: str, data: any = None, headers: dict[str, str] = {}, auth: tuple[str, str | None] = None) -> requests.Response: if Service.curl: @@ -423,14 +463,14 @@ def __request__(url: str, method: str, data: any = None, headers: dict[str, str] print(" -d {}".format(data)) if method.upper() == "POST": r = requests.post(url, data=data, headers=headers, auth=auth, verify=False) + elif method.upper() == "PATCH": + r = requests.patch(url, data=data, headers=headers, auth=auth, verify=False) + elif method == "PUT": + r = requests.put(url, data=data, headers=headers, auth=auth, verify=False) + elif method == "DELETE": + r = requests.delete(url, headers=headers, auth=auth, verify=False) else: - if method == "PUT": - r = requests.put(url, data=data, headers=headers, auth=auth, verify=False) - else: - if method == "DELETE": - r = requests.delete(url, headers=headers, auth=auth, verify=False) - else: - r = requests.get(url, headers=headers, auth=auth, verify=False) + r = requests.get(url, headers=headers, auth=auth, verify=False) return r def __fetch__(resource: Resource, bytes: bool = False): @@ -474,13 +514,12 @@ def __get_token__(arlas: str) -> str: if r.status_code >= 200 and r.status_code < 300: if r.json().get("accessToken"): return r.json()["accessToken"] + elif r.json().get("access_token"): + return r.json()["access_token"] else: - if r.json().get("access_token"): - return r.json()["access_token"] - else: - print("Error: Failed to find access token in response {}".format(r.content), file=sys.stderr) - print(" url: {}".format(auth.token_url.location), file=sys.stderr) - exit(1) + print("Error: Failed to find access token in response {}".format(r.content), file=sys.stderr) + print(" url: {}".format(auth.token_url.location), file=sys.stderr) + exit(1) else: print("Error: request to get token failed with status {}: {}".format(str(r.status_code), r.content), file=sys.stderr) print(" url: {}".format(auth.token_url.location), file=sys.stderr)