pvw-cli 1.0.11__py3-none-any.whl → 1.0.14__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.

@@ -27,7 +27,7 @@ def list(ctx, output_json):
27
27
  try:
28
28
  if ctx.obj and ctx.obj.get("mock"):
29
29
  console.print("[yellow]🎭 Mock: workflow list command[/yellow]")
30
- console.print("[green] Mock workflow list completed successfully[/green]")
30
+ console.print("[green][OK] Mock workflow list completed successfully[/green]")
31
31
  return
32
32
 
33
33
  from purviewcli.client._workflow import Workflow
@@ -46,7 +46,7 @@ def list(ctx, output_json):
46
46
  workflows = []
47
47
 
48
48
  if not workflows:
49
- console.print("[yellow] No workflows found[/yellow]")
49
+ console.print("[yellow][!] No workflows found[/yellow]")
50
50
  return
51
51
 
52
52
  # Output in JSON format if requested
@@ -81,7 +81,7 @@ def list(ctx, output_json):
81
81
  console.print(f"\n[dim]Total: {len(workflows)} workflow(s)[/dim]")
82
82
 
83
83
  except Exception as e:
84
- console.print(f"[red] Error executing workflow list: {str(e)}[/red]")
84
+ console.print(f"[red][X] Error executing workflow list: {str(e)}[/red]")
85
85
 
86
86
 
87
87
  @workflow.command()
@@ -100,7 +100,7 @@ def create(ctx, workflow_id, payload_file):
100
100
  console.print("[yellow]🎭 Mock: workflow create command[/yellow]")
101
101
  console.print(f"[dim]Workflow ID: {workflow_id}[/dim]")
102
102
  console.print(f"[dim]Payload File: {payload_file}[/dim]")
103
- console.print("[green] Mock workflow create completed successfully[/green]")
103
+ console.print("[green][OK] Mock workflow create completed successfully[/green]")
104
104
  return
105
105
 
106
106
  from purviewcli.client._workflow import Workflow
@@ -110,12 +110,12 @@ def create(ctx, workflow_id, payload_file):
110
110
  result = workflow_client.workflowCreateWorkflow(args)
111
111
 
112
112
  if result:
113
- console.print("[green] Workflow create completed successfully[/green]")
113
+ console.print("[green][OK] Workflow create completed successfully[/green]")
114
114
  console.print(json.dumps(result, indent=2))
115
115
  else:
116
- console.print("[yellow] Workflow create completed with no result[/yellow]")
116
+ console.print("[yellow][!] Workflow create completed with no result[/yellow]")
117
117
  except Exception as e:
118
- console.print(f"[red] Error executing workflow create: {str(e)}[/red]")
118
+ console.print(f"[red][X] Error executing workflow create: {str(e)}[/red]")
119
119
 
120
120
 
121
121
  @workflow.command()
@@ -127,7 +127,7 @@ def get(ctx, workflow_id):
127
127
  if ctx.obj and ctx.obj.get("mock"):
128
128
  console.print("[yellow]🎭 Mock: workflow get command[/yellow]")
129
129
  console.print(f"[dim]Workflow ID: {workflow_id}[/dim]")
130
- console.print("[green] Mock workflow get completed successfully[/green]")
130
+ console.print("[green][OK] Mock workflow get completed successfully[/green]")
131
131
  return
132
132
 
133
133
  from purviewcli.client._workflow import Workflow
@@ -137,12 +137,12 @@ def get(ctx, workflow_id):
137
137
  result = workflow_client.workflowGetWorkflow(args)
138
138
 
139
139
  if result:
140
- console.print("[green] Workflow get completed successfully[/green]")
140
+ console.print("[green][OK] Workflow get completed successfully[/green]")
141
141
  console.print(json.dumps(result, indent=2))
142
142
  else:
143
- console.print("[yellow] Workflow not found[/yellow]")
143
+ console.print("[yellow][!] Workflow not found[/yellow]")
144
144
  except Exception as e:
