From 8ae19800670c3f4a412b413016b13e787ea31456 Mon Sep 17 00:00:00 2001 From: Parth Bansal Date: Thu, 31 Jul 2025 09:24:29 +0000 Subject: [PATCH] Update SDK to the latest API definition --- .codegen/_openapi_sha | 2 +- NEXT_CHANGELOG.md | 20 + databricks/sdk/__init__.py | 16 +- databricks/sdk/service/catalog.py | 298 ++++++----- databricks/sdk/service/cleanrooms.py | 506 +++++++++++++++++- databricks/sdk/service/dashboards.py | 1 + databricks/sdk/service/database.py | 51 +- databricks/sdk/service/serving.py | 1 + databricks/sdk/service/sharing.py | 65 +++ databricks/sdk/service/sql.py | 9 + docs/account/iam/service_principals.rst | 5 +- docs/account/iam/workspace_assignment.rst | 4 +- docs/account/provisioning/credentials.rst | 6 +- docs/account/provisioning/storage.rst | 7 +- docs/dbdataclasses/catalog.rst | 23 +- docs/dbdataclasses/cleanrooms.rst | 25 + docs/dbdataclasses/dashboards.rst | 3 + docs/dbdataclasses/serving.rst | 3 + docs/dbdataclasses/sharing.rst | 4 + docs/workspace/catalog/catalogs.rst | 4 +- docs/workspace/catalog/external_locations.rst | 24 +- docs/workspace/catalog/quality_monitors.rst | 124 +++-- .../workspace/catalog/storage_credentials.rst | 13 +- docs/workspace/catalog/tables.rst | 2 +- .../cleanrooms/clean_room_asset_revisions.rst | 41 ++ .../cleanrooms/clean_room_assets.rst | 15 + .../clean_room_auto_approval_rules.rst | 66 +++ docs/workspace/cleanrooms/clean_rooms.rst | 13 +- docs/workspace/cleanrooms/index.rst | 2 + docs/workspace/compute/clusters.rst | 3 +- docs/workspace/database/database.rst | 13 +- docs/workspace/iam/groups.rst | 3 + docs/workspace/iam/service_principals.rst | 10 +- docs/workspace/iam/users.rst | 7 +- docs/workspace/jobs/jobs.rst | 16 +- docs/workspace/ml/model_registry.rst | 6 +- docs/workspace/sharing/providers.rst | 17 +- docs/workspace/workspace/workspace.rst | 12 +- 38 files changed, 1158 insertions(+), 282 deletions(-) create mode 100644 docs/workspace/cleanrooms/clean_room_asset_revisions.rst create mode 100644 docs/workspace/cleanrooms/clean_room_auto_approval_rules.rst diff --git a/.codegen/_openapi_sha b/.codegen/_openapi_sha index 9a25b980c..4d77e7183 100644 --- a/.codegen/_openapi_sha +++ b/.codegen/_openapi_sha @@ -1 +1 @@ -129063c55cb0cf4bda0d561f0bdb7e77d00b9df6 \ No newline at end of file +7de38b0552c78117c01aab884acd9b899a9f4d7f \ No newline at end of file diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index 50a58aac7..a61cfb3a8 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -11,3 +11,23 @@ ### Internal Changes ### API Changes +* Added [w.clean_room_asset_revisions](https://databricks-sdk-py.readthedocs.io/en/latest/workspace/cleanrooms/clean_room_asset_revisions.html) workspace-level service and [w.clean_room_auto_approval_rules](https://databricks-sdk-py.readthedocs.io/en/latest/workspace/cleanrooms/clean_room_auto_approval_rules.html) workspace-level service. +* Added `create_clean_room_asset_review()` method for [w.clean_room_assets](https://databricks-sdk-py.readthedocs.io/en/latest/workspace/cleanrooms/clean_room_assets.html) workspace-level service. +* Added `latest_monitor_failure_msg` field for `databricks.sdk.service.catalog.CreateMonitor`. +* Added `latest_monitor_failure_msg` field for `databricks.sdk.service.catalog.UpdateMonitor`. +* Added `share` field for `databricks.sdk.service.sharing.ListProviderShareAssetsResponse`. +* Added `projected_remaining_wallclock_time_ms` field for `databricks.sdk.service.sql.QueryMetrics`. +* Added `unspecified` enum value for `databricks.sdk.service.catalog.MonitorCronSchedulePauseStatus`. +* Added `unknown` enum value for `databricks.sdk.service.catalog.MonitorRefreshInfoState`. +* Added `unknown_trigger` enum value for `databricks.sdk.service.catalog.MonitorRefreshInfoTrigger`. +* Added `message_attachment_too_long_error` enum value for `databricks.sdk.service.dashboards.MessageErrorType`. +* Added `mask` enum value for `databricks.sdk.service.serving.AiGatewayGuardrailPiiBehaviorBehavior`. +* [Breaking] Added waiter for [CleanRoomsAPI.create](https://databricks-sdk-py.readthedocs.io/en/latest/workspace/cleanrooms/clean_rooms.html#databricks.sdk.service.cleanrooms.CleanRoomsAPI.create) method. +* [Breaking] Added waiter for [DatabaseAPI.create_database_instance](https://databricks-sdk-py.readthedocs.io/en/latest/workspace/database/database.html#databricks.sdk.service.database.DatabaseAPI.create_database_instance) method. +* [Breaking] Changed `cancel_refresh()` method for [w.quality_monitors](https://databricks-sdk-py.readthedocs.io/en/latest/workspace/catalog/quality_monitors.html) workspace-level service to start returning `databricks.sdk.service.catalog.CancelRefreshResponse` dataclass. +* [Breaking] Changed `create()` method for [w.quality_monitors](https://databricks-sdk-py.readthedocs.io/en/latest/workspace/catalog/quality_monitors.html) workspace-level service with new required argument order. +* [Breaking] Changed `delete()` method for [w.quality_monitors](https://databricks-sdk-py.readthedocs.io/en/latest/workspace/catalog/quality_monitors.html) workspace-level service to start returning `databricks.sdk.service.catalog.DeleteMonitorResponse` dataclass. +* [Breaking] Changed `refresh_id` field for `databricks.sdk.service.catalog.CancelRefreshRequest` to type `int` dataclass. +* [Breaking] Changed `refresh_id` field for `databricks.sdk.service.catalog.GetRefreshRequest` to type `int` dataclass. +* [Breaking] Changed `monitor_version` field for `databricks.sdk.service.catalog.MonitorInfo` to type `int` dataclass. +* Changed `output_schema_name` field for `databricks.sdk.service.catalog.MonitorInfo` to be required. \ No newline at end of file diff --git a/databricks/sdk/__init__.py b/databricks/sdk/__init__.py index 43f696256..7c0bd489c 100755 --- a/databricks/sdk/__init__.py +++ b/databricks/sdk/__init__.py @@ -59,7 +59,9 @@ TableConstraintsAPI, TablesAPI, TemporaryTableCredentialsAPI, VolumesAPI, WorkspaceBindingsAPI) -from databricks.sdk.service.cleanrooms import (CleanRoomAssetsAPI, +from databricks.sdk.service.cleanrooms import (CleanRoomAssetRevisionsAPI, + CleanRoomAssetsAPI, + CleanRoomAutoApprovalRulesAPI, CleanRoomsAPI, CleanRoomTaskRunsAPI) from databricks.sdk.service.compute import (ClusterPoliciesAPI, ClustersAPI, @@ -247,7 +249,9 @@ def __init__( self._apps = pkg_apps.AppsAPI(self._api_client) self._artifact_allowlists = pkg_catalog.ArtifactAllowlistsAPI(self._api_client) self._catalogs = pkg_catalog.CatalogsAPI(self._api_client) + self._clean_room_asset_revisions = pkg_cleanrooms.CleanRoomAssetRevisionsAPI(self._api_client) self._clean_room_assets = pkg_cleanrooms.CleanRoomAssetsAPI(self._api_client) + self._clean_room_auto_approval_rules = pkg_cleanrooms.CleanRoomAutoApprovalRulesAPI(self._api_client) self._clean_room_task_runs = pkg_cleanrooms.CleanRoomTaskRunsAPI(self._api_client) self._clean_rooms = pkg_cleanrooms.CleanRoomsAPI(self._api_client) self._cluster_policies = pkg_compute.ClusterPoliciesAPI(self._api_client) @@ -411,11 +415,21 @@ def catalogs(self) -> pkg_catalog.CatalogsAPI: """A catalog is the first layer of Unity Catalog’s three-level namespace.""" return self._catalogs + @property + def clean_room_asset_revisions(self) -> pkg_cleanrooms.CleanRoomAssetRevisionsAPI: + """Clean Room Asset Revisions denote new versions of uploaded assets (e.g.""" + return self._clean_room_asset_revisions + @property def clean_room_assets(self) -> pkg_cleanrooms.CleanRoomAssetsAPI: """Clean room assets are data and code objects — Tables, volumes, and notebooks that are shared with the clean room.""" return self._clean_room_assets + @property + def clean_room_auto_approval_rules(self) -> pkg_cleanrooms.CleanRoomAutoApprovalRulesAPI: + """Clean room auto-approval rules automatically create an approval on your behalf when an asset (e.g.""" + return self._clean_room_auto_approval_rules + @property def clean_room_task_runs(self) -> pkg_cleanrooms.CleanRoomTaskRunsAPI: """Clean room task runs are the executions of notebooks in a clean room.""" diff --git a/databricks/sdk/service/catalog.py b/databricks/sdk/service/catalog.py index 72b874b8a..10dc41468 100755 --- a/databricks/sdk/service/catalog.py +++ b/databricks/sdk/service/catalog.py @@ -2360,6 +2360,24 @@ def from_dict(cls, d: Dict[str, Any]) -> DeleteCredentialResponse: return cls() +@dataclass +class DeleteMonitorResponse: + def as_dict(self) -> dict: + """Serializes the DeleteMonitorResponse into a dictionary suitable for use as a JSON request body.""" + body = {} + return body + + def as_shallow_dict(self) -> dict: + """Serializes the DeleteMonitorResponse into a shallow dictionary of its immediate attributes.""" + body = {} + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> DeleteMonitorResponse: + """Deserializes the DeleteMonitorResponse from a dictionary.""" + return cls() + + @dataclass class DeleteRequestExternalLineage: source: ExternalLineageObject @@ -5833,7 +5851,7 @@ class MonitorCronSchedule: [examples]: https://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html""" timezone_id: str - """The timezone id (e.g., ``"PST"``) in which to evaluate the quartz expression.""" + """The timezone id (e.g., ``PST``) in which to evaluate the quartz expression.""" pause_status: Optional[MonitorCronSchedulePauseStatus] = None """Read only field that indicates whether a schedule is paused or not.""" @@ -5871,16 +5889,21 @@ def from_dict(cls, d: Dict[str, Any]) -> MonitorCronSchedule: class MonitorCronSchedulePauseStatus(Enum): - """Read only field that indicates whether a schedule is paused or not.""" + """Source link: + https://src.dev.databricks.com/databricks/universe/-/blob/elastic-spark-common/api/messages/schedule.proto + Monitoring workflow schedule pause status.""" PAUSED = "PAUSED" UNPAUSED = "UNPAUSED" + UNSPECIFIED = "UNSPECIFIED" @dataclass class MonitorDataClassificationConfig: + """Data classification related configuration.""" + enabled: Optional[bool] = None - """Whether data classification is enabled.""" + """Whether to enable data classification.""" def as_dict(self) -> dict: """Serializes the MonitorDataClassificationConfig into a dictionary suitable for use as a JSON request body.""" @@ -5930,36 +5953,26 @@ def from_dict(cls, d: Dict[str, Any]) -> MonitorDestination: @dataclass class MonitorInferenceLog: + problem_type: MonitorInferenceLogProblemType + """Problem type the model aims to solve.""" + timestamp_col: str - """Column that contains the timestamps of requests. The column must be one of the following: - A - ``TimestampType`` column - A column whose values can be converted to timestamps through the - pyspark ``to_timestamp`` [function]. - - [function]: https://spark.apache.org/docs/latest/api/python/reference/pyspark.sql/api/pyspark.sql.functions.to_timestamp.html""" + """Column for the timestamp.""" granularities: List[str] - """Granularities for aggregating data into time windows based on their timestamp. Currently the - following static granularities are supported: {``"5 minutes"``, ``"30 minutes"``, ``"1 hour"``, - ``"1 day"``, ``" week(s)"``, ``"1 month"``, ``"1 year"``}.""" - - model_id_col: str - """Column that contains the id of the model generating the predictions. Metrics will be computed - per model id by default, and also across all model ids.""" - - problem_type: MonitorInferenceLogProblemType - """Problem type the model aims to solve. Determines the type of model-quality metrics that will be - computed.""" + """List of granularities to use when aggregating data into time windows based on their timestamp.""" prediction_col: str - """Column that contains the output/prediction from the model.""" + """Column for the prediction.""" + + model_id_col: str + """Column for the model identifier.""" label_col: Optional[str] = None - """Optional column that contains the ground truth for the prediction.""" + """Column for the label.""" prediction_proba_col: Optional[str] = None - """Optional column that contains the prediction probabilities for each class in a classification - problem type. The values in this column should be a map, mapping each class label to the - prediction probability for a given sample. The map should be of PySpark MapType().""" + """Column for prediction probabilities""" def as_dict(self) -> dict: """Serializes the MonitorInferenceLog into a dictionary suitable for use as a JSON request body.""" @@ -6014,8 +6027,6 @@ def from_dict(cls, d: Dict[str, Any]) -> MonitorInferenceLog: class MonitorInferenceLogProblemType(Enum): - """Problem type the model aims to solve. Determines the type of model-quality metrics that will be - computed.""" PROBLEM_TYPE_CLASSIFICATION = "PROBLEM_TYPE_CLASSIFICATION" PROBLEM_TYPE_REGRESSION = "PROBLEM_TYPE_REGRESSION" @@ -6023,60 +6034,66 @@ class MonitorInferenceLogProblemType(Enum): @dataclass class MonitorInfo: + output_schema_name: str + """[Create:REQ Update:REQ] Schema where output tables are created. Needs to be in 2-level format + {catalog}.{schema}""" + table_name: str - """The full name of the table to monitor. Format: __catalog_name__.__schema_name__.__table_name__.""" + """[Create:ERR Update:IGN] UC table to monitor. Format: `catalog.schema.table_name`""" status: MonitorInfoStatus - - monitor_version: str - """The version of the monitor config (e.g. 1,2,3). If negative, the monitor may be corrupted.""" + """[Create:ERR Update:IGN] The monitor status.""" profile_metrics_table_name: str - """The full name of the profile metrics table. Format: - __catalog_name__.__schema_name__.__table_name__.""" + """[Create:ERR Update:IGN] Table that stores profile metrics data. Format: + `catalog.schema.table_name`.""" drift_metrics_table_name: str - """The full name of the drift metrics table. Format: - __catalog_name__.__schema_name__.__table_name__.""" + """[Create:ERR Update:IGN] Table that stores drift metrics data. Format: + `catalog.schema.table_name`.""" + + monitor_version: int + """[Create:ERR Update:IGN] Represents the current monitor configuration version in use. The version + will be represented in a numeric fashion (1,2,3...). The field has flexibility to take on + negative values, which can indicate corrupted monitor_version numbers.""" assets_dir: Optional[str] = None - """The directory to store monitoring assets (e.g. dashboard, metric tables).""" + """[Create:REQ Update:IGN] Field for specifying the absolute path to a custom directory to store + data-monitoring assets. Normally prepopulated to a default user location via UI and Python APIs.""" baseline_table_name: Optional[str] = None - """Name of the baseline table from which drift metrics are computed from. Columns in the monitored - table should also be present in the baseline table.""" + """[Create:OPT Update:OPT] Baseline table name. Baseline data is used to compute drift from the + data in the monitored `table_name`. The baseline table and the monitored table shall have the + same schema.""" custom_metrics: Optional[List[MonitorMetric]] = None - """Custom metrics to compute on the monitored table. These can be aggregate metrics, derived - metrics (from already computed aggregate metrics), or drift metrics (comparing metrics across - time windows).""" + """[Create:OPT Update:OPT] Custom metrics.""" dashboard_id: Optional[str] = None - """Id of dashboard that visualizes the computed metrics. This can be empty if the monitor is in - PENDING state.""" + """[Create:ERR Update:OPT] Id of dashboard that visualizes the computed metrics. This can be empty + if the monitor is in PENDING state.""" data_classification_config: Optional[MonitorDataClassificationConfig] = None - """The data classification config for the monitor.""" + """[Create:OPT Update:OPT] Data classification related config.""" inference_log: Optional[MonitorInferenceLog] = None - """Configuration for monitoring inference logs.""" latest_monitor_failure_msg: Optional[str] = None - """The latest failure message of the monitor (if any).""" + """[Create:ERR Update:IGN] The latest error message for a monitor failure.""" notifications: Optional[MonitorNotifications] = None - """The notification settings for the monitor.""" - - output_schema_name: Optional[str] = None - """Schema where output metric tables are created.""" + """[Create:OPT Update:OPT] Field for specifying notification settings.""" schedule: Optional[MonitorCronSchedule] = None - """The schedule for automatically updating and refreshing metric tables.""" + """[Create:OPT Update:OPT] The monitor schedule.""" slicing_exprs: Optional[List[str]] = None - """List of column expressions to slice data with for targeted analysis. The data is grouped by each - expression independently, resulting in a separate slice for each predicate and its complements. - For high-cardinality columns, only the top 100 unique values by frequency will generate slices.""" + """[Create:OPT Update:OPT] List of column expressions to slice data with for targeted analysis. The + data is grouped by each expression independently, resulting in a separate slice for each + predicate and its complements. For example `slicing_exprs=[“col_1”, “col_2 > 10”]` will + generate the following slices: two slices for `col_2 > 10` (True and False), and one slice per + unique value in `col1`. For high-cardinality columns, only the top 100 unique values by + frequency will generate slices.""" snapshot: Optional[MonitorSnapshot] = None """Configuration for monitoring snapshot tables.""" @@ -6192,7 +6209,6 @@ def from_dict(cls, d: Dict[str, Any]) -> MonitorInfo: class MonitorInfoStatus(Enum): - """The status of the monitor.""" MONITOR_STATUS_ACTIVE = "MONITOR_STATUS_ACTIVE" MONITOR_STATUS_DELETE_PENDING = "MONITOR_STATUS_DELETE_PENDING" @@ -6203,6 +6219,8 @@ class MonitorInfoStatus(Enum): @dataclass class MonitorMetric: + """Custom metric definition.""" + name: str """Name of the metric in the output tables.""" @@ -6271,10 +6289,10 @@ def from_dict(cls, d: Dict[str, Any]) -> MonitorMetric: class MonitorMetricType(Enum): - """Can only be one of ``"CUSTOM_METRIC_TYPE_AGGREGATE"``, ``"CUSTOM_METRIC_TYPE_DERIVED"``, or - ``"CUSTOM_METRIC_TYPE_DRIFT"``. The ``"CUSTOM_METRIC_TYPE_AGGREGATE"`` and - ``"CUSTOM_METRIC_TYPE_DERIVED"`` metrics are computed on a single table, whereas the - ``"CUSTOM_METRIC_TYPE_DRIFT"`` compare metrics across baseline and input table, or across the + """Can only be one of ``\"CUSTOM_METRIC_TYPE_AGGREGATE\"``, ``\"CUSTOM_METRIC_TYPE_DERIVED\"``, or + ``\"CUSTOM_METRIC_TYPE_DRIFT\"``. The ``\"CUSTOM_METRIC_TYPE_AGGREGATE\"`` and + ``\"CUSTOM_METRIC_TYPE_DERIVED\"`` metrics are computed on a single table, whereas the + ``\"CUSTOM_METRIC_TYPE_DRIFT\"`` compare metrics across baseline and input table, or across the two consecutive time windows. - CUSTOM_METRIC_TYPE_AGGREGATE: only depend on the existing columns in your table - CUSTOM_METRIC_TYPE_DERIVED: depend on previously computed aggregate metrics - CUSTOM_METRIC_TYPE_DRIFT: depend on previously computed aggregate or derived metrics""" @@ -6287,10 +6305,10 @@ class MonitorMetricType(Enum): @dataclass class MonitorNotifications: on_failure: Optional[MonitorDestination] = None - """Who to send notifications to on monitor failure.""" + """Destinations to send notifications on failure/timeout.""" on_new_classification_tag_detected: Optional[MonitorDestination] = None - """Who to send notifications to when new data classification tags are detected.""" + """Destinations to send notifications on new classification tag detected.""" def as_dict(self) -> dict: """Serializes the MonitorNotifications into a dictionary suitable for use as a JSON request body.""" @@ -6394,13 +6412,14 @@ class MonitorRefreshInfoState(Enum): PENDING = "PENDING" RUNNING = "RUNNING" SUCCESS = "SUCCESS" + UNKNOWN = "UNKNOWN" class MonitorRefreshInfoTrigger(Enum): - """The method by which the refresh was triggered.""" MANUAL = "MANUAL" SCHEDULE = "SCHEDULE" + UNKNOWN_TRIGGER = "UNKNOWN_TRIGGER" @dataclass @@ -6430,6 +6449,8 @@ def from_dict(cls, d: Dict[str, Any]) -> MonitorRefreshListResponse: @dataclass class MonitorSnapshot: + """Snapshot analysis configuration""" + def as_dict(self) -> dict: """Serializes the MonitorSnapshot into a dictionary suitable for use as a JSON request body.""" body = {} @@ -6448,17 +6469,15 @@ def from_dict(cls, d: Dict[str, Any]) -> MonitorSnapshot: @dataclass class MonitorTimeSeries: + """Time series analysis configuration.""" + timestamp_col: str - """Column that contains the timestamps of requests. The column must be one of the following: - A - ``TimestampType`` column - A column whose values can be converted to timestamps through the - pyspark ``to_timestamp`` [function]. - - [function]: https://spark.apache.org/docs/latest/api/python/reference/pyspark.sql/api/pyspark.sql.functions.to_timestamp.html""" + """Column for the timestamp.""" granularities: List[str] """Granularities for aggregating data into time windows based on their timestamp. Currently the - following static granularities are supported: {``"5 minutes"``, ``"30 minutes"``, ``"1 hour"``, - ``"1 day"``, ``" week(s)"``, ``"1 month"``, ``"1 year"``}.""" + following static granularities are supported: {``\"5 minutes\"``, ``\"30 minutes\"``, ``\"1 + hour\"``, ``\"1 day\"``, ``\"\u003cn\u003e week(s)\"``, ``\"1 month\"``, ``\"1 year\"``}.""" def as_dict(self) -> dict: """Serializes the MonitorTimeSeries into a dictionary suitable for use as a JSON request body.""" @@ -7360,10 +7379,9 @@ def from_dict(cls, d: Dict[str, Any]) -> R2Credentials: @dataclass class RegenerateDashboardResponse: dashboard_id: Optional[str] = None - """Id of the regenerated monitoring dashboard.""" parent_folder: Optional[str] = None - """The directory where the regenerated dashboard is stored.""" + """Parent folder is equivalent to {assets_dir}/{tableName}""" def as_dict(self) -> dict: """Serializes the RegenerateDashboardResponse into a dictionary suitable for use as a JSON request body.""" @@ -7723,7 +7741,6 @@ def from_dict(cls, d: Dict[str, Any]) -> SchemaInfo: class SecurableKind(Enum): - """Latest kind: CONNECTION_SQLSERVER_OAUTH_M2M = 254; Next id:255""" TABLE_DB_STORAGE = "TABLE_DB_STORAGE" TABLE_DELTA = "TABLE_DELTA" @@ -12160,34 +12177,28 @@ def get(self, name: str) -> OnlineTable: class QualityMonitorsAPI: """A monitor computes and monitors data or model quality metrics for a table over time. It generates metrics - tables and a dashboard that you can use to monitor table health and set alerts. - - Most write operations require the user to be the owner of the table (or its parent schema or parent - catalog). Viewing the dashboard, computed metrics, or monitor configuration only requires the user to have - **SELECT** privileges on the table (along with **USE_SCHEMA** and **USE_CATALOG**).""" + tables and a dashboard that you can use to monitor table health and set alerts. Most write operations + require the user to be the owner of the table (or its parent schema or parent catalog). Viewing the + dashboard, computed metrics, or monitor configuration only requires the user to have **SELECT** privileges + on the table (along with **USE_SCHEMA** and **USE_CATALOG**).""" def __init__(self, api_client): self._api = api_client - def cancel_refresh(self, table_name: str, refresh_id: str): - """Cancel an active monitor refresh for the given refresh ID. - - The caller must either: 1. be an owner of the table's parent catalog 2. have **USE_CATALOG** on the - table's parent catalog and be an owner of the table's parent schema 3. have the following permissions: - - **USE_CATALOG** on the table's parent catalog - **USE_SCHEMA** on the table's parent schema - be an - owner of the table - - Additionally, the call must be made from the workspace where the monitor was created. + def cancel_refresh(self, table_name: str, refresh_id: int): + """Cancels an already-initiated refresh job. :param table_name: str - Full name of the table. - :param refresh_id: str - ID of the refresh. + UC table name in format `catalog.schema.table_name`. table_name is case insensitive and spaces are + disallowed. + :param refresh_id: int """ - headers = {} + headers = { + "Accept": "application/json", + } self._api.do( "POST", f"/api/2.1/unity-catalog/tables/{table_name}/monitor/refreshes/{refresh_id}/cancel", headers=headers @@ -12196,13 +12207,14 @@ def cancel_refresh(self, table_name: str, refresh_id: str): def create( self, table_name: str, - assets_dir: str, output_schema_name: str, + assets_dir: str, *, baseline_table_name: Optional[str] = None, custom_metrics: Optional[List[MonitorMetric]] = None, data_classification_config: Optional[MonitorDataClassificationConfig] = None, inference_log: Optional[MonitorInferenceLog] = None, + latest_monitor_failure_msg: Optional[str] = None, notifications: Optional[MonitorNotifications] = None, schedule: Optional[MonitorCronSchedule] = None, skip_builtin_dashboard: Optional[bool] = None, @@ -12222,31 +12234,37 @@ def create( Workspace assets, such as the dashboard, will be created in the workspace where this call was made. :param table_name: str - Full name of the table. - :param assets_dir: str - The directory to store monitoring assets (e.g. dashboard, metric tables). + UC table name in format `catalog.schema.table_name`. This field corresponds to the + {full_table_name_arg} arg in the endpoint path. :param output_schema_name: str - Schema where output metric tables are created. + [Create:REQ Update:REQ] Schema where output tables are created. Needs to be in 2-level format + {catalog}.{schema} + :param assets_dir: str + [Create:REQ Update:IGN] Field for specifying the absolute path to a custom directory to store + data-monitoring assets. Normally prepopulated to a default user location via UI and Python APIs. :param baseline_table_name: str (optional) - Name of the baseline table from which drift metrics are computed from. Columns in the monitored - table should also be present in the baseline table. + [Create:OPT Update:OPT] Baseline table name. Baseline data is used to compute drift from the data in + the monitored `table_name`. The baseline table and the monitored table shall have the same schema. :param custom_metrics: List[:class:`MonitorMetric`] (optional) - Custom metrics to compute on the monitored table. These can be aggregate metrics, derived metrics - (from already computed aggregate metrics), or drift metrics (comparing metrics across time windows). + [Create:OPT Update:OPT] Custom metrics. :param data_classification_config: :class:`MonitorDataClassificationConfig` (optional) - The data classification config for the monitor. + [Create:OPT Update:OPT] Data classification related config. :param inference_log: :class:`MonitorInferenceLog` (optional) - Configuration for monitoring inference logs. + :param latest_monitor_failure_msg: str (optional) + [Create:ERR Update:IGN] The latest error message for a monitor failure. :param notifications: :class:`MonitorNotifications` (optional) - The notification settings for the monitor. + [Create:OPT Update:OPT] Field for specifying notification settings. :param schedule: :class:`MonitorCronSchedule` (optional) - The schedule for automatically updating and refreshing metric tables. + [Create:OPT Update:OPT] The monitor schedule. :param skip_builtin_dashboard: bool (optional) Whether to skip creating a default dashboard summarizing data quality metrics. :param slicing_exprs: List[str] (optional) - List of column expressions to slice data with for targeted analysis. The data is grouped by each - expression independently, resulting in a separate slice for each predicate and its complements. For - high-cardinality columns, only the top 100 unique values by frequency will generate slices. + [Create:OPT Update:OPT] List of column expressions to slice data with for targeted analysis. The + data is grouped by each expression independently, resulting in a separate slice for each predicate + and its complements. For example `slicing_exprs=[“col_1”, “col_2 > 10”]` will generate the + following slices: two slices for `col_2 > 10` (True and False), and one slice per unique value in + `col1`. For high-cardinality columns, only the top 100 unique values by frequency will generate + slices. :param snapshot: :class:`MonitorSnapshot` (optional) Configuration for monitoring snapshot tables. :param time_series: :class:`MonitorTimeSeries` (optional) @@ -12268,6 +12286,8 @@ def create( body["data_classification_config"] = data_classification_config.as_dict() if inference_log is not None: body["inference_log"] = inference_log.as_dict() + if latest_monitor_failure_msg is not None: + body["latest_monitor_failure_msg"] = latest_monitor_failure_msg if notifications is not None: body["notifications"] = notifications.as_dict() if output_schema_name is not None: @@ -12292,7 +12312,7 @@ def create( res = self._api.do("POST", f"/api/2.1/unity-catalog/tables/{table_name}/monitor", body=body, headers=headers) return MonitorInfo.from_dict(res) - def delete(self, table_name: str): + def delete(self, table_name: str) -> DeleteMonitorResponse: """Deletes a monitor for the specified table. The caller must either: 1. be an owner of the table's parent catalog 2. have **USE_CATALOG** on the @@ -12306,14 +12326,18 @@ def delete(self, table_name: str): be manually cleaned up (if desired). :param table_name: str - Full name of the table. - + UC table name in format `catalog.schema.table_name`. This field corresponds to the + {full_table_name_arg} arg in the endpoint path. + :returns: :class:`DeleteMonitorResponse` """ - headers = {} + headers = { + "Accept": "application/json", + } - self._api.do("DELETE", f"/api/2.1/unity-catalog/tables/{table_name}/monitor", headers=headers) + res = self._api.do("DELETE", f"/api/2.1/unity-catalog/tables/{table_name}/monitor", headers=headers) + return DeleteMonitorResponse.from_dict(res) def get(self, table_name: str) -> MonitorInfo: """Gets a monitor for the specified table. @@ -12328,7 +12352,8 @@ def get(self, table_name: str) -> MonitorInfo: workspace than where the monitor was created. :param table_name: str - Full name of the table. + UC table name in format `catalog.schema.table_name`. This field corresponds to the + {full_table_name_arg} arg in the endpoint path. :returns: :class:`MonitorInfo` """ @@ -12340,7 +12365,7 @@ def get(self, table_name: str) -> MonitorInfo: res = self._api.do("GET", f"/api/2.1/unity-catalog/tables/{table_name}/monitor", headers=headers) return MonitorInfo.from_dict(res) - def get_refresh(self, table_name: str, refresh_id: str) -> MonitorRefreshInfo: + def get_refresh(self, table_name: str, refresh_id: int) -> MonitorRefreshInfo: """Gets info about a specific monitor refresh using the given refresh ID. The caller must either: 1. be an owner of the table's parent catalog 2. have **USE_CATALOG** on the @@ -12352,7 +12377,7 @@ def get_refresh(self, table_name: str, refresh_id: str) -> MonitorRefreshInfo: :param table_name: str Full name of the table. - :param refresh_id: str + :param refresh_id: int ID of the refresh. :returns: :class:`MonitorRefreshInfo` @@ -12378,7 +12403,8 @@ def list_refreshes(self, table_name: str) -> MonitorRefreshListResponse: Additionally, the call must be made from the workspace where the monitor was created. :param table_name: str - Full name of the table. + UC table name in format `catalog.schema.table_name`. table_name is case insensitive and spaces are + disallowed. :returns: :class:`MonitorRefreshListResponse` """ @@ -12404,7 +12430,8 @@ def regenerate_dashboard( regenerated in the assets directory that was specified when the monitor was created. :param table_name: str - Full name of the table. + UC table name in format `catalog.schema.table_name`. This field corresponds to the + {full_table_name_arg} arg in the endpoint path. :param warehouse_id: str (optional) Optional argument to specify the warehouse for dashboard regeneration. If not specified, the first running warehouse will be used. @@ -12436,7 +12463,8 @@ def run_refresh(self, table_name: str) -> MonitorRefreshInfo: Additionally, the call must be made from the workspace where the monitor was created. :param table_name: str - Full name of the table. + UC table name in format `catalog.schema.table_name`. table_name is case insensitive and spaces are + disallowed. :returns: :class:`MonitorRefreshInfo` """ @@ -12458,6 +12486,7 @@ def update( dashboard_id: Optional[str] = None, data_classification_config: Optional[MonitorDataClassificationConfig] = None, inference_log: Optional[MonitorInferenceLog] = None, + latest_monitor_failure_msg: Optional[str] = None, notifications: Optional[MonitorNotifications] = None, schedule: Optional[MonitorCronSchedule] = None, slicing_exprs: Optional[List[str]] = None, @@ -12477,30 +12506,35 @@ def update( Certain configuration fields, such as output asset identifiers, cannot be updated. :param table_name: str - Full name of the table. + UC table name in format `catalog.schema.table_name`. This field corresponds to the + {full_table_name_arg} arg in the endpoint path. :param output_schema_name: str - Schema where output metric tables are created. + [Create:REQ Update:REQ] Schema where output tables are created. Needs to be in 2-level format + {catalog}.{schema} :param baseline_table_name: str (optional) - Name of the baseline table from which drift metrics are computed from. Columns in the monitored - table should also be present in the baseline table. + [Create:OPT Update:OPT] Baseline table name. Baseline data is used to compute drift from the data in + the monitored `table_name`. The baseline table and the monitored table shall have the same schema. :param custom_metrics: List[:class:`MonitorMetric`] (optional) - Custom metrics to compute on the monitored table. These can be aggregate metrics, derived metrics - (from already computed aggregate metrics), or drift metrics (comparing metrics across time windows). + [Create:OPT Update:OPT] Custom metrics. :param dashboard_id: str (optional) - Id of dashboard that visualizes the computed metrics. This can be empty if the monitor is in PENDING - state. + [Create:ERR Update:OPT] Id of dashboard that visualizes the computed metrics. This can be empty if + the monitor is in PENDING state. :param data_classification_config: :class:`MonitorDataClassificationConfig` (optional) - The data classification config for the monitor. + [Create:OPT Update:OPT] Data classification related config. :param inference_log: :class:`MonitorInferenceLog` (optional) - Configuration for monitoring inference logs. + :param latest_monitor_failure_msg: str (optional) + [Create:ERR Update:IGN] The latest error message for a monitor failure. :param notifications: :class:`MonitorNotifications` (optional) - The notification settings for the monitor. + [Create:OPT Update:OPT] Field for specifying notification settings. :param schedule: :class:`MonitorCronSchedule` (optional) - The schedule for automatically updating and refreshing metric tables. + [Create:OPT Update:OPT] The monitor schedule. :param slicing_exprs: List[str] (optional) - List of column expressions to slice data with for targeted analysis. The data is grouped by each - expression independently, resulting in a separate slice for each predicate and its complements. For - high-cardinality columns, only the top 100 unique values by frequency will generate slices. + [Create:OPT Update:OPT] List of column expressions to slice data with for targeted analysis. The + data is grouped by each expression independently, resulting in a separate slice for each predicate + and its complements. For example `slicing_exprs=[“col_1”, “col_2 > 10”]` will generate the + following slices: two slices for `col_2 > 10` (True and False), and one slice per unique value in + `col1`. For high-cardinality columns, only the top 100 unique values by frequency will generate + slices. :param snapshot: :class:`MonitorSnapshot` (optional) Configuration for monitoring snapshot tables. :param time_series: :class:`MonitorTimeSeries` (optional) @@ -12519,6 +12553,8 @@ def update( body["data_classification_config"] = data_classification_config.as_dict() if inference_log is not None: body["inference_log"] = inference_log.as_dict() + if latest_monitor_failure_msg is not None: + body["latest_monitor_failure_msg"] = latest_monitor_failure_msg if notifications is not None: body["notifications"] = notifications.as_dict() if output_schema_name is not None: diff --git a/databricks/sdk/service/cleanrooms.py b/databricks/sdk/service/cleanrooms.py index eaf2df11a..06ca9c2fe 100755 --- a/databricks/sdk/service/cleanrooms.py +++ b/databricks/sdk/service/cleanrooms.py @@ -3,11 +3,14 @@ from __future__ import annotations import logging +import random +import time from dataclasses import dataclass +from datetime import timedelta from enum import Enum -from typing import Any, Dict, Iterator, List, Optional +from typing import Any, Callable, Dict, Iterator, List, Optional -from ._internal import _enum, _from_dict, _repeated_dict +from ._internal import Wait, _enum, _from_dict, _repeated_dict _LOG = logging.getLogger("databricks.sdk") @@ -540,6 +543,83 @@ def from_dict(cls, d: Dict[str, Any]) -> CleanRoomAssetVolumeLocalDetails: return cls(local_name=d.get("local_name", None)) +@dataclass +class CleanRoomAutoApprovalRule: + author_collaborator_alias: Optional[str] = None + + author_scope: Optional[CleanRoomAutoApprovalRuleAuthorScope] = None + + clean_room_name: Optional[str] = None + """The name of the clean room this auto-approval rule belongs to.""" + + created_at: Optional[int] = None + """Timestamp of when the rule was created, in epoch milliseconds.""" + + rule_id: Optional[str] = None + """A generated UUID identifying the rule.""" + + rule_owner_collaborator_alias: Optional[str] = None + """The owner of the rule to whom the rule applies.""" + + runner_collaborator_alias: Optional[str] = None + + def as_dict(self) -> dict: + """Serializes the CleanRoomAutoApprovalRule into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.author_collaborator_alias is not None: + body["author_collaborator_alias"] = self.author_collaborator_alias + if self.author_scope is not None: + body["author_scope"] = self.author_scope.value + if self.clean_room_name is not None: + body["clean_room_name"] = self.clean_room_name + if self.created_at is not None: + body["created_at"] = self.created_at + if self.rule_id is not None: + body["rule_id"] = self.rule_id + if self.rule_owner_collaborator_alias is not None: + body["rule_owner_collaborator_alias"] = self.rule_owner_collaborator_alias + if self.runner_collaborator_alias is not None: + body["runner_collaborator_alias"] = self.runner_collaborator_alias + return body + + def as_shallow_dict(self) -> dict: + """Serializes the CleanRoomAutoApprovalRule into a shallow dictionary of its immediate attributes.""" + body = {} + if self.author_collaborator_alias is not None: + body["author_collaborator_alias"] = self.author_collaborator_alias + if self.author_scope is not None: + body["author_scope"] = self.author_scope + if self.clean_room_name is not None: + body["clean_room_name"] = self.clean_room_name + if self.created_at is not None: + body["created_at"] = self.created_at + if self.rule_id is not None: + body["rule_id"] = self.rule_id + if self.rule_owner_collaborator_alias is not None: + body["rule_owner_collaborator_alias"] = self.rule_owner_collaborator_alias + if self.runner_collaborator_alias is not None: + body["runner_collaborator_alias"] = self.runner_collaborator_alias + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> CleanRoomAutoApprovalRule: + """Deserializes the CleanRoomAutoApprovalRule from a dictionary.""" + return cls( + author_collaborator_alias=d.get("author_collaborator_alias", None), + author_scope=_enum(d, "author_scope", CleanRoomAutoApprovalRuleAuthorScope), + clean_room_name=d.get("clean_room_name", None), + created_at=d.get("created_at", None), + rule_id=d.get("rule_id", None), + rule_owner_collaborator_alias=d.get("rule_owner_collaborator_alias", None), + runner_collaborator_alias=d.get("runner_collaborator_alias", None), + ) + + +class CleanRoomAutoApprovalRuleAuthorScope(Enum): + + ANY_AUTHOR = "ANY_AUTHOR" + + @dataclass class CleanRoomCollaborator: """Publicly visible clean room collaborator.""" @@ -1017,6 +1097,41 @@ def from_dict(cls, d: Dict[str, Any]) -> ComplianceSecurityProfile: ) +@dataclass +class CreateCleanRoomAssetReviewResponse: + notebook_review_state: Optional[CleanRoomNotebookReviewNotebookReviewState] = None + """top-level status derived from all reviews""" + + notebook_reviews: Optional[List[CleanRoomNotebookReview]] = None + """All existing notebook approvals or rejections""" + + def as_dict(self) -> dict: + """Serializes the CreateCleanRoomAssetReviewResponse into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.notebook_review_state is not None: + body["notebook_review_state"] = self.notebook_review_state.value + if self.notebook_reviews: + body["notebook_reviews"] = [v.as_dict() for v in self.notebook_reviews] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the CreateCleanRoomAssetReviewResponse into a shallow dictionary of its immediate attributes.""" + body = {} + if self.notebook_review_state is not None: + body["notebook_review_state"] = self.notebook_review_state + if self.notebook_reviews: + body["notebook_reviews"] = self.notebook_reviews + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> CreateCleanRoomAssetReviewResponse: + """Deserializes the CreateCleanRoomAssetReviewResponse from a dictionary.""" + return cls( + notebook_review_state=_enum(d, "notebook_review_state", CleanRoomNotebookReviewNotebookReviewState), + notebook_reviews=_repeated_dict(d, "notebook_reviews", CleanRoomNotebookReview), + ) + + @dataclass class CreateCleanRoomOutputCatalogResponse: output_catalog: Optional[CleanRoomOutputCatalog] = None @@ -1062,6 +1177,38 @@ def from_dict(cls, d: Dict[str, Any]) -> DeleteCleanRoomAssetResponse: return cls() +@dataclass +class ListCleanRoomAssetRevisionsResponse: + next_page_token: Optional[str] = None + + revisions: Optional[List[CleanRoomAsset]] = None + + def as_dict(self) -> dict: + """Serializes the ListCleanRoomAssetRevisionsResponse into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.next_page_token is not None: + body["next_page_token"] = self.next_page_token + if self.revisions: + body["revisions"] = [v.as_dict() for v in self.revisions] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the ListCleanRoomAssetRevisionsResponse into a shallow dictionary of its immediate attributes.""" + body = {} + if self.next_page_token is not None: + body["next_page_token"] = self.next_page_token + if self.revisions: + body["revisions"] = self.revisions + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> ListCleanRoomAssetRevisionsResponse: + """Deserializes the ListCleanRoomAssetRevisionsResponse from a dictionary.""" + return cls( + next_page_token=d.get("next_page_token", None), revisions=_repeated_dict(d, "revisions", CleanRoomAsset) + ) + + @dataclass class ListCleanRoomAssetsResponse: assets: Optional[List[CleanRoomAsset]] = None @@ -1095,6 +1242,40 @@ def from_dict(cls, d: Dict[str, Any]) -> ListCleanRoomAssetsResponse: return cls(assets=_repeated_dict(d, "assets", CleanRoomAsset), next_page_token=d.get("next_page_token", None)) +@dataclass +class ListCleanRoomAutoApprovalRulesResponse: + next_page_token: Optional[str] = None + """Opaque token to retrieve the next page of results. Absent if there are no more pages. page_token + should be set to this value for the next request (for the next page of results).""" + + rules: Optional[List[CleanRoomAutoApprovalRule]] = None + + def as_dict(self) -> dict: + """Serializes the ListCleanRoomAutoApprovalRulesResponse into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.next_page_token is not None: + body["next_page_token"] = self.next_page_token + if self.rules: + body["rules"] = [v.as_dict() for v in self.rules] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the ListCleanRoomAutoApprovalRulesResponse into a shallow dictionary of its immediate attributes.""" + body = {} + if self.next_page_token is not None: + body["next_page_token"] = self.next_page_token + if self.rules: + body["rules"] = self.rules + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> ListCleanRoomAutoApprovalRulesResponse: + """Deserializes the ListCleanRoomAutoApprovalRulesResponse from a dictionary.""" + return cls( + next_page_token=d.get("next_page_token", None), rules=_repeated_dict(d, "rules", CleanRoomAutoApprovalRule) + ) + + @dataclass class ListCleanRoomNotebookTaskRunsResponse: next_page_token: Optional[str] = None @@ -1164,6 +1345,130 @@ def from_dict(cls, d: Dict[str, Any]) -> ListCleanRoomsResponse: ) +@dataclass +class NotebookVersionReview: + etag: str + """etag that identifies the notebook version""" + + review_state: CleanRoomNotebookReviewNotebookReviewState + """review outcome""" + + comment: Optional[str] = None + """review comment""" + + def as_dict(self) -> dict: + """Serializes the NotebookVersionReview into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.comment is not None: + body["comment"] = self.comment + if self.etag is not None: + body["etag"] = self.etag + if self.review_state is not None: + body["review_state"] = self.review_state.value + return body + + def as_shallow_dict(self) -> dict: + """Serializes the NotebookVersionReview into a shallow dictionary of its immediate attributes.""" + body = {} + if self.comment is not None: + body["comment"] = self.comment + if self.etag is not None: + body["etag"] = self.etag + if self.review_state is not None: + body["review_state"] = self.review_state + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> NotebookVersionReview: + """Deserializes the NotebookVersionReview from a dictionary.""" + return cls( + comment=d.get("comment", None), + etag=d.get("etag", None), + review_state=_enum(d, "review_state", CleanRoomNotebookReviewNotebookReviewState), + ) + + +class CleanRoomAssetRevisionsAPI: + """Clean Room Asset Revisions denote new versions of uploaded assets (e.g. notebooks) in the clean room.""" + + def __init__(self, api_client): + self._api = api_client + + def get(self, clean_room_name: str, asset_type: CleanRoomAssetAssetType, name: str, etag: str) -> CleanRoomAsset: + """Get a specific revision of an asset + + :param clean_room_name: str + Name of the clean room. + :param asset_type: :class:`CleanRoomAssetAssetType` + Asset type. Only NOTEBOOK_FILE is supported. + :param name: str + Name of the asset. + :param etag: str + Revision etag to fetch. If not provided, the latest revision will be returned. + + :returns: :class:`CleanRoomAsset` + """ + + headers = { + "Accept": "application/json", + } + + res = self._api.do( + "GET", + f"/api/2.0/clean-rooms/{clean_room_name}/assets/{asset_type.value}/{name}/revisions/{etag}", + headers=headers, + ) + return CleanRoomAsset.from_dict(res) + + def list( + self, + clean_room_name: str, + asset_type: CleanRoomAssetAssetType, + name: str, + *, + page_size: Optional[int] = None, + page_token: Optional[str] = None, + ) -> Iterator[CleanRoomAsset]: + """List revisions for an asset + + :param clean_room_name: str + Name of the clean room. + :param asset_type: :class:`CleanRoomAssetAssetType` + Asset type. Only NOTEBOOK_FILE is supported. + :param name: str + Name of the asset. + :param page_size: int (optional) + Maximum number of asset revisions to return. Defaults to 10. + :param page_token: str (optional) + Opaque pagination token to go to next page based on the previous query. + + :returns: Iterator over :class:`CleanRoomAsset` + """ + + query = {} + if page_size is not None: + query["page_size"] = page_size + if page_token is not None: + query["page_token"] = page_token + headers = { + "Accept": "application/json", + } + + while True: + json = self._api.do( + "GET", + f"/api/2.0/clean-rooms/{clean_room_name}/assets/{asset_type.value}/{name}/revisions", + query=query, + headers=headers, + ) + if "revisions" in json: + for v in json["revisions"]: + yield CleanRoomAsset.from_dict(v) + if "next_page_token" not in json or not json["next_page_token"]: + return + query["page_token"] = json["next_page_token"] + + class CleanRoomAssetsAPI: """Clean room assets are data and code objects — Tables, volumes, and notebooks that are shared with the clean room.""" @@ -1193,6 +1498,41 @@ def create(self, clean_room_name: str, asset: CleanRoomAsset) -> CleanRoomAsset: res = self._api.do("POST", f"/api/2.0/clean-rooms/{clean_room_name}/assets", body=body, headers=headers) return CleanRoomAsset.from_dict(res) + def create_clean_room_asset_review( + self, + clean_room_name: str, + asset_type: CleanRoomAssetAssetType, + name: str, + notebook_review: NotebookVersionReview, + ) -> CreateCleanRoomAssetReviewResponse: + """submit an asset review + + :param clean_room_name: str + Name of the clean room + :param asset_type: :class:`CleanRoomAssetAssetType` + can only be NOTEBOOK_FILE for now + :param name: str + Name of the asset + :param notebook_review: :class:`NotebookVersionReview` + + :returns: :class:`CreateCleanRoomAssetReviewResponse` + """ + body = {} + if notebook_review is not None: + body["notebook_review"] = notebook_review.as_dict() + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do( + "POST", + f"/api/2.0/clean-rooms/{clean_room_name}/assets/{asset_type.value}/{name}/reviews", + body=body, + headers=headers, + ) + return CreateCleanRoomAssetReviewResponse.from_dict(res) + def delete(self, clean_room_name: str, asset_type: CleanRoomAssetAssetType, name: str): """Delete a clean room asset - unshare/remove the asset from the clean room @@ -1302,6 +1642,128 @@ def update( return CleanRoomAsset.from_dict(res) +class CleanRoomAutoApprovalRulesAPI: + """Clean room auto-approval rules automatically create an approval on your behalf when an asset (e.g. + notebook) meeting specific criteria is shared in a clean room.""" + + def __init__(self, api_client): + self._api = api_client + + def create(self, clean_room_name: str, auto_approval_rule: CleanRoomAutoApprovalRule) -> CleanRoomAutoApprovalRule: + """Create an auto-approval rule + + :param clean_room_name: str + The name of the clean room this auto-approval rule belongs to. + :param auto_approval_rule: :class:`CleanRoomAutoApprovalRule` + + :returns: :class:`CleanRoomAutoApprovalRule` + """ + body = {} + if auto_approval_rule is not None: + body["auto_approval_rule"] = auto_approval_rule.as_dict() + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do( + "POST", f"/api/2.0/clean-rooms/{clean_room_name}/auto-approval-rules", body=body, headers=headers + ) + return CleanRoomAutoApprovalRule.from_dict(res) + + def delete(self, clean_room_name: str, rule_id: str): + """Delete a auto-approval rule by rule ID + + :param clean_room_name: str + :param rule_id: str + + + """ + + headers = { + "Accept": "application/json", + } + + self._api.do("DELETE", f"/api/2.0/clean-rooms/{clean_room_name}/auto-approval-rules/{rule_id}", headers=headers) + + def get(self, clean_room_name: str, rule_id: str) -> CleanRoomAutoApprovalRule: + """Get a auto-approval rule by rule ID + + :param clean_room_name: str + :param rule_id: str + + :returns: :class:`CleanRoomAutoApprovalRule` + """ + + headers = { + "Accept": "application/json", + } + + res = self._api.do( + "GET", f"/api/2.0/clean-rooms/{clean_room_name}/auto-approval-rules/{rule_id}", headers=headers + ) + return CleanRoomAutoApprovalRule.from_dict(res) + + def list( + self, clean_room_name: str, *, page_size: Optional[int] = None, page_token: Optional[str] = None + ) -> Iterator[CleanRoomAutoApprovalRule]: + """List all auto-approval rules for the caller + + :param clean_room_name: str + :param page_size: int (optional) + Maximum number of auto-approval rules to return. Defaults to 100. + :param page_token: str (optional) + Opaque pagination token to go to next page based on previous query. + + :returns: Iterator over :class:`CleanRoomAutoApprovalRule` + """ + + query = {} + if page_size is not None: + query["page_size"] = page_size + if page_token is not None: + query["page_token"] = page_token + headers = { + "Accept": "application/json", + } + + while True: + json = self._api.do( + "GET", f"/api/2.0/clean-rooms/{clean_room_name}/auto-approval-rules", query=query, headers=headers + ) + if "rules" in json: + for v in json["rules"]: + yield CleanRoomAutoApprovalRule.from_dict(v) + if "next_page_token" not in json or not json["next_page_token"]: + return + query["page_token"] = json["next_page_token"] + + def update( + self, clean_room_name: str, rule_id: str, auto_approval_rule: CleanRoomAutoApprovalRule + ) -> CleanRoomAutoApprovalRule: + """Update a auto-approval rule by rule ID + + :param clean_room_name: str + The name of the clean room this auto-approval rule belongs to. + :param rule_id: str + A generated UUID identifying the rule. + :param auto_approval_rule: :class:`CleanRoomAutoApprovalRule` + The auto-approval rule to update. The rule_id field is used to identify the rule to update. + + :returns: :class:`CleanRoomAutoApprovalRule` + """ + body = auto_approval_rule.as_dict() + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do( + "PATCH", f"/api/2.0/clean-rooms/{clean_room_name}/auto-approval-rules/{rule_id}", body=body, headers=headers + ) + return CleanRoomAutoApprovalRule.from_dict(res) + + class CleanRoomTaskRunsAPI: """Clean room task runs are the executions of notebooks in a clean room.""" @@ -1359,7 +1821,32 @@ class CleanRoomsAPI: def __init__(self, api_client): self._api = api_client - def create(self, clean_room: CleanRoom) -> CleanRoom: + def wait_get_clean_room_active( + self, name: str, timeout=timedelta(minutes=20), callback: Optional[Callable[[CleanRoom], None]] = None + ) -> CleanRoom: + deadline = time.time() + timeout.total_seconds() + target_states = (CleanRoomStatusEnum.ACTIVE,) + status_message = "polling..." + attempt = 1 + while time.time() < deadline: + poll = self.get(name=name) + status = poll.status + status_message = f"current status: {status}" + if status in target_states: + return poll + if callback: + callback(poll) + prefix = f"name={name}" + sleep = attempt + if sleep > 10: + # sleep 10s max per attempt + sleep = 10 + _LOG.debug(f"{prefix}: ({status}) {status_message} (sleeping ~{sleep}s)") + time.sleep(sleep + random.random()) + attempt += 1 + raise TimeoutError(f"timed out after {timeout}: {status_message}") + + def create(self, clean_room: CleanRoom) -> Wait[CleanRoom]: """Create a new clean room with the specified collaborators. This method is asynchronous; the returned name field inside the clean_room field can be used to poll the clean room status, using the :method:cleanrooms/get method. When this method returns, the clean room will be in a PROVISIONING @@ -1370,7 +1857,9 @@ def create(self, clean_room: CleanRoom) -> CleanRoom: :param clean_room: :class:`CleanRoom` - :returns: :class:`CleanRoom` + :returns: + Long-running operation waiter for :class:`CleanRoom`. + See :method:wait_get_clean_room_active for more details. """ body = clean_room.as_dict() headers = { @@ -1378,8 +1867,13 @@ def create(self, clean_room: CleanRoom) -> CleanRoom: "Content-Type": "application/json", } - res = self._api.do("POST", "/api/2.0/clean-rooms", body=body, headers=headers) - return CleanRoom.from_dict(res) + op_response = self._api.do("POST", "/api/2.0/clean-rooms", body=body, headers=headers) + return Wait( + self.wait_get_clean_room_active, response=CleanRoom.from_dict(op_response), name=op_response["name"] + ) + + def create_and_wait(self, clean_room: CleanRoom, timeout=timedelta(minutes=20)) -> CleanRoom: + return self.create(clean_room=clean_room).result(timeout=timeout) def create_output_catalog( self, clean_room_name: str, output_catalog: CleanRoomOutputCatalog diff --git a/databricks/sdk/service/dashboards.py b/databricks/sdk/service/dashboards.py index 01ac655a4..8861916d9 100755 --- a/databricks/sdk/service/dashboards.py +++ b/databricks/sdk/service/dashboards.py @@ -1034,6 +1034,7 @@ class MessageErrorType(Enum): INVALID_SQL_UNKNOWN_TABLE_EXCEPTION = "INVALID_SQL_UNKNOWN_TABLE_EXCEPTION" INVALID_TABLE_IDENTIFIER_EXCEPTION = "INVALID_TABLE_IDENTIFIER_EXCEPTION" LOCAL_CONTEXT_EXCEEDED_EXCEPTION = "LOCAL_CONTEXT_EXCEEDED_EXCEPTION" + MESSAGE_ATTACHMENT_TOO_LONG_ERROR = "MESSAGE_ATTACHMENT_TOO_LONG_ERROR" MESSAGE_CANCELLED_WHILE_EXECUTING_EXCEPTION = "MESSAGE_CANCELLED_WHILE_EXECUTING_EXCEPTION" MESSAGE_DELETED_WHILE_EXECUTING_EXCEPTION = "MESSAGE_DELETED_WHILE_EXECUTING_EXCEPTION" MESSAGE_UPDATED_WHILE_EXECUTING_EXCEPTION = "MESSAGE_UPDATED_WHILE_EXECUTING_EXCEPTION" diff --git a/databricks/sdk/service/database.py b/databricks/sdk/service/database.py index 672ff15b2..25ddf5a0e 100755 --- a/databricks/sdk/service/database.py +++ b/databricks/sdk/service/database.py @@ -3,11 +3,14 @@ from __future__ import annotations import logging +import random +import time from dataclasses import dataclass +from datetime import timedelta from enum import Enum -from typing import Any, Dict, Iterator, List, Optional +from typing import Any, Callable, Dict, Iterator, List, Optional -from ._internal import _enum, _from_dict, _repeated_dict +from ._internal import Wait, _enum, _from_dict, _repeated_dict _LOG = logging.getLogger("databricks.sdk") @@ -1358,6 +1361,31 @@ class DatabaseAPI: def __init__(self, api_client): self._api = api_client + def wait_get_database_instance_database_available( + self, name: str, timeout=timedelta(minutes=20), callback: Optional[Callable[[DatabaseInstance], None]] = None + ) -> DatabaseInstance: + deadline = time.time() + timeout.total_seconds() + target_states = (DatabaseInstanceState.AVAILABLE,) + status_message = "polling..." + attempt = 1 + while time.time() < deadline: + poll = self.get_database_instance(name=name) + status = poll.state + status_message = f"current status: {status}" + if status in target_states: + return poll + if callback: + callback(poll) + prefix = f"name={name}" + sleep = attempt + if sleep > 10: + # sleep 10s max per attempt + sleep = 10 + _LOG.debug(f"{prefix}: ({status}) {status_message} (sleeping ~{sleep}s)") + time.sleep(sleep + random.random()) + attempt += 1 + raise TimeoutError(f"timed out after {timeout}: {status_message}") + def create_database_catalog(self, catalog: DatabaseCatalog) -> DatabaseCatalog: """Create a Database Catalog. @@ -1374,13 +1402,15 @@ def create_database_catalog(self, catalog: DatabaseCatalog) -> DatabaseCatalog: res = self._api.do("POST", "/api/2.0/database/catalogs", body=body, headers=headers) return DatabaseCatalog.from_dict(res) - def create_database_instance(self, database_instance: DatabaseInstance) -> DatabaseInstance: + def create_database_instance(self, database_instance: DatabaseInstance) -> Wait[DatabaseInstance]: """Create a Database Instance. :param database_instance: :class:`DatabaseInstance` Instance to create. - :returns: :class:`DatabaseInstance` + :returns: + Long-running operation waiter for :class:`DatabaseInstance`. + See :method:wait_get_database_instance_database_available for more details. """ body = database_instance.as_dict() headers = { @@ -1388,8 +1418,17 @@ def create_database_instance(self, database_instance: DatabaseInstance) -> Datab "Content-Type": "application/json", } - res = self._api.do("POST", "/api/2.0/database/instances", body=body, headers=headers) - return DatabaseInstance.from_dict(res) + op_response = self._api.do("POST", "/api/2.0/database/instances", body=body, headers=headers) + return Wait( + self.wait_get_database_instance_database_available, + response=DatabaseInstance.from_dict(op_response), + name=op_response["name"], + ) + + def create_database_instance_and_wait( + self, database_instance: DatabaseInstance, timeout=timedelta(minutes=20) + ) -> DatabaseInstance: + return self.create_database_instance(database_instance=database_instance).result(timeout=timeout) def create_database_instance_role( self, instance_name: str, database_instance_role: DatabaseInstanceRole diff --git a/databricks/sdk/service/serving.py b/databricks/sdk/service/serving.py index 3df700055..f6fb4ccf2 100755 --- a/databricks/sdk/service/serving.py +++ b/databricks/sdk/service/serving.py @@ -205,6 +205,7 @@ def from_dict(cls, d: Dict[str, Any]) -> AiGatewayGuardrailPiiBehavior: class AiGatewayGuardrailPiiBehaviorBehavior(Enum): BLOCK = "BLOCK" + MASK = "MASK" NONE = "NONE" diff --git a/databricks/sdk/service/sharing.py b/databricks/sdk/service/sharing.py index a6d69efdf..f9bde5646 100755 --- a/databricks/sdk/service/sharing.py +++ b/databricks/sdk/service/sharing.py @@ -727,6 +727,9 @@ class ListProviderShareAssetsResponse: notebooks: Optional[List[NotebookFile]] = None """The list of notebooks in the share.""" + share: Optional[Share] = None + """The metadata of the share.""" + tables: Optional[List[Table]] = None """The list of tables in the share.""" @@ -740,6 +743,8 @@ def as_dict(self) -> dict: body["functions"] = [v.as_dict() for v in self.functions] if self.notebooks: body["notebooks"] = [v.as_dict() for v in self.notebooks] + if self.share: + body["share"] = self.share.as_dict() if self.tables: body["tables"] = [v.as_dict() for v in self.tables] if self.volumes: @@ -753,6 +758,8 @@ def as_shallow_dict(self) -> dict: body["functions"] = self.functions if self.notebooks: body["notebooks"] = self.notebooks + if self.share: + body["share"] = self.share if self.tables: body["tables"] = self.tables if self.volumes: @@ -765,6 +772,7 @@ def from_dict(cls, d: Dict[str, Any]) -> ListProviderShareAssetsResponse: return cls( functions=_repeated_dict(d, "functions", DeltaSharingFunction), notebooks=_repeated_dict(d, "notebooks", NotebookFile), + share=_from_dict(d, "share", Share), tables=_repeated_dict(d, "tables", Table), volumes=_repeated_dict(d, "volumes", Volume), ) @@ -1817,6 +1825,63 @@ def from_dict(cls, d: Dict[str, Any]) -> SecurablePropertiesKvPairs: return cls(properties=d.get("properties", None)) +@dataclass +class Share: + comment: Optional[str] = None + """The comment of the share.""" + + display_name: Optional[str] = None + """The display name of the share. If defined, it will be shown in the UI.""" + + id: Optional[str] = None + + name: Optional[str] = None + + tags: Optional[List[catalog.TagKeyValue]] = None + """The tags of the share.""" + + def as_dict(self) -> dict: + """Serializes the Share into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.comment is not None: + body["comment"] = self.comment + if self.display_name is not None: + body["display_name"] = self.display_name + if self.id is not None: + body["id"] = self.id + if self.name is not None: + body["name"] = self.name + if self.tags: + body["tags"] = [v.as_dict() for v in self.tags] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the Share into a shallow dictionary of its immediate attributes.""" + body = {} + if self.comment is not None: + body["comment"] = self.comment + if self.display_name is not None: + body["display_name"] = self.display_name + if self.id is not None: + body["id"] = self.id + if self.name is not None: + body["name"] = self.name + if self.tags: + body["tags"] = self.tags + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> Share: + """Deserializes the Share from a dictionary.""" + return cls( + comment=d.get("comment", None), + display_name=d.get("display_name", None), + id=d.get("id", None), + name=d.get("name", None), + tags=_repeated_dict(d, "tags", catalog.TagKeyValue), + ) + + @dataclass class ShareInfo: comment: Optional[str] = None diff --git a/databricks/sdk/service/sql.py b/databricks/sdk/service/sql.py index 6c213b7ca..b35039c98 100755 --- a/databricks/sdk/service/sql.py +++ b/databricks/sdk/service/sql.py @@ -5017,6 +5017,10 @@ class QueryMetrics: projected_remaining_task_total_time_ms: Optional[int] = None """projected remaining work to be done aggregated across all stages in the query, in milliseconds""" + projected_remaining_wallclock_time_ms: Optional[int] = None + """projected lower bound on remaining total task time based on + projected_remaining_task_total_time_ms / maximum concurrency""" + provisioning_queue_start_timestamp: Optional[int] = None """Timestamp of when the query was enqueued waiting for a cluster to be provisioned for the warehouse. This field is optional and will not appear if the query skipped the provisioning @@ -5102,6 +5106,8 @@ def as_dict(self) -> dict: body["photon_total_time_ms"] = self.photon_total_time_ms if self.projected_remaining_task_total_time_ms is not None: body["projected_remaining_task_total_time_ms"] = self.projected_remaining_task_total_time_ms + if self.projected_remaining_wallclock_time_ms is not None: + body["projected_remaining_wallclock_time_ms"] = self.projected_remaining_wallclock_time_ms if self.provisioning_queue_start_timestamp is not None: body["provisioning_queue_start_timestamp"] = self.provisioning_queue_start_timestamp if self.pruned_bytes is not None: @@ -5161,6 +5167,8 @@ def as_shallow_dict(self) -> dict: body["photon_total_time_ms"] = self.photon_total_time_ms if self.projected_remaining_task_total_time_ms is not None: body["projected_remaining_task_total_time_ms"] = self.projected_remaining_task_total_time_ms + if self.projected_remaining_wallclock_time_ms is not None: + body["projected_remaining_wallclock_time_ms"] = self.projected_remaining_wallclock_time_ms if self.provisioning_queue_start_timestamp is not None: body["provisioning_queue_start_timestamp"] = self.provisioning_queue_start_timestamp if self.pruned_bytes is not None: @@ -5215,6 +5223,7 @@ def from_dict(cls, d: Dict[str, Any]) -> QueryMetrics: overloading_queue_start_timestamp=d.get("overloading_queue_start_timestamp", None), photon_total_time_ms=d.get("photon_total_time_ms", None), projected_remaining_task_total_time_ms=d.get("projected_remaining_task_total_time_ms", None), + projected_remaining_wallclock_time_ms=d.get("projected_remaining_wallclock_time_ms", None), provisioning_queue_start_timestamp=d.get("provisioning_queue_start_timestamp", None), pruned_bytes=d.get("pruned_bytes", None), pruned_files_count=d.get("pruned_files_count", None), diff --git a/docs/account/iam/service_principals.rst b/docs/account/iam/service_principals.rst index 6ec4fb814..78816845f 100644 --- a/docs/account/iam/service_principals.rst +++ b/docs/account/iam/service_principals.rst @@ -23,10 +23,7 @@ a = AccountClient() - sp_create = a.service_principals.create(active=True, display_name=f"sdk-{time.time_ns()}") - - # cleanup - a.service_principals.delete(id=sp_create.id) + spn = a.service_principals.create(display_name=f"sdk-{time.time_ns()}") Creates a new service principal in the Databricks account. diff --git a/docs/account/iam/workspace_assignment.rst b/docs/account/iam/workspace_assignment.rst index fa9c2ee3e..2a8043172 100644 --- a/docs/account/iam/workspace_assignment.rst +++ b/docs/account/iam/workspace_assignment.rst @@ -74,9 +74,9 @@ spn_id = spn.id - workspace_id = os.environ["DUMMY_WORKSPACE_ID"] + workspace_id = os.environ["TEST_WORKSPACE_ID"] - _ = a.workspace_assignment.update( + a.workspace_assignment.update( workspace_id=workspace_id, principal_id=spn_id, permissions=[iam.WorkspacePermission.USER], diff --git a/docs/account/provisioning/credentials.rst b/docs/account/provisioning/credentials.rst index acb958c8c..e0103ea36 100644 --- a/docs/account/provisioning/credentials.rst +++ b/docs/account/provisioning/credentials.rst @@ -24,15 +24,15 @@ a = AccountClient() - role = a.credentials.create( + creds = a.credentials.create( credentials_name=f"sdk-{time.time_ns()}", aws_credentials=provisioning.CreateCredentialAwsCredentials( - sts_role=provisioning.CreateCredentialStsRole(role_arn=os.environ["TEST_CROSSACCOUNT_ARN"]) + sts_role=provisioning.CreateCredentialStsRole(role_arn=os.environ["TEST_LOGDELIVERY_ARN"]) ), ) # cleanup - a.credentials.delete(credentials_id=role.credentials_id) + a.credentials.delete(credentials_id=creds.credentials_id) Creates a Databricks credential configuration that represents cloud cross-account credentials for a specified account. Databricks uses this to set up network infrastructure properly to host Databricks diff --git a/docs/account/provisioning/storage.rst b/docs/account/provisioning/storage.rst index a72721a6d..1da53fb45 100644 --- a/docs/account/provisioning/storage.rst +++ b/docs/account/provisioning/storage.rst @@ -16,7 +16,6 @@ .. code-block:: - import os import time from databricks.sdk import AccountClient @@ -24,13 +23,13 @@ a = AccountClient() - storage = a.storage.create( + bucket = a.storage.create( storage_configuration_name=f"sdk-{time.time_ns()}", - root_bucket_info=provisioning.RootBucketInfo(bucket_name=os.environ["TEST_ROOT_BUCKET"]), + root_bucket_info=provisioning.RootBucketInfo(bucket_name=f"sdk-{time.time_ns()}"), ) # cleanup - a.storage.delete(storage_configuration_id=storage.storage_configuration_id) + a.storage.delete(storage_configuration_id=bucket.storage_configuration_id) Creates new storage configuration for an account, specified by ID. Uploads a storage configuration object that represents the root AWS S3 bucket in your account. Databricks stores related workspace diff --git a/docs/dbdataclasses/catalog.rst b/docs/dbdataclasses/catalog.rst index fb15f345f..595a14ac7 100644 --- a/docs/dbdataclasses/catalog.rst +++ b/docs/dbdataclasses/catalog.rst @@ -531,6 +531,10 @@ These dataclasses are used in the SDK to represent API requests and responses fo :members: :undoc-members: +.. autoclass:: DeleteMonitorResponse + :members: + :undoc-members: + .. autoclass:: DeleteRequestExternalLineage :members: :undoc-members: @@ -924,7 +928,7 @@ These dataclasses are used in the SDK to represent API requests and responses fo .. py:class:: MonitorCronSchedulePauseStatus - Read only field that indicates whether a schedule is paused or not. + Source link: https://src.dev.databricks.com/databricks/universe/-/blob/elastic-spark-common/api/messages/schedule.proto Monitoring workflow schedule pause status. .. py:attribute:: PAUSED :value: "PAUSED" @@ -932,6 +936,9 @@ These dataclasses are used in the SDK to represent API requests and responses fo .. py:attribute:: UNPAUSED :value: "UNPAUSED" + .. py:attribute:: UNSPECIFIED + :value: "UNSPECIFIED" + .. autoclass:: MonitorDataClassificationConfig :members: :undoc-members: @@ -946,8 +953,6 @@ These dataclasses are used in the SDK to represent API requests and responses fo .. py:class:: MonitorInferenceLogProblemType - Problem type the model aims to solve. Determines the type of model-quality metrics that will be computed. - .. py:attribute:: PROBLEM_TYPE_CLASSIFICATION :value: "PROBLEM_TYPE_CLASSIFICATION" @@ -960,8 +965,6 @@ These dataclasses are used in the SDK to represent API requests and responses fo .. py:class:: MonitorInfoStatus - The status of the monitor. - .. py:attribute:: MONITOR_STATUS_ACTIVE :value: "MONITOR_STATUS_ACTIVE" @@ -1021,9 +1024,10 @@ These dataclasses are used in the SDK to represent API requests and responses fo .. py:attribute:: SUCCESS :value: "SUCCESS" -.. py:class:: MonitorRefreshInfoTrigger + .. py:attribute:: UNKNOWN + :value: "UNKNOWN" - The method by which the refresh was triggered. +.. py:class:: MonitorRefreshInfoTrigger .. py:attribute:: MANUAL :value: "MANUAL" @@ -1031,6 +1035,9 @@ These dataclasses are used in the SDK to represent API requests and responses fo .. py:attribute:: SCHEDULE :value: "SCHEDULE" + .. py:attribute:: UNKNOWN_TRIGGER + :value: "UNKNOWN_TRIGGER" + .. autoclass:: MonitorRefreshListResponse :members: :undoc-members: @@ -1362,8 +1369,6 @@ These dataclasses are used in the SDK to represent API requests and responses fo .. py:class:: SecurableKind - Latest kind: CONNECTION_SQLSERVER_OAUTH_M2M = 254; Next id:255 - .. py:attribute:: TABLE_DB_STORAGE :value: "TABLE_DB_STORAGE" diff --git a/docs/dbdataclasses/cleanrooms.rst b/docs/dbdataclasses/cleanrooms.rst index f771b4c40..d1d800bc5 100644 --- a/docs/dbdataclasses/cleanrooms.rst +++ b/docs/dbdataclasses/cleanrooms.rst @@ -80,6 +80,15 @@ These dataclasses are used in the SDK to represent API requests and responses fo :members: :undoc-members: +.. autoclass:: CleanRoomAutoApprovalRule + :members: + :undoc-members: + +.. py:class:: CleanRoomAutoApprovalRuleAuthorScope + + .. py:attribute:: ANY_AUTHOR + :value: "ANY_AUTHOR" + .. autoclass:: CleanRoomCollaborator :members: :undoc-members: @@ -152,6 +161,10 @@ These dataclasses are used in the SDK to represent API requests and responses fo :members: :undoc-members: +.. autoclass:: CreateCleanRoomAssetReviewResponse + :members: + :undoc-members: + .. autoclass:: CreateCleanRoomOutputCatalogResponse :members: :undoc-members: @@ -160,10 +173,18 @@ These dataclasses are used in the SDK to represent API requests and responses fo :members: :undoc-members: +.. autoclass:: ListCleanRoomAssetRevisionsResponse + :members: + :undoc-members: + .. autoclass:: ListCleanRoomAssetsResponse :members: :undoc-members: +.. autoclass:: ListCleanRoomAutoApprovalRulesResponse + :members: + :undoc-members: + .. autoclass:: ListCleanRoomNotebookTaskRunsResponse :members: :undoc-members: @@ -171,3 +192,7 @@ These dataclasses are used in the SDK to represent API requests and responses fo .. autoclass:: ListCleanRoomsResponse :members: :undoc-members: + +.. autoclass:: NotebookVersionReview + :members: + :undoc-members: diff --git a/docs/dbdataclasses/dashboards.rst b/docs/dbdataclasses/dashboards.rst index 19cc5815d..105bc108c 100644 --- a/docs/dbdataclasses/dashboards.rst +++ b/docs/dbdataclasses/dashboards.rst @@ -198,6 +198,9 @@ These dataclasses are used in the SDK to represent API requests and responses fo .. py:attribute:: LOCAL_CONTEXT_EXCEEDED_EXCEPTION :value: "LOCAL_CONTEXT_EXCEEDED_EXCEPTION" + .. py:attribute:: MESSAGE_ATTACHMENT_TOO_LONG_ERROR + :value: "MESSAGE_ATTACHMENT_TOO_LONG_ERROR" + .. py:attribute:: MESSAGE_CANCELLED_WHILE_EXECUTING_EXCEPTION :value: "MESSAGE_CANCELLED_WHILE_EXECUTING_EXCEPTION" diff --git a/docs/dbdataclasses/serving.rst b/docs/dbdataclasses/serving.rst index b242ee2b2..852991cfe 100644 --- a/docs/dbdataclasses/serving.rst +++ b/docs/dbdataclasses/serving.rst @@ -25,6 +25,9 @@ These dataclasses are used in the SDK to represent API requests and responses fo .. py:attribute:: BLOCK :value: "BLOCK" + .. py:attribute:: MASK + :value: "MASK" + .. py:attribute:: NONE :value: "NONE" diff --git a/docs/dbdataclasses/sharing.rst b/docs/dbdataclasses/sharing.rst index b94fbee44..948e33bd6 100644 --- a/docs/dbdataclasses/sharing.rst +++ b/docs/dbdataclasses/sharing.rst @@ -386,6 +386,10 @@ These dataclasses are used in the SDK to represent API requests and responses fo :members: :undoc-members: +.. autoclass:: Share + :members: + :undoc-members: + .. autoclass:: ShareInfo :members: :undoc-members: diff --git a/docs/workspace/catalog/catalogs.rst b/docs/workspace/catalog/catalogs.rst index 9a18ede8a..c486ab0d1 100644 --- a/docs/workspace/catalog/catalogs.rst +++ b/docs/workspace/catalog/catalogs.rst @@ -24,10 +24,10 @@ w = WorkspaceClient() - created = w.catalogs.create(name=f"sdk-{time.time_ns()}") + created_catalog = w.catalogs.create(name=f"sdk-{time.time_ns()}") # cleanup - w.catalogs.delete(name=created.name, force=True) + w.catalogs.delete(name=created_catalog.name, force=True) Creates a new catalog instance in the parent metastore if the caller is a metastore admin or has the **CREATE_CATALOG** privilege. diff --git a/docs/workspace/catalog/external_locations.rst b/docs/workspace/catalog/external_locations.rst index 668d4726b..e7c1fd75e 100644 --- a/docs/workspace/catalog/external_locations.rst +++ b/docs/workspace/catalog/external_locations.rst @@ -30,22 +30,20 @@ w = WorkspaceClient() - storage_credential = w.storage_credentials.create( + credential = w.storage_credentials.create( name=f"sdk-{time.time_ns()}", aws_iam_role=catalog.AwsIamRoleRequest(role_arn=os.environ["TEST_METASTORE_DATA_ACCESS_ARN"]), - comment="created via SDK", ) - external_location = w.external_locations.create( + created = w.external_locations.create( name=f"sdk-{time.time_ns()}", - credential_name=storage_credential.name, - comment="created via SDK", - url="s3://" + os.environ["TEST_BUCKET"] + "/" + f"sdk-{time.time_ns()}", + credential_name=credential.name, + url="s3://%s/%s" % (os.environ["TEST_BUCKET"], f"sdk-{time.time_ns()}"), ) # cleanup - w.storage_credentials.delete(name=storage_credential.name) - w.external_locations.delete(name=external_location.name) + w.storage_credentials.delete(name=credential.name) + w.external_locations.delete(name=created.name) Creates a new external location entry in the metastore. The caller must be a metastore admin or have the **CREATE_EXTERNAL_LOCATION** privilege on both the metastore and the associated storage @@ -181,24 +179,24 @@ credential = w.storage_credentials.create( name=f"sdk-{time.time_ns()}", - aws_iam_role=catalog.AwsIamRole(role_arn=os.environ["TEST_METASTORE_DATA_ACCESS_ARN"]), + aws_iam_role=catalog.AwsIamRoleRequest(role_arn=os.environ["TEST_METASTORE_DATA_ACCESS_ARN"]), ) created = w.external_locations.create( name=f"sdk-{time.time_ns()}", credential_name=credential.name, - url=f's3://{os.environ["TEST_BUCKET"]}/sdk-{time.time_ns()}', + url="s3://%s/%s" % (os.environ["TEST_BUCKET"], f"sdk-{time.time_ns()}"), ) _ = w.external_locations.update( name=created.name, credential_name=credential.name, - url=f's3://{os.environ["TEST_BUCKET"]}/sdk-{time.time_ns()}', + url="s3://%s/%s" % (os.environ["TEST_BUCKET"], f"sdk-{time.time_ns()}"), ) # cleanup - w.storage_credentials.delete(delete=credential.name) - w.external_locations.delete(delete=created.name) + w.storage_credentials.delete(name=credential.name) + w.external_locations.delete(name=created.name) Updates an external location in the metastore. The caller must be the owner of the external location, or be a metastore admin. In the second case, the admin can only update the name of the external diff --git a/docs/workspace/catalog/quality_monitors.rst b/docs/workspace/catalog/quality_monitors.rst index 21066be5f..79786717a 100644 --- a/docs/workspace/catalog/quality_monitors.rst +++ b/docs/workspace/catalog/quality_monitors.rst @@ -5,32 +5,24 @@ .. py:class:: QualityMonitorsAPI A monitor computes and monitors data or model quality metrics for a table over time. It generates metrics - tables and a dashboard that you can use to monitor table health and set alerts. + tables and a dashboard that you can use to monitor table health and set alerts. Most write operations + require the user to be the owner of the table (or its parent schema or parent catalog). Viewing the + dashboard, computed metrics, or monitor configuration only requires the user to have **SELECT** privileges + on the table (along with **USE_SCHEMA** and **USE_CATALOG**). - Most write operations require the user to be the owner of the table (or its parent schema or parent - catalog). Viewing the dashboard, computed metrics, or monitor configuration only requires the user to have - **SELECT** privileges on the table (along with **USE_SCHEMA** and **USE_CATALOG**). + .. py:method:: cancel_refresh(table_name: str, refresh_id: int) - .. py:method:: cancel_refresh(table_name: str, refresh_id: str) - - Cancel an active monitor refresh for the given refresh ID. - - The caller must either: 1. be an owner of the table's parent catalog 2. have **USE_CATALOG** on the - table's parent catalog and be an owner of the table's parent schema 3. have the following permissions: - - **USE_CATALOG** on the table's parent catalog - **USE_SCHEMA** on the table's parent schema - be an - owner of the table - - Additionally, the call must be made from the workspace where the monitor was created. + Cancels an already-initiated refresh job. :param table_name: str - Full name of the table. - :param refresh_id: str - ID of the refresh. + UC table name in format `catalog.schema.table_name`. table_name is case insensitive and spaces are + disallowed. + :param refresh_id: int - .. py:method:: create(table_name: str, assets_dir: str, output_schema_name: str [, baseline_table_name: Optional[str], custom_metrics: Optional[List[MonitorMetric]], data_classification_config: Optional[MonitorDataClassificationConfig], inference_log: Optional[MonitorInferenceLog], notifications: Optional[MonitorNotifications], schedule: Optional[MonitorCronSchedule], skip_builtin_dashboard: Optional[bool], slicing_exprs: Optional[List[str]], snapshot: Optional[MonitorSnapshot], time_series: Optional[MonitorTimeSeries], warehouse_id: Optional[str]]) -> MonitorInfo + .. py:method:: create(table_name: str, output_schema_name: str, assets_dir: str [, baseline_table_name: Optional[str], custom_metrics: Optional[List[MonitorMetric]], data_classification_config: Optional[MonitorDataClassificationConfig], inference_log: Optional[MonitorInferenceLog], latest_monitor_failure_msg: Optional[str], notifications: Optional[MonitorNotifications], schedule: Optional[MonitorCronSchedule], skip_builtin_dashboard: Optional[bool], slicing_exprs: Optional[List[str]], snapshot: Optional[MonitorSnapshot], time_series: Optional[MonitorTimeSeries], warehouse_id: Optional[str]]) -> MonitorInfo Creates a new monitor for the specified table. @@ -43,31 +35,37 @@ Workspace assets, such as the dashboard, will be created in the workspace where this call was made. :param table_name: str - Full name of the table. - :param assets_dir: str - The directory to store monitoring assets (e.g. dashboard, metric tables). + UC table name in format `catalog.schema.table_name`. This field corresponds to the + {full_table_name_arg} arg in the endpoint path. :param output_schema_name: str - Schema where output metric tables are created. + [Create:REQ Update:REQ] Schema where output tables are created. Needs to be in 2-level format + {catalog}.{schema} + :param assets_dir: str + [Create:REQ Update:IGN] Field for specifying the absolute path to a custom directory to store + data-monitoring assets. Normally prepopulated to a default user location via UI and Python APIs. :param baseline_table_name: str (optional) - Name of the baseline table from which drift metrics are computed from. Columns in the monitored - table should also be present in the baseline table. + [Create:OPT Update:OPT] Baseline table name. Baseline data is used to compute drift from the data in + the monitored `table_name`. The baseline table and the monitored table shall have the same schema. :param custom_metrics: List[:class:`MonitorMetric`] (optional) - Custom metrics to compute on the monitored table. These can be aggregate metrics, derived metrics - (from already computed aggregate metrics), or drift metrics (comparing metrics across time windows). + [Create:OPT Update:OPT] Custom metrics. :param data_classification_config: :class:`MonitorDataClassificationConfig` (optional) - The data classification config for the monitor. + [Create:OPT Update:OPT] Data classification related config. :param inference_log: :class:`MonitorInferenceLog` (optional) - Configuration for monitoring inference logs. + :param latest_monitor_failure_msg: str (optional) + [Create:ERR Update:IGN] The latest error message for a monitor failure. :param notifications: :class:`MonitorNotifications` (optional) - The notification settings for the monitor. + [Create:OPT Update:OPT] Field for specifying notification settings. :param schedule: :class:`MonitorCronSchedule` (optional) - The schedule for automatically updating and refreshing metric tables. + [Create:OPT Update:OPT] The monitor schedule. :param skip_builtin_dashboard: bool (optional) Whether to skip creating a default dashboard summarizing data quality metrics. :param slicing_exprs: List[str] (optional) - List of column expressions to slice data with for targeted analysis. The data is grouped by each - expression independently, resulting in a separate slice for each predicate and its complements. For - high-cardinality columns, only the top 100 unique values by frequency will generate slices. + [Create:OPT Update:OPT] List of column expressions to slice data with for targeted analysis. The + data is grouped by each expression independently, resulting in a separate slice for each predicate + and its complements. For example `slicing_exprs=[“col_1”, “col_2 > 10”]` will generate the + following slices: two slices for `col_2 > 10` (True and False), and one slice per unique value in + `col1`. For high-cardinality columns, only the top 100 unique values by frequency will generate + slices. :param snapshot: :class:`MonitorSnapshot` (optional) Configuration for monitoring snapshot tables. :param time_series: :class:`MonitorTimeSeries` (optional) @@ -79,7 +77,7 @@ :returns: :class:`MonitorInfo` - .. py:method:: delete(table_name: str) + .. py:method:: delete(table_name: str) -> DeleteMonitorResponse Deletes a monitor for the specified table. @@ -94,9 +92,10 @@ be manually cleaned up (if desired). :param table_name: str - Full name of the table. - + UC table name in format `catalog.schema.table_name`. This field corresponds to the + {full_table_name_arg} arg in the endpoint path. + :returns: :class:`DeleteMonitorResponse` .. py:method:: get(table_name: str) -> MonitorInfo @@ -113,12 +112,13 @@ workspace than where the monitor was created. :param table_name: str - Full name of the table. + UC table name in format `catalog.schema.table_name`. This field corresponds to the + {full_table_name_arg} arg in the endpoint path. :returns: :class:`MonitorInfo` - .. py:method:: get_refresh(table_name: str, refresh_id: str) -> MonitorRefreshInfo + .. py:method:: get_refresh(table_name: str, refresh_id: int) -> MonitorRefreshInfo Gets info about a specific monitor refresh using the given refresh ID. @@ -131,7 +131,7 @@ :param table_name: str Full name of the table. - :param refresh_id: str + :param refresh_id: int ID of the refresh. :returns: :class:`MonitorRefreshInfo` @@ -149,7 +149,8 @@ Additionally, the call must be made from the workspace where the monitor was created. :param table_name: str - Full name of the table. + UC table name in format `catalog.schema.table_name`. table_name is case insensitive and spaces are + disallowed. :returns: :class:`MonitorRefreshListResponse` @@ -167,7 +168,8 @@ regenerated in the assets directory that was specified when the monitor was created. :param table_name: str - Full name of the table. + UC table name in format `catalog.schema.table_name`. This field corresponds to the + {full_table_name_arg} arg in the endpoint path. :param warehouse_id: str (optional) Optional argument to specify the warehouse for dashboard regeneration. If not specified, the first running warehouse will be used. @@ -188,12 +190,13 @@ Additionally, the call must be made from the workspace where the monitor was created. :param table_name: str - Full name of the table. + UC table name in format `catalog.schema.table_name`. table_name is case insensitive and spaces are + disallowed. :returns: :class:`MonitorRefreshInfo` - .. py:method:: update(table_name: str, output_schema_name: str [, baseline_table_name: Optional[str], custom_metrics: Optional[List[MonitorMetric]], dashboard_id: Optional[str], data_classification_config: Optional[MonitorDataClassificationConfig], inference_log: Optional[MonitorInferenceLog], notifications: Optional[MonitorNotifications], schedule: Optional[MonitorCronSchedule], slicing_exprs: Optional[List[str]], snapshot: Optional[MonitorSnapshot], time_series: Optional[MonitorTimeSeries]]) -> MonitorInfo + .. py:method:: update(table_name: str, output_schema_name: str [, baseline_table_name: Optional[str], custom_metrics: Optional[List[MonitorMetric]], dashboard_id: Optional[str], data_classification_config: Optional[MonitorDataClassificationConfig], inference_log: Optional[MonitorInferenceLog], latest_monitor_failure_msg: Optional[str], notifications: Optional[MonitorNotifications], schedule: Optional[MonitorCronSchedule], slicing_exprs: Optional[List[str]], snapshot: Optional[MonitorSnapshot], time_series: Optional[MonitorTimeSeries]]) -> MonitorInfo Updates a monitor for the specified table. @@ -208,30 +211,35 @@ Certain configuration fields, such as output asset identifiers, cannot be updated. :param table_name: str - Full name of the table. + UC table name in format `catalog.schema.table_name`. This field corresponds to the + {full_table_name_arg} arg in the endpoint path. :param output_schema_name: str - Schema where output metric tables are created. + [Create:REQ Update:REQ] Schema where output tables are created. Needs to be in 2-level format + {catalog}.{schema} :param baseline_table_name: str (optional) - Name of the baseline table from which drift metrics are computed from. Columns in the monitored - table should also be present in the baseline table. + [Create:OPT Update:OPT] Baseline table name. Baseline data is used to compute drift from the data in + the monitored `table_name`. The baseline table and the monitored table shall have the same schema. :param custom_metrics: List[:class:`MonitorMetric`] (optional) - Custom metrics to compute on the monitored table. These can be aggregate metrics, derived metrics - (from already computed aggregate metrics), or drift metrics (comparing metrics across time windows). + [Create:OPT Update:OPT] Custom metrics. :param dashboard_id: str (optional) - Id of dashboard that visualizes the computed metrics. This can be empty if the monitor is in PENDING - state. + [Create:ERR Update:OPT] Id of dashboard that visualizes the computed metrics. This can be empty if + the monitor is in PENDING state. :param data_classification_config: :class:`MonitorDataClassificationConfig` (optional) - The data classification config for the monitor. + [Create:OPT Update:OPT] Data classification related config. :param inference_log: :class:`MonitorInferenceLog` (optional) - Configuration for monitoring inference logs. + :param latest_monitor_failure_msg: str (optional) + [Create:ERR Update:IGN] The latest error message for a monitor failure. :param notifications: :class:`MonitorNotifications` (optional) - The notification settings for the monitor. + [Create:OPT Update:OPT] Field for specifying notification settings. :param schedule: :class:`MonitorCronSchedule` (optional) - The schedule for automatically updating and refreshing metric tables. + [Create:OPT Update:OPT] The monitor schedule. :param slicing_exprs: List[str] (optional) - List of column expressions to slice data with for targeted analysis. The data is grouped by each - expression independently, resulting in a separate slice for each predicate and its complements. For - high-cardinality columns, only the top 100 unique values by frequency will generate slices. + [Create:OPT Update:OPT] List of column expressions to slice data with for targeted analysis. The + data is grouped by each expression independently, resulting in a separate slice for each predicate + and its complements. For example `slicing_exprs=[“col_1”, “col_2 > 10”]` will generate the + following slices: two slices for `col_2 > 10` (True and False), and one slice per unique value in + `col1`. For high-cardinality columns, only the top 100 unique values by frequency will generate + slices. :param snapshot: :class:`MonitorSnapshot` (optional) Configuration for monitoring snapshot tables. :param time_series: :class:`MonitorTimeSeries` (optional) diff --git a/docs/workspace/catalog/storage_credentials.rst b/docs/workspace/catalog/storage_credentials.rst index 194069200..2d4dc160c 100644 --- a/docs/workspace/catalog/storage_credentials.rst +++ b/docs/workspace/catalog/storage_credentials.rst @@ -30,13 +30,13 @@ w = WorkspaceClient() - created = w.storage_credentials.create( + credential = w.storage_credentials.create( name=f"sdk-{time.time_ns()}", aws_iam_role=catalog.AwsIamRoleRequest(role_arn=os.environ["TEST_METASTORE_DATA_ACCESS_ARN"]), ) # cleanup - w.storage_credentials.delete(name=created.name) + w.storage_credentials.delete(name=credential.name) Creates a new storage credential. @@ -123,10 +123,11 @@ .. code-block:: from databricks.sdk import WorkspaceClient + from databricks.sdk.service import catalog w = WorkspaceClient() - all = w.storage_credentials.list() + all = w.storage_credentials.list(catalog.ListStorageCredentialsRequest()) Gets an array of storage credentials (as __StorageCredentialInfo__ objects). The array is limited to only those storage credentials the caller has permission to access. If the caller is a metastore @@ -162,17 +163,17 @@ created = w.storage_credentials.create( name=f"sdk-{time.time_ns()}", - aws_iam_role=catalog.AwsIamRoleRequest(role_arn=os.environ["TEST_METASTORE_DATA_ACCESS_ARN"]), + aws_iam_role=catalog.AwsIamRole(role_arn=os.environ["TEST_METASTORE_DATA_ACCESS_ARN"]), ) _ = w.storage_credentials.update( name=created.name, comment=f"sdk-{time.time_ns()}", - aws_iam_role=catalog.AwsIamRoleRequest(role_arn=os.environ["TEST_METASTORE_DATA_ACCESS_ARN"]), + aws_iam_role=catalog.AwsIamRole(role_arn=os.environ["TEST_METASTORE_DATA_ACCESS_ARN"]), ) # cleanup - w.storage_credentials.delete(name=created.name) + w.storage_credentials.delete(delete=created.name) Updates a storage credential on the metastore. diff --git a/docs/workspace/catalog/tables.rst b/docs/workspace/catalog/tables.rst index 0ecc0774c..efeea33f6 100644 --- a/docs/workspace/catalog/tables.rst +++ b/docs/workspace/catalog/tables.rst @@ -117,7 +117,7 @@ created_schema = w.schemas.create(name=f"sdk-{time.time_ns()}", catalog_name=created_catalog.name) - summaries = w.tables.list_summaries(catalog_name=created_catalog.name, schema_name_pattern=created_schema.name) + all_tables = w.tables.list(catalog_name=created_catalog.name, schema_name=created_schema.name) # cleanup w.schemas.delete(full_name=created_schema.full_name) diff --git a/docs/workspace/cleanrooms/clean_room_asset_revisions.rst b/docs/workspace/cleanrooms/clean_room_asset_revisions.rst new file mode 100644 index 000000000..dccbeab03 --- /dev/null +++ b/docs/workspace/cleanrooms/clean_room_asset_revisions.rst @@ -0,0 +1,41 @@ +``w.clean_room_asset_revisions``: Asset Revisions +================================================= +.. currentmodule:: databricks.sdk.service.cleanrooms + +.. py:class:: CleanRoomAssetRevisionsAPI + + Clean Room Asset Revisions denote new versions of uploaded assets (e.g. notebooks) in the clean room. + + .. py:method:: get(clean_room_name: str, asset_type: CleanRoomAssetAssetType, name: str, etag: str) -> CleanRoomAsset + + Get a specific revision of an asset + + :param clean_room_name: str + Name of the clean room. + :param asset_type: :class:`CleanRoomAssetAssetType` + Asset type. Only NOTEBOOK_FILE is supported. + :param name: str + Name of the asset. + :param etag: str + Revision etag to fetch. If not provided, the latest revision will be returned. + + :returns: :class:`CleanRoomAsset` + + + .. py:method:: list(clean_room_name: str, asset_type: CleanRoomAssetAssetType, name: str [, page_size: Optional[int], page_token: Optional[str]]) -> Iterator[CleanRoomAsset] + + List revisions for an asset + + :param clean_room_name: str + Name of the clean room. + :param asset_type: :class:`CleanRoomAssetAssetType` + Asset type. Only NOTEBOOK_FILE is supported. + :param name: str + Name of the asset. + :param page_size: int (optional) + Maximum number of asset revisions to return. Defaults to 10. + :param page_token: str (optional) + Opaque pagination token to go to next page based on the previous query. + + :returns: Iterator over :class:`CleanRoomAsset` + \ No newline at end of file diff --git a/docs/workspace/cleanrooms/clean_room_assets.rst b/docs/workspace/cleanrooms/clean_room_assets.rst index ef32dca12..af7be1f3a 100644 --- a/docs/workspace/cleanrooms/clean_room_assets.rst +++ b/docs/workspace/cleanrooms/clean_room_assets.rst @@ -22,6 +22,21 @@ :returns: :class:`CleanRoomAsset` + .. py:method:: create_clean_room_asset_review(clean_room_name: str, asset_type: CleanRoomAssetAssetType, name: str, notebook_review: NotebookVersionReview) -> CreateCleanRoomAssetReviewResponse + + submit an asset review + + :param clean_room_name: str + Name of the clean room + :param asset_type: :class:`CleanRoomAssetAssetType` + can only be NOTEBOOK_FILE for now + :param name: str + Name of the asset + :param notebook_review: :class:`NotebookVersionReview` + + :returns: :class:`CreateCleanRoomAssetReviewResponse` + + .. py:method:: delete(clean_room_name: str, asset_type: CleanRoomAssetAssetType, name: str) Delete a clean room asset - unshare/remove the asset from the clean room diff --git a/docs/workspace/cleanrooms/clean_room_auto_approval_rules.rst b/docs/workspace/cleanrooms/clean_room_auto_approval_rules.rst new file mode 100644 index 000000000..317fabcd8 --- /dev/null +++ b/docs/workspace/cleanrooms/clean_room_auto_approval_rules.rst @@ -0,0 +1,66 @@ +``w.clean_room_auto_approval_rules``: Auto-approval Rules +========================================================= +.. currentmodule:: databricks.sdk.service.cleanrooms + +.. py:class:: CleanRoomAutoApprovalRulesAPI + + Clean room auto-approval rules automatically create an approval on your behalf when an asset (e.g. + notebook) meeting specific criteria is shared in a clean room. + + .. py:method:: create(clean_room_name: str, auto_approval_rule: CleanRoomAutoApprovalRule) -> CleanRoomAutoApprovalRule + + Create an auto-approval rule + + :param clean_room_name: str + The name of the clean room this auto-approval rule belongs to. + :param auto_approval_rule: :class:`CleanRoomAutoApprovalRule` + + :returns: :class:`CleanRoomAutoApprovalRule` + + + .. py:method:: delete(clean_room_name: str, rule_id: str) + + Delete a auto-approval rule by rule ID + + :param clean_room_name: str + :param rule_id: str + + + + + .. py:method:: get(clean_room_name: str, rule_id: str) -> CleanRoomAutoApprovalRule + + Get a auto-approval rule by rule ID + + :param clean_room_name: str + :param rule_id: str + + :returns: :class:`CleanRoomAutoApprovalRule` + + + .. py:method:: list(clean_room_name: str [, page_size: Optional[int], page_token: Optional[str]]) -> Iterator[CleanRoomAutoApprovalRule] + + List all auto-approval rules for the caller + + :param clean_room_name: str + :param page_size: int (optional) + Maximum number of auto-approval rules to return. Defaults to 100. + :param page_token: str (optional) + Opaque pagination token to go to next page based on previous query. + + :returns: Iterator over :class:`CleanRoomAutoApprovalRule` + + + .. py:method:: update(clean_room_name: str, rule_id: str, auto_approval_rule: CleanRoomAutoApprovalRule) -> CleanRoomAutoApprovalRule + + Update a auto-approval rule by rule ID + + :param clean_room_name: str + The name of the clean room this auto-approval rule belongs to. + :param rule_id: str + A generated UUID identifying the rule. + :param auto_approval_rule: :class:`CleanRoomAutoApprovalRule` + The auto-approval rule to update. The rule_id field is used to identify the rule to update. + + :returns: :class:`CleanRoomAutoApprovalRule` + \ No newline at end of file diff --git a/docs/workspace/cleanrooms/clean_rooms.rst b/docs/workspace/cleanrooms/clean_rooms.rst index 7288faf26..1059cf784 100644 --- a/docs/workspace/cleanrooms/clean_rooms.rst +++ b/docs/workspace/cleanrooms/clean_rooms.rst @@ -8,7 +8,7 @@ environment where multiple parties can work together on sensitive enterprise data without direct access to each other's data. - .. py:method:: create(clean_room: CleanRoom) -> CleanRoom + .. py:method:: create(clean_room: CleanRoom) -> Wait[CleanRoom] Create a new clean room with the specified collaborators. This method is asynchronous; the returned name field inside the clean_room field can be used to poll the clean room status, using the @@ -20,9 +20,14 @@ :param clean_room: :class:`CleanRoom` - :returns: :class:`CleanRoom` + :returns: + Long-running operation waiter for :class:`CleanRoom`. + See :method:wait_get_clean_room_active for more details. + .. py:method:: create_and_wait(clean_room: CleanRoom, timeout: datetime.timedelta = 0:20:00) -> CleanRoom + + .. py:method:: create_output_catalog(clean_room_name: str, output_catalog: CleanRoomOutputCatalog) -> CreateCleanRoomOutputCatalogResponse Create the output catalog of the clean room. @@ -80,4 +85,6 @@ :param clean_room: :class:`CleanRoom` (optional) :returns: :class:`CleanRoom` - \ No newline at end of file + + + .. py:method:: wait_get_clean_room_active(name: str, timeout: datetime.timedelta = 0:20:00, callback: Optional[Callable[[CleanRoom], None]]) -> CleanRoom diff --git a/docs/workspace/cleanrooms/index.rst b/docs/workspace/cleanrooms/index.rst index a979ac201..b3cfaa790 100644 --- a/docs/workspace/cleanrooms/index.rst +++ b/docs/workspace/cleanrooms/index.rst @@ -7,6 +7,8 @@ Manage clean rooms and their assets and task runs .. toctree:: :maxdepth: 1 + clean_room_asset_revisions clean_room_assets + clean_room_auto_approval_rules clean_room_task_runs clean_rooms \ No newline at end of file diff --git a/docs/workspace/compute/clusters.rst b/docs/workspace/compute/clusters.rst index d46b8ecd0..db78626ff 100644 --- a/docs/workspace/compute/clusters.rst +++ b/docs/workspace/compute/clusters.rst @@ -647,11 +647,10 @@ .. code-block:: from databricks.sdk import WorkspaceClient - from databricks.sdk.service import compute w = WorkspaceClient() - all = w.clusters.list(compute.ListClustersRequest()) + nodes = w.clusters.list_node_types() Return information about all pinned and active clusters, and all clusters terminated within the last 30 days. Clusters terminated prior to this period are not included. diff --git a/docs/workspace/database/database.rst b/docs/workspace/database/database.rst index dca0dfa6a..ee7a3d4e1 100644 --- a/docs/workspace/database/database.rst +++ b/docs/workspace/database/database.rst @@ -15,16 +15,21 @@ :returns: :class:`DatabaseCatalog` - .. py:method:: create_database_instance(database_instance: DatabaseInstance) -> DatabaseInstance + .. py:method:: create_database_instance(database_instance: DatabaseInstance) -> Wait[DatabaseInstance] Create a Database Instance. :param database_instance: :class:`DatabaseInstance` Instance to create. - :returns: :class:`DatabaseInstance` + :returns: + Long-running operation waiter for :class:`DatabaseInstance`. + See :method:wait_get_database_instance_database_available for more details. + .. py:method:: create_database_instance_and_wait(database_instance: DatabaseInstance, timeout: datetime.timedelta = 0:20:00) -> DatabaseInstance + + .. py:method:: create_database_instance_role(instance_name: str, database_instance_role: DatabaseInstanceRole) -> DatabaseInstanceRole Create a role for a Database Instance. @@ -221,4 +226,6 @@ The list of fields to update. This field is not yet supported, and is ignored by the server. :returns: :class:`DatabaseInstance` - \ No newline at end of file + + + .. py:method:: wait_get_database_instance_database_available(name: str, timeout: datetime.timedelta = 0:20:00, callback: Optional[Callable[[DatabaseInstance], None]]) -> DatabaseInstance diff --git a/docs/workspace/iam/groups.rst b/docs/workspace/iam/groups.rst index 737939095..764a81ab9 100644 --- a/docs/workspace/iam/groups.rst +++ b/docs/workspace/iam/groups.rst @@ -69,6 +69,9 @@ group = w.groups.create(display_name=f"sdk-{time.time_ns()}") w.groups.delete(id=group.id) + + # cleanup + w.groups.delete(id=group.id) Deletes a group from the Databricks workspace. diff --git a/docs/workspace/iam/service_principals.rst b/docs/workspace/iam/service_principals.rst index 0d0d447b9..ce8978afb 100644 --- a/docs/workspace/iam/service_principals.rst +++ b/docs/workspace/iam/service_principals.rst @@ -20,13 +20,19 @@ import time from databricks.sdk import WorkspaceClient + from databricks.sdk.service import iam w = WorkspaceClient() - created = w.service_principals.create(display_name=f"sdk-{time.time_ns()}") + groups = w.groups.group_display_name_to_id_map(iam.ListGroupsRequest()) + + spn = w.service_principals.create( + display_name=f"sdk-{time.time_ns()}", + groups=[iam.ComplexValue(value=groups["admins"])], + ) # cleanup - w.service_principals.delete(id=created.id) + w.service_principals.delete(id=spn.id) Creates a new service principal in the Databricks workspace. diff --git a/docs/workspace/iam/users.rst b/docs/workspace/iam/users.rst index 04188cc1f..fbee85661 100644 --- a/docs/workspace/iam/users.rst +++ b/docs/workspace/iam/users.rst @@ -78,12 +78,9 @@ w = WorkspaceClient() - user = w.users.create( - display_name=f"sdk-{time.time_ns()}", - user_name=f"sdk-{time.time_ns()}@example.com", - ) + other_owner = w.users.create(user_name=f"sdk-{time.time_ns()}@example.com") - w.users.delete(id=user.id) + w.users.delete(id=other_owner.id) Deletes a user. Deleting a user from a Databricks workspace also removes objects associated with the user. diff --git a/docs/workspace/jobs/jobs.rst b/docs/workspace/jobs/jobs.rst index d68e92a5c..d4fdba07f 100644 --- a/docs/workspace/jobs/jobs.rst +++ b/docs/workspace/jobs/jobs.rst @@ -353,21 +353,23 @@ w.clusters.ensure_cluster_is_running(os.environ["DATABRICKS_CLUSTER_ID"]) and os.environ["DATABRICKS_CLUSTER_ID"] ) - run = w.jobs.submit( - run_name=f"sdk-{time.time_ns()}", + created_job = w.jobs.create( + name=f"sdk-{time.time_ns()}", tasks=[ - jobs.SubmitTask( + jobs.Task( + description="test", existing_cluster_id=cluster_id, notebook_task=jobs.NotebookTask(notebook_path=notebook_path), - task_key=f"sdk-{time.time_ns()}", + task_key="test", + timeout_seconds=0, ) ], - ).result() + ) - output = w.jobs.get_run_output(run_id=run.tasks[0].run_id) + by_id = w.jobs.get(job_id=created_job.job_id) # cleanup - w.jobs.delete_run(run_id=run.run_id) + w.jobs.delete(job_id=created_job.job_id) Get a single job. diff --git a/docs/workspace/ml/model_registry.rst b/docs/workspace/ml/model_registry.rst index e416ac56b..98d803a63 100644 --- a/docs/workspace/ml/model_registry.rst +++ b/docs/workspace/ml/model_registry.rst @@ -90,9 +90,7 @@ w = WorkspaceClient() - model = w.model_registry.create_model(name=f"sdk-{time.time_ns()}") - - mv = w.model_registry.create_model_version(name=model.registered_model.name, source="dbfs:/tmp") + created = w.model_registry.create_model(name=f"sdk-{time.time_ns()}") Creates a new registered model with the name specified in the request body. Throws `RESOURCE_ALREADY_EXISTS` if a registered model with the given name exists. @@ -122,7 +120,7 @@ model = w.model_registry.create_model(name=f"sdk-{time.time_ns()}") - mv = w.model_registry.create_model_version(name=model.registered_model.name, source="dbfs:/tmp") + created = w.model_registry.create_model_version(name=model.registered_model.name, source="dbfs:/tmp") Creates a model version. diff --git a/docs/workspace/sharing/providers.rst b/docs/workspace/sharing/providers.rst index fd81e1b24..1a7c88de9 100644 --- a/docs/workspace/sharing/providers.rst +++ b/docs/workspace/sharing/providers.rst @@ -101,12 +101,25 @@ .. code-block:: + import time + from databricks.sdk import WorkspaceClient - from databricks.sdk.service import sharing w = WorkspaceClient() - all = w.providers.list(sharing.ListProvidersRequest()) + public_share_recipient = """{ + "shareCredentialsVersion":1, + "bearerToken":"dapiabcdefghijklmonpqrstuvwxyz", + "endpoint":"https://sharing.delta.io/delta-sharing/" + } + """ + + created = w.providers.create(name=f"sdk-{time.time_ns()}", recipient_profile_str=public_share_recipient) + + shares = w.providers.list_shares(name=created.name) + + # cleanup + w.providers.delete(name=created.name) Gets an array of available authentication providers. The caller must either be a metastore admin or the owner of the providers. Providers not owned by the caller are not included in the response. There diff --git a/docs/workspace/workspace/workspace.rst b/docs/workspace/workspace/workspace.rst index 2c369968e..03dae035c 100644 --- a/docs/workspace/workspace/workspace.rst +++ b/docs/workspace/workspace/workspace.rst @@ -178,7 +178,7 @@ content=base64.b64encode(("CREATE LIVE TABLE dlt_sample AS SELECT 1").encode()).decode(), format=workspace.ImportFormat.SOURCE, language=workspace.Language.SQL, - overwrite=True, + overwrite=true_, path=notebook_path, ) @@ -223,16 +223,14 @@ .. code-block:: - import os - import time - from databricks.sdk import WorkspaceClient w = WorkspaceClient() - notebook = f"/Users/{w.current_user.me().user_name}/sdk-{time.time_ns()}" - - objects = w.workspace.list(path=os.path.dirname(notebook)) + names = [] + for i in w.workspace.list(f"/Users/{w.current_user.me().user_name}", recursive=True): + names.append(i.path) + assert len(names) > 0 List workspace objects