databricks-sdk 0.60.0__py3-none-any.whl → 0.61.0__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.
- databricks/sdk/__init__.py +15 -1
- databricks/sdk/service/catalog.py +167 -131
- databricks/sdk/service/cleanrooms.py +500 -6
- databricks/sdk/service/dashboards.py +1 -0
- databricks/sdk/service/database.py +45 -6
- databricks/sdk/service/serving.py +1 -0
- databricks/sdk/service/sharing.py +65 -0
- databricks/sdk/service/sql.py +9 -0
- databricks/sdk/version.py +1 -1
- {databricks_sdk-0.60.0.dist-info → databricks_sdk-0.61.0.dist-info}/METADATA +1 -1
- {databricks_sdk-0.60.0.dist-info → databricks_sdk-0.61.0.dist-info}/RECORD +15 -15
- {databricks_sdk-0.60.0.dist-info → databricks_sdk-0.61.0.dist-info}/WHEEL +0 -0
- {databricks_sdk-0.60.0.dist-info → databricks_sdk-0.61.0.dist-info}/licenses/LICENSE +0 -0
- {databricks_sdk-0.60.0.dist-info → databricks_sdk-0.61.0.dist-info}/licenses/NOTICE +0 -0
- {databricks_sdk-0.60.0.dist-info → databricks_sdk-0.61.0.dist-info}/top_level.txt +0 -0
|
@@ -3,11 +3,14 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
|
+
import random
|
|
7
|
+
import time
|
|
6
8
|
from dataclasses import dataclass
|
|
9
|
+
from datetime import timedelta
|
|
7
10
|
from enum import Enum
|
|
8
|
-
from typing import Any, Dict, Iterator, List, Optional
|
|
11
|
+
from typing import Any, Callable, Dict, Iterator, List, Optional
|
|
9
12
|
|
|
10
|
-
from ._internal import _enum, _from_dict, _repeated_dict
|
|
13
|
+
from ._internal import Wait, _enum, _from_dict, _repeated_dict
|
|
11
14
|
|
|
12
15
|
_LOG = logging.getLogger("databricks.sdk")
|
|
13
16
|
|
|
@@ -540,6 +543,83 @@ class CleanRoomAssetVolumeLocalDetails:
|
|
|
540
543
|
return cls(local_name=d.get("local_name", None))
|
|
541
544
|
|
|
542
545
|
|
|
546
|
+
@dataclass
|
|
547
|
+
class CleanRoomAutoApprovalRule:
|
|
548
|
+
author_collaborator_alias: Optional[str] = None
|
|
549
|
+
|
|
550
|
+
author_scope: Optional[CleanRoomAutoApprovalRuleAuthorScope] = None
|
|
551
|
+
|
|
552
|
+
clean_room_name: Optional[str] = None
|
|
553
|
+
"""The name of the clean room this auto-approval rule belongs to."""
|
|
554
|
+
|
|
555
|
+
created_at: Optional[int] = None
|
|
556
|
+
"""Timestamp of when the rule was created, in epoch milliseconds."""
|
|
557
|
+
|
|
558
|
+
rule_id: Optional[str] = None
|
|
559
|
+
"""A generated UUID identifying the rule."""
|
|
560
|
+
|
|
561
|
+
rule_owner_collaborator_alias: Optional[str] = None
|
|
562
|
+
"""The owner of the rule to whom the rule applies."""
|
|
563
|
+
|
|
564
|
+
runner_collaborator_alias: Optional[str] = None
|
|
565
|
+
|
|
566
|
+
def as_dict(self) -> dict:
|
|
567
|
+
"""Serializes the CleanRoomAutoApprovalRule into a dictionary suitable for use as a JSON request body."""
|
|
568
|
+
body = {}
|
|
569
|
+
if self.author_collaborator_alias is not None:
|
|
570
|
+
body["author_collaborator_alias"] = self.author_collaborator_alias
|
|
571
|
+
if self.author_scope is not None:
|
|
572
|
+
body["author_scope"] = self.author_scope.value
|
|
573
|
+
if self.clean_room_name is not None:
|
|
574
|
+
body["clean_room_name"] = self.clean_room_name
|
|
575
|
+
if self.created_at is not None:
|
|
576
|
+
body["created_at"] = self.created_at
|
|
577
|
+
if self.rule_id is not None:
|
|
578
|
+
body["rule_id"] = self.rule_id
|
|
579
|
+
if self.rule_owner_collaborator_alias is not None:
|
|
580
|
+
body["rule_owner_collaborator_alias"] = self.rule_owner_collaborator_alias
|
|
581
|
+
if self.runner_collaborator_alias is not None:
|
|
582
|
+
body["runner_collaborator_alias"] = self.runner_collaborator_alias
|
|
583
|
+
return body
|
|
584
|
+
|
|
585
|
+
def as_shallow_dict(self) -> dict:
|
|
586
|
+
"""Serializes the CleanRoomAutoApprovalRule into a shallow dictionary of its immediate attributes."""
|
|
587
|
+
body = {}
|
|
588
|
+
if self.author_collaborator_alias is not None:
|
|
589
|
+
body["author_collaborator_alias"] = self.author_collaborator_alias
|
|
590
|
+
if self.author_scope is not None:
|
|
591
|
+
body["author_scope"] = self.author_scope
|
|
592
|
+
if self.clean_room_name is not None:
|
|
593
|
+
body["clean_room_name"] = self.clean_room_name
|
|
594
|
+
if self.created_at is not None:
|
|
595
|
+
body["created_at"] = self.created_at
|
|
596
|
+
if self.rule_id is not None:
|
|
597
|
+
body["rule_id"] = self.rule_id
|
|
598
|
+
if self.rule_owner_collaborator_alias is not None:
|
|
599
|
+
body["rule_owner_collaborator_alias"] = self.rule_owner_collaborator_alias
|
|
600
|
+
if self.runner_collaborator_alias is not None:
|
|
601
|
+
body["runner_collaborator_alias"] = self.runner_collaborator_alias
|
|
602
|
+
return body
|
|
603
|
+
|
|
604
|
+
@classmethod
|
|
605
|
+
def from_dict(cls, d: Dict[str, Any]) -> CleanRoomAutoApprovalRule:
|
|
606
|
+
"""Deserializes the CleanRoomAutoApprovalRule from a dictionary."""
|
|
607
|
+
return cls(
|
|
608
|
+
author_collaborator_alias=d.get("author_collaborator_alias", None),
|
|
609
|
+
author_scope=_enum(d, "author_scope", CleanRoomAutoApprovalRuleAuthorScope),
|
|
610
|
+
clean_room_name=d.get("clean_room_name", None),
|
|
611
|
+
created_at=d.get("created_at", None),
|
|
612
|
+
rule_id=d.get("rule_id", None),
|
|
613
|
+
rule_owner_collaborator_alias=d.get("rule_owner_collaborator_alias", None),
|
|
614
|
+
runner_collaborator_alias=d.get("runner_collaborator_alias", None),
|
|
615
|
+
)
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
class CleanRoomAutoApprovalRuleAuthorScope(Enum):
|
|
619
|
+
|
|
620
|
+
ANY_AUTHOR = "ANY_AUTHOR"
|
|
621
|
+
|
|
622
|
+
|
|
543
623
|
@dataclass
|
|
544
624
|
class CleanRoomCollaborator:
|
|
545
625
|
"""Publicly visible clean room collaborator."""
|
|
@@ -1017,6 +1097,41 @@ class ComplianceSecurityProfile:
|
|
|
1017
1097
|
)
|
|
1018
1098
|
|
|
1019
1099
|
|
|
1100
|
+
@dataclass
|
|
1101
|
+
class CreateCleanRoomAssetReviewResponse:
|
|
1102
|
+
notebook_review_state: Optional[CleanRoomNotebookReviewNotebookReviewState] = None
|
|
1103
|
+
"""top-level status derived from all reviews"""
|
|
1104
|
+
|
|
1105
|
+
notebook_reviews: Optional[List[CleanRoomNotebookReview]] = None
|
|
1106
|
+
"""All existing notebook approvals or rejections"""
|
|
1107
|
+
|
|
1108
|
+
def as_dict(self) -> dict:
|
|
1109
|
+
"""Serializes the CreateCleanRoomAssetReviewResponse into a dictionary suitable for use as a JSON request body."""
|
|
1110
|
+
body = {}
|
|
1111
|
+
if self.notebook_review_state is not None:
|
|
1112
|
+
body["notebook_review_state"] = self.notebook_review_state.value
|
|
1113
|
+
if self.notebook_reviews:
|
|
1114
|
+
body["notebook_reviews"] = [v.as_dict() for v in self.notebook_reviews]
|
|
1115
|
+
return body
|
|
1116
|
+
|
|
1117
|
+
def as_shallow_dict(self) -> dict:
|
|
1118
|
+
"""Serializes the CreateCleanRoomAssetReviewResponse into a shallow dictionary of its immediate attributes."""
|
|
1119
|
+
body = {}
|
|
1120
|
+
if self.notebook_review_state is not None:
|
|
1121
|
+
body["notebook_review_state"] = self.notebook_review_state
|
|
1122
|
+
if self.notebook_reviews:
|
|
1123
|
+
body["notebook_reviews"] = self.notebook_reviews
|
|
1124
|
+
return body
|
|
1125
|
+
|
|
1126
|
+
@classmethod
|
|
1127
|
+
def from_dict(cls, d: Dict[str, Any]) -> CreateCleanRoomAssetReviewResponse:
|
|
1128
|
+
"""Deserializes the CreateCleanRoomAssetReviewResponse from a dictionary."""
|
|
1129
|
+
return cls(
|
|
1130
|
+
notebook_review_state=_enum(d, "notebook_review_state", CleanRoomNotebookReviewNotebookReviewState),
|
|
1131
|
+
notebook_reviews=_repeated_dict(d, "notebook_reviews", CleanRoomNotebookReview),
|
|
1132
|
+
)
|
|
1133
|
+
|
|
1134
|
+
|
|
1020
1135
|
@dataclass
|
|
1021
1136
|
class CreateCleanRoomOutputCatalogResponse:
|
|
1022
1137
|
output_catalog: Optional[CleanRoomOutputCatalog] = None
|
|
@@ -1062,6 +1177,38 @@ class DeleteCleanRoomAssetResponse:
|
|
|
1062
1177
|
return cls()
|
|
1063
1178
|
|
|
1064
1179
|
|
|
1180
|
+
@dataclass
|
|
1181
|
+
class ListCleanRoomAssetRevisionsResponse:
|
|
1182
|
+
next_page_token: Optional[str] = None
|
|
1183
|
+
|
|
1184
|
+
revisions: Optional[List[CleanRoomAsset]] = None
|
|
1185
|
+
|
|
1186
|
+
def as_dict(self) -> dict:
|
|
1187
|
+
"""Serializes the ListCleanRoomAssetRevisionsResponse into a dictionary suitable for use as a JSON request body."""
|
|
1188
|
+
body = {}
|
|
1189
|
+
if self.next_page_token is not None:
|
|
1190
|
+
body["next_page_token"] = self.next_page_token
|
|
1191
|
+
if self.revisions:
|
|
1192
|
+
body["revisions"] = [v.as_dict() for v in self.revisions]
|
|
1193
|
+
return body
|
|
1194
|
+
|
|
1195
|
+
def as_shallow_dict(self) -> dict:
|
|
1196
|
+
"""Serializes the ListCleanRoomAssetRevisionsResponse into a shallow dictionary of its immediate attributes."""
|
|
1197
|
+
body = {}
|
|
1198
|
+
if self.next_page_token is not None:
|
|
1199
|
+
body["next_page_token"] = self.next_page_token
|
|
1200
|
+
if self.revisions:
|
|
1201
|
+
body["revisions"] = self.revisions
|
|
1202
|
+
return body
|
|
1203
|
+
|
|
1204
|
+
@classmethod
|
|
1205
|
+
def from_dict(cls, d: Dict[str, Any]) -> ListCleanRoomAssetRevisionsResponse:
|
|
1206
|
+
"""Deserializes the ListCleanRoomAssetRevisionsResponse from a dictionary."""
|
|
1207
|
+
return cls(
|
|
1208
|
+
next_page_token=d.get("next_page_token", None), revisions=_repeated_dict(d, "revisions", CleanRoomAsset)
|
|
1209
|
+
)
|
|
1210
|
+
|
|
1211
|
+
|
|
1065
1212
|
@dataclass
|
|
1066
1213
|
class ListCleanRoomAssetsResponse:
|
|
1067
1214
|
assets: Optional[List[CleanRoomAsset]] = None
|
|
@@ -1095,6 +1242,40 @@ class ListCleanRoomAssetsResponse:
|
|
|
1095
1242
|
return cls(assets=_repeated_dict(d, "assets", CleanRoomAsset), next_page_token=d.get("next_page_token", None))
|
|
1096
1243
|
|
|
1097
1244
|
|
|
1245
|
+
@dataclass
|
|
1246
|
+
class ListCleanRoomAutoApprovalRulesResponse:
|
|
1247
|
+
next_page_token: Optional[str] = None
|
|
1248
|
+
"""Opaque token to retrieve the next page of results. Absent if there are no more pages. page_token
|
|
1249
|
+
should be set to this value for the next request (for the next page of results)."""
|
|
1250
|
+
|
|
1251
|
+
rules: Optional[List[CleanRoomAutoApprovalRule]] = None
|
|
1252
|
+
|
|
1253
|
+
def as_dict(self) -> dict:
|
|
1254
|
+
"""Serializes the ListCleanRoomAutoApprovalRulesResponse into a dictionary suitable for use as a JSON request body."""
|
|
1255
|
+
body = {}
|
|
1256
|
+
if self.next_page_token is not None:
|
|
1257
|
+
body["next_page_token"] = self.next_page_token
|
|
1258
|
+
if self.rules:
|
|
1259
|
+
body["rules"] = [v.as_dict() for v in self.rules]
|
|
1260
|
+
return body
|
|
1261
|
+
|
|
1262
|
+
def as_shallow_dict(self) -> dict:
|
|
1263
|
+
"""Serializes the ListCleanRoomAutoApprovalRulesResponse into a shallow dictionary of its immediate attributes."""
|
|
1264
|
+
body = {}
|
|
1265
|
+
if self.next_page_token is not None:
|
|
1266
|
+
body["next_page_token"] = self.next_page_token
|
|
1267
|
+
if self.rules:
|
|
1268
|
+
body["rules"] = self.rules
|
|
1269
|
+
return body
|
|
1270
|
+
|
|
1271
|
+
@classmethod
|
|
1272
|
+
def from_dict(cls, d: Dict[str, Any]) -> ListCleanRoomAutoApprovalRulesResponse:
|
|
1273
|
+
"""Deserializes the ListCleanRoomAutoApprovalRulesResponse from a dictionary."""
|
|
1274
|
+
return cls(
|
|
1275
|
+
next_page_token=d.get("next_page_token", None), rules=_repeated_dict(d, "rules", CleanRoomAutoApprovalRule)
|
|
1276
|
+
)
|
|
1277
|
+
|
|
1278
|
+
|
|
1098
1279
|
@dataclass
|
|
1099
1280
|
class ListCleanRoomNotebookTaskRunsResponse:
|
|
1100
1281
|
next_page_token: Optional[str] = None
|
|
@@ -1164,6 +1345,130 @@ class ListCleanRoomsResponse:
|
|
|
1164
1345
|
)
|
|
1165
1346
|
|
|
1166
1347
|
|
|
1348
|
+
@dataclass
|
|
1349
|
+
class NotebookVersionReview:
|
|
1350
|
+
etag: str
|
|
1351
|
+
"""etag that identifies the notebook version"""
|
|
1352
|
+
|
|
1353
|
+
review_state: CleanRoomNotebookReviewNotebookReviewState
|
|
1354
|
+
"""review outcome"""
|
|
1355
|
+
|
|
1356
|
+
comment: Optional[str] = None
|
|
1357
|
+
"""review comment"""
|
|
1358
|
+
|
|
1359
|
+
def as_dict(self) -> dict:
|
|
1360
|
+
"""Serializes the NotebookVersionReview into a dictionary suitable for use as a JSON request body."""
|
|
1361
|
+
body = {}
|
|
1362
|
+
if self.comment is not None:
|
|
1363
|
+
body["comment"] = self.comment
|
|
1364
|
+
if self.etag is not None:
|
|
1365
|
+
body["etag"] = self.etag
|
|
1366
|
+
if self.review_state is not None:
|
|
1367
|
+
body["review_state"] = self.review_state.value
|
|
1368
|
+
return body
|
|
1369
|
+
|
|
1370
|
+
def as_shallow_dict(self) -> dict:
|
|
1371
|
+
"""Serializes the NotebookVersionReview into a shallow dictionary of its immediate attributes."""
|
|
1372
|
+
body = {}
|
|
1373
|
+
if self.comment is not None:
|
|
1374
|
+
body["comment"] = self.comment
|
|
1375
|
+
if self.etag is not None:
|
|
1376
|
+
body["etag"] = self.etag
|
|
1377
|
+
if self.review_state is not None:
|
|
1378
|
+
body["review_state"] = self.review_state
|
|
1379
|
+
return body
|
|
1380
|
+
|
|
1381
|
+
@classmethod
|
|
1382
|
+
def from_dict(cls, d: Dict[str, Any]) -> NotebookVersionReview:
|
|
1383
|
+
"""Deserializes the NotebookVersionReview from a dictionary."""
|
|
1384
|
+
return cls(
|
|
1385
|
+
comment=d.get("comment", None),
|
|
1386
|
+
etag=d.get("etag", None),
|
|
1387
|
+
review_state=_enum(d, "review_state", CleanRoomNotebookReviewNotebookReviewState),
|
|
1388
|
+
)
|
|
1389
|
+
|
|
1390
|
+
|
|
1391
|
+
class CleanRoomAssetRevisionsAPI:
|
|
1392
|
+
"""Clean Room Asset Revisions denote new versions of uploaded assets (e.g. notebooks) in the clean room."""
|
|
1393
|
+
|
|
1394
|
+
def __init__(self, api_client):
|
|
1395
|
+
self._api = api_client
|
|
1396
|
+
|
|
1397
|
+
def get(self, clean_room_name: str, asset_type: CleanRoomAssetAssetType, name: str, etag: str) -> CleanRoomAsset:
|
|
1398
|
+
"""Get a specific revision of an asset
|
|
1399
|
+
|
|
1400
|
+
:param clean_room_name: str
|
|
1401
|
+
Name of the clean room.
|
|
1402
|
+
:param asset_type: :class:`CleanRoomAssetAssetType`
|
|
1403
|
+
Asset type. Only NOTEBOOK_FILE is supported.
|
|
1404
|
+
:param name: str
|
|
1405
|
+
Name of the asset.
|
|
1406
|
+
:param etag: str
|
|
1407
|
+
Revision etag to fetch. If not provided, the latest revision will be returned.
|
|
1408
|
+
|
|
1409
|
+
:returns: :class:`CleanRoomAsset`
|
|
1410
|
+
"""
|
|
1411
|
+
|
|
1412
|
+
headers = {
|
|
1413
|
+
"Accept": "application/json",
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
res = self._api.do(
|
|
1417
|
+
"GET",
|
|
1418
|
+
f"/api/2.0/clean-rooms/{clean_room_name}/assets/{asset_type.value}/{name}/revisions/{etag}",
|
|
1419
|
+
headers=headers,
|
|
1420
|
+
)
|
|
1421
|
+
return CleanRoomAsset.from_dict(res)
|
|
1422
|
+
|
|
1423
|
+
def list(
|
|
1424
|
+
self,
|
|
1425
|
+
clean_room_name: str,
|
|
1426
|
+
asset_type: CleanRoomAssetAssetType,
|
|
1427
|
+
name: str,
|
|
1428
|
+
*,
|
|
1429
|
+
page_size: Optional[int] = None,
|
|
1430
|
+
page_token: Optional[str] = None,
|
|
1431
|
+
) -> Iterator[CleanRoomAsset]:
|
|
1432
|
+
"""List revisions for an asset
|
|
1433
|
+
|
|
1434
|
+
:param clean_room_name: str
|
|
1435
|
+
Name of the clean room.
|
|
1436
|
+
:param asset_type: :class:`CleanRoomAssetAssetType`
|
|
1437
|
+
Asset type. Only NOTEBOOK_FILE is supported.
|
|
1438
|
+
:param name: str
|
|
1439
|
+
Name of the asset.
|
|
1440
|
+
:param page_size: int (optional)
|
|
1441
|
+
Maximum number of asset revisions to return. Defaults to 10.
|
|
1442
|
+
:param page_token: str (optional)
|
|
1443
|
+
Opaque pagination token to go to next page based on the previous query.
|
|
1444
|
+
|
|
1445
|
+
:returns: Iterator over :class:`CleanRoomAsset`
|
|
1446
|
+
"""
|
|
1447
|
+
|
|
1448
|
+
query = {}
|
|
1449
|
+
if page_size is not None:
|
|
1450
|
+
query["page_size"] = page_size
|
|
1451
|
+
if page_token is not None:
|
|
1452
|
+
query["page_token"] = page_token
|
|
1453
|
+
headers = {
|
|
1454
|
+
"Accept": "application/json",
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
while True:
|
|
1458
|
+
json = self._api.do(
|
|
1459
|
+
"GET",
|
|
1460
|
+
f"/api/2.0/clean-rooms/{clean_room_name}/assets/{asset_type.value}/{name}/revisions",
|
|
1461
|
+
query=query,
|
|
1462
|
+
headers=headers,
|
|
1463
|
+
)
|
|
1464
|
+
if "revisions" in json:
|
|
1465
|
+
for v in json["revisions"]:
|
|
1466
|
+
yield CleanRoomAsset.from_dict(v)
|
|
1467
|
+
if "next_page_token" not in json or not json["next_page_token"]:
|
|
1468
|
+
return
|
|
1469
|
+
query["page_token"] = json["next_page_token"]
|
|
1470
|
+
|
|
1471
|
+
|
|
1167
1472
|
class CleanRoomAssetsAPI:
|
|
1168
1473
|
"""Clean room assets are data and code objects — Tables, volumes, and notebooks that are shared with the
|
|
1169
1474
|
clean room."""
|
|
@@ -1193,6 +1498,41 @@ class CleanRoomAssetsAPI:
|
|
|
1193
1498
|
res = self._api.do("POST", f"/api/2.0/clean-rooms/{clean_room_name}/assets", body=body, headers=headers)
|
|
1194
1499
|
return CleanRoomAsset.from_dict(res)
|
|
1195
1500
|
|
|
1501
|
+
def create_clean_room_asset_review(
|
|
1502
|
+
self,
|
|
1503
|
+
clean_room_name: str,
|
|
1504
|
+
asset_type: CleanRoomAssetAssetType,
|
|
1505
|
+
name: str,
|
|
1506
|
+
notebook_review: NotebookVersionReview,
|
|
1507
|
+
) -> CreateCleanRoomAssetReviewResponse:
|
|
1508
|
+
"""submit an asset review
|
|
1509
|
+
|
|
1510
|
+
:param clean_room_name: str
|
|
1511
|
+
Name of the clean room
|
|
1512
|
+
:param asset_type: :class:`CleanRoomAssetAssetType`
|
|
1513
|
+
can only be NOTEBOOK_FILE for now
|
|
1514
|
+
:param name: str
|
|
1515
|
+
Name of the asset
|
|
1516
|
+
:param notebook_review: :class:`NotebookVersionReview`
|
|
1517
|
+
|
|
1518
|
+
:returns: :class:`CreateCleanRoomAssetReviewResponse`
|
|
1519
|
+
"""
|
|
1520
|
+
body = {}
|
|
1521
|
+
if notebook_review is not None:
|
|
1522
|
+
body["notebook_review"] = notebook_review.as_dict()
|
|
1523
|
+
headers = {
|
|
1524
|
+
"Accept": "application/json",
|
|
1525
|
+
"Content-Type": "application/json",
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
res = self._api.do(
|
|
1529
|
+
"POST",
|
|
1530
|
+
f"/api/2.0/clean-rooms/{clean_room_name}/assets/{asset_type.value}/{name}/reviews",
|
|
1531
|
+
body=body,
|
|
1532
|
+
headers=headers,
|
|
1533
|
+
)
|
|
1534
|
+
return CreateCleanRoomAssetReviewResponse.from_dict(res)
|
|
1535
|
+
|
|
1196
1536
|
def delete(self, clean_room_name: str, asset_type: CleanRoomAssetAssetType, name: str):
|
|
1197
1537
|
"""Delete a clean room asset - unshare/remove the asset from the clean room
|
|
1198
1538
|
|
|
@@ -1302,6 +1642,128 @@ class CleanRoomAssetsAPI:
|
|
|
1302
1642
|
return CleanRoomAsset.from_dict(res)
|
|
1303
1643
|
|
|
1304
1644
|
|
|
1645
|
+
class CleanRoomAutoApprovalRulesAPI:
|
|
1646
|
+
"""Clean room auto-approval rules automatically create an approval on your behalf when an asset (e.g.
|
|
1647
|
+
notebook) meeting specific criteria is shared in a clean room."""
|
|
1648
|
+
|
|
1649
|
+
def __init__(self, api_client):
|
|
1650
|
+
self._api = api_client
|
|
1651
|
+
|
|
1652
|
+
def create(self, clean_room_name: str, auto_approval_rule: CleanRoomAutoApprovalRule) -> CleanRoomAutoApprovalRule:
|
|
1653
|
+
"""Create an auto-approval rule
|
|
1654
|
+
|
|
1655
|
+
:param clean_room_name: str
|
|
1656
|
+
The name of the clean room this auto-approval rule belongs to.
|
|
1657
|
+
:param auto_approval_rule: :class:`CleanRoomAutoApprovalRule`
|
|
1658
|
+
|
|
1659
|
+
:returns: :class:`CleanRoomAutoApprovalRule`
|
|
1660
|
+
"""
|
|
1661
|
+
body = {}
|
|
1662
|
+
if auto_approval_rule is not None:
|
|
1663
|
+
body["auto_approval_rule"] = auto_approval_rule.as_dict()
|
|
1664
|
+
headers = {
|
|
1665
|
+
"Accept": "application/json",
|
|
1666
|
+
"Content-Type": "application/json",
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
res = self._api.do(
|
|
1670
|
+
"POST", f"/api/2.0/clean-rooms/{clean_room_name}/auto-approval-rules", body=body, headers=headers
|
|
1671
|
+
)
|
|
1672
|
+
return CleanRoomAutoApprovalRule.from_dict(res)
|
|
1673
|
+
|
|
1674
|
+
def delete(self, clean_room_name: str, rule_id: str):
|
|
1675
|
+
"""Delete a auto-approval rule by rule ID
|
|
1676
|
+
|
|
1677
|
+
:param clean_room_name: str
|
|
1678
|
+
:param rule_id: str
|
|
1679
|
+
|
|
1680
|
+
|
|
1681
|
+
"""
|
|
1682
|
+
|
|
1683
|
+
headers = {
|
|
1684
|
+
"Accept": "application/json",
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
self._api.do("DELETE", f"/api/2.0/clean-rooms/{clean_room_name}/auto-approval-rules/{rule_id}", headers=headers)
|
|
1688
|
+
|
|
1689
|
+
def get(self, clean_room_name: str, rule_id: str) -> CleanRoomAutoApprovalRule:
|
|
1690
|
+
"""Get a auto-approval rule by rule ID
|
|
1691
|
+
|
|
1692
|
+
:param clean_room_name: str
|
|
1693
|
+
:param rule_id: str
|
|
1694
|
+
|
|
1695
|
+
:returns: :class:`CleanRoomAutoApprovalRule`
|
|
1696
|
+
"""
|
|
1697
|
+
|
|
1698
|
+
headers = {
|
|
1699
|
+
"Accept": "application/json",
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
res = self._api.do(
|
|
1703
|
+
"GET", f"/api/2.0/clean-rooms/{clean_room_name}/auto-approval-rules/{rule_id}", headers=headers
|
|
1704
|
+
)
|
|
1705
|
+
return CleanRoomAutoApprovalRule.from_dict(res)
|
|
1706
|
+
|
|
1707
|
+
def list(
|
|
1708
|
+
self, clean_room_name: str, *, page_size: Optional[int] = None, page_token: Optional[str] = None
|
|
1709
|
+
) -> Iterator[CleanRoomAutoApprovalRule]:
|
|
1710
|
+
"""List all auto-approval rules for the caller
|
|
1711
|
+
|
|
1712
|
+
:param clean_room_name: str
|
|
1713
|
+
:param page_size: int (optional)
|
|
1714
|
+
Maximum number of auto-approval rules to return. Defaults to 100.
|
|
1715
|
+
:param page_token: str (optional)
|
|
1716
|
+
Opaque pagination token to go to next page based on previous query.
|
|
1717
|
+
|
|
1718
|
+
:returns: Iterator over :class:`CleanRoomAutoApprovalRule`
|
|
1719
|
+
"""
|
|
1720
|
+
|
|
1721
|
+
query = {}
|
|
1722
|
+
if page_size is not None:
|
|
1723
|
+
query["page_size"] = page_size
|
|
1724
|
+
if page_token is not None:
|
|
1725
|
+
query["page_token"] = page_token
|
|
1726
|
+
headers = {
|
|
1727
|
+
"Accept": "application/json",
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
while True:
|
|
1731
|
+
json = self._api.do(
|
|
1732
|
+
"GET", f"/api/2.0/clean-rooms/{clean_room_name}/auto-approval-rules", query=query, headers=headers
|
|
1733
|
+
)
|
|
1734
|
+
if "rules" in json:
|
|
1735
|
+
for v in json["rules"]:
|
|
1736
|
+
yield CleanRoomAutoApprovalRule.from_dict(v)
|
|
1737
|
+
if "next_page_token" not in json or not json["next_page_token"]:
|
|
1738
|
+
return
|
|
1739
|
+
query["page_token"] = json["next_page_token"]
|
|
1740
|
+
|
|
1741
|
+
def update(
|
|
1742
|
+
self, clean_room_name: str, rule_id: str, auto_approval_rule: CleanRoomAutoApprovalRule
|
|
1743
|
+
) -> CleanRoomAutoApprovalRule:
|
|
1744
|
+
"""Update a auto-approval rule by rule ID
|
|
1745
|
+
|
|
1746
|
+
:param clean_room_name: str
|
|
1747
|
+
The name of the clean room this auto-approval rule belongs to.
|
|
1748
|
+
:param rule_id: str
|
|
1749
|
+
A generated UUID identifying the rule.
|
|
1750
|
+
:param auto_approval_rule: :class:`CleanRoomAutoApprovalRule`
|
|
1751
|
+
The auto-approval rule to update. The rule_id field is used to identify the rule to update.
|
|
1752
|
+
|
|
1753
|
+
:returns: :class:`CleanRoomAutoApprovalRule`
|
|
1754
|
+
"""
|
|
1755
|
+
body = auto_approval_rule.as_dict()
|
|
1756
|
+
headers = {
|
|
1757
|
+
"Accept": "application/json",
|
|
1758
|
+
"Content-Type": "application/json",
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
res = self._api.do(
|
|
1762
|
+
"PATCH", f"/api/2.0/clean-rooms/{clean_room_name}/auto-approval-rules/{rule_id}", body=body, headers=headers
|
|
1763
|
+
)
|
|
1764
|
+
return CleanRoomAutoApprovalRule.from_dict(res)
|
|
1765
|
+
|
|
1766
|
+
|
|
1305
1767
|
class CleanRoomTaskRunsAPI:
|
|
1306
1768
|
"""Clean room task runs are the executions of notebooks in a clean room."""
|
|
1307
1769
|
|
|
@@ -1359,7 +1821,32 @@ class CleanRoomsAPI:
|
|
|
1359
1821
|
def __init__(self, api_client):
|
|
1360
1822
|
self._api = api_client
|
|
1361
1823
|
|
|
1362
|
-
def
|
|
1824
|
+
def wait_get_clean_room_active(
|
|
1825
|
+
self, name: str, timeout=timedelta(minutes=20), callback: Optional[Callable[[CleanRoom], None]] = None
|
|
1826
|
+
) -> CleanRoom:
|
|
1827
|
+
deadline = time.time() + timeout.total_seconds()
|
|
1828
|
+
target_states = (CleanRoomStatusEnum.ACTIVE,)
|
|
1829
|
+
status_message = "polling..."
|
|
1830
|
+
attempt = 1
|
|
1831
|
+
while time.time() < deadline:
|
|
1832
|
+
poll = self.get(name=name)
|
|
1833
|
+
status = poll.status
|
|
1834
|
+
status_message = f"current status: {status}"
|
|
1835
|
+
if status in target_states:
|
|
1836
|
+
return poll
|
|
1837
|
+
if callback:
|
|
1838
|
+
callback(poll)
|
|
1839
|
+
prefix = f"name={name}"
|
|
1840
|
+
sleep = attempt
|
|
1841
|
+
if sleep > 10:
|
|
1842
|
+
# sleep 10s max per attempt
|
|
1843
|
+
sleep = 10
|
|
1844
|
+
_LOG.debug(f"{prefix}: ({status}) {status_message} (sleeping ~{sleep}s)")
|
|
1845
|
+
time.sleep(sleep + random.random())
|
|
1846
|
+
attempt += 1
|
|
1847
|
+
raise TimeoutError(f"timed out after {timeout}: {status_message}")
|
|
1848
|
+
|
|
1849
|
+
def create(self, clean_room: CleanRoom) -> Wait[CleanRoom]:
|
|
1363
1850
|
"""Create a new clean room with the specified collaborators. This method is asynchronous; the returned
|
|
1364
1851
|
name field inside the clean_room field can be used to poll the clean room status, using the
|
|
1365
1852
|
:method:cleanrooms/get method. When this method returns, the clean room will be in a PROVISIONING
|
|
@@ -1370,7 +1857,9 @@ class CleanRoomsAPI:
|
|
|
1370
1857
|
|
|
1371
1858
|
:param clean_room: :class:`CleanRoom`
|
|
1372
1859
|
|
|
1373
|
-
:returns:
|
|
1860
|
+
:returns:
|
|
1861
|
+
Long-running operation waiter for :class:`CleanRoom`.
|
|
1862
|
+
See :method:wait_get_clean_room_active for more details.
|
|
1374
1863
|
"""
|
|
1375
1864
|
body = clean_room.as_dict()
|
|
1376
1865
|
headers = {
|
|
@@ -1378,8 +1867,13 @@ class CleanRoomsAPI:
|
|
|
1378
1867
|
"Content-Type": "application/json",
|
|
1379
1868
|
}
|
|
1380
1869
|
|
|
1381
|
-
|
|
1382
|
-
return
|
|
1870
|
+
op_response = self._api.do("POST", "/api/2.0/clean-rooms", body=body, headers=headers)
|
|
1871
|
+
return Wait(
|
|
1872
|
+
self.wait_get_clean_room_active, response=CleanRoom.from_dict(op_response), name=op_response["name"]
|
|
1873
|
+
)
|
|
1874
|
+
|
|
1875
|
+
def create_and_wait(self, clean_room: CleanRoom, timeout=timedelta(minutes=20)) -> CleanRoom:
|
|
1876
|
+
return self.create(clean_room=clean_room).result(timeout=timeout)
|
|
1383
1877
|
|
|
1384
1878
|
def create_output_catalog(
|
|
1385
1879
|
self, clean_room_name: str, output_catalog: CleanRoomOutputCatalog
|
|
@@ -1034,6 +1034,7 @@ class MessageErrorType(Enum):
|
|
|
1034
1034
|
INVALID_SQL_UNKNOWN_TABLE_EXCEPTION = "INVALID_SQL_UNKNOWN_TABLE_EXCEPTION"
|
|
1035
1035
|
INVALID_TABLE_IDENTIFIER_EXCEPTION = "INVALID_TABLE_IDENTIFIER_EXCEPTION"
|
|
1036
1036
|
LOCAL_CONTEXT_EXCEEDED_EXCEPTION = "LOCAL_CONTEXT_EXCEEDED_EXCEPTION"
|
|
1037
|
+
MESSAGE_ATTACHMENT_TOO_LONG_ERROR = "MESSAGE_ATTACHMENT_TOO_LONG_ERROR"
|
|
1037
1038
|
MESSAGE_CANCELLED_WHILE_EXECUTING_EXCEPTION = "MESSAGE_CANCELLED_WHILE_EXECUTING_EXCEPTION"
|
|
1038
1039
|
MESSAGE_DELETED_WHILE_EXECUTING_EXCEPTION = "MESSAGE_DELETED_WHILE_EXECUTING_EXCEPTION"
|
|
1039
1040
|
MESSAGE_UPDATED_WHILE_EXECUTING_EXCEPTION = "MESSAGE_UPDATED_WHILE_EXECUTING_EXCEPTION"
|
|
@@ -3,11 +3,14 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
|
+
import random
|
|
7
|
+
import time
|
|
6
8
|
from dataclasses import dataclass
|
|
9
|
+
from datetime import timedelta
|
|
7
10
|
from enum import Enum
|
|
8
|
-
from typing import Any, Dict, Iterator, List, Optional
|
|
11
|
+
from typing import Any, Callable, Dict, Iterator, List, Optional
|
|
9
12
|
|
|
10
|
-
from ._internal import _enum, _from_dict, _repeated_dict
|
|
13
|
+
from ._internal import Wait, _enum, _from_dict, _repeated_dict
|
|
11
14
|
|
|
12
15
|
_LOG = logging.getLogger("databricks.sdk")
|
|
13
16
|
|
|
@@ -1358,6 +1361,31 @@ class DatabaseAPI:
|
|
|
1358
1361
|
def __init__(self, api_client):
|
|
1359
1362
|
self._api = api_client
|
|
1360
1363
|
|
|
1364
|
+
def wait_get_database_instance_database_available(
|
|
1365
|
+
self, name: str, timeout=timedelta(minutes=20), callback: Optional[Callable[[DatabaseInstance], None]] = None
|
|
1366
|
+
) -> DatabaseInstance:
|
|
1367
|
+
deadline = time.time() + timeout.total_seconds()
|
|
1368
|
+
target_states = (DatabaseInstanceState.AVAILABLE,)
|
|
1369
|
+
status_message = "polling..."
|
|
1370
|
+
attempt = 1
|
|
1371
|
+
while time.time() < deadline:
|
|
1372
|
+
poll = self.get_database_instance(name=name)
|
|
1373
|
+
status = poll.state
|
|
1374
|
+
status_message = f"current status: {status}"
|
|
1375
|
+
if status in target_states:
|
|
1376
|
+
return poll
|
|
1377
|
+
if callback:
|
|
1378
|
+
callback(poll)
|
|
1379
|
+
prefix = f"name={name}"
|
|
1380
|
+
sleep = attempt
|
|
1381
|
+
if sleep > 10:
|
|
1382
|
+
# sleep 10s max per attempt
|
|
1383
|
+
sleep = 10
|
|
1384
|
+
_LOG.debug(f"{prefix}: ({status}) {status_message} (sleeping ~{sleep}s)")
|
|
1385
|
+
time.sleep(sleep + random.random())
|
|
1386
|
+
attempt += 1
|
|
1387
|
+
raise TimeoutError(f"timed out after {timeout}: {status_message}")
|
|
1388
|
+
|
|
1361
1389
|
def create_database_catalog(self, catalog: DatabaseCatalog) -> DatabaseCatalog:
|
|
1362
1390
|
"""Create a Database Catalog.
|
|
1363
1391
|
|
|
@@ -1374,13 +1402,15 @@ class DatabaseAPI:
|
|
|
1374
1402
|
res = self._api.do("POST", "/api/2.0/database/catalogs", body=body, headers=headers)
|
|
1375
1403
|
return DatabaseCatalog.from_dict(res)
|
|
1376
1404
|
|
|
1377
|
-
def create_database_instance(self, database_instance: DatabaseInstance) -> DatabaseInstance:
|
|
1405
|
+
def create_database_instance(self, database_instance: DatabaseInstance) -> Wait[DatabaseInstance]:
|
|
1378
1406
|
"""Create a Database Instance.
|
|
1379
1407
|
|
|
1380
1408
|
:param database_instance: :class:`DatabaseInstance`
|
|
1381
1409
|
Instance to create.
|
|
1382
1410
|
|
|
1383
|
-
:returns:
|
|
1411
|
+
:returns:
|
|
1412
|
+
Long-running operation waiter for :class:`DatabaseInstance`.
|
|
1413
|
+
See :method:wait_get_database_instance_database_available for more details.
|
|
1384
1414
|
"""
|
|
1385
1415
|
body = database_instance.as_dict()
|
|
1386
1416
|
headers = {
|
|
@@ -1388,8 +1418,17 @@ class DatabaseAPI:
|
|
|
1388
1418
|
"Content-Type": "application/json",
|
|
1389
1419
|
}
|
|
1390
1420
|
|
|
1391
|
-
|
|
1392
|
-
return
|
|
1421
|
+
op_response = self._api.do("POST", "/api/2.0/database/instances", body=body, headers=headers)
|
|
1422
|
+
return Wait(
|
|
1423
|
+
self.wait_get_database_instance_database_available,
|
|
1424
|
+
response=DatabaseInstance.from_dict(op_response),
|
|
1425
|
+
name=op_response["name"],
|
|
1426
|
+
)
|
|
1427
|
+
|
|
1428
|
+
def create_database_instance_and_wait(
|
|
1429
|
+
self, database_instance: DatabaseInstance, timeout=timedelta(minutes=20)
|
|
1430
|
+
) -> DatabaseInstance:
|
|
1431
|
+
return self.create_database_instance(database_instance=database_instance).result(timeout=timeout)
|
|
1393
1432
|
|
|
1394
1433
|
def create_database_instance_role(
|
|
1395
1434
|
self, instance_name: str, database_instance_role: DatabaseInstanceRole
|