pvw-cli 1.2.4__tar.gz → 1.2.5__tar.gz

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.

Files changed (67) hide show
  1. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/PKG-INFO +19 -9
  2. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/README.md +18 -8
  3. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/__init__.py +1 -1
  4. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/types.py +333 -0
  5. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/unified_catalog.py +1329 -9
  6. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_entity.py +74 -17
  7. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_types.py +31 -0
  8. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_unified_catalog.py +646 -30
  9. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/endpoints.py +75 -0
  10. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/pvw_cli.egg-info/PKG-INFO +19 -9
  11. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/pvw_cli.egg-info/SOURCES.txt +2 -1
  12. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/pyproject.toml +3 -3
  13. pvw_cli-1.2.5/tests/test_mcp_server.py +200 -0
  14. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/__main__.py +0 -0
  15. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/__init__.py +0 -0
  16. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/account.py +0 -0
  17. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/cli.py +0 -0
  18. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/collections.py +0 -0
  19. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/domain.py +0 -0
  20. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/entity.py +0 -0
  21. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/glossary.py +0 -0
  22. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/health.py +0 -0
  23. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/insight.py +0 -0
  24. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/lineage.py +0 -0
  25. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/management.py +0 -0
  26. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/policystore.py +0 -0
  27. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/relationship.py +0 -0
  28. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/scan.py +0 -0
  29. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/search.py +0 -0
  30. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/share.py +0 -0
  31. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/workflow.py +0 -0
  32. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/__init__.py +0 -0
  33. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_account.py +0 -0
  34. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_collections.py +0 -0
  35. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_domain.py +0 -0
  36. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_glossary.py +0 -0
  37. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_health.py +0 -0
  38. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_insight.py +0 -0
  39. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_lineage.py +0 -0
  40. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_management.py +0 -0
  41. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_policystore.py +0 -0
  42. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_relationship.py +0 -0
  43. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_scan.py +0 -0
  44. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_search.py +0 -0
  45. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_share.py +0 -0
  46. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_workflow.py +0 -0
  47. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/api_client.py +0 -0
  48. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/business_rules.py +0 -0
  49. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/config.py +0 -0
  50. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/data_quality.py +0 -0
  51. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/endpoint.py +0 -0
  52. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/exceptions.py +0 -0
  53. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/lineage_visualization.py +0 -0
  54. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/monitoring_dashboard.py +0 -0
  55. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/rate_limiter.py +0 -0
  56. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/retry_handler.py +0 -0
  57. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/scanning_operations.py +0 -0
  58. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/settings.py +0 -0
  59. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/sync_client.py +0 -0
  60. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/plugins/__init__.py +0 -0
  61. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/plugins/plugin_system.py +0 -0
  62. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/pvw_cli.egg-info/dependency_links.txt +0 -0
  63. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/pvw_cli.egg-info/entry_points.txt +0 -0
  64. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/pvw_cli.egg-info/not-zip-safe +0 -0
  65. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/pvw_cli.egg-info/requires.txt +0 -0
  66. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/pvw_cli.egg-info/top_level.txt +0 -0
  67. {pvw_cli-1.2.4 → pvw_cli-1.2.5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pvw-cli
3
- Version: 1.2.4
3
+ Version: 1.2.5
4
4
  Summary: Microsoft Purview CLI with comprehensive automation capabilities
5
5
  Author-email: AYOUB KEBAILI <keayoub@msn.com>
6
6
  Maintainer-email: AYOUB KEBAILI <keayoub@msn.com>
@@ -56,9 +56,9 @@ Requires-Dist: pytest-asyncio>=0.20.0; extra == "test"
56
56
  Requires-Dist: pytest-cov>=4.0.0; extra == "test"
57
57
  Requires-Dist: requests-mock>=1.9.0; extra == "test"
58
58
 
59
- # PURVIEW CLI v1.2.4 - Microsoft Purview Automation & Data Governance
59
+ # PURVIEW CLI v1.2.5 - Microsoft Purview Automation & Data Governance
60
60
 
61
- > **LATEST UPDATE v1.2.4 (October 2025):**
61
+ > **LATEST UPDATE v1.2.5 (October 2025):**
62
62
  >
63
63
  > - **[NEW]** Lineage CSV Import - Bulk import lineage relationships from CSV files with validation and sample generation
64
64
  > - **[FIXED]** Search API Integration - Fixed `suggest` and `autocomplete` API payload format (HTTP 400 errors resolved)
@@ -77,7 +77,7 @@ Requires-Dist: requests-mock>=1.9.0; extra == "test"
77
77
 
78
78
  ## What is PVW CLI?
79
79
 
80
- **PVW CLI v1.2.4** is a modern, full-featured command-line interface and Python library for Microsoft Purview. It enables automation and management of *all major Purview APIs* including:
80
+ **PVW CLI v1.2.5** is a modern, full-featured command-line interface and Python library for Microsoft Purview. It enables automation and management of *all major Purview APIs* including:
81
81
 
82
82
  - **Unified Catalog (UC) Management** - Complete governance domains, glossary terms, data products, OKRs, CDEs
83
83
  - **Bulk Operations** - Import/export terms from CSV/JSON, bulk delete scripts with progress tracking
@@ -92,6 +92,15 @@ Requires-Dist: requests-mock>=1.9.0; extra == "test"
92
92
 
93
93
  The CLI is designed for data engineers, stewards, architects, and platform teams to automate, scale, and enhance their Microsoft Purview experience.
94
94
 
95
+ ### NEW: MCP Server for AI Assistants
96
+
97
+ **[NEW]** Model Context Protocol (MCP) server enables LLM-powered data governance workflows!
98
+
99
+ - Natural language interface to Purview catalog
100
+ - 20+ tools for AI assistants (Claude, Cline, etc.)
101
+ - Automate complex multi-step operations
102
+ - See `mcp/README.md` for setup instructions
103
+
95
104
  ---
96
105
 
97
106
  ## Getting Started
@@ -169,8 +178,9 @@ For more advanced usage, see the documentation in `doc/` or the project docs: <h
169
178
 
170
179
  ## Overview
171
180
 
172
- **PVW CLI v1.2.4** is a modern command-line interface and Python library for Microsoft Purview, enabling:
181
+ **PVW CLI v1.2.5** is a modern command-line interface and Python library for Microsoft Purview, enabling:
173
182
 
183
+ - **MCP Server** - Natural language interface for AI assistants (Claude, Cline)
174
184
  - Advanced data catalog search and discovery
175
185
  - Bulk import/export of entities, glossary terms, and lineage
176
186
  - Real-time monitoring and analytics
@@ -533,7 +543,7 @@ The PVW CLI provides advanced search using the latest Microsoft Purview Discover
533
543
  - Use autocomplete and suggestion endpoints
534
544
  - Perform faceted, time-based, and entity-type-specific queries
535
545
 
536
- **v1.2.4 Improvements:**
546
+ **v1.2.5 Improvements:**
537
547
 
538
548
  - Fixed `suggest` and `autocomplete` API payload format (removed empty filter causing HTTP 400 errors)
539
549
  - Enhanced collection display with robust type checking and fallback logic
@@ -1297,7 +1307,7 @@ PVW CLI includes comprehensive sample files and scripts for bulk operations:
1297
1307
  - Success/failure tracking per term
1298
1308
  - Rate limiting (200ms delay)
1299
1309
 
1300
- ### Critical Fixes (v1.2.4)
1310
+ ### Critical Fixes (v1.2.5)
1301
1311
 
1302
1312
  - **Search API Suggest/Autocomplete:** Fixed HTTP 400 errors by removing empty filter objects from payload
1303
1313
  - **Collection Display:** Enhanced collection name detection with proper fallback logic (isinstance checks)
@@ -1360,9 +1370,9 @@ See [LICENSE](LICENSE) file for details.
1360
1370
 
1361
1371
  ---
1362
1372
 
1363
- **PVW CLI v1.2.4 empowers data engineers, stewards, and architects to automate, scale, and enhance their Microsoft Purview experience with powerful command-line and programmatic capabilities.**
1373
+ **PVW CLI v1.2.5 empowers data engineers, stewards, and architects to automate, scale, and enhance their Microsoft Purview experience with powerful command-line and programmatic capabilities.**
1364
1374
 
1365
- **Latest in v1.2.4:**
1375
+ **Latest in v1.2.5:**
1366
1376
 
1367
1377
  - Fixed Search API suggest/autocomplete (HTTP 400 errors resolved)
1368
1378
  - Enhanced collection display with robust fallback logic
@@ -1,6 +1,6 @@
1
- # PURVIEW CLI v1.2.4 - Microsoft Purview Automation & Data Governance
1
+ # PURVIEW CLI v1.2.5 - Microsoft Purview Automation & Data Governance
2
2
 
3
- > **LATEST UPDATE v1.2.4 (October 2025):**
3
+ > **LATEST UPDATE v1.2.5 (October 2025):**
4
4
  >
5
5
  > - **[NEW]** Lineage CSV Import - Bulk import lineage relationships from CSV files with validation and sample generation
6
6
  > - **[FIXED]** Search API Integration - Fixed `suggest` and `autocomplete` API payload format (HTTP 400 errors resolved)
@@ -19,7 +19,7 @@
19
19
 
20
20
  ## What is PVW CLI?
21
21
 
22
- **PVW CLI v1.2.4** is a modern, full-featured command-line interface and Python library for Microsoft Purview. It enables automation and management of *all major Purview APIs* including:
22
+ **PVW CLI v1.2.5** is a modern, full-featured command-line interface and Python library for Microsoft Purview. It enables automation and management of *all major Purview APIs* including:
23
23
 
24
24
  - **Unified Catalog (UC) Management** - Complete governance domains, glossary terms, data products, OKRs, CDEs
25
25
  - **Bulk Operations** - Import/export terms from CSV/JSON, bulk delete scripts with progress tracking
@@ -34,6 +34,15 @@
34
34
 
35
35
  The CLI is designed for data engineers, stewards, architects, and platform teams to automate, scale, and enhance their Microsoft Purview experience.
36
36
 
37
+ ### NEW: MCP Server for AI Assistants
38
+
39
+ **[NEW]** Model Context Protocol (MCP) server enables LLM-powered data governance workflows!
40
+
41
+ - Natural language interface to Purview catalog
42
+ - 20+ tools for AI assistants (Claude, Cline, etc.)
43
+ - Automate complex multi-step operations
44
+ - See `mcp/README.md` for setup instructions
45
+
37
46
  ---
38
47
 
39
48
  ## Getting Started
@@ -111,8 +120,9 @@ For more advanced usage, see the documentation in `doc/` or the project docs: <h
111
120
 
112
121
  ## Overview
113
122
 
114
- **PVW CLI v1.2.4** is a modern command-line interface and Python library for Microsoft Purview, enabling:
123
+ **PVW CLI v1.2.5** is a modern command-line interface and Python library for Microsoft Purview, enabling:
115
124
 
125
+ - **MCP Server** - Natural language interface for AI assistants (Claude, Cline)
116
126
  - Advanced data catalog search and discovery
117
127
  - Bulk import/export of entities, glossary terms, and lineage
118
128
  - Real-time monitoring and analytics
@@ -475,7 +485,7 @@ The PVW CLI provides advanced search using the latest Microsoft Purview Discover
475
485
  - Use autocomplete and suggestion endpoints
476
486
  - Perform faceted, time-based, and entity-type-specific queries
477
487
 
478
- **v1.2.4 Improvements:**
488
+ **v1.2.5 Improvements:**
479
489
 
480
490
  - Fixed `suggest` and `autocomplete` API payload format (removed empty filter causing HTTP 400 errors)
481
491
  - Enhanced collection display with robust type checking and fallback logic
@@ -1239,7 +1249,7 @@ PVW CLI includes comprehensive sample files and scripts for bulk operations:
1239
1249
  - Success/failure tracking per term
1240
1250
  - Rate limiting (200ms delay)
1241
1251
 
1242
- ### Critical Fixes (v1.2.4)
1252
+ ### Critical Fixes (v1.2.5)
1243
1253
 
1244
1254
  - **Search API Suggest/Autocomplete:** Fixed HTTP 400 errors by removing empty filter objects from payload
1245
1255
  - **Collection Display:** Enhanced collection name detection with proper fallback logic (isinstance checks)
@@ -1302,9 +1312,9 @@ See [LICENSE](LICENSE) file for details.
1302
1312
 
1303
1313
  ---
1304
1314
 
1305
- **PVW CLI v1.2.4 empowers data engineers, stewards, and architects to automate, scale, and enhance their Microsoft Purview experience with powerful command-line and programmatic capabilities.**
1315
+ **PVW CLI v1.2.5 empowers data engineers, stewards, and architects to automate, scale, and enhance their Microsoft Purview experience with powerful command-line and programmatic capabilities.**
1306
1316
 
1307
- **Latest in v1.2.4:**
1317
+ **Latest in v1.2.5:**
1308
1318
 
1309
1319
  - Fixed Search API suggest/autocomplete (HTTP 400 errors resolved)
1310
1320
  - Enhanced collection display with robust fallback logic
@@ -1,4 +1,4 @@
1
- __version__ = "1.2.4"
1
+ __version__ = "1.2.5"
2
2
 
3
3
  # Import main client modules
4
4
  from .client import *
@@ -495,4 +495,337 @@ def update_enum_def(payload_file, dry_run, validate):
495
495
  except Exception as e:
496
496
  click.echo(f"Error: {e}")
497
497
 
498
+
499
+ @types.command(name="list-business-attributes")
500
+ @click.option('--output', type=click.Choice(['table', 'json']), default='table', help='Output format')
501
+ @click.option('--show-empty-groups/--hide-empty-groups', default=True, help='Show groups with no attributes')
502
+ def list_business_attributes(output, show_empty_groups):
503
+ """List all business metadata attributes (Custom metadata in Purview UI)
504
+
505
+ This command displays individual attributes organized by their parent groups,
506
+ matching what you see in the Purview "Custom metadata (preview)" interface
507
+ under "Business concept attributes" tab.
508
+
509
+ Examples:
510
+ pvw types list-business-attributes
511
+ pvw types list-business-attributes --output json
512
+ pvw types list-business-attributes --hide-empty-groups
513
+ """
514
+ from rich.console import Console
515
+ from rich.table import Table
516
+
517
+ console = Console()
518
+
519
+ try:
520
+ client = Types()
521
+ result = client.typesRead({})
522
+
523
+ if not result:
524
+ console.print("[red]ERROR:[/red] Failed to retrieve type definitions")
525
+ return
526
+
527
+ biz = result.get('businessMetadataDefs', [])
528
+
529
+ if not biz:
530
+ console.print("[yellow]No business metadata found[/yellow]")
531
+ return
532
+
533
+ if output == 'json':
534
+ # JSON output: list of attributes with their group info
535
+ attributes_list = []
536
+ for group in biz:
537
+ group_name = group.get('name', 'N/A')
538
+ group_guid = group.get('guid', 'N/A')
539
+
540
+ attributes = group.get('attributeDefs', [])
541
+ if not attributes and not show_empty_groups:
542
+ continue
543
+
544
+ for attr in attributes:
545
+ attributes_list.append({
546
+ 'attributeName': attr.get('name'),
547
+ 'group': group_name,
548
+ 'groupGuid': group_guid,
549
+ 'type': attr.get('typeName'),
550
+ 'description': attr.get('description', ''),
551
+ 'isOptional': attr.get('isOptional', True),
552
+ 'isIndexable': attr.get('isIndexable', False)
553
+ })
554
+
555
+ click.echo(json.dumps(attributes_list, indent=2))
556
+ return
557
+
558
+ # Table output
559
+ table = Table(title="[bold cyan]Business Concept Attributes (Custom Metadata)[/bold cyan]")
560
+ table.add_column("Attribute Name", style="green", no_wrap=True)
561
+ table.add_column("Group", style="cyan")
562
+ table.add_column("Type", style="yellow")
563
+ table.add_column("Description", style="white", max_width=40)
564
+ table.add_column("Scope", style="magenta")
565
+
566
+ total_attributes = 0
567
+
568
+ for group in biz:
569
+ group_name = group.get('name', 'N/A')
570
+
571
+ # Parse data governance options for scope
572
+ scope_info = "N/A"
573
+ options = group.get('options', {})
574
+ if 'dataGovernanceOptions' in options:
575
+ try:
576
+ dg_opts_str = options.get('dataGovernanceOptions', '{}')
577
+ dg_opts = json.loads(dg_opts_str) if isinstance(dg_opts_str, str) else dg_opts_str
578
+ applicable = dg_opts.get('applicableConstructs', [])
579
+ if applicable:
580
+ # Extract construct types (domain, businessConcept, etc.)
581
+ scope_parts = []
582
+ for construct in applicable[:2]: # Show first 2
583
+ if ':' in construct:
584
+ scope_parts.append(construct.split(':')[0])
585
+ else:
586
+ scope_parts.append(construct)
587
+ scope_info = ', '.join(scope_parts)
588
+ if len(applicable) > 2:
589
+ scope_info += f", +{len(applicable)-2} more"
590
+ except:
591
+ pass
592
+
593
+ # List all attributes in this group
594
+ attributes = group.get('attributeDefs', [])
595
+
596
+ if attributes:
597
+ for attr in attributes:
598
+ total_attributes += 1
599
+ attr_name = attr.get('name', 'N/A')
600
+ attr_type = attr.get('typeName', 'N/A')
601
+
602
+ # Simplify enum types for display
603
+ if 'ATTRIBUTE_ENUM_' in attr_type:
604
+ attr_type = 'Enum (Single choice)'
605
+
606
+ attr_desc = attr.get('description', '')
607
+
608
+ # Get scope from attribute if it overrides group
609
+ attr_scope = scope_info
610
+ attr_opts = attr.get('options', {})
611
+ if 'dataGovernanceOptions' in attr_opts:
612
+ try:
613
+ attr_dg_str = attr_opts.get('dataGovernanceOptions', '{}')
614
+ attr_dg = json.loads(attr_dg_str) if isinstance(attr_dg_str, str) else attr_dg_str
615
+ inherit = attr_dg.get('inheritApplicableConstructsFromGroup', True)
616
+ if not inherit:
617
+ attr_applicable = attr_dg.get('applicableConstructs', [])
618
+ if attr_applicable:
619
+ attr_scope = f"{len(attr_applicable)} custom scope(s)"
620
+ except:
621
+ pass
622
+
623
+ table.add_row(
624
+ attr_name,
625
+ group_name,
626
+ attr_type,
627
+ attr_desc[:40] + "..." if len(attr_desc) > 40 else attr_desc,
628
+ attr_scope
629
+ )
630
+ elif show_empty_groups:
631
+ # Show group with no attributes
632
+ table.add_row(
633
+ f"[dim](no attributes)[/dim]",
634
+ group_name,
635
+ "-",
636
+ f"[dim]Empty group[/dim]",
637
+ scope_info
638
+ )
639
+
640
+ console.print(table)
641
+ console.print(f"\n[cyan]Total:[/cyan] {total_attributes} attribute(s) in {len(biz)} group(s)")
642
+
643
+ if total_attributes > 0:
644
+ console.print("\n[dim]Tip: Use 'pvw types read-business-metadata-def --name <GroupName>' for details[/dim]")
645
+ console.print("[dim]Tip: Use 'pvw types list-business-metadata-groups' to see group-level summary[/dim]")
646
+
647
+ except Exception as e:
648
+ console.print(f"[red]ERROR:[/red] {str(e)}")
649
+
650
+
651
+ @types.command(name="list-business-metadata-groups")
652
+ @click.option('--output', type=click.Choice(['table', 'json']), default='table', help='Output format')
653
+ def list_business_metadata_groups(output):
654
+ """List business metadata groups with their scope (Business Concept vs Data Asset).
655
+
656
+ Shows a summary view of metadata groups to distinguish which apply to:
657
+ - Business Concepts (Terms, Domains, Business Rules)
658
+ - Data Assets (Tables, Files, Databases)
659
+ - Universal (Both)
660
+
661
+ Examples:
662
+ pvw types list-business-metadata-groups
663
+ pvw types list-business-metadata-groups --output json
664
+ """
665
+ from rich.console import Console
666
+ from rich.table import Table
667
+ import json
668
+
669
+ console = Console()
670
+
671
+ try:
672
+ client = Types()
673
+ result = client.typesRead({})
674
+
675
+ if not result:
676
+ console.print("[red]ERROR:[/red] Failed to retrieve type definitions")
677
+ return
678
+
679
+ biz = result.get('businessMetadataDefs', [])
680
+
681
+ if not biz:
682
+ console.print("[yellow]No business metadata groups found[/yellow]")
683
+ return
684
+
685
+ if output == 'json':
686
+ # JSON output
687
+ groups_list = []
688
+ for group in biz:
689
+ group_name = group.get('name', 'N/A')
690
+ group_guid = group.get('guid', 'N/A')
691
+ attr_count = len(group.get('attributeDefs', []))
692
+
693
+ # Determine scope
694
+ scope = "N/A"
695
+ scope_type = "unknown"
696
+ options = group.get('options', {})
697
+
698
+ if 'dataGovernanceOptions' in options:
699
+ try:
700
+ dg_opts_str = options.get('dataGovernanceOptions', '{}')
701
+ dg_opts = json.loads(dg_opts_str) if isinstance(dg_opts_str, str) else dg_opts_str
702
+ applicable = dg_opts.get('applicableConstructs', [])
703
+
704
+ if applicable:
705
+ has_business_concept = any('businessConcept' in c or 'domain' in c for c in applicable)
706
+ has_dataset = any('dataset' in c.lower() for c in applicable)
707
+
708
+ if has_business_concept and has_dataset:
709
+ scope = "Universal (Concept + Dataset)"
710
+ scope_type = "universal"
711
+ elif has_business_concept:
712
+ scope = "Business Concept"
713
+ scope_type = "business_concept"
714
+ elif has_dataset:
715
+ scope = "Data Asset"
716
+ scope_type = "data_asset"
717
+ else:
718
+ scope = ', '.join([c.split(':')[0] if ':' in c else c for c in applicable[:3]])
719
+ scope_type = "custom"
720
+ except:
721
+ pass
722
+
723
+ # Check for legacy applicableEntityTypes in attributes
724
+ if scope == "N/A":
725
+ for attr in group.get('attributeDefs', []):
726
+ attr_opts = attr.get('options', {})
727
+ if 'applicableEntityTypes' in attr_opts:
728
+ try:
729
+ entity_types_str = attr_opts.get('applicableEntityTypes', '[]')
730
+ entity_types = json.loads(entity_types_str) if isinstance(entity_types_str, str) else entity_types_str
731
+ if entity_types and isinstance(entity_types, list):
732
+ if any('table' in et.lower() or 'database' in et.lower() for et in entity_types):
733
+ scope = "Data Asset (Legacy)"
734
+ scope_type = "data_asset_legacy"
735
+ break
736
+ except:
737
+ pass
738
+
739
+ groups_list.append({
740
+ 'groupName': group_name,
741
+ 'groupGuid': group_guid,
742
+ 'scope': scope,
743
+ 'scopeType': scope_type,
744
+ 'attributeCount': attr_count,
745
+ 'description': group.get('description', '')
746
+ })
747
+
748
+ click.echo(json.dumps(groups_list, indent=2))
749
+ return
750
+
751
+ # Table output
752
+ table = Table(title="[bold cyan]Business Metadata Groups[/bold cyan]", show_header=True)
753
+ table.add_column("Group Name", style="cyan", no_wrap=True)
754
+ table.add_column("Scope", style="magenta", max_width=30)
755
+ table.add_column("Attributes", style="yellow", justify="center")
756
+ table.add_column("Description", style="white", max_width=40)
757
+
758
+ for group in biz:
759
+ group_name = group.get('name', 'N/A')
760
+ attr_count = len(group.get('attributeDefs', []))
761
+ group_desc = group.get('description', '')
762
+
763
+ # Determine scope
764
+ scope = "N/A"
765
+ scope_style = "white"
766
+ options = group.get('options', {})
767
+
768
+ if 'dataGovernanceOptions' in options:
769
+ try:
770
+ dg_opts_str = options.get('dataGovernanceOptions', '{}')
771
+ dg_opts = json.loads(dg_opts_str) if isinstance(dg_opts_str, str) else dg_opts_str
772
+ applicable = dg_opts.get('applicableConstructs', [])
773
+
774
+ if applicable:
775
+ has_business_concept = any('businessConcept' in c or 'domain' in c for c in applicable)
776
+ has_dataset = any('dataset' in c.lower() for c in applicable)
777
+
778
+ if has_business_concept and has_dataset:
779
+ scope = "Universal"
780
+ scope_style = "magenta bold"
781
+ elif has_business_concept:
782
+ scope = "Business Concept"
783
+ scope_style = "green"
784
+ elif has_dataset:
785
+ scope = "Data Asset"
786
+ scope_style = "blue"
787
+ else:
788
+ scope = ', '.join([c.split(':')[0] if ':' in c else c for c in applicable[:2]])
789
+ scope_style = "yellow"
790
+ except:
791
+ pass
792
+
793
+ # Check for legacy applicableEntityTypes
794
+ if scope == "N/A":
795
+ for attr in group.get('attributeDefs', []):
796
+ attr_opts = attr.get('options', {})
797
+ if 'applicableEntityTypes' in attr_opts:
798
+ try:
799
+ entity_types_str = attr_opts.get('applicableEntityTypes', '[]')
800
+ entity_types = json.loads(entity_types_str) if isinstance(entity_types_str, str) else entity_types_str
801
+ if entity_types and isinstance(entity_types, list):
802
+ if any('table' in et.lower() or 'database' in et.lower() for et in entity_types):
803
+ scope = "Data Asset (Legacy)"
804
+ scope_style = "blue dim"
805
+ break
806
+ except:
807
+ pass
808
+
809
+ table.add_row(
810
+ group_name,
811
+ f"[{scope_style}]{scope}[/{scope_style}]",
812
+ str(attr_count),
813
+ group_desc[:40] + "..." if len(group_desc) > 40 else group_desc
814
+ )
815
+
816
+ console.print(table)
817
+ console.print(f"\n[cyan]Total:[/cyan] {len(biz)} group(s)")
818
+
819
+ console.print("\n[bold]Legend:[/bold]")
820
+ console.print(" [green]Business Concept[/green] = Applies to Glossary Terms, Domains, Business Rules")
821
+ console.print(" [blue]Data Asset[/blue] = Applies to Tables, Files, Databases, etc.")
822
+ console.print(" [magenta bold]Universal[/magenta bold] = Applies to both Concepts and Assets")
823
+
824
+ console.print("\n[dim]Tip: Use 'pvw types list-business-attributes' to see individual attributes[/dim]")
825
+ console.print("[dim]Tip: Use 'pvw types read-business-metadata-def --name <GroupName>' for full details[/dim]")
826
+
827
+ except Exception as e:
828
+ console.print(f"[red]ERROR:[/red] {str(e)}")
829
+
830
+
498
831
  __all__ = ['types']