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.
- purviewcli/__init__.py +2 -2
- purviewcli/cli/__init__.py +2 -2
- purviewcli/cli/cli.py +11 -13
- purviewcli/cli/collections.py +363 -0
- purviewcli/cli/entity.py +464 -0
- purviewcli/cli/search.py +201 -10
- purviewcli/cli/unified_catalog.py +857 -0
- purviewcli/client/_search.py +7 -2
- purviewcli/client/_unified_catalog.py +292 -295
- purviewcli/client/endpoint.py +13 -1
- purviewcli/client/sync_client.py +72 -15
- pvw_cli-1.0.9.dist-info/METADATA +800 -0
- {pvw_cli-1.0.6.dist-info → pvw_cli-1.0.9.dist-info}/RECORD +16 -17
- {pvw_cli-1.0.6.dist-info → pvw_cli-1.0.9.dist-info}/WHEEL +1 -1
- purviewcli/cli/data_product.py +0 -278
- purviewcli/client/_data_product.py +0 -168
- pvw_cli-1.0.6.dist-info/METADATA +0 -399
- {pvw_cli-1.0.6.dist-info → pvw_cli-1.0.9.dist-info}/entry_points.txt +0 -0
- {pvw_cli-1.0.6.dist-info → pvw_cli-1.0.9.dist-info}/top_level.txt +0 -0
purviewcli/client/endpoint.py
CHANGED
|
@@ -22,10 +22,15 @@ def get_data(http_dict):
|
|
|
22
22
|
account_name = os.getenv(
|
|
23
23
|
"PURVIEW_ACCOUNT_NAME", http_dict.get("account_name", "test-purview-account")
|
|
24
24
|
)
|
|
25
|
+
|
|
26
|
+
# Get account ID from environment (optional)
|
|
27
|
+
account_id = os.getenv("PURVIEW_ACCOUNT_ID")
|
|
25
28
|
|
|
26
29
|
# Create config
|
|
27
30
|
config = SyncPurviewConfig(
|
|
28
|
-
account_name=account_name,
|
|
31
|
+
account_name=account_name,
|
|
32
|
+
azure_region=os.getenv("AZURE_REGION", "public"),
|
|
33
|
+
account_id=account_id
|
|
29
34
|
)
|
|
30
35
|
|
|
31
36
|
# Create synchronous client
|
|
@@ -39,6 +44,13 @@ def get_data(http_dict):
|
|
|
39
44
|
json=http_dict.get("payload"),
|
|
40
45
|
)
|
|
41
46
|
|
|
47
|
+
# The synchronous client returns a wrapper dict like
|
|
48
|
+
# {"status": "success", "data": <json>, "status_code": 200}
|
|
49
|
+
# Normalize to return the raw JSON payload when available so
|
|
50
|
+
# calling code (which expects the API JSON) works consistently
|
|
51
|
+
if isinstance(result, dict) and result.get("status") == "success" and "data" in result:
|
|
52
|
+
return result.get("data")
|
|
53
|
+
|
|
42
54
|
return result
|
|
43
55
|
|
|
44
56
|
except Exception as e:
|
purviewcli/client/sync_client.py
CHANGED
|
@@ -13,9 +13,10 @@ from azure.core.exceptions import ClientAuthenticationError
|
|
|
13
13
|
class SyncPurviewConfig:
|
|
14
14
|
"""Simple synchronous config"""
|
|
15
15
|
|
|
16
|
-
def __init__(self, account_name: str, azure_region: str = "public"):
|
|
16
|
+
def __init__(self, account_name: str, azure_region: str = "public", account_id: Optional[str] = None):
|
|
17
17
|
self.account_name = account_name
|
|
18
18
|
self.azure_region = azure_region
|
|
19
|
+
self.account_id = account_id # Optional Purview account ID for UC endpoints
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class SyncPurviewClient:
|
|
@@ -24,7 +25,7 @@ class SyncPurviewClient:
|
|
|
24
25
|
def __init__(self, config: SyncPurviewConfig):
|
|
25
26
|
self.config = config
|
|
26
27
|
|
|
27
|
-
# Set up endpoints based on Azure region
|
|
28
|
+
# Set up regular Purview API endpoints based on Azure region, using account name in the URL
|
|
28
29
|
if config.azure_region and config.azure_region.lower() == "china":
|
|
29
30
|
self.base_url = f"https://{config.account_name}.purview.azure.cn"
|
|
30
31
|
self.auth_scope = "https://purview.azure.cn/.default"
|
|
@@ -35,11 +36,49 @@ class SyncPurviewClient:
|
|
|
35
36
|
self.base_url = f"https://{config.account_name}.purview.azure.com"
|
|
36
37
|
self.auth_scope = "https://purview.azure.net/.default"
|
|
37
38
|
|
|
39
|
+
# Set up Unified Catalog endpoint using Purview account ID format
|
|
40
|
+
self.account_id = config.account_id or self._get_purview_account_id()
|
|
41
|
+
self.uc_base_url = f"https://{self.account_id}-api.purview-service.microsoft.com"
|
|
42
|
+
self.uc_auth_scope = "73c2949e-da2d-457a-9607-fcc665198967/.default"
|
|
43
|
+
|
|
38
44
|
self._token = None
|
|
45
|
+
self._uc_token = None
|
|
39
46
|
self._credential = None
|
|
40
47
|
|
|
41
|
-
def
|
|
42
|
-
"""Get
|
|
48
|
+
def _get_purview_account_id(self):
|
|
49
|
+
"""Get Purview account ID from Atlas endpoint URL"""
|
|
50
|
+
account_id = os.getenv("PURVIEW_ACCOUNT_ID")
|
|
51
|
+
if not account_id:
|
|
52
|
+
import subprocess
|
|
53
|
+
try:
|
|
54
|
+
# Get the Atlas catalog endpoint and extract account ID from it
|
|
55
|
+
result = subprocess.run([
|
|
56
|
+
"az", "purview", "account", "show",
|
|
57
|
+
"--name", self.config.account_name,
|
|
58
|
+
"--resource-group", os.getenv("PURVIEW_RESOURCE_GROUP", "fabric-artifacts"),
|
|
59
|
+
"--query", "endpoints.catalog",
|
|
60
|
+
"-o", "tsv"
|
|
61
|
+
], capture_output=True, text=True, check=True)
|
|
62
|
+
atlas_url = result.stdout.strip()
|
|
63
|
+
|
|
64
|
+
if atlas_url and "-api.purview-service.microsoft.com" in atlas_url:
|
|
65
|
+
account_id = atlas_url.split("://")[1].split("-api.purview-service.microsoft.com")[0]
|
|
66
|
+
else:
|
|
67
|
+
raise Exception(f"Could not extract account ID from Atlas URL: {atlas_url}")
|
|
68
|
+
except Exception as e:
|
|
69
|
+
# For Unified Catalog, the account ID is typically the Azure Tenant ID
|
|
70
|
+
try:
|
|
71
|
+
tenant_result = subprocess.run([
|
|
72
|
+
"az", "account", "show", "--query", "tenantId", "-o", "tsv"
|
|
73
|
+
], capture_output=True, text=True, check=True)
|
|
74
|
+
account_id = tenant_result.stdout.strip()
|
|
75
|
+
print(f"Info: Using Tenant ID as Purview Account ID for Unified Catalog: {account_id}")
|
|
76
|
+
except Exception:
|
|
77
|
+
raise Exception(f"Could not determine Purview account ID. For Unified Catalog, this is typically your Azure Tenant ID. Please set PURVIEW_ACCOUNT_ID environment variable. Error: {e}")
|
|
78
|
+
return account_id
|
|
79
|
+
|
|
80
|
+
def _get_authentication_token(self, for_unified_catalog=False):
|
|
81
|
+
"""Get Azure authentication token for regular Purview or Unified Catalog APIs"""
|
|
43
82
|
try:
|
|
44
83
|
# Try different authentication methods in order of preference
|
|
45
84
|
|
|
@@ -56,10 +95,15 @@ class SyncPurviewClient:
|
|
|
56
95
|
# 2. Use default credential (managed identity, VS Code, CLI, etc.)
|
|
57
96
|
self._credential = DefaultAzureCredential()
|
|
58
97
|
|
|
59
|
-
# Get the token
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
98
|
+
# Get the appropriate token based on the API type
|
|
99
|
+
if for_unified_catalog:
|
|
100
|
+
token = self._credential.get_token(self.uc_auth_scope)
|
|
101
|
+
self._uc_token = token.token
|
|
102
|
+
return self._uc_token
|
|
103
|
+
else:
|
|
104
|
+
token = self._credential.get_token(self.auth_scope)
|
|
105
|
+
self._token = token.token
|
|
106
|
+
return self._token
|
|
63
107
|
|
|
64
108
|
except ClientAuthenticationError as e:
|
|
65
109
|
raise Exception(f"Azure authentication failed: {str(e)}")
|
|
@@ -69,12 +113,25 @@ class SyncPurviewClient:
|
|
|
69
113
|
def make_request(self, method: str, endpoint: str, **kwargs) -> Dict:
|
|
70
114
|
"""Make actual HTTP request to Microsoft Purview"""
|
|
71
115
|
try:
|
|
72
|
-
#
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
116
|
+
# Determine if this is a Unified Catalog request
|
|
117
|
+
is_unified_catalog = endpoint.startswith('/datagovernance/catalog')
|
|
118
|
+
|
|
119
|
+
# Get the appropriate authentication token and base URL
|
|
120
|
+
if is_unified_catalog:
|
|
121
|
+
if not self._uc_token:
|
|
122
|
+
self._get_authentication_token(for_unified_catalog=True)
|
|
123
|
+
token = self._uc_token
|
|
124
|
+
base_url = self.uc_base_url
|
|
125
|
+
else:
|
|
126
|
+
if not self._token:
|
|
127
|
+
self._get_authentication_token(for_unified_catalog=False)
|
|
128
|
+
token = self._token
|
|
129
|
+
base_url = self.base_url
|
|
130
|
+
|
|
131
|
+
# Prepare the request
|
|
132
|
+
url = f"{base_url}{endpoint}"
|
|
76
133
|
headers = {
|
|
77
|
-
"Authorization": f"Bearer {
|
|
134
|
+
"Authorization": f"Bearer {token}",
|
|
78
135
|
"Content-Type": "application/json",
|
|
79
136
|
"User-Agent": "purviewcli/2.0",
|
|
80
137
|
}
|
|
@@ -89,7 +146,7 @@ class SyncPurviewClient:
|
|
|
89
146
|
timeout=30,
|
|
90
147
|
)
|
|
91
148
|
# Handle the response
|
|
92
|
-
if response.status_code
|
|
149
|
+
if response.status_code in [200, 201]:
|
|
93
150
|
try:
|
|
94
151
|
data = response.json()
|
|
95
152
|
return {"status": "success", "data": data, "status_code": response.status_code}
|
|
@@ -115,7 +172,7 @@ class SyncPurviewClient:
|
|
|
115
172
|
timeout=30,
|
|
116
173
|
)
|
|
117
174
|
|
|
118
|
-
if response.status_code
|
|
175
|
+
if response.status_code in [200, 201]:
|
|
119
176
|
try:
|
|
120
177
|
data = response.json()
|
|
121
178
|
return {
|