145
- console.print(f"[red] Error executing workflow get: {str(e)}[/red]")
145
+ console.print(f"[red][X] Error executing workflow get: {str(e)}[/red]")
146
146
 
147
147
 
148
148
  @workflow.command()
@@ -159,7 +159,7 @@ def execute(ctx, workflow_id, payload_file):
159
159
  console.print(f"[dim]Workflow ID: {workflow_id}[/dim]")
160
160
  if payload_file:
161
161
  console.print(f"[dim]Payload File: {payload_file}[/dim]")
162
- console.print("[green] Mock workflow execute completed successfully[/green]")
162
+ console.print("[green][OK] Mock workflow execute completed successfully[/green]")
163
163
  return
164
164
 
165
165
  from purviewcli.client._workflow import Workflow
@@ -171,12 +171,12 @@ def execute(ctx, workflow_id, payload_file):
171
171
  result = workflow_client.workflowExecuteWorkflow(args)
172
172
 
173
173
  if result:
174
- console.print("[green] Workflow execute completed successfully[/green]")
174
+ console.print("[green][OK] Workflow execute completed successfully[/green]")
175
175
  console.print(json.dumps(result, indent=2))
176
176
  else:
177
- console.print("[yellow] Workflow execute completed with no result[/yellow]")
177
+ console.print("[yellow][!] Workflow execute completed with no result[/yellow]")
178
178
  except Exception as e:
179
- console.print(f"[red] Error executing workflow: {str(e)}[/red]")
179
+ console.print(f"[red][X] Error executing workflow: {str(e)}[/red]")
180
180
 
181
181
 
182
182
  @workflow.command()
@@ -188,7 +188,7 @@ def executions(ctx, workflow_id):
188
188
  if ctx.obj and ctx.obj.get("mock"):
189
189
  console.print("[yellow]🎭 Mock: workflow executions command[/yellow]")
190
190
  console.print(f"[dim]Workflow ID: {workflow_id}[/dim]")
191
- console.print("[green] Mock workflow executions completed successfully[/green]")
191
+ console.print("[green][OK] Mock workflow executions completed successfully[/green]")
192
192
  return
193
193
 
194
194
  from purviewcli.client._workflow import Workflow
@@ -198,12 +198,12 @@ def executions(ctx, workflow_id):
198
198
  result = workflow_client.workflowListWorkflowExecutions(args)
199
199
 
200
200
  if result:
201
- console.print("[green] Workflow executions list completed successfully[/green]")
201
+ console.print("[green][OK] Workflow executions list completed successfully[/green]")
202
202
  console.print(json.dumps(result, indent=2))
203
203
  else:
204
- console.print("[yellow] No workflow executions found[/yellow]")
204
+ console.print("[yellow][!] No workflow executions found[/yellow]")
205
205
  except Exception as e:
206
- console.print(f"[red] Error listing workflow executions: {str(e)}[/red]")
206
+ console.print(f"[red][X] Error listing workflow executions: {str(e)}[/red]")
207
207
 
208
208
 
209
209
  # ========== Approval Commands ==========
@@ -222,7 +222,7 @@ def approvals(ctx, status, assigned_to):
222
222
  console.print(f"[dim]Status Filter: {status}[/dim]")
223
223
  if assigned_to:
224
224
  console.print(f"[dim]Assigned To: {assigned_to}[/dim]")
225
- console.print("[green] Mock workflow approvals completed successfully[/green]")
225
+ console.print("[green][OK] Mock workflow approvals completed successfully[/green]")
226
226
  return
227
227
 
228
228
  from purviewcli.client._workflow import Workflow
@@ -236,12 +236,12 @@ def approvals(ctx, status, assigned_to):
236
236
  result = workflow_client.workflowGetApprovalRequests(args)
237
237
 
238
238
  if result:
239
- console.print("[green] Approval requests list completed successfully[/green]")
239
+ console.print("[green][OK] Approval requests list completed successfully[/green]")
240
240
  console.print(json.dumps(result, indent=2))
241
241
  else:
242
- console.print("[yellow] No approval requests found[/yellow]")
242
+ console.print("[yellow][!] No approval requests found[/yellow]")
243
243
  except Exception as e:
244
- console.print(f"[red] Error listing approval requests: {str(e)}[/red]")
244
+ console.print(f"[red][X] Error listing approval requests: {str(e)}[/red]")
245
245
 
246
246
 
247
247
  @workflow.command()
@@ -256,7 +256,7 @@ def approve(ctx, request_id, comments):
256
256
  console.print(f"[dim]Request ID: {request_id}[/dim]")
257
257
  if comments:
258
258
  console.print(f"[dim]Comments: {comments}[/dim]")
259
- console.print("[green] Mock workflow approve completed successfully[/green]")
259
+ console.print("[green][OK] Mock workflow approve completed successfully[/green]")
260
260
  return
261
261
 
262
262
  from purviewcli.client._workflow import Workflow
@@ -268,12 +268,12 @@ def approve(ctx, request_id, comments):
268
268
  result = workflow_client.workflowApproveRequest(args)
269
269
 
270
270
  if result:
271
- console.print("[green] Request approved successfully[/green]")
271
+ console.print("[green][OK] Request approved successfully[/green]")
272
272
  console.print(json.dumps(result, indent=2))
273
273
  else:
274
- console.print("[yellow] Request approval completed with no result[/yellow]")
274
+ console.print("[yellow][!] Request approval completed with no result[/yellow]")
275
275
  except Exception as e:
276
- console.print(f"[red] Error approving request: {str(e)}[/red]")
276
+ console.print(f"[red][X] Error approving request: {str(e)}[/red]")
277
277
 
278
278
 
279
279
  @workflow.command()
@@ -288,7 +288,7 @@ def reject(ctx, request_id, comments):
288
288
  console.print(f"[dim]Request ID: {request_id}[/dim]")
289
289
  if comments:
290
290
  console.print(f"[dim]Comments: {comments}[/dim]")
291
- console.print("[green] Mock workflow reject completed successfully[/green]")
291
+ console.print("[green][OK] Mock workflow reject completed successfully[/green]")
292
292
  return
293
293
 
294
294
  from purviewcli.client._workflow import Workflow
@@ -300,12 +300,12 @@ def reject(ctx, request_id, comments):
300
300
  result = workflow_client.workflowRejectRequest(args)
301
301
 
302
302
  if result:
303
- console.print("[green] Request rejected successfully[/green]")
303
+ console.print("[green][OK] Request rejected successfully[/green]")
304
304
  console.print(json.dumps(result, indent=2))
305
305
  else:
306
- console.print("[yellow] Request rejection completed with no result[/yellow]")
306
+ console.print("[yellow][!] Request rejection completed with no result[/yellow]")
307
307
  except Exception as e:
308
- console.print(f"[red] Error rejecting request: {str(e)}[/red]")
308
+ console.print(f"[red][X] Error rejecting request: {str(e)}[/red]")
309
309
 
310
310
 
311
311
  # ========== Template Commands ==========
@@ -318,7 +318,7 @@ def templates(ctx):
318
318
  try:
319
319
  if ctx.obj and ctx.obj.get("mock"):
320
320
  console.print("[yellow]🎭 Mock: workflow templates command[/yellow]")
321
- console.print("[green] Mock workflow templates completed successfully[/green]")
321
+ console.print("[green][OK] Mock workflow templates completed successfully[/green]")
322
322
  return
323
323
 
324
324
  from purviewcli.client._workflow import Workflow
@@ -328,12 +328,12 @@ def templates(ctx):
328
328
  result = workflow_client.workflowListWorkflowTemplates(args)
329
329
 
