Skip to content

Commit 2ef3dbd

Browse files
enhancement: Adding filter to quicksight delete_all methods (#1913)
* Adding filter to delete_all methods * renaming arg * adding delete all datasets tests * adding test with unmatching and matching filter * adding data to glue table * adding randomly generated suffixes to avoid conflicts * fixing syntax * consolidating tests * fixing test
1 parent bd51887 commit 2ef3dbd

File tree

2 files changed

+100
-25
lines changed

2 files changed

+100
-25
lines changed

awswrangler/quicksight/_delete.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Amazon QuickSight Delete Module."""
22

33
import logging
4+
import re
45
from typing import Any, Callable, Dict, Optional
56

67
import boto3
@@ -231,13 +232,17 @@ def delete_template(
231232
_delete(**args)
232233

233234

234-
def delete_all_dashboards(account_id: Optional[str] = None, boto3_session: Optional[boto3.Session] = None) -> None:
235+
def delete_all_dashboards(
236+
account_id: Optional[str] = None, regex_filter: Optional[str] = None, boto3_session: Optional[boto3.Session] = None
237+
) -> None:
235238
"""Delete all dashboards.
236239
237240
Parameters
238241
----------
239242
account_id : str, optional
240243
If None, the account ID will be inferred from your boto3 session.
244+
regex_filter : str, optional
245+
Regex regex_filter that will delete all dashboards with a match in their ``Name``
241246
boto3_session : boto3.Session(), optional
242247
Boto3 Session. The default boto3 session will be used if boto3_session receive None.
243248
@@ -255,16 +260,23 @@ def delete_all_dashboards(account_id: Optional[str] = None, boto3_session: Optio
255260
if account_id is None:
256261
account_id = sts.get_account_id(boto3_session=session)
257262
for dashboard in list_dashboards(account_id=account_id, boto3_session=session):
263+
if regex_filter:
264+
if not re.search(f"^{regex_filter}$", dashboard["Name"]):
265+
continue
258266
delete_dashboard(dashboard_id=dashboard["DashboardId"], account_id=account_id, boto3_session=session)
259267

260268

261-
def delete_all_datasets(account_id: Optional[str] = None, boto3_session: Optional[boto3.Session] = None) -> None:
269+
def delete_all_datasets(
270+
account_id: Optional[str] = None, regex_filter: Optional[str] = None, boto3_session: Optional[boto3.Session] = None
271+
) -> None:
262272
"""Delete all datasets.
263273
264274
Parameters
265275
----------
266276
account_id : str, optional
267277
If None, the account ID will be inferred from your boto3 session.
278+
regex_filter : str, optional
279+
Regex regex_filter that will delete all datasets with a match in their ``Name``
268280
boto3_session : boto3.Session(), optional
269281
Boto3 Session. The default boto3 session will be used if boto3_session receive None.
270282
@@ -282,16 +294,23 @@ def delete_all_datasets(account_id: Optional[str] = None, boto3_session: Optiona
282294
if account_id is None:
283295
account_id = sts.get_account_id(boto3_session=session)
284296
for dataset in list_datasets(account_id=account_id, boto3_session=session):
297+
if regex_filter:
298+
if not re.search(f"^{regex_filter}$", dataset["Name"]):
299+
continue
285300
delete_dataset(dataset_id=dataset["DataSetId"], account_id=account_id, boto3_session=session)
286301

287302

288-
def delete_all_data_sources(account_id: Optional[str] = None, boto3_session: Optional[boto3.Session] = None) -> None:
303+
def delete_all_data_sources(
304+
account_id: Optional[str] = None, regex_filter: Optional[str] = None, boto3_session: Optional[boto3.Session] = None
305+
) -> None:
289306
"""Delete all data sources.
290307
291308
Parameters
292309
----------
293310
account_id : str, optional
294311
If None, the account ID will be inferred from your boto3 session.
312+
regex_filter : str, optional
313+
Regex regex_filter that will delete all data sources with a match in their ``Name``
295314
boto3_session : boto3.Session(), optional
296315
Boto3 Session. The default boto3 session will be used if boto3_session receive None.
297316
@@ -309,16 +328,23 @@ def delete_all_data_sources(account_id: Optional[str] = None, boto3_session: Opt
309328
if account_id is None:
310329
account_id = sts.get_account_id(boto3_session=session)
311330
for data_source in list_data_sources(account_id=account_id, boto3_session=session):
331+
if regex_filter:
332+
if not re.search(f"^{regex_filter}$", data_source["Name"]):
333+
continue
312334
delete_data_source(data_source_id=data_source["DataSourceId"], account_id=account_id, boto3_session=session)
313335

314336

315-
def delete_all_templates(account_id: Optional[str] = None, boto3_session: Optional[boto3.Session] = None) -> None:
337+
def delete_all_templates(
338+
account_id: Optional[str] = None, regex_filter: Optional[str] = None, boto3_session: Optional[boto3.Session] = None
339+
) -> None:
316340
"""Delete all templates.
317341
318342
Parameters
319343
----------
320344
account_id : str, optional
321345
If None, the account ID will be inferred from your boto3 session.
346+
regex_filter : str, optional
347+
Regex regex_filter that will delete all templates with a match in their ``Name``
322348
boto3_session : boto3.Session(), optional
323349
Boto3 Session. The default boto3 session will be used if boto3_session receive None.
324350
@@ -336,4 +362,7 @@ def delete_all_templates(account_id: Optional[str] = None, boto3_session: Option
336362
if account_id is None:
337363
account_id = sts.get_account_id(boto3_session=session)
338364
for template in list_templates(account_id=account_id, boto3_session=session):
365+
if regex_filter:
366+
if not re.search(f"^{regex_filter}$", template["Name"]):
367+
continue
339368
delete_template(template_id=template["TemplateId"], account_id=account_id, boto3_session=session)

tests/test_quicksight.py

Lines changed: 67 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
import uuid
23

34
import boto3
45
import pytest
@@ -18,39 +19,40 @@ def test_quicksight(path, glue_database, glue_table):
1819
wr.s3.to_parquet(
1920
df=df, path=path, dataset=True, database=glue_database, table=glue_table, partition_cols=["par0", "par1"]
2021
)
22+
resource_name = f"test{str(uuid.uuid4())[:8]}"
2123

22-
wr.quicksight.delete_all_dashboards()
23-
wr.quicksight.delete_all_datasets()
24-
wr.quicksight.delete_all_data_sources()
25-
wr.quicksight.delete_all_templates()
24+
wr.quicksight.delete_all_dashboards(regex_filter="test.*")
25+
wr.quicksight.delete_all_datasets(regex_filter="test.*")
26+
wr.quicksight.delete_all_data_sources(regex_filter="test.*")
27+
wr.quicksight.delete_all_templates(regex_filter="test.*")
2628

2729
wr.quicksight.create_athena_data_source(
28-
name="test", allowed_to_manage=[wr.sts.get_current_identity_name()], tags={"Env": "aws-sdk-pandas"}
30+
name=resource_name, allowed_to_manage=[wr.sts.get_current_identity_name()], tags={"Env": "aws-sdk-pandas"}
2931
)
30-
assert wr.quicksight.describe_data_source("test")["Name"] == "test"
32+
assert wr.quicksight.describe_data_source(resource_name)["Name"] == resource_name
3133
assert (
32-
wr.quicksight.describe_data_source_permissions("test")[0]["Principal"].endswith(
34+
wr.quicksight.describe_data_source_permissions(resource_name)[0]["Principal"].endswith(
3335
wr.sts.get_current_identity_name()
3436
)
3537
is True
3638
)
37-
39+
dataset_name = f"{resource_name}-table"
3840
wr.quicksight.create_athena_dataset(
39-
name="test-table",
41+
name=dataset_name,
4042
database=glue_database,
4143
table=glue_table,
42-
data_source_name="test",
44+
data_source_name=resource_name,
4345
allowed_to_manage=[wr.sts.get_current_identity_name()],
4446
rename_columns={"iint16": "new_col"},
4547
cast_columns_types={"new_col": "STRING"},
4648
tag_columns={"string": [{"ColumnGeographicRole": "CITY"}, {"ColumnDescription": {"Text": "some description"}}]},
4749
)
48-
assert wr.quicksight.describe_dataset("test-table")["Name"] == "test-table"
50+
assert wr.quicksight.describe_dataset(dataset_name)["Name"] == dataset_name
4951

5052
wr.quicksight.create_athena_dataset(
51-
name="test-sql",
53+
name=f"{resource_name}-sql",
5254
sql=f"SELECT * FROM {glue_database}.{glue_table}",
53-
data_source_name="test",
55+
data_source_name=resource_name,
5456
import_mode="SPICE",
5557
allowed_to_use=[wr.sts.get_current_identity_name()],
5658
allowed_to_manage=[wr.sts.get_current_identity_name()],
@@ -60,23 +62,67 @@ def test_quicksight(path, glue_database, glue_table):
6062
tags={"foo": "boo"},
6163
)
6264

63-
ingestion_id = wr.quicksight.create_ingestion("test-sql")
65+
ingestion_id = wr.quicksight.create_ingestion(f"{resource_name}-sql")
6466
status = None
6567
while status not in ["FAILED", "COMPLETED", "CANCELLED"]:
66-
status = wr.quicksight.describe_ingestion(ingestion_id, "test-sql")["IngestionStatus"]
68+
status = wr.quicksight.describe_ingestion(ingestion_id, f"{resource_name}-sql")["IngestionStatus"]
6769
assert status == "COMPLETED"
6870

69-
ingestion_id = wr.quicksight.create_ingestion("test-sql")
70-
wr.quicksight.cancel_ingestion(ingestion_id, "test-sql")
71-
assert len(wr.quicksight.list_ingestions("test-sql")) == 3
71+
ingestion_id = wr.quicksight.create_ingestion(f"{resource_name}-sql")
72+
wr.quicksight.cancel_ingestion(ingestion_id, f"{resource_name}-sql")
73+
assert len(wr.quicksight.list_ingestions(f"{resource_name}-sql")) == 3
7274

7375
wr.quicksight.list_groups()
7476
wr.quicksight.list_iam_policy_assignments()
7577
wr.quicksight.list_iam_policy_assignments_for_user(wr.sts.get_current_identity_name())
7678
wr.quicksight.list_user_groups(wr.sts.get_current_identity_name())
7779
wr.quicksight.list_users()
78-
wr.quicksight.get_dataset_ids("test-sql")
80+
wr.quicksight.get_dataset_ids(f"{resource_name}-sql")
7981
wr.quicksight.get_data_source_ids("test")
8082

81-
wr.quicksight.delete_all_datasets()
82-
wr.quicksight.delete_all_data_sources()
83+
wr.quicksight.delete_all_datasets(regex_filter="test.*")
84+
wr.quicksight.delete_all_data_sources(regex_filter="test.*")
85+
86+
87+
def test_quicksight_delete_all_datasources_filter():
88+
wr.quicksight.delete_all_data_sources(regex_filter="test.*")
89+
resource_name = "test-delete"
90+
wr.quicksight.create_athena_data_source(
91+
name=resource_name, allowed_to_manage=[wr.sts.get_current_identity_name()], tags={"Env": "aws-sdk-pandas"}
92+
)
93+
wr.quicksight.delete_all_data_sources(regex_filter="test-no-delete")
94+
95+
assert len(wr.quicksight.get_data_source_ids(resource_name)) == 1
96+
97+
wr.quicksight.delete_all_data_sources(regex_filter="test-delete.*")
98+
assert len(wr.quicksight.get_data_source_ids(resource_name)) == 0
99+
100+
101+
def test_quicksight_delete_all_datasets(path, glue_database, glue_table):
102+
df = get_df_quicksight()
103+
wr.s3.to_parquet(
104+
df=df, path=path, dataset=True, database=glue_database, table=glue_table, partition_cols=["par0", "par1"]
105+
)
106+
wr.quicksight.delete_all_datasets(regex_filter="test.*")
107+
wr.quicksight.delete_all_data_sources(regex_filter="test.*")
108+
109+
resource_name = f"test{str(uuid.uuid4())[:8]}"
110+
wr.quicksight.create_athena_data_source(
111+
name=resource_name, allowed_to_manage=[wr.sts.get_current_identity_name()], tags={"Env": "aws-sdk-pandas"}
112+
)
113+
wr.quicksight.create_athena_dataset(
114+
name=f"{resource_name}-sql",
115+
sql=f"SELECT * FROM {glue_database}.{glue_table}",
116+
data_source_name=resource_name,
117+
import_mode="SPICE",
118+
allowed_to_use=[wr.sts.get_current_identity_name()],
119+
allowed_to_manage=[wr.sts.get_current_identity_name()],
120+
rename_columns={"iint16": "new_col"},
121+
cast_columns_types={"new_col": "STRING"},
122+
tag_columns={"string": [{"ColumnGeographicRole": "CITY"}, {"ColumnDescription": {"Text": "some description"}}]},
123+
tags={"foo": "boo"},
124+
)
125+
wr.quicksight.delete_all_datasets(regex_filter="test.*")
126+
wr.quicksight.delete_all_data_sources(regex_filter="test.*")
127+
128+
assert len(wr.quicksight.get_dataset_ids(resource_name)) == 0

0 commit comments

Comments
 (0)