pvw-cli 1.0.6__py3-none-any.whl → 1.0.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pvw-cli might be problematic. Click here for more details.

@@ -1,12 +1,11 @@
1
- purviewcli/__init__.py,sha256=4Em70RIQIEjQmZFrzWalQOgeNx_7jLGYVoevuxAjEwg,423
1
+ purviewcli/__init__.py,sha256=21ukxxDPxmUrkqK6GkhoW6I4r54OajBPaPO_WTppCrY,413
2
2
  purviewcli/__main__.py,sha256=n_PFo1PjW8L1OKCNLsW0vlVSo8tzac_saEYYLTu93iQ,372
3
- purviewcli/cli/__init__.py,sha256=vrqitIgq2VoisqaHifSpvfZ7DDRpskoF9YUUiPNOKBE,73
3
+ purviewcli/cli/__init__.py,sha256=UGMctZaXXsV2l2ycnmhTgyksH81_JBQjAPq3oRF2Dqk,56
4
4
  purviewcli/cli/account.py,sha256=YENHkBD0VREajDqtlkTJ-zUvq8aq7LF52HDSOSsgku8,7080
5
- purviewcli/cli/cli.py,sha256=imIuh8FoyT8OQGJAyoIYEP8ArAEtnF7GzRc-m1uQ3G8,5930
6
- purviewcli/cli/collections.py,sha256=eIhqdURUkc9SthZYJBbAAp6rYGUYylqPyUfjO0eaLhU,4416
7
- purviewcli/cli/data_product.py,sha256=WAoUlvugKAR0OHSDuzgtZ_3-QZVAtPzO4cmPWO6qgu0,11190
5
+ purviewcli/cli/cli.py,sha256=ZuthJ24ixbKmqRd-1zv8-jXQtQjn2qQR2plg8TyD0Mg,5801
6
+ purviewcli/cli/collections.py,sha256=cuSp-XiONXyXWHuUGrDYL462j7WNYRF08mU99gPQlg8,18606
8
7
  purviewcli/cli/domain.py,sha256=zI7YPhcCa4u4MIwnWHQPngTUCKOs6C_rEkdpW-Kl6hM,20897
9
- purviewcli/cli/entity.py,sha256=j3TCedQq9ELa-dz2BOLw_l1jhiwqrjpsGKPlS789Wr0,69113
8
+ purviewcli/cli/entity.py,sha256=ZEm-j9BoqFTjkmdm0IftEyn7XGs_W-1LY9pQ0oQgrmQ,87860
10
9
  purviewcli/cli/glossary.py,sha256=Lt4ifyESuuaZLOzssv4GcQEO2AQKYS8Zy8L9UFfdglU,25696
11
10
  purviewcli/cli/insight.py,sha256=Kevqla6iZ7hPgb-gIWQiXSl2er-N0-Z7Q--IH1icWbs,3853
12
11
  purviewcli/cli/lineage.py,sha256=J7_KSwtdElWc2N5i5l1YBP6QzaWrqP6nnPrjDKzVEkE,21356
@@ -14,14 +13,14 @@ purviewcli/cli/management.py,sha256=ArSN7ADWMKUJ-fVYjPiuab4g666GQUmxU2WZov3JxzI,
14
13
  purviewcli/cli/policystore.py,sha256=Xj3Yx6kNt9W8v577x2iacqSlPatR6AuFfMBpjtHTj1E,4439
15
14
  purviewcli/cli/relationship.py,sha256=Ky4klI-clKh6sRK7bsI7SwgtVrpo1ljegVyrbqjkeOY,2682
16
15
  purviewcli/cli/scan.py,sha256=91iKDH8iVNJKndJAisrKx3J4HRoPH2qfmxguLZH3xHY,13807
17
- purviewcli/cli/search.py,sha256=MQazyv_DumX6YJAWzQyN1PXwU5xiteoSgTcEwHHBxc8,5801
16
+ purviewcli/cli/search.py,sha256=B0Ae3-9JCTKICOkJrYS29tMiFTuLJzlx0ISW_23OHF0,13599
18
17
  purviewcli/cli/share.py,sha256=QRZhHM59RxdYqXOjSYLfVRZmjwMg4Y-bWxMSQVTQiIE,20197
19
18
  purviewcli/cli/types.py,sha256=zo_8rAqDQ1vqi5y-dBh_sVY6i16UaJLLx_vBJBfZrrw,23729