330
330
  if result:
331
- console.print("[green] Workflow templates list completed successfully[/green]")
331
+ console.print("[green][OK] Workflow templates list completed successfully[/green]")
332
332
  console.print(json.dumps(result, indent=2))
333
333
  else:
334
- console.print("[yellow] No workflow templates found[/yellow]")
334
+ console.print("[yellow][!] No workflow templates found[/yellow]")
335
335
  except Exception as e:
336
- console.print(f"[red] Error listing workflow templates: {str(e)}[/red]")
336
+ console.print(f"[red][X] Error listing workflow templates: {str(e)}[/red]")
337
337
 
338
338
 
339
339
  @workflow.command()
@@ -345,7 +345,7 @@ def template(ctx, template_id):
345
345
  if ctx.obj and ctx.obj.get("mock"):
346
346
  console.print("[yellow]🎭 Mock: workflow template command[/yellow]")
347
347
  console.print(f"[dim]Template ID: {template_id}[/dim]")
348
- console.print("[green] Mock workflow template completed successfully[/green]")
348
+ console.print("[green][OK] Mock workflow template completed successfully[/green]")
349
349
  return
350
350
 
351
351
  from purviewcli.client._workflow import Workflow
@@ -355,12 +355,12 @@ def template(ctx, template_id):
355
355
  result = workflow_client.workflowGetWorkflowTemplate(args)
356
356
 
357
357
  if result:
358
- console.print("[green] Workflow template get completed successfully[/green]")
358
+ console.print("[green][OK] Workflow template get completed successfully[/green]")
359
359
  console.print(json.dumps(result, indent=2))
360
360
  else:
361
- console.print("[yellow] Workflow template not found[/yellow]")
361
+ console.print("[yellow][!] Workflow template not found[/yellow]")
362
362
  except Exception as e:
363
- console.print(f"[red] Error getting workflow template: {str(e)}[/red]")
363
+ console.print(f"[red][X] Error getting workflow template: {str(e)}[/red]")
364
364
 
365
365
 
366
366
  # ========== Validation Commands ==========
@@ -380,7 +380,7 @@ def validate(ctx, payload_file):
380
380
  if ctx.obj and ctx.obj.get("mock"):
381
381
  console.print("[yellow]🎭 Mock: workflow validate command[/yellow]")
382
382
  console.print(f"[dim]Payload File: {payload_file}[/dim]")
383
- console.print("[green] Mock workflow validate completed successfully[/green]")
383
+ console.print("[green][OK] Mock workflow validate completed successfully[/green]")
384
384
  return
385
385
 
386
386
  from purviewcli.client._workflow import Workflow
@@ -390,12 +390,12 @@ def validate(ctx, payload_file):
390
390
  result = workflow_client.workflowValidateWorkflow(args)
391
391
 
392
392
  if result:
393
- console.print("[green] Workflow validation completed successfully[/green]")
393
+ console.print("[green][OK] Workflow validation completed successfully[/green]")
394
394
  console.print(json.dumps(result, indent=2))
395
395
  else:
396
- console.print("[yellow] Workflow validation completed with no result[/yellow]")
396
+ console.print("[yellow][!] Workflow validation completed with no result[/yellow]")
397
397
  except Exception as e:
398
- console.print(f"[red] Error validating workflow: {str(e)}[/red]")
398
+ console.print(f"[red][X] Error validating workflow: {str(e)}[/red]")
399
399
 
400
400
 
401
401
  if __name__ == "__main__":
@@ -39,12 +39,22 @@ class UnifiedCatalogClient(Endpoint):
39
39
  """Create a new governance domain."""
40
40
  self.method = "POST"
41
41
  self.endpoint = "/datagovernance/catalog/businessdomains"
