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.
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/PKG-INFO +19 -9
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/README.md +18 -8
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/__init__.py +1 -1
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/types.py +333 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/unified_catalog.py +1329 -9
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_entity.py +74 -17
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_types.py +31 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_unified_catalog.py +646 -30
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/endpoints.py +75 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/pvw_cli.egg-info/PKG-INFO +19 -9
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/pvw_cli.egg-info/SOURCES.txt +2 -1
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/pyproject.toml +3 -3
- pvw_cli-1.2.5/tests/test_mcp_server.py +200 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/__main__.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/__init__.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/account.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/cli.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/collections.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/domain.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/entity.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/glossary.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/health.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/insight.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/lineage.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/management.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/policystore.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/relationship.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/scan.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/search.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/share.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/cli/workflow.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/__init__.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_account.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_collections.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_domain.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_glossary.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_health.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_insight.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_lineage.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_management.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_policystore.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_relationship.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_scan.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_search.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_share.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/_workflow.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/api_client.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/business_rules.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/config.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/data_quality.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/endpoint.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/exceptions.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/lineage_visualization.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/monitoring_dashboard.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/rate_limiter.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/retry_handler.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/scanning_operations.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/settings.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/client/sync_client.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/plugins/__init__.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/purviewcli/plugins/plugin_system.py +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/pvw_cli.egg-info/dependency_links.txt +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/pvw_cli.egg-info/entry_points.txt +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/pvw_cli.egg-info/not-zip-safe +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/pvw_cli.egg-info/requires.txt +0 -0
- {pvw_cli-1.2.4 → pvw_cli-1.2.5}/pvw_cli.egg-info/top_level.txt +0 -0
- {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.
|
|
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
|
-
|
|
59
|
+
# PURVIEW CLI v1.2.5 - Microsoft Purview Automation & Data Governance
|
|
60
60
|
|
|
61
|
-
> **LATEST UPDATE v1.2.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
1
|
+
# PURVIEW CLI v1.2.5 - Microsoft Purview Automation & Data Governance
|
|
2
2
|
|
|
3
|
-
> **LATEST UPDATE v1.2.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
@@ -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']
|