19
+ purviewcli/cli/unified_catalog.py,sha256=hW33LqjfP_wK9g72RGfCEOdKqurV2dRhGubhTJVu9p0,27087
20
20
  purviewcli/cli/workflow.py,sha256=DZLzVCZVp5k7gfBrh7YTARBHHEDgffQ8M0CBQh-h548,13260
21
21
  purviewcli/client/__init__.py,sha256=qjhTkXkgxlNUY3R1HkrT_Znt03-2d8JDolPVOeVv2xI,37
22
22
  purviewcli/client/_account.py,sha256=5lacA7vvjGBLHUDRjFR7B5E8eN6T07rctVDRXR9JFTY,12397
23
23
  purviewcli/client/_collections.py,sha256=17ma6aD6gftIe-Nhwy96TPE42O6qp0hmw84xei4VPpo,12101
24
- purviewcli/client/_data_product.py,sha256=UFN0ZRvLJk05g6WMhlYbCaD6IM8J0G_WbBfy2aefQ9g,7372
25
24
  purviewcli/client/_domain.py,sha256=Yt4RsIoGWz6ND9Ii4CtoGM6leEAL_KNXYcp6AK9KMqs,3744
26
25
  purviewcli/client/_entity.py,sha256=6yU_j0RSGUF-xzzOhLaensm6ulkmrQPWkSar6pycC04,20601
27
26
  purviewcli/client/_glossary.py,sha256=7kB3RXVOCCR1RGlaALmr_BwN6S76-xCoyVqD5ZMzt-k,20985
@@ -31,16 +30,16 @@ purviewcli/client/_management.py,sha256=2_ZXRSeEzGuHv1muUbn8mQb07enYYuHPI3NskIHI
31
30
  purviewcli/client/_policystore.py,sha256=wx9Yw6wcvj236ZmmitUKxW5u4YAtfJqAFNuxMpa7HIU,18078
32
31
  purviewcli/client/_relationship.py,sha256=KJZRltrzpTw7cfKjlZH2MuoTPS7eHxxp3cqU2etDSEA,9997
33
32
  purviewcli/client/_scan.py,sha256=2atEBD-kKWtFuBSWh2P0cwp42gfg7qgwWq-072QZMs4,15154
34
- purviewcli/client/_search.py,sha256=IlqqBfDZ4dJqPh1PShMrkIm0_x_5wwMzIqkYZMUWMkQ,11678
33
+ purviewcli/client/_search.py,sha256=vUDgjZtnNkHaCqsCXPp1Drq9Kknrkid17RNSXZhi1yw,11890
35
34
  purviewcli/client/_share.py,sha256=vKENIhePuzi3WQazNfv5U9y-6yxRk222zrFA-SGh1pc,10494
36
35
  purviewcli/client/_types.py,sha256=ONa3wh1F02QOVy51UGq54121TkqRcWczdXIvNqPIFU0,15454
37
- purviewcli/client/_unified_catalog.py,sha256=ySIx2t5gfCGn4KDqNB2AvHQbfB4UC4e5DkujoyEZu2I,11814
36
+ purviewcli/client/_unified_catalog.py,sha256=uQ2i3UQLHC6-_XkdvqNaJcOP7q9LQ0kzwPzv18BuGjE,11850
38
37
  purviewcli/client/_workflow.py,sha256=ohZpEOHxfXoxVqgUWz7HjKebWxYRngBFgfEGu3fS3vY,17886
39
38
  purviewcli/client/api_client.py,sha256=ZLpNdItu8H2Rfj0HCud2A67Gml7YCS5ZuhR5lrR0r5g,22637
40
39
  purviewcli/client/business_rules.py,sha256=VR4QqOE1Pg0nFjqAE-zbt-KqIenvzImLU-TBLki9bYc,27560
41
40
  purviewcli/client/config.py,sha256=pQIA168XxeddTSaZJ5qXI7KolIrLqDyBTgbILdDzEs0,7963
42
41
  purviewcli/client/data_quality.py,sha256=lAb-ma5MY2nyY4Dq8Q5wN9wzY0J9FikiQN8jPO2u6VU,14988
43
- purviewcli/client/endpoint.py,sha256=3p7NCS3h8cy827vwLGl8-n4w42_e7BSbkHI7_LVaQ4M,2477
42
+ purviewcli/client/endpoint.py,sha256=Th8fr81cTrPARyUdG932rLctv40t3k1JEfeKXKbVsDY,3044
44
43
  purviewcli/client/endpoints.py,sha256=GcY4ygmBQ2h14uY3uXcyJofmlK0ACCjl7uR33QDaO2w,27066