42
- self.payload = get_json(args, "--payloadFile") or {
43
- "name": args.get("--name", [""])[0],
44
- "description": args.get("--description", [""])[0],
45
- "type": args.get("--type", ["FunctionalUnit"])[0],
46
- "status": args.get("--status", ["Draft"])[0],
47
- }
42
+ # Allow payload file to fully control creation; otherwise build payload from flags
43
+ payload = get_json(args, "--payloadFile")
44
+ if not payload:
45
+ payload = {
46
+ "name": args.get("--name", [""])[0],
47
+ "description": args.get("--description", [""])[0],
48
+ "type": args.get("--type", ["FunctionalUnit"])[0],
49
+ "status": args.get("--status", ["Draft"])[0],
50
+ }
51
+ # Support parent domain ID passed via CLI as --parent-domain-id
52
+ parent_id = args.get("--parent-domain-id", [""])[0]
53
+ if parent_id:
54
+ payload["parentId"] = parent_id
55
+
56
+ # If payload file contains parentId or parentDomainId, keep it as-is
57
+ self.payload = payload
48
58
 
49
59
  @decorator
50
60
  def update_governance_domain(self, args):
@@ -411,57 +421,102 @@ class UnifiedCatalogClient(Endpoint):
411
421
 
412
422
  self.payload = payload
413
423
 
414
- @decorator
415
424
  def update_term(self, args):
416
- """Update an existing Unified Catalog term."""
425
+ """Update an existing Unified Catalog term (supports partial updates)."""
426
+ from purviewcli.client.endpoint import get_data
427
+
417
428
  term_id = args.get("--term-id", [""])[0]
418
- self.method = "PUT"
419
- self.endpoint = f"/datagovernance/catalog/terms/{term_id}"
420
-
421
- # Build payload with all fields (UC API requires full object)
422
- domain_id = args.get("--governance-domain-id", [""])[0]
423
- name = args.get("--name", [""])[0]
424
- description = args.get("--description", [""])[0]
425
- status = args.get("--status", ["Draft"])[0]
426
429
 
427
- # Get owner IDs if provided
428
- owner_ids = args.get("--owner-id", [])
429
- owners = []
430
- if owner_ids:
431
- for owner_id in owner_ids:
432
- owners.append({"id": owner_id})
430
+ # First, fetch the existing term to get current values
431
+ fetch_client = UnifiedCatalogClient()
432
+ existing_term = fetch_client.get_term_by_id({"--term-id": [term_id]})
433
433
 
434
- # Get acronyms if provided
435
- acronyms = args.get("--acronym", [])
434
+ if not existing_term or (isinstance(existing_term, dict) and existing_term.get("error")):
435
+ return {"error": f"Could not fetch existing term {term_id}"}
436
436
 
