diff --git a/cortexapps_cli/commands/catalog.py b/cortexapps_cli/commands/catalog.py index 2717b891..26d73f00 100644 --- a/cortexapps_cli/commands/catalog.py +++ b/cortexapps_cli/commands/catalog.py @@ -123,22 +123,29 @@ def delete( client.delete("api/v1/catalog/" + tag) @app.command() -def delete_by_type( +def delete_by_id( ctx: typer.Context, - types: CatalogCommandOptions.types = None, + ids: Optional[List[str]] = typer.Option(None, "--id", "-i", help="CID of an entity to delete (e.g. en2da8159dbeefb974). Can be specified multiple times.", show_default=False), + file_input: Annotated[Optional[typer.FileText], typer.Option("--file", "-f", help="File containing one CID per line; can be passed as stdin with -, example: -f-")] = None, + batch_size: int = typer.Option(100, "--batch-size", hidden=True, help="Number of IDs per API call when processing file input"), ): """ - Dangerous operation that will delete all entities that are of the given type + Delete entities by CID. Accepts one or more --id flags, or a file with one CID per line. """ - client = ctx.obj["client"] + if ids and file_input: + raise typer.BadParameter("Specify either --id or --file, not both") - #TODO: check if types is a regex of form: ([-A-Za-z]+,)+ + if not ids and not file_input: + raise typer.BadParameter("One of --id or --file is required") - params = { - "types": types - } + client = ctx.obj["client"] - client.delete("api/v1/catalog", params=params) + if file_input: + all_ids = [line.strip() for line in file_input if line.strip()] + for i in range(0, len(all_ids), batch_size): + client.delete("api/v1/catalog", params={"ids": all_ids[i:i + batch_size]}) + else: + client.delete("api/v1/catalog", params={"ids": ids}) @app.command() def descriptor( diff --git a/data/run-time/catalog-delete-by-type-entity.yaml b/data/run-time/catalog-delete-by-type-entity.yaml deleted file mode 100644 index b33be910..00000000 --- a/data/run-time/catalog-delete-by-type-entity.yaml +++ /dev/null @@ -1,5 +0,0 @@ -openapi: 3.0.1 -info: - title: Delete By Type Test Entity - x-cortex-tag: cli-test-delete-by-type-entity - x-cortex-type: cli-test-delete-by-type diff --git a/data/run-time/entity-type-delete-by-type.json b/data/run-time/entity-type-delete-by-type.json deleted file mode 100644 index 8af27dbb..00000000 --- a/data/run-time/entity-type-delete-by-type.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "description": "Temporary entity type for testing delete-by-type.", - "name": "CLI Test Delete By Type", - "schema": {}, - "type": "cli-test-delete-by-type" -} diff --git a/tests/test_catalog_delete_by_id.py b/tests/test_catalog_delete_by_id.py new file mode 100644 index 00000000..5b12343d --- /dev/null +++ b/tests/test_catalog_delete_by_id.py @@ -0,0 +1,59 @@ +from tests.helpers.utils import * +import tempfile +import os + +def _create_entity(tag): + content = f"""openapi: 3.0.1 +info: + title: Delete By ID Test Entity + x-cortex-tag: {tag} + x-cortex-type: service +""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + f.write(content) + return f.name + +def test_delete_by_id_flag(): + tag = "cli-test-delete-by-id-flag" + path = _create_entity(tag) + try: + cli(["catalog", "create", "-f", path]) + + response = cli(["catalog", "details", "-t", tag]) + cid = response['id'] + assert cid.startswith("en"), f"Expected a CID starting with 'en', got: {cid}" + + cli(["catalog", "delete-by-id", "--id", cid]) + + response = cli(["catalog", "details", "-t", tag], ReturnType.RAW) + assert "HTTP Error 404:" in response.stdout, "Entity should be deleted (404 expected)" + finally: + os.unlink(path) + +def test_delete_by_id_file(): + tag1 = "cli-test-delete-by-id-file-1" + tag2 = "cli-test-delete-by-id-file-2" + path1 = _create_entity(tag1) + path2 = _create_entity(tag2) + try: + cli(["catalog", "create", "-f", path1]) + cli(["catalog", "create", "-f", path2]) + + cid1 = cli(["catalog", "details", "-t", tag1])['id'] + cid2 = cli(["catalog", "details", "-t", tag2])['id'] + + with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f: + f.write(f"{cid1}\n{cid2}\n") + ids_file = f.name + + try: + cli(["catalog", "delete-by-id", "--file", ids_file]) + + for tag in [tag1, tag2]: + response = cli(["catalog", "details", "-t", tag], ReturnType.RAW) + assert "HTTP Error 404:" in response.stdout, f"Entity {tag} should be deleted (404 expected)" + finally: + os.unlink(ids_file) + finally: + os.unlink(path1) + os.unlink(path2) diff --git a/tests/test_catalog_delete_by_type.py b/tests/test_catalog_delete_by_type.py deleted file mode 100644 index 0fe5e796..00000000 --- a/tests/test_catalog_delete_by_type.py +++ /dev/null @@ -1,20 +0,0 @@ -from tests.helpers.utils import * - -#@pytest.mark.skip(reason="Disabled until CET-24425 is resolved.") -def test(): - # Create the entity type - cli(["entity-types", "create", "-f", "data/run-time/entity-type-delete-by-type.json"], ReturnType.RAW) - - # Create an entity of that type - cli(["catalog", "create", "-f", "data/run-time/catalog-delete-by-type-entity.yaml"]) - - # Verify the entity exists - response = cli(["catalog", "list", "-t", "cli-test-delete-by-type"]) - assert response['total'] > 0, "Should find at least 1 entity of type 'cli-test-delete-by-type'" - - # Delete all entities of that type - cli(["catalog", "delete-by-type", "-t", "cli-test-delete-by-type"]) - - # Verify 0 entities remain of that type - response = cli(["catalog", "list", "-t", "cli-test-delete-by-type"]) - assert response['total'] == 0, f"Expected 0 entities of x-cortex-type:cli-test-delete-by-type, but found: {response['total']}"