45
44
  purviewcli/client/exceptions.py,sha256=3UA6wUa-SLxfhGvkdOgJouvOJGulvG1zt6eRN_4imsg,1244
46
45
  purviewcli/client/lineage_visualization.py,sha256=rFKr5cYDauwg4QEHKI7kkqKdHQKgMG6CSUPOmhAz3fI,29644
@@ -49,11 +48,11 @@ purviewcli/client/rate_limiter.py,sha256=wn9iUrL79oq3s7nBo2shsAtiiLgH0Zbb0mzHXXZ
49
48
  purviewcli/client/retry_handler.py,sha256=eMxtDTUYsa4_BgAsnE1MPjitnYqCQddNKwnNTTk1w6Q,3960
50
49
  purviewcli/client/scanning_operations.py,sha256=qPZGuVVbi1cV1Z-K_WKZ-Up3vlk6O8hGArsljfWpZ_Y,22304
51
50
  purviewcli/client/settings.py,sha256=nYdnYurTZsgv9vcgljnzVxLPtYVl9q6IplqOzi1aRvI,27
52
- purviewcli/client/sync_client.py,sha256=IEuQVEud7bpBCwqfmAFGIj9c5Q_m2vb9uoK4ugGfa9c,5977
51
+ purviewcli/client/sync_client.py,sha256=Bad9T7jdmKYH8YEI1mtOow6d_2QSfZMRnQij2-ovj-I,9249
53
52
  purviewcli/plugins/__init__.py,sha256=rpt3OhFt_wSE_o8Ga8AXvw1pqkdBxLmjrhYtE_-LuJo,29
54
53
  purviewcli/plugins/plugin_system.py,sha256=C-_dL4FUj90o1JS7Saxkpov6fz0GIF5PFhZTYwqBkWE,26774
55
- pvw_cli-1.0.6.dist-info/METADATA,sha256=LNBPiYTOSi2u0zYiNFkptV6bfwyVCcYKCkKcaZfVppU,12624
56
- pvw_cli-1.0.6.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
57
- pvw_cli-1.0.6.dist-info/entry_points.txt,sha256=VI6AAbc6sWahOCX7sn_lhJIr9OiJM0pHF7rmw1YVGlE,82
58
- pvw_cli-1.0.6.dist-info/top_level.txt,sha256=LrADzPoKwF1xY0pGKpWauyOVruHCIWKCkT7cwIl6IuI,11
59
- pvw_cli-1.0.6.dist-info/RECORD,,
54
+ pvw_cli-1.0.9.dist-info/METADATA,sha256=R8eaIx1HrazAy3zZyg8eYzbBmTpnDk3UXL0eJRTD1TQ,25246
55
+ pvw_cli-1.0.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
56
+ pvw_cli-1.0.9.dist-info/entry_points.txt,sha256=VI6AAbc6sWahOCX7sn_lhJIr9OiJM0pHF7rmw1YVGlE,82
57
+ pvw_cli-1.0.9.dist-info/top_level.txt,sha256=LrADzPoKwF1xY0pGKpWauyOVruHCIWKCkT7cwIl6IuI,11
58
+ pvw_cli-1.0.9.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.45.1)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,278 +0,0 @@
1
- import click
2
- import csv
3
- import json
4
- import tempfile
5
- import os
6
- from rich.console import Console
7
- from purviewcli.client._unified_catalog import UnifiedCatalogDataProduct
8
-
9
- console = Console()
10
-
11
- @click.group()
12
- def data_product():
13
- """Manage data products in Microsoft Purview using Unified Catalog API."""
14
- pass
15
-
16
- @data_product.command()
17
- @click.option('--name', required=True, help="Name of the data product")
18
- @click.option('--description', required=False, help="Description of the data product")
19
- @click.option('--domain-guid', required=False, help="GUID of the domain to associate with")
20
- def create(name, description, domain_guid):
21
- """Create a new data product using Unified Catalog API."""
22
- try:
23
- data_product_client = UnifiedCatalogDataProduct()
24
- result = data_product_client.create_data_product(
25
- name=name,
26
- description=description,
27
- domain_guid=domain_guid
28
- )
29
-
30
- if result.get("status") == "error":
31
- console.print(f"[red]ERROR:[/red] {result.get('message', 'Unknown error')}")
32
- return
33
-
34
- console.print(f"[green]SUCCESS:[/green] Created data product '{name}'")
35
- console.print(json.dumps(result, indent=2))
36
-
37
- except Exception as e:
38
- console.print(f"[red]ERROR:[/red] {str(e)}")
39
-
40
- @data_product.command()
41
- @click.option('--data-product-id', required=True, help="ID of the data product")
42
- def show(data_product_id):
43
- """Show details of a data product using Unified Catalog API."""
44
- try:
45
- data_product_client = UnifiedCatalogDataProduct()
46
- result = data_product_client.get_data_product(data_product_id)
47
-
48
- if result.get("status") == "error":
49
- console.print(f"[red]ERROR:[/red] {result.get('message', 'Unknown error')}")
50
- return
51
-
52
- console.print(json.dumps(result, indent=2))
53
- except Exception as e:
54
- console.print(f"[red]ERROR:[/red] {str(e)}")
55
-
56
- @data_product.command()
57
- @click.option('--data-product-id', required=True, help="ID of the data product")
58
- @click.option('--name', required=False, help="New name for the data product")
59
- @click.option('--description', required=False, help="New description for the data product")
60
- def update(data_product_id, name, description):
61
- """Update a data product using Unified Catalog API."""
62
- try:
63
- data_product_client = UnifiedCatalogDataProduct()
64
- updates = {}
65
- if name:
66
- updates['name'] = name
67
- if description:
68
- updates['description'] = description
69
-
70
- if not updates:
71
- console.print("[yellow]WARNING:[/yellow] No updates specified")
72
- return
73
-
74
- result = data_product_client.update_data_product(data_product_id, updates)
75
-
76
- if result.get("status") == "error":
77
- console.print(f"[red]ERROR:[/red] {result.get('message', 'Unknown error')}")
78
- return
79
-
80
- console.print(f"[green]SUCCESS:[/green] Updated data product '{data_product_id}'")
81
- console.print(json.dumps(result, indent=2))
82
-
83
- except Exception as e:
84
- console.print(f"[red]ERROR:[/red] {str(e)}")
85
-
86
- @data_product.command()
87
- @click.option('--data-product-id', required=True, help="ID of the data product")
88
- def delete(data_product_id):
89
- """Delete a data product using Unified Catalog API."""
90
- try:
91
- data_product_client = UnifiedCatalogDataProduct()
92
- result = data_product_client.delete_data_product(data_product_id)
93
-
94
- if result.get("status") == "error":
95
- console.print(f"[red]ERROR:[/red] {result.get('message', 'Unknown error')}")
96
- return
97
-
98
- console.print(f"[green]SUCCESS:[/green] Deleted data product '{data_product_id}'")
99
- console.print(json.dumps(result, indent=2))
100
- except Exception as e:
101
- console.print(f"[red]ERROR:[/red] {str(e)}")
102
-
103
- @data_product.command()
104
- @click.option('--limit', default=50, help="Maximum number of data products to list")
105
- @click.option('--skip', default=0, help="Number of data products to skip")
106
- @click.option('--domain-id', required=False, help="Filter by governance domain ID")
107
- def list(limit, skip, domain_id):
108
- """List data products using Unified Catalog API."""
109
- try:
110
- data_product_client = UnifiedCatalogDataProduct()
111
- result = data_product_client.list_data_products(
112
- limit=limit,
113
- offset=skip,
114
- domain_id=domain_id
115
- )
116
-
117
- if result.get("status") == "error":
118
- console.print(f"[red]ERROR:[/red] {result.get('message', 'Unknown error')}")
119
- return
120
-
121
- console.print(json.dumps(result, indent=2))
122
- except Exception as e:
123
- console.print(f"[red]ERROR:[/red] {str(e)}")
124
-
125
- @data_product.command()
126
- @click.option('--data-product-id', required=True, help="ID of the data product")
127
- def publish(data_product_id):
128
- """Publish a data product using Unified Catalog API."""
129
- try:
130
- data_product_client = UnifiedCatalogDataProduct()
131
- result = data_product_client.update_data_product(data_product_id, {"status": "Published"})
132
-
133
- if result.get("status") == "error":
134
- console.print(f"[red]ERROR:[/red] {result.get('message', 'Unknown error')}")
135
- return
136
-
137
- console.print(f"[green]SUCCESS:[/green] Published data product '{data_product_id}'")
138
- console.print(json.dumps(result, indent=2))
139
- except Exception as e:
140
- console.print(f"[red]ERROR:[/red] {str(e)}")
141
-
142
- @data_product.command()
143
- @click.option('--data-product-id', required=True, help="ID of the data product")
144
- def unpublish(data_product_id):
145
- """Unpublish a data product using Unified Catalog API."""
146
- try:
147
- data_product_client = UnifiedCatalogDataProduct()
148
- result = data_product_client.update_data_product(data_product_id, {"status": "Draft"})
149
-
150
- if result.get("status") == "error":
151
- console.print(f"[red]ERROR:[/red] {result.get('message', 'Unknown error')}")
152
- return
153
-
154
- console.print(f"[green]SUCCESS:[/green] Unpublished data product '{data_product_id}'")
155
- console.print(json.dumps(result, indent=2))
156
- except Exception as e:
157
- console.print(f"[red]ERROR:[/red] {str(e)}")
158
-
159
- # === BUSINESS DOMAIN COMMANDS ===
160
- # Business domains are required for data product creation
161
-
162
- @data_product.command()
163
- def list_domains():
164
- """List all business domains."""
165
- try:
166
- data_product_client = UnifiedCatalogDataProduct()
167
- result = data_product_client.businessDomainList({})
168
-
169
- if result.get("status") == "error":
170
- console.print(f"[red]ERROR:[/red] {result.get('message', 'Unknown error')}")
171
- return
172
-
173
- console.print(json.dumps(result, indent=2))
174
- except Exception as e:
175
- console.print(f"[red]ERROR:[/red] {str(e)}")
176
-
177
- @data_product.command()
178
- @click.option('--name', required=True, help="Name of the business domain")
179
- @click.option('--description', required=False, help="Description of the business domain")
180
- @click.option('--type', default="DataDomain", help="Type of the business domain (DataDomain, LineOfBusiness, etc.)")
181
- def create_domain(name, description, type):
182
- """Create a new business domain."""
183
- try:
184
- data_product_client = UnifiedCatalogDataProduct()
185
- args = {
186
- "--name": name,
187
- "--description": description or "",
188
- "--type": type,
189
- "--status": "Draft"
190
- }
191
- result = data_product_client.businessDomainCreate(args)
192
-
193
- if result.get("status") == "error":
194
- console.print(f"[red]ERROR:[/red] {result.get('message', 'Unknown error')}")
195
- return
196
-
197
- console.print(f"[green]SUCCESS:[/green] Created business domain '{name}'")
198
- console.print(json.dumps(result, indent=2))
199
- except Exception as e:
200
- console.print(f"[red]ERROR:[/red] {str(e)}")
201
-
202
- @data_product.command()
203
- @click.option('--domain-id', required=True, help="ID of the business domain")
204
- def show_domain(domain_id):
205
- """Show details of a business domain."""
206
- try:
207
- data_product_client = UnifiedCatalogDataProduct()
208
- result = data_product_client.businessDomainRead({"--domainId": domain_id})
209
-
210
- if result.get("status") == "error":
211
- console.print(f"[red]ERROR:[/red] {result.get('message', 'Unknown error')}")
212
- return
213
-
214
- console.print(json.dumps(result, indent=2))
215
- except Exception as e:
216
- console.print(f"[red]ERROR:[/red] {str(e)}")
217
-
218
- # === GLOSSARY TERM COMMANDS ===
219
-
220
- @data_product.command()
221
- @click.option('--domain-id', required=False, help="Filter by governance domain ID")
222
- def list_terms(domain_id):
223
- """List glossary terms."""
224
- try:
225
- data_product_client = UnifiedCatalogDataProduct()
226
- args = {}
227
- if domain_id:
228
- args["--governanceDomain"] = domain_id
229
- result = data_product_client.glossaryTermsList(args)
230
-
231
- if result.get("status") == "error":
232
- console.print(f"[red]ERROR:[/red] {result.get('message', 'Unknown error')}")
233
- return
234
-
235
- console.print(json.dumps(result, indent=2))
236
- except Exception as e:
237
- console.print(f"[red]ERROR:[/red] {str(e)}")
238
-
239
- @data_product.command()
240
- @click.option('--name', required=True, help="Name of the glossary term")
241
- @click.option('--description', required=False, help="Description of the glossary term")
242
- @click.option('--domain-id', required=True, help="ID of the governance domain")
243
- def create_term(name, description, domain_id):
244
- """Create a new glossary term."""
245
- try:
246
- data_product_client = UnifiedCatalogDataProduct()
247
- result = data_product_client.create_glossary_term(
248
- name=name,
249
- description=description or "",
250
- domain_id=domain_id
251
- )
252
-
253
- if result.get("status") == "error":
254
- console.print(f"[red]ERROR:[/red] {result.get('message', 'Unknown error')}")
255
- return
256
-
257
- console.print(f"[green]SUCCESS:[/green] Created glossary term '{name}'")
258
- console.print(json.dumps(result, indent=2))
259
- except Exception as e:
260
- console.print(f"[red]ERROR:[/red] {str(e)}")
261
-
262
- @data_product.command()
263
- @click.option('--data-product-id', required=True, help="ID of the data product")
264
- @click.option('--term-id', required=True, help="ID of the glossary term")
265
- def link_term(data_product_id, term_id):
266
- """Link a glossary term to a data product."""
267
- try:
268
- data_product_client = UnifiedCatalogDataProduct()
269
- result = data_product_client.link_term_to_data_product(data_product_id, term_id)
270
-
271
- if result.get("status") == "error":
272
- console.print(f"[red]ERROR:[/red] {result.get('message', 'Unknown error')}")
273
- return
274
-
275
- console.print(f"[green]SUCCESS:[/green] Linked term '{term_id}' to data product '{data_product_id}'")
276
- console.print(json.dumps(result, indent=2))
277
- except Exception as e:
278
- console.print(f"[red]ERROR:[/red] {str(e)}")
@@ -1,168 +0,0 @@
1
- import json
2
- import tempfile
3
- import os
4
- import csv
5
- from purviewcli.client._entity import Entity
6
-
7
- class DataProduct:
8
- """Client for managing data products in Microsoft Purview."""
9
-
10
- def __init__(self):
11
- self.entity_client = Entity()
12
-
13
- def import_from_csv(self, products):
14
- with open("import_from_csv_debug.log", "a") as logf:
15
- logf.write(f"products type: {type(products)}\n")
16
- if products:
17
- logf.write(f"first product type: {type(products[0])}\n")
18
- logf.write(f"first product: {products[0]}\n")
19
- if not isinstance(products, list):
20
- raise TypeError(f"Expected a list, got: {type(products)} with value: {products}")
21
- if not products or not isinstance(products[0], dict):
22
- raise TypeError(
23
- f"Expected a list of dicts, got: {type(products[0])} with value: {products[0]}"
24
- )
25
- required_fields = ["qualifiedName"]
26
- entities = []
27
- for product in products:
28
- # Validate required fields
29
- for field in required_fields:
30
- if not product.get(field):
31
- raise ValueError(f"Missing required field '{field}' in row: {product}")
32
- # Always use typeName DataSet (Purview default)
33
- attributes = {k: v for k, v in product.items() if k != "typeName"}
34
- entity = {
35
- "typeName": "DataSet",
36
- "attributes": attributes
37
- }
38
- entities.append(entity)
39
- # Write the bulk payload to a temp file (always as {"entities": [...]})
40
- bulk_payload = {"entities": entities}
41
- with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as tmpf:
42
- json.dump(bulk_payload, tmpf, indent=2)
43
- tmpf.flush()
44
- payload_file = tmpf.name
45
- try:
46
- result = self.entity_client.entityCreateBulk({"--payloadFile": payload_file})
47
- return [(e["attributes"]["qualifiedName"], result) for e in entities]
48
- finally:
49
- os.remove(payload_file)
50
-
51
- def import_from_csv_file(self, csv_file_path, dry_run=False):
52
- """Load data products from a CSV file and import them. If dry_run is True, return the parsed products only."""
53
- with open(csv_file_path, 'r', encoding='utf-8') as f:
54
- reader = csv.DictReader(f)
55
- products = list(reader)
56
- if dry_run:
57
- return products
58
- return self.import_from_csv(products)
59
-
60
- def create(self, qualified_name, name=None, description=None, type_name="DataProduct"):
61
- """Create a single data product entity."""
62
- payload = {
63
- "typeName": type_name,
64
- "attributes": {
65
- "qualifiedName": qualified_name,
66
- "name": name or qualified_name,
67
- "description": description or ""
68
- },
69
- }
70
- # Write payload to temp file since entity client expects a file path
71
- with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as tmpf:
72
- json.dump(payload, tmpf, indent=2)
73
- tmpf.flush()
74
- payload_file = tmpf.name
75
-
76
- try:
77
- return self.entity_client.entityCreate({"--payloadFile": payload_file})
78
- finally:
79
- os.remove(payload_file)
80
-
81
- def list(self, type_name="DataProduct"):
82
- """List all data products (by typeName)."""
83
- # Use the entity client's search method to find all entities of type DataProduct
84
- search_args = {
85
- "keywords": "*", # match all
86
- "filter": {
87
- "entityType": [type_name]
88
- },
89
- "limit": 100
90
- }
91
- # If the entity client has a search method, use it; otherwise, return empty list
92
- if hasattr(self.entity_client, "search_entities"):
93
- return self.entity_client.search_entities(search_args)
94
- # No suitable fallback for listing all entities by type; return empty list
95
- return []
96
-
97
- def show(self, qualified_name, type_name="DataProduct"):
98
- """Show a data product by qualifiedName."""
99
- args = {"--typeName": type_name, "--qualifiedName": qualified_name}
100
- return self.entity_client.entityReadUniqueAttribute(args)
101
-
102
- def delete(self, qualified_name, type_name="DataProduct"):
103
- """Delete a data product by qualifiedName."""
104
- args = {"--typeName": type_name, "--qualifiedName": qualified_name}
105
- return self.entity_client.entityDeleteUniqueAttribute(args)
106
-
107
- def add_classification(self, qualified_name, classification, type_name="DataProduct"):
108
- """Add a classification to a data product."""
109
- args = {
110
- "--typeName": type_name,
111
- "--qualifiedName": qualified_name,
112
- "--payloadFile": {"classifications": [classification]}
113
- }
114
- return self.entity_client.entityAddClassificationsByUniqueAttribute(args)
115
-
116
- def remove_classification(self, qualified_name, classification, type_name="DataProduct"):
117
- """Remove a classification from a data product."""
118
- args = {
119
- "--typeName": type_name,
120
- "--qualifiedName": qualified_name,
121
- "--classificationName": classification
122
- }
123
- return self.entity_client.entityDeleteClassificationByUniqueAttribute(args)
124
-
125
- def add_label(self, qualified_name, label, type_name="DataProduct"):
126
- """Add a label to a data product."""
127
- args = {
128
- "--typeName": type_name,
129
- "--qualifiedName": qualified_name,
130
- "--payloadFile": {"labels": [label]}
131
- }
132
- return self.entity_client.entityAddLabelsByUniqueAttribute(args)
133
-
134
- def remove_label(self, qualified_name, label, type_name="DataProduct"):
135
- """Remove a label from a data product."""
136
- args = {
137
- "--typeName": type_name,
138
- "--qualifiedName": qualified_name,
139
- "--payloadFile": {"labels": [label]}
140
- }
141
- return self.entity_client.entityRemoveLabelsByUniqueAttribute(args)
142
-
143
- def link_glossary(self, qualified_name, term, type_name="DataProduct"):
144
- """Link a glossary term to a data product."""
145
- # This assumes business metadata or a custom attribute for glossary terms
146
- # You may need to adjust this to your Purview model
147
- args = {
148
- "--typeName": type_name,
149
- "--qualifiedName": qualified_name,
150
- "--payloadFile": {"meanings": [term]}
151
- }
152
- return self.entity_client.entityPartialUpdateByUniqueAttribute(args)
153
-
154
- def show_lineage(self, qualified_name, type_name="DataProduct"):
155
- """Show lineage for a data product."""
156
- args = {"--typeName": type_name, "--qualifiedName": qualified_name}
157
- # This assumes you have a lineage client or can call entityReadUniqueAttribute and extract lineage
158
- # For now, just return the entity details
159
- return self.entity_client.entityReadUniqueAttribute(args)
160
-
161
- def set_status(self, qualified_name, status, type_name="DataProduct"):
162
- """Set the status of a data product (e.g., active, deprecated)."""
163
- args = {
164
- "--typeName": type_name,
165
- "--qualifiedName": qualified_name,
166
- "--payloadFile": {"status": status}
167
- }
168
- return self.entity_client.entityPartialUpdateByUniqueAttribute(args)