437
- # Get resources if provided
438
- resources = []
437
+ # Start with existing term data
438
+ payload = {
439
+ "id": term_id,
440
+ "name": existing_term.get("name", ""),
441
+ "description": existing_term.get("description", ""),
442
+ "domain": existing_term.get("domain", ""),
443
+ "status": existing_term.get("status", "Draft"),
444
+ }
445
+
446
+ # Update with provided values (only if explicitly provided)
447
+ if args.get("--name"):
448
+ payload["name"] = args["--name"][0]
449
+ if "--description" in args: # Allow empty string
450
+ payload["description"] = args.get("--description", [""])[0]
451
+ if args.get("--governance-domain-id"):
452
+ payload["domain"] = args["--governance-domain-id"][0]
453
+ if args.get("--status"):
454
+ payload["status"] = args["--status"][0]
455
+
456
+ # Handle owners - replace or add to existing
457
+ contacts = existing_term.get("contacts") or {}
458
+ existing_owners = contacts.get("owner", []) if isinstance(contacts, dict) else []
459
+ if args.get("--owner-id"):
460
+ # Replace owners
461
+ owners = [{"id": oid} for oid in args["--owner-id"]]
462
+ payload["contacts"] = {"owner": owners}
463
+ elif args.get("--add-owner-id"):
464
+ # Add to existing owners
465
+ existing_owner_ids = set()
466
+ if isinstance(existing_owners, list):
467
+ for o in existing_owners:
468
+ if isinstance(o, dict) and o.get("id"):
469
+ existing_owner_ids.add(o.get("id"))
470
+ new_owner_ids = args["--add-owner-id"]
471
+ combined_owner_ids = existing_owner_ids.union(set(new_owner_ids))
472
+ owners = [{"id": oid} for oid in combined_owner_ids]
473
+ payload["contacts"] = {"owner": owners}
474
+ elif existing_owners:
475
+ # Keep existing owners
476
+ payload["contacts"] = {"owner": existing_owners}
477
+
478
+ # Handle acronyms - replace or add to existing
479
+ existing_acronyms = existing_term.get("acronyms", []) or []
480
+ if args.get("--acronym"):
481
+ # Replace acronyms
482
+ payload["acronyms"] = list(args["--acronym"])
483
+ elif args.get("--add-acronym"):
484
+ # Add to existing acronyms
485
+ combined_acronyms = list(set(existing_acronyms + list(args["--add-acronym"])))
486
+ payload["acronyms"] = combined_acronyms
487
+ elif existing_acronyms:
488
+ # Keep existing acronyms
489
+ payload["acronyms"] = existing_acronyms
490
+
491
+ # Handle resources - replace with new ones if provided
492
+ existing_resources = existing_term.get("resources", []) or []
439
493
  resource_names = args.get("--resource-name", [])
440
494
  resource_urls = args.get("--resource-url", [])
441
495
  if resource_names and resource_urls:
496
+ # Replace resources
497
+ resources = []
442
498
  for i in range(min(len(resource_names), len(resource_urls))):
443
499
  resources.append({
444
500
  "name": resource_names[i],
445
501
  "url": resource_urls[i]
446
502
  })
447
-
448
- payload = {
449
- "id": term_id,
450
- "name": name,
451
- "description": description,
452
- "domain": domain_id,
453
- "status": status,
503
+ payload["resources"] = resources
504
+ elif existing_resources:
505
+ # Keep existing resources
506
+ payload["resources"] = existing_resources
507
+
508
+ # Now make the actual PUT request
509
+ http_dict = {
510
+ "app": "datagovernance",
511
+ "method": "PUT",
512
+ "endpoint": f"/datagovernance/catalog/terms/{term_id}",
513
+ "params": {},
514
+ "payload": payload,
515
+ "files": None,
516
+ "headers": {},
454
517
  }
455
518
 
456
- # Add optional fields
457
- if owners:
458
- payload["contacts"] = {"owner": owners}
459
- if acronyms:
460
- payload["acronyms"] = acronyms
461
- if resources:
462
- payload["resources"] = resources
463
-
464
- self.payload = payload
519
+ return get_data(http_dict)
465
520
 
466
521
  @decorator
467
522
  def delete_term(self, args):
@@ -5,7 +5,10 @@ Supports the latest Microsoft Purview REST API specifications with comprehensive
5
5
 
6
6
  import json
7
7
  import asyncio
8
- import aiohttp
8
+ try:
9
+ import aiohttp
10
+ except Exception:
11
+ aiohttp = None
9
12
  import pandas as pd
10
13
  from typing import Dict, List, Optional, Union, Any
11
14
  from dataclasses import dataclass
@@ -73,6 +76,11 @@ class PurviewClient:
73
76
 
74
77
  async def _initialize_session(self):
75
78
  """Initialize HTTP session and authentication"""
79
+ if aiohttp is None:
80
+ raise RuntimeError(
81
+ "The 'aiohttp' package is required for Purview async operations. "
82
+ "Install it in your environment (e.g. '.venv\\Scripts\\pip.exe install aiohttp' or 'pip install aiohttp')."
83
+ )
76
84
  self._credential = DefaultAzureCredential()
77
85
 
78
86
  try: