diff --git a/.codegen/_openapi_sha b/.codegen/_openapi_sha index 9834ec958..3461bf684 100644 --- a/.codegen/_openapi_sha +++ b/.codegen/_openapi_sha @@ -1 +1 @@ -3ae6f76120079424c8654263eafbc30ec0551854 \ No newline at end of file +6701524136501ef070774942ef5d6e01cfaafb88 \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index 67bb57ca4..e26b931e3 100755 --- a/.gitattributes +++ b/.gitattributes @@ -20,7 +20,9 @@ databricks/sdk/service/provisioning.py linguist-generated=true databricks/sdk/service/qualitymonitorv2.py linguist-generated=true databricks/sdk/service/serving.py linguist-generated=true databricks/sdk/service/settings.py linguist-generated=true +databricks/sdk/service/settingsv2.py linguist-generated=true databricks/sdk/service/sharing.py linguist-generated=true databricks/sdk/service/sql.py linguist-generated=true +databricks/sdk/service/tags.py linguist-generated=true databricks/sdk/service/vectorsearch.py linguist-generated=true databricks/sdk/service/workspace.py linguist-generated=true diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index bcf084760..5c2f27de1 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -11,3 +11,23 @@ ### Internal Changes ### API Changes +* Added `databricks.sdk.service.settingsv2` and `databricks.sdk.service.tags` packages. +* Added [w.apps_settings](https://databricks-sdk-py.readthedocs.io/en/latest/workspace/apps/apps_settings.html) workspace-level service. +* Added [w.entity_tag_assignments](https://databricks-sdk-py.readthedocs.io/en/latest/workspace/catalog/entity_tag_assignments.html) workspace-level service and [w.rfa](https://databricks-sdk-py.readthedocs.io/en/latest/workspace/catalog/rfa.html) workspace-level service. +* Added [a.account_settings_v2](https://databricks-sdk-py.readthedocs.io/en/latest/account/settingsv2/account_settings_v2.html) account-level service and [w.workspace_settings_v2](https://databricks-sdk-py.readthedocs.io/en/latest/workspace/settingsv2/workspace_settings_v2.html) workspace-level service. +* Added [w.tag_policies](https://databricks-sdk-py.readthedocs.io/en/latest/workspace/tags/tag_policies.html) workspace-level service. +* Added `delete_conversation_message()`, `list_conversation_messages()` and `send_message_feedback()` methods for [w.genie](https://databricks-sdk-py.readthedocs.io/en/latest/workspace/dashboards/genie.html) workspace-level service. +* Added `include_all` field for `databricks.sdk.service.dashboards.GenieListConversationsRequest`. +* Added `effective_usage_policy_id` field for `databricks.sdk.service.jobs.BaseJob`. +* Added `effective_usage_policy_id` field for `databricks.sdk.service.jobs.BaseRun`. +* Added `effective_usage_policy_id` field for `databricks.sdk.service.jobs.Job`. +* Added `effective_usage_policy_id` field for `databricks.sdk.service.jobs.Run`. +* Added `tokens` field for `databricks.sdk.service.serving.AiGatewayRateLimit`. +* Added `usage_policy_id` field for `databricks.sdk.service.serving.ServingEndpoint`. +* Added `effective_run_as` and `run_as` fields for `databricks.sdk.service.sql.AlertV2`. +* Added `cache_query_id` field for `databricks.sdk.service.sql.QueryInfo`. +* Added `model_endpoint_name_for_query` field for `databricks.sdk.service.vectorsearch.EmbeddingSourceColumn`. +* [Breaking] Removed `environment_settings` field for `databricks.sdk.service.catalog.ConnectionInfo`. +* [Breaking] Removed `environment_settings` field for `databricks.sdk.service.catalog.CreateConnection`. +* [Breaking] Removed `environment_settings` field for `databricks.sdk.service.catalog.UpdateConnection`. +* [Breaking] Removed `comment`, `display_name` and `tags` fields for `databricks.sdk.service.sharing.Share`. \ No newline at end of file diff --git a/databricks/sdk/__init__.py b/databricks/sdk/__init__.py index 288762efe..a426eebfb 100755 --- a/databricks/sdk/__init__.py +++ b/databricks/sdk/__init__.py @@ -32,12 +32,14 @@ from databricks.sdk.service import qualitymonitorv2 as pkg_qualitymonitorv2 from databricks.sdk.service import serving as pkg_serving from databricks.sdk.service import settings as pkg_settings +from databricks.sdk.service import settingsv2 as pkg_settingsv2 from databricks.sdk.service import sharing as pkg_sharing from databricks.sdk.service import sql as pkg_sql +from databricks.sdk.service import tags as pkg_tags from databricks.sdk.service import vectorsearch as pkg_vectorsearch from databricks.sdk.service import workspace as pkg_workspace from databricks.sdk.service.agentbricks import AgentBricksAPI -from databricks.sdk.service.apps import AppsAPI +from databricks.sdk.service.apps import AppsAPI, AppsSettingsAPI from databricks.sdk.service.billing import (BillableUsageAPI, BudgetPolicyAPI, BudgetsAPI, LogDeliveryAPI, UsageDashboardsAPI) @@ -46,6 +48,7 @@ AccountStorageCredentialsAPI, ArtifactAllowlistsAPI, CatalogsAPI, ConnectionsAPI, CredentialsAPI, + EntityTagAssignmentsAPI, ExternalLineageAPI, ExternalLocationsAPI, ExternalMetadataAPI, FunctionsAPI, @@ -53,8 +56,8 @@ ModelVersionsAPI, OnlineTablesAPI, PoliciesAPI, QualityMonitorsAPI, RegisteredModelsAPI, - ResourceQuotasAPI, SchemasAPI, - StorageCredentialsAPI, + ResourceQuotasAPI, RfaAPI, + SchemasAPI, StorageCredentialsAPI, SystemSchemasAPI, TableConstraintsAPI, TablesAPI, TemporaryPathCredentialsAPI, @@ -128,6 +131,8 @@ RestrictWorkspaceAdminsAPI, SettingsAPI, SqlResultsDownloadAPI, TokenManagementAPI, TokensAPI, WorkspaceConfAPI, WorkspaceNetworkConfigurationAPI) +from databricks.sdk.service.settingsv2 import (AccountSettingsV2API, + WorkspaceSettingsV2API) from databricks.sdk.service.sharing import (ProvidersAPI, RecipientActivationAPI, RecipientFederationPoliciesAPI, @@ -141,6 +146,7 @@ QueryVisualizationsLegacyAPI, RedashConfigAPI, StatementExecutionAPI, WarehousesAPI) +from databricks.sdk.service.tags import TagPoliciesAPI from databricks.sdk.service.vectorsearch import (VectorSearchEndpointsAPI, VectorSearchIndexesAPI) from databricks.sdk.service.workspace import (GitCredentialsAPI, ReposAPI, @@ -248,6 +254,7 @@ def __init__( self._alerts_legacy = pkg_sql.AlertsLegacyAPI(self._api_client) self._alerts_v2 = pkg_sql.AlertsV2API(self._api_client) self._apps = pkg_apps.AppsAPI(self._api_client) + self._apps_settings = pkg_apps.AppsSettingsAPI(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) @@ -273,6 +280,7 @@ def __init__( self._database = pkg_database.DatabaseAPI(self._api_client) self._dbfs = DbfsExt(self._api_client) self._dbsql_permissions = pkg_sql.DbsqlPermissionsAPI(self._api_client) + self._entity_tag_assignments = pkg_catalog.EntityTagAssignmentsAPI(self._api_client) self._experiments = pkg_ml.ExperimentsAPI(self._api_client) self._external_lineage = pkg_catalog.ExternalLineageAPI(self._api_client) self._external_locations = pkg_catalog.ExternalLocationsAPI(self._api_client) @@ -329,6 +337,7 @@ def __init__( self._registered_models = pkg_catalog.RegisteredModelsAPI(self._api_client) self._repos = pkg_workspace.ReposAPI(self._api_client) self._resource_quotas = pkg_catalog.ResourceQuotasAPI(self._api_client) + self._rfa = pkg_catalog.RfaAPI(self._api_client) self._schemas = pkg_catalog.SchemasAPI(self._api_client) self._secrets = pkg_workspace.SecretsAPI(self._api_client) self._service_principal_secrets_proxy = pkg_oauth2.ServicePrincipalSecretsProxyAPI(self._api_client) @@ -347,6 +356,7 @@ def __init__( self._system_schemas = pkg_catalog.SystemSchemasAPI(self._api_client) self._table_constraints = pkg_catalog.TableConstraintsAPI(self._api_client) self._tables = pkg_catalog.TablesAPI(self._api_client) + self._tag_policies = pkg_tags.TagPoliciesAPI(self._api_client) self._temporary_path_credentials = pkg_catalog.TemporaryPathCredentialsAPI(self._api_client) self._temporary_table_credentials = pkg_catalog.TemporaryTableCredentialsAPI(self._api_client) self._token_management = pkg_settings.TokenManagementAPI(self._api_client) @@ -359,6 +369,7 @@ def __init__( self._workspace = WorkspaceExt(self._api_client) self._workspace_bindings = pkg_catalog.WorkspaceBindingsAPI(self._api_client) self._workspace_conf = pkg_settings.WorkspaceConfAPI(self._api_client) + self._workspace_settings_v2 = pkg_settingsv2.WorkspaceSettingsV2API(self._api_client) self._forecasting = pkg_ml.ForecastingAPI(self._api_client) @property @@ -408,6 +419,11 @@ def apps(self) -> pkg_apps.AppsAPI: """Apps run directly on a customer’s Databricks instance, integrate with their data, use and extend Databricks services, and enable users to interact through single sign-on.""" return self._apps + @property + def apps_settings(self) -> pkg_apps.AppsSettingsAPI: + """Apps Settings manage the settings for the Apps service on a customer's Databricks instance.""" + return self._apps_settings + @property def artifact_allowlists(self) -> pkg_catalog.ArtifactAllowlistsAPI: """In Databricks Runtime 13.3 and above, you can add libraries and init scripts to the `allowlist` in UC so that users can leverage these artifacts on compute configured with shared access mode.""" @@ -533,6 +549,11 @@ def dbsql_permissions(self) -> pkg_sql.DbsqlPermissionsAPI: """The SQL Permissions API is similar to the endpoints of the :method:permissions/set.""" return self._dbsql_permissions + @property + def entity_tag_assignments(self) -> pkg_catalog.EntityTagAssignmentsAPI: + """Tags are attributes that include keys and optional values that you can use to organize and categorize entities in Unity Catalog.""" + return self._entity_tag_assignments + @property def experiments(self) -> pkg_ml.ExperimentsAPI: """Experiments are the primary unit of organization in MLflow; all MLflow runs belong to an experiment.""" @@ -803,6 +824,11 @@ def resource_quotas(self) -> pkg_catalog.ResourceQuotasAPI: """Unity Catalog enforces resource quotas on all securable objects, which limits the number of resources that can be created.""" return self._resource_quotas + @property + def rfa(self) -> pkg_catalog.RfaAPI: + """Request for Access enables customers to request access to and manage access request destinations for Unity Catalog securables.""" + return self._rfa + @property def schemas(self) -> pkg_catalog.SchemasAPI: """A schema (also called a database) is the second layer of Unity Catalog’s three-level namespace.""" @@ -868,6 +894,11 @@ def tables(self) -> pkg_catalog.TablesAPI: """A table resides in the third layer of Unity Catalog’s three-level namespace.""" return self._tables + @property + def tag_policies(self) -> pkg_tags.TagPoliciesAPI: + """The Tag Policy API allows you to manage tag policies in Databricks.""" + return self._tag_policies + @property def temporary_path_credentials(self) -> pkg_catalog.TemporaryPathCredentialsAPI: """Temporary Path Credentials refer to short-lived, downscoped credentials used to access external cloud storage locations registered in Databricks.""" @@ -928,6 +959,11 @@ def workspace_conf(self) -> pkg_settings.WorkspaceConfAPI: """This API allows updating known workspace settings for advanced users.""" return self._workspace_conf + @property + def workspace_settings_v2(self) -> pkg_settingsv2.WorkspaceSettingsV2API: + """APIs to manage workspace level settings.""" + return self._workspace_settings_v2 + @property def forecasting(self) -> pkg_ml.ForecastingAPI: """The Forecasting API allows you to create and get serverless forecasting experiments.""" @@ -1029,6 +1065,7 @@ def __init__( self._service_principal_secrets = pkg_oauth2.ServicePrincipalSecretsAPI(self._api_client) self._service_principals = pkg_iam.AccountServicePrincipalsAPI(self._api_client) self._settings = pkg_settings.AccountSettingsAPI(self._api_client) + self._settings_v2 = pkg_settingsv2.AccountSettingsV2API(self._api_client) self._storage = pkg_provisioning.StorageAPI(self._api_client) self._storage_credentials = pkg_catalog.AccountStorageCredentialsAPI(self._api_client) self._usage_dashboards = pkg_billing.UsageDashboardsAPI(self._api_client) @@ -1157,6 +1194,11 @@ def settings(self) -> pkg_settings.AccountSettingsAPI: """Accounts Settings API allows users to manage settings at the account level.""" return self._settings + @property + def settings_v2(self) -> pkg_settingsv2.AccountSettingsV2API: + """APIs to manage account level settings.""" + return self._settings_v2 + @property def storage(self) -> pkg_provisioning.StorageAPI: """These APIs manage storage configurations for this workspace.""" diff --git a/databricks/sdk/service/agentbricks.py b/databricks/sdk/service/agentbricks.py index 8cda7ac26..25175acf0 100755 --- a/databricks/sdk/service/agentbricks.py +++ b/databricks/sdk/service/agentbricks.py @@ -227,9 +227,9 @@ def create_custom_llm( :param instructions: str Instructions for the custom LLM to follow :param agent_artifact_path: str (optional) - Optional: UC path for agent artifacts. If you are using a dataset that you only have read - permissions, please provide a destination path where you have write permissions. Please provide this - in catalog.schema format. + This will soon be deprecated!! Optional: UC path for agent artifacts. If you are using a dataset + that you only have read permissions, please provide a destination path where you have write + permissions. Please provide this in catalog.schema format. :param datasets: List[:class:`Dataset`] (optional) Datasets used for training and evaluating the model, not for inference. Currently, only 1 dataset is accepted. diff --git a/databricks/sdk/service/apps.py b/databricks/sdk/service/apps.py index 22caa3809..aeedb7146 100755 --- a/databricks/sdk/service/apps.py +++ b/databricks/sdk/service/apps.py @@ -483,6 +483,312 @@ def from_dict(cls, d: Dict[str, Any]) -> AppDeploymentStatus: return cls(message=d.get("message", None), state=_enum(d, "state", AppDeploymentState)) +@dataclass +class AppManifest: + """App manifest definition""" + + version: int + """The manifest schema version, for now only 1 is allowed""" + + name: str + """Name of the app defined by manifest author / publisher""" + + description: Optional[str] = None + """Description of the app defined by manifest author / publisher""" + + resource_specs: Optional[List[AppManifestAppResourceSpec]] = None + + def as_dict(self) -> dict: + """Serializes the AppManifest into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.description is not None: + body["description"] = self.description + if self.name is not None: + body["name"] = self.name + if self.resource_specs: + body["resource_specs"] = [v.as_dict() for v in self.resource_specs] + if self.version is not None: + body["version"] = self.version + return body + + def as_shallow_dict(self) -> dict: + """Serializes the AppManifest into a shallow dictionary of its immediate attributes.""" + body = {} + if self.description is not None: + body["description"] = self.description + if self.name is not None: + body["name"] = self.name + if self.resource_specs: + body["resource_specs"] = self.resource_specs + if self.version is not None: + body["version"] = self.version + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> AppManifest: + """Deserializes the AppManifest from a dictionary.""" + return cls( + description=d.get("description", None), + name=d.get("name", None), + resource_specs=_repeated_dict(d, "resource_specs", AppManifestAppResourceSpec), + version=d.get("version", None), + ) + + +@dataclass +class AppManifestAppResourceJobSpec: + permission: AppManifestAppResourceJobSpecJobPermission + """Permissions to grant on the Job. Supported permissions are: "CAN_MANAGE", "IS_OWNER", + "CAN_MANAGE_RUN", "CAN_VIEW".""" + + def as_dict(self) -> dict: + """Serializes the AppManifestAppResourceJobSpec into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.permission is not None: + body["permission"] = self.permission.value + return body + + def as_shallow_dict(self) -> dict: + """Serializes the AppManifestAppResourceJobSpec into a shallow dictionary of its immediate attributes.""" + body = {} + if self.permission is not None: + body["permission"] = self.permission + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> AppManifestAppResourceJobSpec: + """Deserializes the AppManifestAppResourceJobSpec from a dictionary.""" + return cls(permission=_enum(d, "permission", AppManifestAppResourceJobSpecJobPermission)) + + +class AppManifestAppResourceJobSpecJobPermission(Enum): + + CAN_MANAGE = "CAN_MANAGE" + CAN_MANAGE_RUN = "CAN_MANAGE_RUN" + CAN_VIEW = "CAN_VIEW" + IS_OWNER = "IS_OWNER" + + +@dataclass +class AppManifestAppResourceSecretSpec: + permission: AppManifestAppResourceSecretSpecSecretPermission + """Permission to grant on the secret scope. For secrets, only one permission is allowed. Permission + must be one of: "READ", "WRITE", "MANAGE".""" + + def as_dict(self) -> dict: + """Serializes the AppManifestAppResourceSecretSpec into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.permission is not None: + body["permission"] = self.permission.value + return body + + def as_shallow_dict(self) -> dict: + """Serializes the AppManifestAppResourceSecretSpec into a shallow dictionary of its immediate attributes.""" + body = {} + if self.permission is not None: + body["permission"] = self.permission + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> AppManifestAppResourceSecretSpec: + """Deserializes the AppManifestAppResourceSecretSpec from a dictionary.""" + return cls(permission=_enum(d, "permission", AppManifestAppResourceSecretSpecSecretPermission)) + + +class AppManifestAppResourceSecretSpecSecretPermission(Enum): + """Permission to grant on the secret scope. Supported permissions are: "READ", "WRITE", "MANAGE".""" + + MANAGE = "MANAGE" + READ = "READ" + WRITE = "WRITE" + + +@dataclass +class AppManifestAppResourceServingEndpointSpec: + permission: AppManifestAppResourceServingEndpointSpecServingEndpointPermission + """Permission to grant on the serving endpoint. Supported permissions are: "CAN_MANAGE", + "CAN_QUERY", "CAN_VIEW".""" + + def as_dict(self) -> dict: + """Serializes the AppManifestAppResourceServingEndpointSpec into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.permission is not None: + body["permission"] = self.permission.value + return body + + def as_shallow_dict(self) -> dict: + """Serializes the AppManifestAppResourceServingEndpointSpec into a shallow dictionary of its immediate attributes.""" + body = {} + if self.permission is not None: + body["permission"] = self.permission + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> AppManifestAppResourceServingEndpointSpec: + """Deserializes the AppManifestAppResourceServingEndpointSpec from a dictionary.""" + return cls( + permission=_enum(d, "permission", AppManifestAppResourceServingEndpointSpecServingEndpointPermission) + ) + + +class AppManifestAppResourceServingEndpointSpecServingEndpointPermission(Enum): + + CAN_MANAGE = "CAN_MANAGE" + CAN_QUERY = "CAN_QUERY" + CAN_VIEW = "CAN_VIEW" + + +@dataclass +class AppManifestAppResourceSpec: + """AppResource related fields are copied from app.proto but excludes resource identifiers (e.g. + name, id, key, scope, etc.)""" + + name: str + """Name of the App Resource.""" + + description: Optional[str] = None + """Description of the App Resource.""" + + job_spec: Optional[AppManifestAppResourceJobSpec] = None + + secret_spec: Optional[AppManifestAppResourceSecretSpec] = None + + serving_endpoint_spec: Optional[AppManifestAppResourceServingEndpointSpec] = None + + sql_warehouse_spec: Optional[AppManifestAppResourceSqlWarehouseSpec] = None + + uc_securable_spec: Optional[AppManifestAppResourceUcSecurableSpec] = None + + def as_dict(self) -> dict: + """Serializes the AppManifestAppResourceSpec into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.description is not None: + body["description"] = self.description + if self.job_spec: + body["job_spec"] = self.job_spec.as_dict() + if self.name is not None: + body["name"] = self.name + if self.secret_spec: + body["secret_spec"] = self.secret_spec.as_dict() + if self.serving_endpoint_spec: + body["serving_endpoint_spec"] = self.serving_endpoint_spec.as_dict() + if self.sql_warehouse_spec: + body["sql_warehouse_spec"] = self.sql_warehouse_spec.as_dict() + if self.uc_securable_spec: + body["uc_securable_spec"] = self.uc_securable_spec.as_dict() + return body + + def as_shallow_dict(self) -> dict: + """Serializes the AppManifestAppResourceSpec into a shallow dictionary of its immediate attributes.""" + body = {} + if self.description is not None: + body["description"] = self.description + if self.job_spec: + body["job_spec"] = self.job_spec + if self.name is not None: + body["name"] = self.name + if self.secret_spec: + body["secret_spec"] = self.secret_spec + if self.serving_endpoint_spec: + body["serving_endpoint_spec"] = self.serving_endpoint_spec + if self.sql_warehouse_spec: + body["sql_warehouse_spec"] = self.sql_warehouse_spec + if self.uc_securable_spec: + body["uc_securable_spec"] = self.uc_securable_spec + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> AppManifestAppResourceSpec: + """Deserializes the AppManifestAppResourceSpec from a dictionary.""" + return cls( + description=d.get("description", None), + job_spec=_from_dict(d, "job_spec", AppManifestAppResourceJobSpec), + name=d.get("name", None), + secret_spec=_from_dict(d, "secret_spec", AppManifestAppResourceSecretSpec), + serving_endpoint_spec=_from_dict(d, "serving_endpoint_spec", AppManifestAppResourceServingEndpointSpec), + sql_warehouse_spec=_from_dict(d, "sql_warehouse_spec", AppManifestAppResourceSqlWarehouseSpec), + uc_securable_spec=_from_dict(d, "uc_securable_spec", AppManifestAppResourceUcSecurableSpec), + ) + + +@dataclass +class AppManifestAppResourceSqlWarehouseSpec: + permission: AppManifestAppResourceSqlWarehouseSpecSqlWarehousePermission + """Permission to grant on the SQL warehouse. Supported permissions are: "CAN_MANAGE", "CAN_USE", + "IS_OWNER".""" + + def as_dict(self) -> dict: + """Serializes the AppManifestAppResourceSqlWarehouseSpec into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.permission is not None: + body["permission"] = self.permission.value + return body + + def as_shallow_dict(self) -> dict: + """Serializes the AppManifestAppResourceSqlWarehouseSpec into a shallow dictionary of its immediate attributes.""" + body = {} + if self.permission is not None: + body["permission"] = self.permission + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> AppManifestAppResourceSqlWarehouseSpec: + """Deserializes the AppManifestAppResourceSqlWarehouseSpec from a dictionary.""" + return cls(permission=_enum(d, "permission", AppManifestAppResourceSqlWarehouseSpecSqlWarehousePermission)) + + +class AppManifestAppResourceSqlWarehouseSpecSqlWarehousePermission(Enum): + + CAN_MANAGE = "CAN_MANAGE" + CAN_USE = "CAN_USE" + IS_OWNER = "IS_OWNER" + + +@dataclass +class AppManifestAppResourceUcSecurableSpec: + securable_type: AppManifestAppResourceUcSecurableSpecUcSecurableType + + permission: AppManifestAppResourceUcSecurableSpecUcSecurablePermission + + def as_dict(self) -> dict: + """Serializes the AppManifestAppResourceUcSecurableSpec into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.permission is not None: + body["permission"] = self.permission.value + if self.securable_type is not None: + body["securable_type"] = self.securable_type.value + return body + + def as_shallow_dict(self) -> dict: + """Serializes the AppManifestAppResourceUcSecurableSpec into a shallow dictionary of its immediate attributes.""" + body = {} + if self.permission is not None: + body["permission"] = self.permission + if self.securable_type is not None: + body["securable_type"] = self.securable_type + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> AppManifestAppResourceUcSecurableSpec: + """Deserializes the AppManifestAppResourceUcSecurableSpec from a dictionary.""" + return cls( + permission=_enum(d, "permission", AppManifestAppResourceUcSecurableSpecUcSecurablePermission), + securable_type=_enum(d, "securable_type", AppManifestAppResourceUcSecurableSpecUcSecurableType), + ) + + +class AppManifestAppResourceUcSecurableSpecUcSecurablePermission(Enum): + + MANAGE = "MANAGE" + READ_VOLUME = "READ_VOLUME" + WRITE_VOLUME = "WRITE_VOLUME" + + +class AppManifestAppResourceUcSecurableSpecUcSecurableType(Enum): + + VOLUME = "VOLUME" + + @dataclass class AppPermission: inherited: Optional[bool] = None @@ -1036,6 +1342,81 @@ def from_dict(cls, d: Dict[str, Any]) -> ComputeStatus: return cls(message=d.get("message", None), state=_enum(d, "state", ComputeState)) +@dataclass +class CustomTemplate: + name: str + """The name of the template. It must contain only alphanumeric characters, hyphens, underscores, + and whitespaces. It must be unique within the workspace.""" + + git_repo: str + """The Git repository URL that the template resides in.""" + + path: str + """The path to the template within the Git repository.""" + + manifest: AppManifest + """The manifest of the template. It defines fields and default values when installing the template.""" + + git_provider: str + """The Git provider of the template.""" + + creator: Optional[str] = None + + description: Optional[str] = None + """The description of the template.""" + + def as_dict(self) -> dict: + """Serializes the CustomTemplate into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.creator is not None: + body["creator"] = self.creator + if self.description is not None: + body["description"] = self.description + if self.git_provider is not None: + body["git_provider"] = self.git_provider + if self.git_repo is not None: + body["git_repo"] = self.git_repo + if self.manifest: + body["manifest"] = self.manifest.as_dict() + if self.name is not None: + body["name"] = self.name + if self.path is not None: + body["path"] = self.path + return body + + def as_shallow_dict(self) -> dict: + """Serializes the CustomTemplate into a shallow dictionary of its immediate attributes.""" + body = {} + if self.creator is not None: + body["creator"] = self.creator + if self.description is not None: + body["description"] = self.description + if self.git_provider is not None: + body["git_provider"] = self.git_provider + if self.git_repo is not None: + body["git_repo"] = self.git_repo + if self.manifest: + body["manifest"] = self.manifest + if self.name is not None: + body["name"] = self.name + if self.path is not None: + body["path"] = self.path + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> CustomTemplate: + """Deserializes the CustomTemplate from a dictionary.""" + return cls( + creator=d.get("creator", None), + description=d.get("description", None), + git_provider=d.get("git_provider", None), + git_repo=d.get("git_repo", None), + manifest=_from_dict(d, "manifest", AppManifest), + name=d.get("name", None), + path=d.get("path", None), + ) + + @dataclass class GetAppPermissionLevelsResponse: permission_levels: Optional[List[AppPermissionsDescription]] = None @@ -1127,6 +1508,39 @@ def from_dict(cls, d: Dict[str, Any]) -> ListAppsResponse: return cls(apps=_repeated_dict(d, "apps", App), next_page_token=d.get("next_page_token", None)) +@dataclass +class ListCustomTemplatesResponse: + next_page_token: Optional[str] = None + """Pagination token to request the next page of custom templates.""" + + templates: Optional[List[CustomTemplate]] = None + + def as_dict(self) -> dict: + """Serializes the ListCustomTemplatesResponse 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.templates: + body["templates"] = [v.as_dict() for v in self.templates] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the ListCustomTemplatesResponse 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.templates: + body["templates"] = self.templates + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> ListCustomTemplatesResponse: + """Deserializes the ListCustomTemplatesResponse from a dictionary.""" + return cls( + next_page_token=d.get("next_page_token", None), templates=_repeated_dict(d, "templates", CustomTemplate) + ) + + class AppsAPI: """Apps run directly on a customer’s Databricks instance, integrate with their data, use and extend Databricks services, and enable users to interact through single sign-on.""" @@ -1542,3 +1956,108 @@ def update_permissions( res = self._api.do("PATCH", f"/api/2.0/permissions/apps/{app_name}", body=body, headers=headers) return AppPermissions.from_dict(res) + + +class AppsSettingsAPI: + """Apps Settings manage the settings for the Apps service on a customer's Databricks instance.""" + + def __init__(self, api_client): + self._api = api_client + + def create_custom_template(self, template: CustomTemplate) -> CustomTemplate: + """Creates a custom template. + + :param template: :class:`CustomTemplate` + + :returns: :class:`CustomTemplate` + """ + body = template.as_dict() + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do("POST", "/api/2.0/apps-settings/templates", body=body, headers=headers) + return CustomTemplate.from_dict(res) + + def delete_custom_template(self, name: str) -> CustomTemplate: + """Deletes the custom template with the specified name. + + :param name: str + The name of the custom template. + + :returns: :class:`CustomTemplate` + """ + + headers = { + "Accept": "application/json", + } + + res = self._api.do("DELETE", f"/api/2.0/apps-settings/templates/{name}", headers=headers) + return CustomTemplate.from_dict(res) + + def get_custom_template(self, name: str) -> CustomTemplate: + """Gets the custom template with the specified name. + + :param name: str + The name of the custom template. + + :returns: :class:`CustomTemplate` + """ + + headers = { + "Accept": "application/json", + } + + res = self._api.do("GET", f"/api/2.0/apps-settings/templates/{name}", headers=headers) + return CustomTemplate.from_dict(res) + + def list_custom_templates( + self, *, page_size: Optional[int] = None, page_token: Optional[str] = None + ) -> Iterator[CustomTemplate]: + """Lists all custom templates in the workspace. + + :param page_size: int (optional) + Upper bound for items returned. + :param page_token: str (optional) + Pagination token to go to the next page of custom templates. Requests first page if absent. + + :returns: Iterator over :class:`CustomTemplate` + """ + + 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", "/api/2.0/apps-settings/templates", query=query, headers=headers) + if "templates" in json: + for v in json["templates"]: + yield CustomTemplate.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_custom_template(self, name: str, template: CustomTemplate) -> CustomTemplate: + """Updates the custom template with the specified name. Note that the template name cannot be updated. + + :param name: str + The name of the template. It must contain only alphanumeric characters, hyphens, underscores, and + whitespaces. It must be unique within the workspace. + :param template: :class:`CustomTemplate` + + :returns: :class:`CustomTemplate` + """ + body = template.as_dict() + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do("PUT", f"/api/2.0/apps-settings/templates/{name}", body=body, headers=headers) + return CustomTemplate.from_dict(res) diff --git a/databricks/sdk/service/catalog.py b/databricks/sdk/service/catalog.py index 255fa6fa5..3db28a23e 100755 --- a/databricks/sdk/service/catalog.py +++ b/databricks/sdk/service/catalog.py @@ -19,6 +19,50 @@ # all definitions in this file are in alphabetical order +@dataclass +class AccessRequestDestinations: + destinations: List[NotificationDestination] + """The access request destinations for the securable.""" + + securable: Securable + """The securable for which the access request destinations are being retrieved.""" + + are_any_destinations_hidden: Optional[bool] = None + """Indicates whether any destinations are hidden from the caller due to a lack of permissions. This + value is true if the caller does not have permission to see all destinations.""" + + def as_dict(self) -> dict: + """Serializes the AccessRequestDestinations into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.are_any_destinations_hidden is not None: + body["are_any_destinations_hidden"] = self.are_any_destinations_hidden + if self.destinations: + body["destinations"] = [v.as_dict() for v in self.destinations] + if self.securable: + body["securable"] = self.securable.as_dict() + return body + + def as_shallow_dict(self) -> dict: + """Serializes the AccessRequestDestinations into a shallow dictionary of its immediate attributes.""" + body = {} + if self.are_any_destinations_hidden is not None: + body["are_any_destinations_hidden"] = self.are_any_destinations_hidden + if self.destinations: + body["destinations"] = self.destinations + if self.securable: + body["securable"] = self.securable + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> AccessRequestDestinations: + """Deserializes the AccessRequestDestinations from a dictionary.""" + return cls( + are_any_destinations_hidden=d.get("are_any_destinations_hidden", None), + destinations=_repeated_dict(d, "destinations", NotificationDestination), + securable=_from_dict(d, "securable", Securable), + ) + + @dataclass class AccountsMetastoreAssignment: metastore_assignment: Optional[MetastoreAssignment] = None @@ -706,6 +750,31 @@ def from_dict(cls, d: Dict[str, Any]) -> AzureUserDelegationSas: return cls(sas_token=d.get("sas_token", None)) +@dataclass +class BatchCreateAccessRequestsResponse: + responses: Optional[List[CreateAccessRequestResponse]] = None + """The access request destinations for each securable object the principal requested.""" + + def as_dict(self) -> dict: + """Serializes the BatchCreateAccessRequestsResponse into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.responses: + body["responses"] = [v.as_dict() for v in self.responses] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the BatchCreateAccessRequestsResponse into a shallow dictionary of its immediate attributes.""" + body = {} + if self.responses: + body["responses"] = self.responses + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> BatchCreateAccessRequestsResponse: + """Deserializes the BatchCreateAccessRequestsResponse from a dictionary.""" + return cls(responses=_repeated_dict(d, "responses", CreateAccessRequestResponse)) + + @dataclass class CancelRefreshResponse: def as_dict(self) -> dict: @@ -1294,9 +1363,6 @@ class ConnectionInfo: credential_type: Optional[CredentialType] = None """The type of credential.""" - environment_settings: Optional[EnvironmentSettings] = None - """[Create,Update:OPT] Connection environment settings as EnvironmentSettings object.""" - full_name: Optional[str] = None """Full name of connection.""" @@ -1346,8 +1412,6 @@ def as_dict(self) -> dict: body["created_by"] = self.created_by if self.credential_type is not None: body["credential_type"] = self.credential_type.value - if self.environment_settings: - body["environment_settings"] = self.environment_settings.as_dict() if self.full_name is not None: body["full_name"] = self.full_name if self.metastore_id is not None: @@ -1389,8 +1453,6 @@ def as_shallow_dict(self) -> dict: body["created_by"] = self.created_by if self.credential_type is not None: body["credential_type"] = self.credential_type - if self.environment_settings: - body["environment_settings"] = self.environment_settings if self.full_name is not None: body["full_name"] = self.full_name if self.metastore_id is not None: @@ -1427,7 +1489,6 @@ def from_dict(cls, d: Dict[str, Any]) -> ConnectionInfo: created_at=d.get("created_at", None), created_by=d.get("created_by", None), credential_type=_enum(d, "credential_type", CredentialType), - environment_settings=_from_dict(d, "environment_settings", EnvironmentSettings), full_name=d.get("full_name", None), metastore_id=d.get("metastore_id", None), name=d.get("name", None), @@ -1516,6 +1577,93 @@ def from_dict(cls, d: Dict[str, Any]) -> ContinuousUpdateStatus: ) +@dataclass +class CreateAccessRequest: + behalf_of: Optional[Principal] = None + """Optional. The principal this request is for. Empty `behalf_of` defaults to the requester's + identity. + + Principals must be unique across the API call.""" + + comment: Optional[str] = None + """Optional. Comment associated with the request. + + At most 200 characters, can only contain lowercase/uppercase letters (a-z, A-Z), numbers (0-9), + punctuation, and spaces.""" + + securable_permissions: Optional[List[SecurablePermissions]] = None + """List of securables and their corresponding requested UC privileges. + + At most 30 securables can be requested for a principal per batched call. Each securable can only + be requested once per principal.""" + + def as_dict(self) -> dict: + """Serializes the CreateAccessRequest into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.behalf_of: + body["behalf_of"] = self.behalf_of.as_dict() + if self.comment is not None: + body["comment"] = self.comment + if self.securable_permissions: + body["securable_permissions"] = [v.as_dict() for v in self.securable_permissions] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the CreateAccessRequest into a shallow dictionary of its immediate attributes.""" + body = {} + if self.behalf_of: + body["behalf_of"] = self.behalf_of + if self.comment is not None: + body["comment"] = self.comment + if self.securable_permissions: + body["securable_permissions"] = self.securable_permissions + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> CreateAccessRequest: + """Deserializes the CreateAccessRequest from a dictionary.""" + return cls( + behalf_of=_from_dict(d, "behalf_of", Principal), + comment=d.get("comment", None), + securable_permissions=_repeated_dict(d, "securable_permissions", SecurablePermissions), + ) + + +@dataclass +class CreateAccessRequestResponse: + behalf_of: Optional[Principal] = None + """The principal the request was made on behalf of.""" + + request_destinations: Optional[List[AccessRequestDestinations]] = None + """The access request destinations for all the securables the principal requested.""" + + def as_dict(self) -> dict: + """Serializes the CreateAccessRequestResponse into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.behalf_of: + body["behalf_of"] = self.behalf_of.as_dict() + if self.request_destinations: + body["request_destinations"] = [v.as_dict() for v in self.request_destinations] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the CreateAccessRequestResponse into a shallow dictionary of its immediate attributes.""" + body = {} + if self.behalf_of: + body["behalf_of"] = self.behalf_of + if self.request_destinations: + body["request_destinations"] = self.request_destinations + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> CreateAccessRequestResponse: + """Deserializes the CreateAccessRequestResponse from a dictionary.""" + return cls( + behalf_of=_from_dict(d, "behalf_of", Principal), + request_destinations=_repeated_dict(d, "request_destinations", AccessRequestDestinations), + ) + + @dataclass class CreateFunction: name: str @@ -2635,6 +2783,15 @@ def from_dict(cls, d: Dict[str, Any]) -> DependencyList: return cls(dependencies=_repeated_dict(d, "dependencies", Dependency)) +class DestinationType(Enum): + + EMAIL = "EMAIL" + GENERIC_WEBHOOK = "GENERIC_WEBHOOK" + MICROSOFT_TEAMS = "MICROSOFT_TEAMS" + SLACK = "SLACK" + URL = "URL" + + @dataclass class DisableResponse: def as_dict(self) -> dict: @@ -2872,34 +3029,56 @@ def from_dict(cls, d: Dict[str, Any]) -> EncryptionDetails: @dataclass -class EnvironmentSettings: - environment_version: Optional[str] = None +class EntityTagAssignment: + """Represents a tag assignment to an entity""" + + entity_name: str + """The fully qualified name of the entity to which the tag is assigned""" - java_dependencies: Optional[List[str]] = None + tag_key: str + """The key of the tag""" + + entity_type: str + """The type of the entity to which the tag is assigned. Allowed values are: catalogs, schemas, + tables, columns, volumes.""" + + tag_value: Optional[str] = None + """The value of the tag""" def as_dict(self) -> dict: - """Serializes the EnvironmentSettings into a dictionary suitable for use as a JSON request body.""" + """Serializes the EntityTagAssignment into a dictionary suitable for use as a JSON request body.""" body = {} - if self.environment_version is not None: - body["environment_version"] = self.environment_version - if self.java_dependencies: - body["java_dependencies"] = [v for v in self.java_dependencies] + if self.entity_name is not None: + body["entity_name"] = self.entity_name + if self.entity_type is not None: + body["entity_type"] = self.entity_type + if self.tag_key is not None: + body["tag_key"] = self.tag_key + if self.tag_value is not None: + body["tag_value"] = self.tag_value return body def as_shallow_dict(self) -> dict: - """Serializes the EnvironmentSettings into a shallow dictionary of its immediate attributes.""" + """Serializes the EntityTagAssignment into a shallow dictionary of its immediate attributes.""" body = {} - if self.environment_version is not None: - body["environment_version"] = self.environment_version - if self.java_dependencies: - body["java_dependencies"] = self.java_dependencies + if self.entity_name is not None: + body["entity_name"] = self.entity_name + if self.entity_type is not None: + body["entity_type"] = self.entity_type + if self.tag_key is not None: + body["tag_key"] = self.tag_key + if self.tag_value is not None: + body["tag_value"] = self.tag_value return body @classmethod - def from_dict(cls, d: Dict[str, Any]) -> EnvironmentSettings: - """Deserializes the EnvironmentSettings from a dictionary.""" + def from_dict(cls, d: Dict[str, Any]) -> EntityTagAssignment: + """Deserializes the EntityTagAssignment from a dictionary.""" return cls( - environment_version=d.get("environment_version", None), java_dependencies=d.get("java_dependencies", None) + entity_name=d.get("entity_name", None), + entity_type=d.get("entity_type", None), + tag_key=d.get("tag_key", None), + tag_value=d.get("tag_value", None), ) @@ -5125,6 +5304,41 @@ def from_dict(cls, d: Dict[str, Any]) -> ListCredentialsResponse: ) +@dataclass +class ListEntityTagAssignmentsResponse: + next_page_token: Optional[str] = None + """Optional. Pagination token for retrieving the next page of results""" + + tag_assignments: Optional[List[EntityTagAssignment]] = None + """The list of tag assignments""" + + def as_dict(self) -> dict: + """Serializes the ListEntityTagAssignmentsResponse 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.tag_assignments: + body["tag_assignments"] = [v.as_dict() for v in self.tag_assignments] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the ListEntityTagAssignmentsResponse 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.tag_assignments: + body["tag_assignments"] = self.tag_assignments + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> ListEntityTagAssignmentsResponse: + """Deserializes the ListEntityTagAssignmentsResponse from a dictionary.""" + return cls( + next_page_token=d.get("next_page_token", None), + tag_assignments=_repeated_dict(d, "tag_assignments", EntityTagAssignment), + ) + + @dataclass class ListExternalLineageRelationshipsResponse: external_lineage_relationships: Optional[List[ExternalLineageInfo]] = None @@ -6763,6 +6977,55 @@ def from_dict(cls, d: Dict[str, Any]) -> NamedTableConstraint: return cls(name=d.get("name", None)) +@dataclass +class NotificationDestination: + destination_id: Optional[str] = None + """The identifier for the destination. This is the email address for EMAIL destinations, the URL + for URL destinations, or the unique Databricks notification destination ID for all other + external destinations.""" + + destination_type: Optional[DestinationType] = None + """The type of the destination.""" + + special_destination: Optional[SpecialDestination] = None + """This field is used to denote whether the destination is the email of the owner of the securable + object. The special destination cannot be assigned to a securable and only represents the + default destination of the securable. The securable types that support default special + destinations are: "catalog", "external_location", "connection", "credential", and "metastore". + The **destination_type** of a **special_destination** is always EMAIL.""" + + def as_dict(self) -> dict: + """Serializes the NotificationDestination into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.destination_id is not None: + body["destination_id"] = self.destination_id + if self.destination_type is not None: + body["destination_type"] = self.destination_type.value + if self.special_destination is not None: + body["special_destination"] = self.special_destination.value + return body + + def as_shallow_dict(self) -> dict: + """Serializes the NotificationDestination into a shallow dictionary of its immediate attributes.""" + body = {} + if self.destination_id is not None: + body["destination_id"] = self.destination_id + if self.destination_type is not None: + body["destination_type"] = self.destination_type + if self.special_destination is not None: + body["special_destination"] = self.special_destination + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> NotificationDestination: + """Deserializes the NotificationDestination from a dictionary.""" + return cls( + destination_id=d.get("destination_id", None), + destination_type=_enum(d, "destination_type", DestinationType), + special_destination=_enum(d, "special_destination", SpecialDestination), + ) + + @dataclass class OnlineTable: """Online Table information.""" @@ -7309,7 +7572,7 @@ class PolicyInfo: update.""" for_securable_type: SecurableType - """Type of securables that the policy should take effect on. Only `table` is supported at this + """Type of securables that the policy should take effect on. Only `TABLE` is supported at this moment. Required on create and optional on update.""" policy_type: PolicyType @@ -7337,19 +7600,19 @@ class PolicyInfo: match_columns: Optional[List[MatchColumn]] = None """Optional list of condition expressions used to match table columns. Only valid when - `for_securable_type` is `table`. When specified, the policy only applies to tables whose columns + `for_securable_type` is `TABLE`. When specified, the policy only applies to tables whose columns satisfy all match conditions.""" name: Optional[str] = None - """Name of the policy. Required on create and ignored on update. To update the name, use the - `new_name` field.""" + """Name of the policy. Required on create and optional on update. To rename the policy, set `name` + to a different value on update.""" on_securable_fullname: Optional[str] = None """Full name of the securable on which the policy is defined. Required on create and ignored on update.""" on_securable_type: Optional[SecurableType] = None - """Type of the securable on which the policy is defined. Only `catalog`, `schema` and `table` are + """Type of the securable on which the policy is defined. Only `CATALOG`, `SCHEMA` and `TABLE` are supported at this moment. Required on create and ignored on update.""" row_filter: Optional[RowFilterOptions] = None @@ -7525,6 +7788,44 @@ def from_dict(cls, d: Dict[str, Any]) -> PrimaryKeyConstraint: ) +@dataclass +class Principal: + id: Optional[str] = None + """Databricks user, group or service principal ID.""" + + principal_type: Optional[PrincipalType] = None + + def as_dict(self) -> dict: + """Serializes the Principal into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.id is not None: + body["id"] = self.id + if self.principal_type is not None: + body["principal_type"] = self.principal_type.value + return body + + def as_shallow_dict(self) -> dict: + """Serializes the Principal into a shallow dictionary of its immediate attributes.""" + body = {} + if self.id is not None: + body["id"] = self.id + if self.principal_type is not None: + body["principal_type"] = self.principal_type + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> Principal: + """Deserializes the Principal from a dictionary.""" + return cls(id=d.get("id", None), principal_type=_enum(d, "principal_type", PrincipalType)) + + +class PrincipalType(Enum): + + GROUP_PRINCIPAL = "GROUP_PRINCIPAL" + SERVICE_PRINCIPAL = "SERVICE_PRINCIPAL" + USER_PRINCIPAL = "USER_PRINCIPAL" + + class Privilege(Enum): ACCESS = "ACCESS" @@ -8190,6 +8491,53 @@ def from_dict(cls, d: Dict[str, Any]) -> SchemaInfo: ) +@dataclass +class Securable: + """Generic definition of a securable, which is uniquely defined in a metastore by its type and full + name.""" + + full_name: Optional[str] = None + """Required. The full name of the catalog/schema/table. Optional if resource_name is present.""" + + provider_share: Optional[str] = None + """Optional. The name of the Share object that contains the securable when the securable is getting + shared in D2D Delta Sharing.""" + + type: Optional[SecurableType] = None + """Required. The type of securable (catalog/schema/table). Optional if resource_name is present.""" + + def as_dict(self) -> dict: + """Serializes the Securable into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.full_name is not None: + body["full_name"] = self.full_name + if self.provider_share is not None: + body["provider_share"] = self.provider_share + if self.type is not None: + body["type"] = self.type.value + return body + + def as_shallow_dict(self) -> dict: + """Serializes the Securable into a shallow dictionary of its immediate attributes.""" + body = {} + if self.full_name is not None: + body["full_name"] = self.full_name + if self.provider_share is not None: + body["provider_share"] = self.provider_share + if self.type is not None: + body["type"] = self.type + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> Securable: + """Deserializes the Securable from a dictionary.""" + return cls( + full_name=d.get("full_name", None), + provider_share=d.get("provider_share", None), + type=_enum(d, "type", SecurableType), + ) + + class SecurableKind(Enum): TABLE_DB_STORAGE = "TABLE_DB_STORAGE" @@ -8321,6 +8669,38 @@ def from_dict(cls, d: Dict[str, Any]) -> SecurableKindManifest: ) +@dataclass +class SecurablePermissions: + permissions: Optional[List[str]] = None + """List of requested Unity Catalog permissions.""" + + securable: Optional[Securable] = None + """The securable for which the access request destinations are being requested.""" + + def as_dict(self) -> dict: + """Serializes the SecurablePermissions into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.permissions: + body["permissions"] = [v for v in self.permissions] + if self.securable: + body["securable"] = self.securable.as_dict() + return body + + def as_shallow_dict(self) -> dict: + """Serializes the SecurablePermissions into a shallow dictionary of its immediate attributes.""" + body = {} + if self.permissions: + body["permissions"] = self.permissions + if self.securable: + body["securable"] = self.securable + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> SecurablePermissions: + """Deserializes the SecurablePermissions from a dictionary.""" + return cls(permissions=d.get("permissions", None), securable=_from_dict(d, "securable", Securable)) + + class SecurableType(Enum): """The type of Unity Catalog securable.""" @@ -8343,6 +8723,15 @@ class SecurableType(Enum): VOLUME = "VOLUME" +class SpecialDestination(Enum): + + SPECIAL_DESTINATION_CATALOG_OWNER = "SPECIAL_DESTINATION_CATALOG_OWNER" + SPECIAL_DESTINATION_CONNECTION_OWNER = "SPECIAL_DESTINATION_CONNECTION_OWNER" + SPECIAL_DESTINATION_CREDENTIAL_OWNER = "SPECIAL_DESTINATION_CREDENTIAL_OWNER" + SPECIAL_DESTINATION_EXTERNAL_LOCATION_OWNER = "SPECIAL_DESTINATION_EXTERNAL_LOCATION_OWNER" + SPECIAL_DESTINATION_METASTORE_OWNER = "SPECIAL_DESTINATION_METASTORE_OWNER" + + @dataclass class SseEncryptionDetails: """Server-Side Encryption properties for clients communicating with AWS s3.""" @@ -10701,7 +11090,6 @@ def create( options: Dict[str, str], *, comment: Optional[str] = None, - environment_settings: Optional[EnvironmentSettings] = None, properties: Optional[Dict[str, str]] = None, read_only: Optional[bool] = None, ) -> ConnectionInfo: @@ -10718,8 +11106,6 @@ def create( A map of key-value properties attached to the securable. :param comment: str (optional) User-provided free-form text description. - :param environment_settings: :class:`EnvironmentSettings` (optional) - [Create,Update:OPT] Connection environment settings as EnvironmentSettings object. :param properties: Dict[str,str] (optional) A map of key-value properties attached to the securable. :param read_only: bool (optional) @@ -10732,8 +11118,6 @@ def create( body["comment"] = comment if connection_type is not None: body["connection_type"] = connection_type.value - if environment_settings is not None: - body["environment_settings"] = environment_settings.as_dict() if name is not None: body["name"] = name if options is not None: @@ -10814,13 +11198,7 @@ def list(self, *, max_results: Optional[int] = None, page_token: Optional[str] = query["page_token"] = json["next_page_token"] def update( - self, - name: str, - options: Dict[str, str], - *, - environment_settings: Optional[EnvironmentSettings] = None, - new_name: Optional[str] = None, - owner: Optional[str] = None, + self, name: str, options: Dict[str, str], *, new_name: Optional[str] = None, owner: Optional[str] = None ) -> ConnectionInfo: """Updates the connection that matches the supplied name. @@ -10828,8 +11206,6 @@ def update( Name of the connection. :param options: Dict[str,str] A map of key-value properties attached to the securable. - :param environment_settings: :class:`EnvironmentSettings` (optional) - [Create,Update:OPT] Connection environment settings as EnvironmentSettings object. :param new_name: str (optional) New name for the connection. :param owner: str (optional) @@ -10838,8 +11214,6 @@ def update( :returns: :class:`ConnectionInfo` """ body = {} - if environment_settings is not None: - body["environment_settings"] = environment_settings.as_dict() if new_name is not None: body["new_name"] = new_name if options is not None: @@ -11204,6 +11578,193 @@ def validate_credential( return ValidateCredentialResponse.from_dict(res) +class EntityTagAssignmentsAPI: + """Tags are attributes that include keys and optional values that you can use to organize and categorize + entities in Unity Catalog. Entity tagging is currently supported on catalogs, schemas, tables (including + views), columns, volumes. With these APIs, users can create, update, delete, and list tag assignments + across Unity Catalog entities""" + + def __init__(self, api_client): + self._api = api_client + + def create(self, tag_assignment: EntityTagAssignment) -> EntityTagAssignment: + """Creates a tag assignment for an Unity Catalog entity. + + To add tags to Unity Catalog entities, you must own the entity or have the following privileges: - + **APPLY TAG** on the entity - **USE SCHEMA** on the entity's parent schema - **USE CATALOG** on the + entity's parent catalog + + To add a governed tag to Unity Catalog entities, you must also have the **ASSIGN** or **MANAGE** + permission on the tag policy. See [Manage tag policy permissions]. + + [Manage tag policy permissions]: https://docs.databricks.com/aws/en/admin/tag-policies/manage-permissions + + :param tag_assignment: :class:`EntityTagAssignment` + + :returns: :class:`EntityTagAssignment` + """ + body = tag_assignment.as_dict() + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do("POST", "/api/2.1/unity-catalog/entity-tag-assignments", body=body, headers=headers) + return EntityTagAssignment.from_dict(res) + + def delete(self, entity_type: str, entity_name: str, tag_key: str): + """Deletes a tag assignment for an Unity Catalog entity by its key. + + To delete tags from Unity Catalog entities, you must own the entity or have the following privileges: + - **APPLY TAG** on the entity - **USE_SCHEMA** on the entity's parent schema - **USE_CATALOG** on the + entity's parent catalog + + To delete a governed tag from Unity Catalog entities, you must also have the **ASSIGN** or **MANAGE** + permission on the tag policy. See [Manage tag policy permissions]. + + [Manage tag policy permissions]: https://docs.databricks.com/aws/en/admin/tag-policies/manage-permissions + + :param entity_type: str + The type of the entity to which the tag is assigned. Allowed values are: catalogs, schemas, tables, + columns, volumes. + :param entity_name: str + The fully qualified name of the entity to which the tag is assigned + :param tag_key: str + Required. The key of the tag to delete + + + """ + + headers = { + "Accept": "application/json", + } + + self._api.do( + "DELETE", + f"/api/2.1/unity-catalog/entity-tag-assignments/{entity_type}/{entity_name}/tags/{tag_key}", + headers=headers, + ) + + def get(self, entity_type: str, entity_name: str, tag_key: str) -> EntityTagAssignment: + """Gets a tag assignment for an Unity Catalog entity by tag key. + + :param entity_type: str + The type of the entity to which the tag is assigned. Allowed values are: catalogs, schemas, tables, + columns, volumes. + :param entity_name: str + The fully qualified name of the entity to which the tag is assigned + :param tag_key: str + Required. The key of the tag + + :returns: :class:`EntityTagAssignment` + """ + + headers = { + "Accept": "application/json", + } + + res = self._api.do( + "GET", + f"/api/2.1/unity-catalog/entity-tag-assignments/{entity_type}/{entity_name}/tags/{tag_key}", + headers=headers, + ) + return EntityTagAssignment.from_dict(res) + + def list( + self, entity_type: str, entity_name: str, *, max_results: Optional[int] = None, page_token: Optional[str] = None + ) -> Iterator[EntityTagAssignment]: + """List tag assignments for an Unity Catalog entity + + :param entity_type: str + The type of the entity to which the tag is assigned. Allowed values are: catalogs, schemas, tables, + columns, volumes. + :param entity_name: str + The fully qualified name of the entity to which the tag is assigned + :param max_results: int (optional) + Optional. Maximum number of tag assignments to return in a single page + :param page_token: str (optional) + Optional. Pagination token to retrieve the next page of results + + :returns: Iterator over :class:`EntityTagAssignment` + """ + + query = {} + if max_results is not None: + query["max_results"] = max_results + 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.1/unity-catalog/entity-tag-assignments/{entity_type}/{entity_name}/tags", + query=query, + headers=headers, + ) + if "tag_assignments" in json: + for v in json["tag_assignments"]: + yield EntityTagAssignment.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, entity_type: str, entity_name: str, tag_key: str, tag_assignment: EntityTagAssignment, update_mask: str + ) -> EntityTagAssignment: + """Updates an existing tag assignment for an Unity Catalog entity. + + To update tags to Unity Catalog entities, you must own the entity or have the following privileges: - + **APPLY TAG** on the entity - **USE SCHEMA** on the entity's parent schema - **USE CATALOG** on the + entity's parent catalog + + To update a governed tag to Unity Catalog entities, you must also have the **ASSIGN** or **MANAGE** + permission on the tag policy. See [Manage tag policy permissions]. + + [Manage tag policy permissions]: https://docs.databricks.com/aws/en/admin/tag-policies/manage-permissions + + :param entity_type: str + The type of the entity to which the tag is assigned. Allowed values are: catalogs, schemas, tables, + columns, volumes. + :param entity_name: str + The fully qualified name of the entity to which the tag is assigned + :param tag_key: str + The key of the tag + :param tag_assignment: :class:`EntityTagAssignment` + :param update_mask: str + The field mask must be a single string, with multiple fields separated by commas (no spaces). The + field path is relative to the resource object, using a dot (`.`) to navigate sub-fields (e.g., + `author.given_name`). Specification of elements in sequence or map fields is not allowed, as only + the entire collection field can be specified. Field names must exactly match the resource field + names. + + A field mask of `*` indicates full replacement. It’s recommended to always explicitly list the + fields being updated and avoid using `*` wildcards, as it can lead to unintended results if the API + changes in the future. + + :returns: :class:`EntityTagAssignment` + """ + body = tag_assignment.as_dict() + query = {} + if update_mask is not None: + query["update_mask"] = update_mask + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do( + "PATCH", + f"/api/2.1/unity-catalog/entity-tag-assignments/{entity_type}/{entity_name}/tags/{tag_key}", + query=query, + body=body, + headers=headers, + ) + return EntityTagAssignment.from_dict(res) + + class ExternalLineageAPI: """External Lineage APIs enable defining and managing lineage relationships between Databricks objects and external systems. These APIs allow users to capture data flows connecting Databricks tables, models, and @@ -13571,6 +14132,112 @@ def list_quotas( query["page_token"] = json["next_page_token"] +class RfaAPI: + """Request for Access enables customers to request access to and manage access request destinations for Unity + Catalog securables. + + These APIs provide a standardized way to update, get, and request to access request destinations. + Fine-grained authorization ensures that only users with appropriate permissions can manage access request + destinations.""" + + def __init__(self, api_client): + self._api = api_client + + def batch_create_access_requests( + self, *, requests: Optional[List[CreateAccessRequest]] = None + ) -> BatchCreateAccessRequestsResponse: + """Creates access requests for Unity Catalog permissions for a specified principal on a securable object. + This Batch API can take in multiple principals, securable objects, and permissions as the input and + returns the access request destinations for each. Principals must be unique across the API call. + + The supported securable types are: "metastore", "catalog", "schema", "table", "external_location", + "connection", "credential", "function", "registered_model", and "volume". + + :param requests: List[:class:`CreateAccessRequest`] (optional) + A list of individual access requests, where each request corresponds to a set of permissions being + requested on a list of securables for a specified principal. + + At most 30 requests per API call. + + :returns: :class:`BatchCreateAccessRequestsResponse` + """ + body = {} + if requests is not None: + body["requests"] = [v.as_dict() for v in requests] + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do("POST", "/api/3.0/rfa/requests", body=body, headers=headers) + return BatchCreateAccessRequestsResponse.from_dict(res) + + def get_access_request_destinations(self, securable_type: str, full_name: str) -> AccessRequestDestinations: + """Gets an array of access request destinations for the specified securable. Any caller can see URL + destinations or the destinations on the metastore. Otherwise, only those with **BROWSE** permissions + on the securable can see destinations. + + The supported securable types are: "metastore", "catalog", "schema", "table", "external_location", + "connection", "credential", "function", "registered_model", and "volume". + + :param securable_type: str + The type of the securable. + :param full_name: str + The full name of the securable. + + :returns: :class:`AccessRequestDestinations` + """ + + headers = { + "Accept": "application/json", + } + + res = self._api.do("GET", f"/api/3.0/rfa/destinations/{securable_type}/{full_name}", headers=headers) + return AccessRequestDestinations.from_dict(res) + + def update_access_request_destinations( + self, access_request_destinations: AccessRequestDestinations, update_mask: str + ) -> AccessRequestDestinations: + """Updates the access request destinations for the given securable. The caller must be a metastore admin, + the owner of the securable, or a user that has the **MANAGE** privilege on the securable in order to + assign destinations. Destinations cannot be updated for securables underneath schemas (tables, + volumes, functions, and models). For these securable types, destinations are inherited from the parent + securable. A maximum of 5 emails and 5 external notification destinations (Slack, Microsoft Teams, and + Generic Webhook destinations) can be assigned to a securable. If a URL destination is assigned, no + other destinations can be set. + + The supported securable types are: "metastore", "catalog", "schema", "table", "external_location", + "connection", "credential", "function", "registered_model", and "volume". + + :param access_request_destinations: :class:`AccessRequestDestinations` + The access request destinations to assign to the securable. For each destination, a + **destination_id** and **destination_type** must be defined. + :param update_mask: str + The field mask must be a single string, with multiple fields separated by commas (no spaces). The + field path is relative to the resource object, using a dot (`.`) to navigate sub-fields (e.g., + `author.given_name`). Specification of elements in sequence or map fields is not allowed, as only + the entire collection field can be specified. Field names must exactly match the resource field + names. + + A field mask of `*` indicates full replacement. It’s recommended to always explicitly list the + fields being updated and avoid using `*` wildcards, as it can lead to unintended results if the API + changes in the future. + + :returns: :class:`AccessRequestDestinations` + """ + body = access_request_destinations.as_dict() + query = {} + if update_mask is not None: + query["update_mask"] = update_mask + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do("PATCH", "/api/3.0/rfa/destinations", query=query, body=body, headers=headers) + return AccessRequestDestinations.from_dict(res) + + class SchemasAPI: """A schema (also called a database) is the second layer of Unity Catalog’s three-level namespace. A schema organizes tables, views and functions. To access (or list) a table or view in a schema, users must have diff --git a/databricks/sdk/service/cleanrooms.py b/databricks/sdk/service/cleanrooms.py index 678f5bf9a..3840c040c 100755 --- a/databricks/sdk/service/cleanrooms.py +++ b/databricks/sdk/service/cleanrooms.py @@ -45,7 +45,7 @@ class CleanRoom: using the separate CreateCleanRoomOutputCatalog API.""" owner: Optional[str] = None - """This is Databricks username of the owner of the local clean room securable for permission + """This is the Databricks username of the owner of the local clean room securable for permission management.""" remote_detailed_info: Optional[CleanRoomRemoteDetail] = None @@ -358,7 +358,7 @@ class CleanRoomAssetNotebook: """All existing approvals or rejections""" runner_collaborator_aliases: Optional[List[str]] = None - """collaborators that can run the notebook""" + """Aliases of collaborators that can run the notebook.""" def as_dict(self) -> dict: """Serializes the CleanRoomAssetNotebook into a dictionary suitable for use as a JSON request body.""" @@ -643,7 +643,7 @@ class CleanRoomCollaborator: It is not restricted to these values and could change in the future""" global_metastore_id: Optional[str] = None - """The global Unity Catalog metastore id of the collaborator. The identifier is of format + """The global Unity Catalog metastore ID of the collaborator. The identifier is of format cloud:region:metastore-uuid.""" invite_recipient_email: Optional[str] = None diff --git a/databricks/sdk/service/dashboards.py b/databricks/sdk/service/dashboards.py index 9917705da..3e13d2e4b 100755 --- a/databricks/sdk/service/dashboards.py +++ b/databricks/sdk/service/dashboards.py @@ -413,6 +413,14 @@ def from_dict(cls, d: Dict[str, Any]) -> GenieConversationSummary: ) +class GenieFeedbackRating(Enum): + """Feedback rating for Genie messages""" + + NEGATIVE = "NEGATIVE" + NONE = "NONE" + POSITIVE = "POSITIVE" + + @dataclass class GenieGetMessageQueryResultResponse: statement_response: Optional[sql.StatementResponse] = None @@ -439,6 +447,38 @@ def from_dict(cls, d: Dict[str, Any]) -> GenieGetMessageQueryResultResponse: return cls(statement_response=_from_dict(d, "statement_response", sql.StatementResponse)) +@dataclass +class GenieListConversationMessagesResponse: + messages: Optional[List[GenieMessage]] = None + """List of messages in the conversation.""" + + next_page_token: Optional[str] = None + """The token to use for retrieving the next page of results.""" + + def as_dict(self) -> dict: + """Serializes the GenieListConversationMessagesResponse into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.messages: + body["messages"] = [v.as_dict() for v in self.messages] + if self.next_page_token is not None: + body["next_page_token"] = self.next_page_token + return body + + def as_shallow_dict(self) -> dict: + """Serializes the GenieListConversationMessagesResponse into a shallow dictionary of its immediate attributes.""" + body = {} + if self.messages: + body["messages"] = self.messages + if self.next_page_token is not None: + body["next_page_token"] = self.next_page_token + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> GenieListConversationMessagesResponse: + """Deserializes the GenieListConversationMessagesResponse from a dictionary.""" + return cls(messages=_repeated_dict(d, "messages", GenieMessage), next_page_token=d.get("next_page_token", None)) + + @dataclass class GenieListConversationsResponse: conversations: Optional[List[GenieConversationSummary]] = None @@ -1630,6 +1670,29 @@ def delete_conversation(self, space_id: str, conversation_id: str): self._api.do("DELETE", f"/api/2.0/genie/spaces/{space_id}/conversations/{conversation_id}", headers=headers) + def delete_conversation_message(self, space_id: str, conversation_id: str, message_id: str): + """Delete a conversation message. + + :param space_id: str + The ID associated with the Genie space where the message is located. + :param conversation_id: str + The ID associated with the conversation. + :param message_id: str + The ID associated with the message to delete. + + + """ + + headers = { + "Accept": "application/json", + } + + self._api.do( + "DELETE", + f"/api/2.0/genie/spaces/{space_id}/conversations/{conversation_id}/messages/{message_id}", + headers=headers, + ) + def execute_message_attachment_query( self, space_id: str, conversation_id: str, message_id: str, attachment_id: str ) -> GenieGetMessageQueryResultResponse: @@ -1662,7 +1725,8 @@ def execute_message_attachment_query( def execute_message_query( self, space_id: str, conversation_id: str, message_id: str ) -> GenieGetMessageQueryResultResponse: - """Execute the SQL query in the message. + """DEPRECATED: Use [Execute Message Attachment Query](:method:genie/executemessageattachmentquery) + instead. :param space_id: str Genie space ID @@ -1741,8 +1805,8 @@ def get_message_attachment_query_result( def get_message_query_result( self, space_id: str, conversation_id: str, message_id: str ) -> GenieGetMessageQueryResultResponse: - """Get the result of SQL query if the message has a query attachment. This is only available if a message - has a query attachment and the message status is `EXECUTING_QUERY`. + """DEPRECATED: Use [Get Message Attachment Query Result](:method:genie/getmessageattachmentqueryresult) + instead. :param space_id: str Genie space ID @@ -1768,8 +1832,8 @@ def get_message_query_result( def get_message_query_result_by_attachment( self, space_id: str, conversation_id: str, message_id: str, attachment_id: str ) -> GenieGetMessageQueryResultResponse: - """Get the result of SQL query if the message has a query attachment. This is only available if a message - has a query attachment and the message status is `EXECUTING_QUERY` OR `COMPLETED`. + """DEPRECATED: Use [Get Message Attachment Query Result](:method:genie/getmessageattachmentqueryresult) + instead. :param space_id: str Genie space ID @@ -1810,13 +1874,55 @@ def get_space(self, space_id: str) -> GenieSpace: res = self._api.do("GET", f"/api/2.0/genie/spaces/{space_id}", headers=headers) return GenieSpace.from_dict(res) + def list_conversation_messages( + self, space_id: str, conversation_id: str, *, page_size: Optional[int] = None, page_token: Optional[str] = None + ) -> GenieListConversationMessagesResponse: + """List messages in a conversation + + :param space_id: str + The ID associated with the Genie space where the conversation is located + :param conversation_id: str + The ID of the conversation to list messages from + :param page_size: int (optional) + Maximum number of messages to return per page + :param page_token: str (optional) + Token to get the next page of results + + :returns: :class:`GenieListConversationMessagesResponse` + """ + + 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", + } + + res = self._api.do( + "GET", + f"/api/2.0/genie/spaces/{space_id}/conversations/{conversation_id}/messages", + query=query, + headers=headers, + ) + return GenieListConversationMessagesResponse.from_dict(res) + def list_conversations( - self, space_id: str, *, page_size: Optional[int] = None, page_token: Optional[str] = None + self, + space_id: str, + *, + include_all: Optional[bool] = None, + page_size: Optional[int] = None, + page_token: Optional[str] = None, ) -> GenieListConversationsResponse: """Get a list of conversations in a Genie Space. :param space_id: str The ID of the Genie space to retrieve conversations from. + :param include_all: bool (optional) + Include all conversations in the space across all users. Requires "Can Manage" permission on the + space. :param page_size: int (optional) Maximum number of conversations to return per page :param page_token: str (optional) @@ -1826,6 +1932,8 @@ def list_conversations( """ query = {} + if include_all is not None: + query["include_all"] = include_all if page_size is not None: query["page_size"] = page_size if page_token is not None: @@ -1862,6 +1970,47 @@ def list_spaces( res = self._api.do("GET", "/api/2.0/genie/spaces", query=query, headers=headers) return GenieListSpacesResponse.from_dict(res) + def send_message_feedback( + self, + space_id: str, + conversation_id: str, + message_id: str, + feedback_rating: GenieFeedbackRating, + *, + feedback_text: Optional[str] = None, + ): + """Send feedback for a message. + + :param space_id: str + The ID associated with the Genie space where the message is located. + :param conversation_id: str + The ID associated with the conversation. + :param message_id: str + The ID associated with the message to provide feedback for. + :param feedback_rating: :class:`GenieFeedbackRating` + The rating (POSITIVE, NEGATIVE, or NONE). + :param feedback_text: str (optional) + Optional text feedback that will be stored as a comment. + + + """ + body = {} + if feedback_rating is not None: + body["feedback_rating"] = feedback_rating.value + if feedback_text is not None: + body["feedback_text"] = feedback_text + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + self._api.do( + "POST", + f"/api/2.0/genie/spaces/{space_id}/conversations/{conversation_id}/messages/{message_id}/feedback", + body=body, + headers=headers, + ) + def start_conversation(self, space_id: str, content: str) -> Wait[GenieMessage]: """Start a new conversation. diff --git a/databricks/sdk/service/jobs.py b/databricks/sdk/service/jobs.py index d8f4e3122..96ee972bf 100755 --- a/databricks/sdk/service/jobs.py +++ b/databricks/sdk/service/jobs.py @@ -42,6 +42,9 @@ class BaseJob: Jobs UI in the job details page and Jobs API using `budget_policy_id` 3. Inferred default based on accessible budget policies of the run_as identity on job creation or modification.""" + effective_usage_policy_id: Optional[str] = None + """The id of the usage policy used by this job for cost attribution purposes.""" + has_more: Optional[bool] = None """Indicates if the job has more array properties (`tasks`, `job_clusters`) that are not shown. They can be accessed via :method:jobs/get endpoint. It is only relevant for API 2.2 @@ -66,6 +69,8 @@ def as_dict(self) -> dict: body["creator_user_name"] = self.creator_user_name if self.effective_budget_policy_id is not None: body["effective_budget_policy_id"] = self.effective_budget_policy_id + if self.effective_usage_policy_id is not None: + body["effective_usage_policy_id"] = self.effective_usage_policy_id if self.has_more is not None: body["has_more"] = self.has_more if self.job_id is not None: @@ -85,6 +90,8 @@ def as_shallow_dict(self) -> dict: body["creator_user_name"] = self.creator_user_name if self.effective_budget_policy_id is not None: body["effective_budget_policy_id"] = self.effective_budget_policy_id + if self.effective_usage_policy_id is not None: + body["effective_usage_policy_id"] = self.effective_usage_policy_id if self.has_more is not None: body["has_more"] = self.has_more if self.job_id is not None: @@ -102,6 +109,7 @@ def from_dict(cls, d: Dict[str, Any]) -> BaseJob: created_time=d.get("created_time", None), creator_user_name=d.get("creator_user_name", None), effective_budget_policy_id=d.get("effective_budget_policy_id", None), + effective_usage_policy_id=d.get("effective_usage_policy_id", None), has_more=d.get("has_more", None), job_id=d.get("job_id", None), settings=_from_dict(d, "settings", JobSettings), @@ -147,6 +155,9 @@ class BaseRun: `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid scaling and optimized cluster performance.""" + effective_usage_policy_id: Optional[str] = None + """The id of the usage policy used by this run for cost attribution purposes.""" + end_time: Optional[int] = None """The time at which this run ended in epoch milliseconds (milliseconds since 1/1/1970 UTC). This field is set to 0 if the job is still running.""" @@ -267,6 +278,8 @@ def as_dict(self) -> dict: body["description"] = self.description if self.effective_performance_target is not None: body["effective_performance_target"] = self.effective_performance_target.value + if self.effective_usage_policy_id is not None: + body["effective_usage_policy_id"] = self.effective_usage_policy_id if self.end_time is not None: body["end_time"] = self.end_time if self.execution_duration is not None: @@ -338,6 +351,8 @@ def as_shallow_dict(self) -> dict: body["description"] = self.description if self.effective_performance_target is not None: body["effective_performance_target"] = self.effective_performance_target + if self.effective_usage_policy_id is not None: + body["effective_usage_policy_id"] = self.effective_usage_policy_id if self.end_time is not None: body["end_time"] = self.end_time if self.execution_duration is not None: @@ -403,6 +418,7 @@ def from_dict(cls, d: Dict[str, Any]) -> BaseRun: creator_user_name=d.get("creator_user_name", None), description=d.get("description", None), effective_performance_target=_enum(d, "effective_performance_target", PerformanceTarget), + effective_usage_policy_id=d.get("effective_usage_policy_id", None), end_time=d.get("end_time", None), execution_duration=d.get("execution_duration", None), git_source=_from_dict(d, "git_source", GitSource), @@ -2212,6 +2228,9 @@ class Job: Jobs UI in the job details page and Jobs API using `budget_policy_id` 3. Inferred default based on accessible budget policies of the run_as identity on job creation or modification.""" + effective_usage_policy_id: Optional[str] = None + """The id of the usage policy used by this job for cost attribution purposes.""" + has_more: Optional[bool] = None """Indicates if the job has more array properties (`tasks`, `job_clusters`) that are not shown. They can be accessed via :method:jobs/get endpoint. It is only relevant for API 2.2 @@ -2248,6 +2267,8 @@ def as_dict(self) -> dict: body["creator_user_name"] = self.creator_user_name if self.effective_budget_policy_id is not None: body["effective_budget_policy_id"] = self.effective_budget_policy_id + if self.effective_usage_policy_id is not None: + body["effective_usage_policy_id"] = self.effective_usage_policy_id if self.has_more is not None: body["has_more"] = self.has_more if self.job_id is not None: @@ -2271,6 +2292,8 @@ def as_shallow_dict(self) -> dict: body["creator_user_name"] = self.creator_user_name if self.effective_budget_policy_id is not None: body["effective_budget_policy_id"] = self.effective_budget_policy_id + if self.effective_usage_policy_id is not None: + body["effective_usage_policy_id"] = self.effective_usage_policy_id if self.has_more is not None: body["has_more"] = self.has_more if self.job_id is not None: @@ -2292,6 +2315,7 @@ def from_dict(cls, d: Dict[str, Any]) -> Job: created_time=d.get("created_time", None), creator_user_name=d.get("creator_user_name", None), effective_budget_policy_id=d.get("effective_budget_policy_id", None), + effective_usage_policy_id=d.get("effective_usage_policy_id", None), has_more=d.get("has_more", None), job_id=d.get("job_id", None), next_page_token=d.get("next_page_token", None), @@ -3039,8 +3063,8 @@ class JobSettings: usage_policy_id: Optional[str] = None """The id of the user specified usage policy to use for this job. If not specified, a default usage - policy may be applied when creating or modifying the job. See `effective_budget_policy_id` for - the budget policy used by this workload.""" + policy may be applied when creating or modifying the job. See `effective_usage_policy_id` for + the usage policy used by this workload.""" webhook_notifications: Optional[WebhookNotifications] = None """A collection of system notification IDs to notify when runs of this job begin or complete.""" @@ -4509,6 +4533,9 @@ class Run: `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid scaling and optimized cluster performance.""" + effective_usage_policy_id: Optional[str] = None + """The id of the usage policy used by this run for cost attribution purposes.""" + end_time: Optional[int] = None """The time at which this run ended in epoch milliseconds (milliseconds since 1/1/1970 UTC). This field is set to 0 if the job is still running.""" @@ -4635,6 +4662,8 @@ def as_dict(self) -> dict: body["description"] = self.description if self.effective_performance_target is not None: body["effective_performance_target"] = self.effective_performance_target.value + if self.effective_usage_policy_id is not None: + body["effective_usage_policy_id"] = self.effective_usage_policy_id if self.end_time is not None: body["end_time"] = self.end_time if self.execution_duration is not None: @@ -4710,6 +4739,8 @@ def as_shallow_dict(self) -> dict: body["description"] = self.description if self.effective_performance_target is not None: body["effective_performance_target"] = self.effective_performance_target + if self.effective_usage_policy_id is not None: + body["effective_usage_policy_id"] = self.effective_usage_policy_id if self.end_time is not None: body["end_time"] = self.end_time if self.execution_duration is not None: @@ -4779,6 +4810,7 @@ def from_dict(cls, d: Dict[str, Any]) -> Run: creator_user_name=d.get("creator_user_name", None), description=d.get("description", None), effective_performance_target=_enum(d, "effective_performance_target", PerformanceTarget), + effective_usage_policy_id=d.get("effective_usage_policy_id", None), end_time=d.get("end_time", None), execution_duration=d.get("execution_duration", None), git_source=_from_dict(d, "git_source", GitSource), @@ -8546,8 +8578,8 @@ def create( `runNow`. :param usage_policy_id: str (optional) The id of the user specified usage policy to use for this job. If not specified, a default usage - policy may be applied when creating or modifying the job. See `effective_budget_policy_id` for the - budget policy used by this workload. + policy may be applied when creating or modifying the job. See `effective_usage_policy_id` for the + usage policy used by this workload. :param webhook_notifications: :class:`WebhookNotifications` (optional) A collection of system notification IDs to notify when runs of this job begin or complete. diff --git a/databricks/sdk/service/serving.py b/databricks/sdk/service/serving.py index dcd951f02..ff7ce801a 100755 --- a/databricks/sdk/service/serving.py +++ b/databricks/sdk/service/serving.py @@ -314,6 +314,9 @@ class AiGatewayRateLimit: """Principal field for a user, user group, or service principal to apply rate limiting to. Accepts a user email, group name, or service principal application ID.""" + tokens: Optional[int] = None + """Used to specify how many tokens are allowed for a key within the renewal_period.""" + def as_dict(self) -> dict: """Serializes the AiGatewayRateLimit into a dictionary suitable for use as a JSON request body.""" body = {} @@ -325,6 +328,8 @@ def as_dict(self) -> dict: body["principal"] = self.principal if self.renewal_period is not None: body["renewal_period"] = self.renewal_period.value + if self.tokens is not None: + body["tokens"] = self.tokens return body def as_shallow_dict(self) -> dict: @@ -338,6 +343,8 @@ def as_shallow_dict(self) -> dict: body["principal"] = self.principal if self.renewal_period is not None: body["renewal_period"] = self.renewal_period + if self.tokens is not None: + body["tokens"] = self.tokens return body @classmethod @@ -348,6 +355,7 @@ def from_dict(cls, d: Dict[str, Any]) -> AiGatewayRateLimit: key=_enum(d, "key", AiGatewayRateLimitKey), principal=d.get("principal", None), renewal_period=_enum(d, "renewal_period", AiGatewayRateLimitRenewalPeriod), + tokens=d.get("tokens", None), ) @@ -3374,6 +3382,9 @@ class ServingEndpoint: task: Optional[str] = None """The task type of the serving endpoint.""" + usage_policy_id: Optional[str] = None + """The usage policy associated with serving endpoint.""" + def as_dict(self) -> dict: """Serializes the ServingEndpoint into a dictionary suitable for use as a JSON request body.""" body = {} @@ -3401,6 +3412,8 @@ def as_dict(self) -> dict: body["tags"] = [v.as_dict() for v in self.tags] if self.task is not None: body["task"] = self.task + if self.usage_policy_id is not None: + body["usage_policy_id"] = self.usage_policy_id return body def as_shallow_dict(self) -> dict: @@ -3430,6 +3443,8 @@ def as_shallow_dict(self) -> dict: body["tags"] = self.tags if self.task is not None: body["task"] = self.task + if self.usage_policy_id is not None: + body["usage_policy_id"] = self.usage_policy_id return body @classmethod @@ -3448,6 +3463,7 @@ def from_dict(cls, d: Dict[str, Any]) -> ServingEndpoint: state=_from_dict(d, "state", EndpointState), tags=_repeated_dict(d, "tags", EndpointTag), task=d.get("task", None), + usage_policy_id=d.get("usage_policy_id", None), ) diff --git a/databricks/sdk/service/settingsv2.py b/databricks/sdk/service/settingsv2.py new file mode 100755 index 000000000..10e609f3b --- /dev/null +++ b/databricks/sdk/service/settingsv2.py @@ -0,0 +1,937 @@ +# Code generated from OpenAPI specs by Databricks SDK Generator. DO NOT EDIT. + +from __future__ import annotations + +import logging +from dataclasses import dataclass +from enum import Enum +from typing import Any, Dict, Iterator, List, Optional + +from ._internal import _enum, _from_dict, _repeated_dict + +_LOG = logging.getLogger("databricks.sdk") + + +# all definitions in this file are in alphabetical order + + +@dataclass +class AibiDashboardEmbeddingAccessPolicy: + access_policy_type: AibiDashboardEmbeddingAccessPolicyAccessPolicyType + + def as_dict(self) -> dict: + """Serializes the AibiDashboardEmbeddingAccessPolicy into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.access_policy_type is not None: + body["access_policy_type"] = self.access_policy_type.value + return body + + def as_shallow_dict(self) -> dict: + """Serializes the AibiDashboardEmbeddingAccessPolicy into a shallow dictionary of its immediate attributes.""" + body = {} + if self.access_policy_type is not None: + body["access_policy_type"] = self.access_policy_type + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> AibiDashboardEmbeddingAccessPolicy: + """Deserializes the AibiDashboardEmbeddingAccessPolicy from a dictionary.""" + return cls( + access_policy_type=_enum(d, "access_policy_type", AibiDashboardEmbeddingAccessPolicyAccessPolicyType) + ) + + +class AibiDashboardEmbeddingAccessPolicyAccessPolicyType(Enum): + + ALLOW_ALL_DOMAINS = "ALLOW_ALL_DOMAINS" + ALLOW_APPROVED_DOMAINS = "ALLOW_APPROVED_DOMAINS" + DENY_ALL_DOMAINS = "DENY_ALL_DOMAINS" + + +@dataclass +class AibiDashboardEmbeddingApprovedDomains: + approved_domains: Optional[List[str]] = None + + def as_dict(self) -> dict: + """Serializes the AibiDashboardEmbeddingApprovedDomains into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.approved_domains: + body["approved_domains"] = [v for v in self.approved_domains] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the AibiDashboardEmbeddingApprovedDomains into a shallow dictionary of its immediate attributes.""" + body = {} + if self.approved_domains: + body["approved_domains"] = self.approved_domains + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> AibiDashboardEmbeddingApprovedDomains: + """Deserializes the AibiDashboardEmbeddingApprovedDomains from a dictionary.""" + return cls(approved_domains=d.get("approved_domains", None)) + + +@dataclass +class BooleanMessage: + value: Optional[bool] = None + + def as_dict(self) -> dict: + """Serializes the BooleanMessage into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.value is not None: + body["value"] = self.value + return body + + def as_shallow_dict(self) -> dict: + """Serializes the BooleanMessage into a shallow dictionary of its immediate attributes.""" + body = {} + if self.value is not None: + body["value"] = self.value + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> BooleanMessage: + """Deserializes the BooleanMessage from a dictionary.""" + return cls(value=d.get("value", None)) + + +@dataclass +class ClusterAutoRestartMessage: + can_toggle: Optional[bool] = None + + enabled: Optional[bool] = None + + enablement_details: Optional[ClusterAutoRestartMessageEnablementDetails] = None + + maintenance_window: Optional[ClusterAutoRestartMessageMaintenanceWindow] = None + + restart_even_if_no_updates_available: Optional[bool] = None + + def as_dict(self) -> dict: + """Serializes the ClusterAutoRestartMessage into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.can_toggle is not None: + body["can_toggle"] = self.can_toggle + if self.enabled is not None: + body["enabled"] = self.enabled + if self.enablement_details: + body["enablement_details"] = self.enablement_details.as_dict() + if self.maintenance_window: + body["maintenance_window"] = self.maintenance_window.as_dict() + if self.restart_even_if_no_updates_available is not None: + body["restart_even_if_no_updates_available"] = self.restart_even_if_no_updates_available + return body + + def as_shallow_dict(self) -> dict: + """Serializes the ClusterAutoRestartMessage into a shallow dictionary of its immediate attributes.""" + body = {} + if self.can_toggle is not None: + body["can_toggle"] = self.can_toggle + if self.enabled is not None: + body["enabled"] = self.enabled + if self.enablement_details: + body["enablement_details"] = self.enablement_details + if self.maintenance_window: + body["maintenance_window"] = self.maintenance_window + if self.restart_even_if_no_updates_available is not None: + body["restart_even_if_no_updates_available"] = self.restart_even_if_no_updates_available + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> ClusterAutoRestartMessage: + """Deserializes the ClusterAutoRestartMessage from a dictionary.""" + return cls( + can_toggle=d.get("can_toggle", None), + enabled=d.get("enabled", None), + enablement_details=_from_dict(d, "enablement_details", ClusterAutoRestartMessageEnablementDetails), + maintenance_window=_from_dict(d, "maintenance_window", ClusterAutoRestartMessageMaintenanceWindow), + restart_even_if_no_updates_available=d.get("restart_even_if_no_updates_available", None), + ) + + +@dataclass +class ClusterAutoRestartMessageEnablementDetails: + """Contains an information about the enablement status judging (e.g. whether the enterprise tier is + enabled) This is only additional information that MUST NOT be used to decide whether the setting + is enabled or not. This is intended to use only for purposes like showing an error message to + the customer with the additional details. For example, using these details we can check why + exactly the feature is disabled for this customer.""" + + forced_for_compliance_mode: Optional[bool] = None + """The feature is force enabled if compliance mode is active""" + + unavailable_for_disabled_entitlement: Optional[bool] = None + """The feature is unavailable if the corresponding entitlement disabled (see + getShieldEntitlementEnable)""" + + unavailable_for_non_enterprise_tier: Optional[bool] = None + """The feature is unavailable if the customer doesn't have enterprise tier""" + + def as_dict(self) -> dict: + """Serializes the ClusterAutoRestartMessageEnablementDetails into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.forced_for_compliance_mode is not None: + body["forced_for_compliance_mode"] = self.forced_for_compliance_mode + if self.unavailable_for_disabled_entitlement is not None: + body["unavailable_for_disabled_entitlement"] = self.unavailable_for_disabled_entitlement + if self.unavailable_for_non_enterprise_tier is not None: + body["unavailable_for_non_enterprise_tier"] = self.unavailable_for_non_enterprise_tier + return body + + def as_shallow_dict(self) -> dict: + """Serializes the ClusterAutoRestartMessageEnablementDetails into a shallow dictionary of its immediate attributes.""" + body = {} + if self.forced_for_compliance_mode is not None: + body["forced_for_compliance_mode"] = self.forced_for_compliance_mode + if self.unavailable_for_disabled_entitlement is not None: + body["unavailable_for_disabled_entitlement"] = self.unavailable_for_disabled_entitlement + if self.unavailable_for_non_enterprise_tier is not None: + body["unavailable_for_non_enterprise_tier"] = self.unavailable_for_non_enterprise_tier + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> ClusterAutoRestartMessageEnablementDetails: + """Deserializes the ClusterAutoRestartMessageEnablementDetails from a dictionary.""" + return cls( + forced_for_compliance_mode=d.get("forced_for_compliance_mode", None), + unavailable_for_disabled_entitlement=d.get("unavailable_for_disabled_entitlement", None), + unavailable_for_non_enterprise_tier=d.get("unavailable_for_non_enterprise_tier", None), + ) + + +@dataclass +class ClusterAutoRestartMessageMaintenanceWindow: + week_day_based_schedule: Optional[ClusterAutoRestartMessageMaintenanceWindowWeekDayBasedSchedule] = None + + def as_dict(self) -> dict: + """Serializes the ClusterAutoRestartMessageMaintenanceWindow into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.week_day_based_schedule: + body["week_day_based_schedule"] = self.week_day_based_schedule.as_dict() + return body + + def as_shallow_dict(self) -> dict: + """Serializes the ClusterAutoRestartMessageMaintenanceWindow into a shallow dictionary of its immediate attributes.""" + body = {} + if self.week_day_based_schedule: + body["week_day_based_schedule"] = self.week_day_based_schedule + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> ClusterAutoRestartMessageMaintenanceWindow: + """Deserializes the ClusterAutoRestartMessageMaintenanceWindow from a dictionary.""" + return cls( + week_day_based_schedule=_from_dict( + d, "week_day_based_schedule", ClusterAutoRestartMessageMaintenanceWindowWeekDayBasedSchedule + ) + ) + + +class ClusterAutoRestartMessageMaintenanceWindowDayOfWeek(Enum): + + FRIDAY = "FRIDAY" + MONDAY = "MONDAY" + SATURDAY = "SATURDAY" + SUNDAY = "SUNDAY" + THURSDAY = "THURSDAY" + TUESDAY = "TUESDAY" + WEDNESDAY = "WEDNESDAY" + + +@dataclass +class ClusterAutoRestartMessageMaintenanceWindowWeekDayBasedSchedule: + day_of_week: Optional[ClusterAutoRestartMessageMaintenanceWindowDayOfWeek] = None + + frequency: Optional[ClusterAutoRestartMessageMaintenanceWindowWeekDayFrequency] = None + + window_start_time: Optional[ClusterAutoRestartMessageMaintenanceWindowWindowStartTime] = None + + def as_dict(self) -> dict: + """Serializes the ClusterAutoRestartMessageMaintenanceWindowWeekDayBasedSchedule into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.day_of_week is not None: + body["day_of_week"] = self.day_of_week.value + if self.frequency is not None: + body["frequency"] = self.frequency.value + if self.window_start_time: + body["window_start_time"] = self.window_start_time.as_dict() + return body + + def as_shallow_dict(self) -> dict: + """Serializes the ClusterAutoRestartMessageMaintenanceWindowWeekDayBasedSchedule into a shallow dictionary of its immediate attributes.""" + body = {} + if self.day_of_week is not None: + body["day_of_week"] = self.day_of_week + if self.frequency is not None: + body["frequency"] = self.frequency + if self.window_start_time: + body["window_start_time"] = self.window_start_time + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> ClusterAutoRestartMessageMaintenanceWindowWeekDayBasedSchedule: + """Deserializes the ClusterAutoRestartMessageMaintenanceWindowWeekDayBasedSchedule from a dictionary.""" + return cls( + day_of_week=_enum(d, "day_of_week", ClusterAutoRestartMessageMaintenanceWindowDayOfWeek), + frequency=_enum(d, "frequency", ClusterAutoRestartMessageMaintenanceWindowWeekDayFrequency), + window_start_time=_from_dict( + d, "window_start_time", ClusterAutoRestartMessageMaintenanceWindowWindowStartTime + ), + ) + + +class ClusterAutoRestartMessageMaintenanceWindowWeekDayFrequency(Enum): + + EVERY_WEEK = "EVERY_WEEK" + FIRST_AND_THIRD_OF_MONTH = "FIRST_AND_THIRD_OF_MONTH" + FIRST_OF_MONTH = "FIRST_OF_MONTH" + FOURTH_OF_MONTH = "FOURTH_OF_MONTH" + SECOND_AND_FOURTH_OF_MONTH = "SECOND_AND_FOURTH_OF_MONTH" + SECOND_OF_MONTH = "SECOND_OF_MONTH" + THIRD_OF_MONTH = "THIRD_OF_MONTH" + + +@dataclass +class ClusterAutoRestartMessageMaintenanceWindowWindowStartTime: + hours: Optional[int] = None + + minutes: Optional[int] = None + + def as_dict(self) -> dict: + """Serializes the ClusterAutoRestartMessageMaintenanceWindowWindowStartTime into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.hours is not None: + body["hours"] = self.hours + if self.minutes is not None: + body["minutes"] = self.minutes + return body + + def as_shallow_dict(self) -> dict: + """Serializes the ClusterAutoRestartMessageMaintenanceWindowWindowStartTime into a shallow dictionary of its immediate attributes.""" + body = {} + if self.hours is not None: + body["hours"] = self.hours + if self.minutes is not None: + body["minutes"] = self.minutes + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> ClusterAutoRestartMessageMaintenanceWindowWindowStartTime: + """Deserializes the ClusterAutoRestartMessageMaintenanceWindowWindowStartTime from a dictionary.""" + return cls(hours=d.get("hours", None), minutes=d.get("minutes", None)) + + +@dataclass +class DefaultDataSecurityModeMessage: + """Changes the behaviour of Jobs service when creating job clusters. + + Before this setting is introduced, all workspaces with metastore attached had behaviour matching + SINGLE_USER setting. + + See: - go/defaultdatasecuritymode - go/defaultdatasecuritymode/setting - go/datasecuritymode""" + + status: DefaultDataSecurityModeMessageStatus + + def as_dict(self) -> dict: + """Serializes the DefaultDataSecurityModeMessage into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.status is not None: + body["status"] = self.status.value + return body + + def as_shallow_dict(self) -> dict: + """Serializes the DefaultDataSecurityModeMessage into a shallow dictionary of its immediate attributes.""" + body = {} + if self.status is not None: + body["status"] = self.status + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> DefaultDataSecurityModeMessage: + """Deserializes the DefaultDataSecurityModeMessage from a dictionary.""" + return cls(status=_enum(d, "status", DefaultDataSecurityModeMessageStatus)) + + +class DefaultDataSecurityModeMessageStatus(Enum): + + NOT_SET = "NOT_SET" + SINGLE_USER = "SINGLE_USER" + USER_ISOLATION = "USER_ISOLATION" + + +@dataclass +class IntegerMessage: + value: Optional[int] = None + + def as_dict(self) -> dict: + """Serializes the IntegerMessage into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.value is not None: + body["value"] = self.value + return body + + def as_shallow_dict(self) -> dict: + """Serializes the IntegerMessage into a shallow dictionary of its immediate attributes.""" + body = {} + if self.value is not None: + body["value"] = self.value + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> IntegerMessage: + """Deserializes the IntegerMessage from a dictionary.""" + return cls(value=d.get("value", None)) + + +@dataclass +class ListAccountSettingsMetadataResponse: + next_page_token: Optional[str] = None + """A token that can be sent as `page_token` to retrieve the next page. If this field is omitted, + there are no subsequent pages.""" + + settings_metadata: Optional[List[SettingsMetadata]] = None + """List of all settings available via public APIs and their metadata""" + + def as_dict(self) -> dict: + """Serializes the ListAccountSettingsMetadataResponse 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.settings_metadata: + body["settings_metadata"] = [v.as_dict() for v in self.settings_metadata] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the ListAccountSettingsMetadataResponse 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.settings_metadata: + body["settings_metadata"] = self.settings_metadata + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> ListAccountSettingsMetadataResponse: + """Deserializes the ListAccountSettingsMetadataResponse from a dictionary.""" + return cls( + next_page_token=d.get("next_page_token", None), + settings_metadata=_repeated_dict(d, "settings_metadata", SettingsMetadata), + ) + + +@dataclass +class ListWorkspaceSettingsMetadataResponse: + next_page_token: Optional[str] = None + """A token that can be sent as `page_token` to retrieve the next page. If this field is omitted, + there are no subsequent pages.""" + + settings_metadata: Optional[List[SettingsMetadata]] = None + """List of all settings available via public APIs and their metadata""" + + def as_dict(self) -> dict: + """Serializes the ListWorkspaceSettingsMetadataResponse 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.settings_metadata: + body["settings_metadata"] = [v.as_dict() for v in self.settings_metadata] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the ListWorkspaceSettingsMetadataResponse 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.settings_metadata: + body["settings_metadata"] = self.settings_metadata + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> ListWorkspaceSettingsMetadataResponse: + """Deserializes the ListWorkspaceSettingsMetadataResponse from a dictionary.""" + return cls( + next_page_token=d.get("next_page_token", None), + settings_metadata=_repeated_dict(d, "settings_metadata", SettingsMetadata), + ) + + +@dataclass +class PersonalComputeMessage: + value: Optional[PersonalComputeMessagePersonalComputeMessageEnum] = None + + def as_dict(self) -> dict: + """Serializes the PersonalComputeMessage into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.value is not None: + body["value"] = self.value.value + return body + + def as_shallow_dict(self) -> dict: + """Serializes the PersonalComputeMessage into a shallow dictionary of its immediate attributes.""" + body = {} + if self.value is not None: + body["value"] = self.value + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> PersonalComputeMessage: + """Deserializes the PersonalComputeMessage from a dictionary.""" + return cls(value=_enum(d, "value", PersonalComputeMessagePersonalComputeMessageEnum)) + + +class PersonalComputeMessagePersonalComputeMessageEnum(Enum): + """ON: Grants all users in all workspaces access to the Personal Compute default policy, allowing + all users to create single-machine compute resources. DELEGATE: Moves access control for the + Personal Compute default policy to individual workspaces and requires a workspace’s users or + groups to be added to the ACLs of that workspace’s Personal Compute default policy before they + will be able to create compute resources through that policy.""" + + DELEGATE = "DELEGATE" + ON = "ON" + + +@dataclass +class RestrictWorkspaceAdminsMessage: + status: RestrictWorkspaceAdminsMessageStatus + + def as_dict(self) -> dict: + """Serializes the RestrictWorkspaceAdminsMessage into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.status is not None: + body["status"] = self.status.value + return body + + def as_shallow_dict(self) -> dict: + """Serializes the RestrictWorkspaceAdminsMessage into a shallow dictionary of its immediate attributes.""" + body = {} + if self.status is not None: + body["status"] = self.status + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> RestrictWorkspaceAdminsMessage: + """Deserializes the RestrictWorkspaceAdminsMessage from a dictionary.""" + return cls(status=_enum(d, "status", RestrictWorkspaceAdminsMessageStatus)) + + +class RestrictWorkspaceAdminsMessageStatus(Enum): + + ALLOW_ALL = "ALLOW_ALL" + RESTRICT_TOKENS_AND_JOB_RUN_AS = "RESTRICT_TOKENS_AND_JOB_RUN_AS" + + +@dataclass +class Setting: + aibi_dashboard_embedding_access_policy: Optional[AibiDashboardEmbeddingAccessPolicy] = None + + aibi_dashboard_embedding_approved_domains: Optional[AibiDashboardEmbeddingApprovedDomains] = None + + automatic_cluster_update_workspace: Optional[ClusterAutoRestartMessage] = None + """todo: Mark these Public after onboarded to DSL""" + + boolean_val: Optional[BooleanMessage] = None + + default_data_security_mode: Optional[DefaultDataSecurityModeMessage] = None + + effective_aibi_dashboard_embedding_access_policy: Optional[AibiDashboardEmbeddingAccessPolicy] = None + + effective_aibi_dashboard_embedding_approved_domains: Optional[AibiDashboardEmbeddingApprovedDomains] = None + + effective_automatic_cluster_update_workspace: Optional[ClusterAutoRestartMessage] = None + + effective_boolean_val: Optional[BooleanMessage] = None + + effective_default_data_security_mode: Optional[DefaultDataSecurityModeMessage] = None + + effective_integer_val: Optional[IntegerMessage] = None + + effective_personal_compute: Optional[PersonalComputeMessage] = None + + effective_restrict_workspace_admins: Optional[RestrictWorkspaceAdminsMessage] = None + + effective_string_val: Optional[StringMessage] = None + + integer_val: Optional[IntegerMessage] = None + + name: Optional[str] = None + """Name of the setting.""" + + personal_compute: Optional[PersonalComputeMessage] = None + + restrict_workspace_admins: Optional[RestrictWorkspaceAdminsMessage] = None + + string_val: Optional[StringMessage] = None + + def as_dict(self) -> dict: + """Serializes the Setting into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.aibi_dashboard_embedding_access_policy: + body["aibi_dashboard_embedding_access_policy"] = self.aibi_dashboard_embedding_access_policy.as_dict() + if self.aibi_dashboard_embedding_approved_domains: + body["aibi_dashboard_embedding_approved_domains"] = self.aibi_dashboard_embedding_approved_domains.as_dict() + if self.automatic_cluster_update_workspace: + body["automatic_cluster_update_workspace"] = self.automatic_cluster_update_workspace.as_dict() + if self.boolean_val: + body["boolean_val"] = self.boolean_val.as_dict() + if self.default_data_security_mode: + body["default_data_security_mode"] = self.default_data_security_mode.as_dict() + if self.effective_aibi_dashboard_embedding_access_policy: + body["effective_aibi_dashboard_embedding_access_policy"] = ( + self.effective_aibi_dashboard_embedding_access_policy.as_dict() + ) + if self.effective_aibi_dashboard_embedding_approved_domains: + body["effective_aibi_dashboard_embedding_approved_domains"] = ( + self.effective_aibi_dashboard_embedding_approved_domains.as_dict() + ) + if self.effective_automatic_cluster_update_workspace: + body["effective_automatic_cluster_update_workspace"] = ( + self.effective_automatic_cluster_update_workspace.as_dict() + ) + if self.effective_boolean_val: + body["effective_boolean_val"] = self.effective_boolean_val.as_dict() + if self.effective_default_data_security_mode: + body["effective_default_data_security_mode"] = self.effective_default_data_security_mode.as_dict() + if self.effective_integer_val: + body["effective_integer_val"] = self.effective_integer_val.as_dict() + if self.effective_personal_compute: + body["effective_personal_compute"] = self.effective_personal_compute.as_dict() + if self.effective_restrict_workspace_admins: + body["effective_restrict_workspace_admins"] = self.effective_restrict_workspace_admins.as_dict() + if self.effective_string_val: + body["effective_string_val"] = self.effective_string_val.as_dict() + if self.integer_val: + body["integer_val"] = self.integer_val.as_dict() + if self.name is not None: + body["name"] = self.name + if self.personal_compute: + body["personal_compute"] = self.personal_compute.as_dict() + if self.restrict_workspace_admins: + body["restrict_workspace_admins"] = self.restrict_workspace_admins.as_dict() + if self.string_val: + body["string_val"] = self.string_val.as_dict() + return body + + def as_shallow_dict(self) -> dict: + """Serializes the Setting into a shallow dictionary of its immediate attributes.""" + body = {} + if self.aibi_dashboard_embedding_access_policy: + body["aibi_dashboard_embedding_access_policy"] = self.aibi_dashboard_embedding_access_policy + if self.aibi_dashboard_embedding_approved_domains: + body["aibi_dashboard_embedding_approved_domains"] = self.aibi_dashboard_embedding_approved_domains + if self.automatic_cluster_update_workspace: + body["automatic_cluster_update_workspace"] = self.automatic_cluster_update_workspace + if self.boolean_val: + body["boolean_val"] = self.boolean_val + if self.default_data_security_mode: + body["default_data_security_mode"] = self.default_data_security_mode + if self.effective_aibi_dashboard_embedding_access_policy: + body["effective_aibi_dashboard_embedding_access_policy"] = ( + self.effective_aibi_dashboard_embedding_access_policy + ) + if self.effective_aibi_dashboard_embedding_approved_domains: + body["effective_aibi_dashboard_embedding_approved_domains"] = ( + self.effective_aibi_dashboard_embedding_approved_domains + ) + if self.effective_automatic_cluster_update_workspace: + body["effective_automatic_cluster_update_workspace"] = self.effective_automatic_cluster_update_workspace + if self.effective_boolean_val: + body["effective_boolean_val"] = self.effective_boolean_val + if self.effective_default_data_security_mode: + body["effective_default_data_security_mode"] = self.effective_default_data_security_mode + if self.effective_integer_val: + body["effective_integer_val"] = self.effective_integer_val + if self.effective_personal_compute: + body["effective_personal_compute"] = self.effective_personal_compute + if self.effective_restrict_workspace_admins: + body["effective_restrict_workspace_admins"] = self.effective_restrict_workspace_admins + if self.effective_string_val: + body["effective_string_val"] = self.effective_string_val + if self.integer_val: + body["integer_val"] = self.integer_val + if self.name is not None: + body["name"] = self.name + if self.personal_compute: + body["personal_compute"] = self.personal_compute + if self.restrict_workspace_admins: + body["restrict_workspace_admins"] = self.restrict_workspace_admins + if self.string_val: + body["string_val"] = self.string_val + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> Setting: + """Deserializes the Setting from a dictionary.""" + return cls( + aibi_dashboard_embedding_access_policy=_from_dict( + d, "aibi_dashboard_embedding_access_policy", AibiDashboardEmbeddingAccessPolicy + ), + aibi_dashboard_embedding_approved_domains=_from_dict( + d, "aibi_dashboard_embedding_approved_domains", AibiDashboardEmbeddingApprovedDomains + ), + automatic_cluster_update_workspace=_from_dict( + d, "automatic_cluster_update_workspace", ClusterAutoRestartMessage + ), + boolean_val=_from_dict(d, "boolean_val", BooleanMessage), + default_data_security_mode=_from_dict(d, "default_data_security_mode", DefaultDataSecurityModeMessage), + effective_aibi_dashboard_embedding_access_policy=_from_dict( + d, "effective_aibi_dashboard_embedding_access_policy", AibiDashboardEmbeddingAccessPolicy + ), + effective_aibi_dashboard_embedding_approved_domains=_from_dict( + d, "effective_aibi_dashboard_embedding_approved_domains", AibiDashboardEmbeddingApprovedDomains + ), + effective_automatic_cluster_update_workspace=_from_dict( + d, "effective_automatic_cluster_update_workspace", ClusterAutoRestartMessage + ), + effective_boolean_val=_from_dict(d, "effective_boolean_val", BooleanMessage), + effective_default_data_security_mode=_from_dict( + d, "effective_default_data_security_mode", DefaultDataSecurityModeMessage + ), + effective_integer_val=_from_dict(d, "effective_integer_val", IntegerMessage), + effective_personal_compute=_from_dict(d, "effective_personal_compute", PersonalComputeMessage), + effective_restrict_workspace_admins=_from_dict( + d, "effective_restrict_workspace_admins", RestrictWorkspaceAdminsMessage + ), + effective_string_val=_from_dict(d, "effective_string_val", StringMessage), + integer_val=_from_dict(d, "integer_val", IntegerMessage), + name=d.get("name", None), + personal_compute=_from_dict(d, "personal_compute", PersonalComputeMessage), + restrict_workspace_admins=_from_dict(d, "restrict_workspace_admins", RestrictWorkspaceAdminsMessage), + string_val=_from_dict(d, "string_val", StringMessage), + ) + + +@dataclass +class SettingsMetadata: + description: Optional[str] = None + """Setting description for what this setting controls""" + + docs_link: Optional[str] = None + """Link to databricks documentation for the setting""" + + name: Optional[str] = None + """Name of the setting.""" + + type: Optional[str] = None + """Type of the setting. To set this setting, the value sent must match this type.""" + + def as_dict(self) -> dict: + """Serializes the SettingsMetadata into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.description is not None: + body["description"] = self.description + if self.docs_link is not None: + body["docs_link"] = self.docs_link + if self.name is not None: + body["name"] = self.name + if self.type is not None: + body["type"] = self.type + return body + + def as_shallow_dict(self) -> dict: + """Serializes the SettingsMetadata into a shallow dictionary of its immediate attributes.""" + body = {} + if self.description is not None: + body["description"] = self.description + if self.docs_link is not None: + body["docs_link"] = self.docs_link + if self.name is not None: + body["name"] = self.name + if self.type is not None: + body["type"] = self.type + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> SettingsMetadata: + """Deserializes the SettingsMetadata from a dictionary.""" + return cls( + description=d.get("description", None), + docs_link=d.get("docs_link", None), + name=d.get("name", None), + type=d.get("type", None), + ) + + +@dataclass +class StringMessage: + value: Optional[str] = None + """Represents a generic string value.""" + + def as_dict(self) -> dict: + """Serializes the StringMessage into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.value is not None: + body["value"] = self.value + return body + + def as_shallow_dict(self) -> dict: + """Serializes the StringMessage into a shallow dictionary of its immediate attributes.""" + body = {} + if self.value is not None: + body["value"] = self.value + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> StringMessage: + """Deserializes the StringMessage from a dictionary.""" + return cls(value=d.get("value", None)) + + +class AccountSettingsV2API: + """APIs to manage account level settings""" + + def __init__(self, api_client): + self._api = api_client + + def get_public_account_setting(self, name: str) -> Setting: + """Get a setting value at account level + + :param name: str + + :returns: :class:`Setting` + """ + + headers = { + "Accept": "application/json", + } + + res = self._api.do("GET", f"/api/2.1/accounts/{self._api.account_id}/settings/{name}", headers=headers) + return Setting.from_dict(res) + + def list_account_settings_metadata( + self, *, page_size: Optional[int] = None, page_token: Optional[str] = None + ) -> Iterator[SettingsMetadata]: + """List valid setting keys and metadata. These settings are available to referenced via [GET + /api/2.1/settings/{name}](#~1api~1account~1settingsv2~1getpublicaccountsetting) and [PATCH + /api/2.1/settings/{name}](#~1api~1account~1settingsv2~patchpublicaccountsetting) APIs + + :param page_size: int (optional) + The maximum number of settings to return. The service may return fewer than this value. If + unspecified, at most 200 settings will be returned. The maximum value is 1000; values above 1000 + will be coerced to 1000. + :param page_token: str (optional) + A page token, received from a previous `ListAccountSettingsMetadataRequest` call. Provide this to + retrieve the subsequent page. + + When paginating, all other parameters provided to `ListAccountSettingsMetadataRequest` must match + the call that provided the page token. + + :returns: Iterator over :class:`SettingsMetadata` + """ + + 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.1/accounts/{self._api.account_id}/settings-metadata", query=query, headers=headers + ) + if "settings_metadata" in json: + for v in json["settings_metadata"]: + yield SettingsMetadata.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 patch_public_account_setting(self, name: str, setting: Setting) -> Setting: + """Patch a setting value at account level + + :param name: str + :param setting: :class:`Setting` + + :returns: :class:`Setting` + """ + body = setting.as_dict() + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do( + "PATCH", f"/api/2.1/accounts/{self._api.account_id}/settings/{name}", body=body, headers=headers + ) + return Setting.from_dict(res) + + +class WorkspaceSettingsV2API: + """APIs to manage workspace level settings""" + + def __init__(self, api_client): + self._api = api_client + + def get_public_workspace_setting(self, name: str) -> Setting: + """Get a setting value at workspace level + + :param name: str + + :returns: :class:`Setting` + """ + + headers = { + "Accept": "application/json", + } + + res = self._api.do("GET", f"/api/2.1/settings/{name}", headers=headers) + return Setting.from_dict(res) + + def list_workspace_settings_metadata( + self, *, page_size: Optional[int] = None, page_token: Optional[str] = None + ) -> Iterator[SettingsMetadata]: + """List valid setting keys and metadata. These settings are available to referenced via [GET + /api/2.1/settings/{name}](#~1api~1workspace~1settingsv2~1getpublicworkspacesetting) and [PATCH + /api/2.1/settings/{name}](#~1api~1workspace~1settingsv2~patchpublicworkspacesetting) APIs + + :param page_size: int (optional) + The maximum number of settings to return. The service may return fewer than this value. If + unspecified, at most 200 settings will be returned. The maximum value is 1000; values above 1000 + will be coerced to 1000. + :param page_token: str (optional) + A page token, received from a previous `ListWorkspaceSettingsMetadataRequest` call. Provide this to + retrieve the subsequent page. + + When paginating, all other parameters provided to `ListWorkspaceSettingsMetadataRequest` must match + the call that provided the page token. + + :returns: Iterator over :class:`SettingsMetadata` + """ + + 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", "/api/2.1/settings-metadata", query=query, headers=headers) + if "settings_metadata" in json: + for v in json["settings_metadata"]: + yield SettingsMetadata.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 patch_public_workspace_setting(self, name: str, setting: Setting) -> Setting: + """Patch a setting value at workspace level + + :param name: str + :param setting: :class:`Setting` + + :returns: :class:`Setting` + """ + body = setting.as_dict() + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do("PATCH", f"/api/2.1/settings/{name}", body=body, headers=headers) + return Setting.from_dict(res) diff --git a/databricks/sdk/service/sharing.py b/databricks/sdk/service/sharing.py index f9bde5646..fd8063c3d 100755 --- a/databricks/sdk/service/sharing.py +++ b/databricks/sdk/service/sharing.py @@ -1827,59 +1827,32 @@ def from_dict(cls, d: Dict[str, Any]) -> SecurablePropertiesKvPairs: @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), - ) + return cls(id=d.get("id", None), name=d.get("name", None)) @dataclass diff --git a/databricks/sdk/service/sql.py b/databricks/sdk/service/sql.py index b35039c98..9a80d1f5b 100755 --- a/databricks/sdk/service/sql.py +++ b/databricks/sdk/service/sql.py @@ -646,6 +646,10 @@ class AlertV2: display_name: Optional[str] = None """The display name of the alert.""" + effective_run_as: Optional[AlertV2RunAs] = None + """The actual identity that will be used to execute the alert. This is an output-only field that + shows the resolved run-as identity after applying permissions and defaults.""" + evaluation: Optional[AlertV2Evaluation] = None id: Optional[str] = None @@ -664,10 +668,18 @@ class AlertV2: query_text: Optional[str] = None """Text of the query to be run.""" + run_as: Optional[AlertV2RunAs] = None + """Specifies the identity that will be used to run the alert. This field allows you to configure + alerts to run as a specific user or service principal. - For user identity: Set `user_name` to + the email of an active workspace user. Users can only set this to their own email. - For service + principal: Set `service_principal_name` to the application ID. Requires the + `servicePrincipal/user` role. If not specified, the alert will run as the request user.""" + run_as_user_name: Optional[str] = None """The run as username or application ID of service principal. On Create and Update, this field can be set to application ID of an active service principal. Setting this field requires the - servicePrincipal/user role.""" + servicePrincipal/user role. Deprecated: Use `run_as` field instead. This field will be removed + in a future release.""" schedule: Optional[CronSchedule] = None @@ -688,6 +700,8 @@ def as_dict(self) -> dict: body["custom_summary"] = self.custom_summary if self.display_name is not None: body["display_name"] = self.display_name + if self.effective_run_as: + body["effective_run_as"] = self.effective_run_as.as_dict() if self.evaluation: body["evaluation"] = self.evaluation.as_dict() if self.id is not None: @@ -700,6 +714,8 @@ def as_dict(self) -> dict: body["parent_path"] = self.parent_path if self.query_text is not None: body["query_text"] = self.query_text + if self.run_as: + body["run_as"] = self.run_as.as_dict() if self.run_as_user_name is not None: body["run_as_user_name"] = self.run_as_user_name if self.schedule: @@ -721,6 +737,8 @@ def as_shallow_dict(self) -> dict: body["custom_summary"] = self.custom_summary if self.display_name is not None: body["display_name"] = self.display_name + if self.effective_run_as: + body["effective_run_as"] = self.effective_run_as if self.evaluation: body["evaluation"] = self.evaluation if self.id is not None: @@ -733,6 +751,8 @@ def as_shallow_dict(self) -> dict: body["parent_path"] = self.parent_path if self.query_text is not None: body["query_text"] = self.query_text + if self.run_as: + body["run_as"] = self.run_as if self.run_as_user_name is not None: body["run_as_user_name"] = self.run_as_user_name if self.schedule: @@ -751,12 +771,14 @@ def from_dict(cls, d: Dict[str, Any]) -> AlertV2: custom_description=d.get("custom_description", None), custom_summary=d.get("custom_summary", None), display_name=d.get("display_name", None), + effective_run_as=_from_dict(d, "effective_run_as", AlertV2RunAs), evaluation=_from_dict(d, "evaluation", AlertV2Evaluation), id=d.get("id", None), lifecycle_state=_enum(d, "lifecycle_state", LifecycleState), owner_user_name=d.get("owner_user_name", None), parent_path=d.get("parent_path", None), query_text=d.get("query_text", None), + run_as=_from_dict(d, "run_as", AlertV2RunAs), run_as_user_name=d.get("run_as_user_name", None), schedule=_from_dict(d, "schedule", CronSchedule), update_time=d.get("update_time", None), @@ -992,6 +1014,39 @@ def from_dict(cls, d: Dict[str, Any]) -> AlertV2OperandValue: ) +@dataclass +class AlertV2RunAs: + service_principal_name: Optional[str] = None + """Application ID of an active service principal. Setting this field requires the + `servicePrincipal/user` role.""" + + user_name: Optional[str] = None + """The email of an active workspace user. Can only set this field to their own email.""" + + def as_dict(self) -> dict: + """Serializes the AlertV2RunAs into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.service_principal_name is not None: + body["service_principal_name"] = self.service_principal_name + if self.user_name is not None: + body["user_name"] = self.user_name + return body + + def as_shallow_dict(self) -> dict: + """Serializes the AlertV2RunAs into a shallow dictionary of its immediate attributes.""" + body = {} + if self.service_principal_name is not None: + body["service_principal_name"] = self.service_principal_name + if self.user_name is not None: + body["user_name"] = self.user_name + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> AlertV2RunAs: + """Deserializes the AlertV2RunAs from a dictionary.""" + return cls(service_principal_name=d.get("service_principal_name", None), user_name=d.get("user_name", None)) + + @dataclass class AlertV2Subscription: destination_id: Optional[str] = None @@ -4723,6 +4778,9 @@ def from_dict(cls, d: Dict[str, Any]) -> QueryFilter: @dataclass class QueryInfo: + cache_query_id: Optional[str] = None + """The ID of the cached query if this result retrieved from cache""" + channel_used: Optional[ChannelInfo] = None """SQL Warehouse channel information at the time of query execution""" @@ -4808,6 +4866,8 @@ class QueryInfo: def as_dict(self) -> dict: """Serializes the QueryInfo into a dictionary suitable for use as a JSON request body.""" body = {} + if self.cache_query_id is not None: + body["cache_query_id"] = self.cache_query_id if self.channel_used: body["channel_used"] = self.channel_used.as_dict() if self.client_application is not None: @@ -4861,6 +4921,8 @@ def as_dict(self) -> dict: def as_shallow_dict(self) -> dict: """Serializes the QueryInfo into a shallow dictionary of its immediate attributes.""" body = {} + if self.cache_query_id is not None: + body["cache_query_id"] = self.cache_query_id if self.channel_used: body["channel_used"] = self.channel_used if self.client_application is not None: @@ -4915,6 +4977,7 @@ def as_shallow_dict(self) -> dict: def from_dict(cls, d: Dict[str, Any]) -> QueryInfo: """Deserializes the QueryInfo from a dictionary.""" return cls( + cache_query_id=d.get("cache_query_id", None), channel_used=_from_dict(d, "channel_used", ChannelInfo), client_application=d.get("client_application", None), duration=d.get("duration", None), diff --git a/databricks/sdk/service/tags.py b/databricks/sdk/service/tags.py new file mode 100755 index 000000000..8962682fa --- /dev/null +++ b/databricks/sdk/service/tags.py @@ -0,0 +1,232 @@ +# Code generated from OpenAPI specs by Databricks SDK Generator. DO NOT EDIT. + +from __future__ import annotations + +import logging +from dataclasses import dataclass +from typing import Any, Dict, Iterator, List, Optional + +from ._internal import _repeated_dict + +_LOG = logging.getLogger("databricks.sdk") + + +# all definitions in this file are in alphabetical order + + +@dataclass +class ListTagPoliciesResponse: + next_page_token: Optional[str] = None + + tag_policies: Optional[List[TagPolicy]] = None + + def as_dict(self) -> dict: + """Serializes the ListTagPoliciesResponse 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.tag_policies: + body["tag_policies"] = [v.as_dict() for v in self.tag_policies] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the ListTagPoliciesResponse 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.tag_policies: + body["tag_policies"] = self.tag_policies + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> ListTagPoliciesResponse: + """Deserializes the ListTagPoliciesResponse from a dictionary.""" + return cls( + next_page_token=d.get("next_page_token", None), tag_policies=_repeated_dict(d, "tag_policies", TagPolicy) + ) + + +@dataclass +class TagPolicy: + tag_key: str + + description: Optional[str] = None + + id: Optional[str] = None + + values: Optional[List[Value]] = None + + def as_dict(self) -> dict: + """Serializes the TagPolicy into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.description is not None: + body["description"] = self.description + if self.id is not None: + body["id"] = self.id + if self.tag_key is not None: + body["tag_key"] = self.tag_key + if self.values: + body["values"] = [v.as_dict() for v in self.values] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the TagPolicy into a shallow dictionary of its immediate attributes.""" + body = {} + if self.description is not None: + body["description"] = self.description + if self.id is not None: + body["id"] = self.id + if self.tag_key is not None: + body["tag_key"] = self.tag_key + if self.values: + body["values"] = self.values + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> TagPolicy: + """Deserializes the TagPolicy from a dictionary.""" + return cls( + description=d.get("description", None), + id=d.get("id", None), + tag_key=d.get("tag_key", None), + values=_repeated_dict(d, "values", Value), + ) + + +@dataclass +class Value: + name: str + + def as_dict(self) -> dict: + """Serializes the Value into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.name is not None: + body["name"] = self.name + return body + + def as_shallow_dict(self) -> dict: + """Serializes the Value into a shallow dictionary of its immediate attributes.""" + body = {} + if self.name is not None: + body["name"] = self.name + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> Value: + """Deserializes the Value from a dictionary.""" + return cls(name=d.get("name", None)) + + +class TagPoliciesAPI: + """The Tag Policy API allows you to manage tag policies in Databricks.""" + + def __init__(self, api_client): + self._api = api_client + + def create_tag_policy(self, tag_policy: TagPolicy) -> TagPolicy: + """Creates a new tag policy. + + :param tag_policy: :class:`TagPolicy` + + :returns: :class:`TagPolicy` + """ + body = tag_policy.as_dict() + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do("POST", "/api/2.1/tag-policies", body=body, headers=headers) + return TagPolicy.from_dict(res) + + def delete_tag_policy(self, tag_key: str): + """Deletes a tag policy by its key. + + :param tag_key: str + + + """ + + headers = { + "Accept": "application/json", + } + + self._api.do("DELETE", f"/api/2.1/tag-policies/{tag_key}", headers=headers) + + def get_tag_policy(self, tag_key: str) -> TagPolicy: + """Gets a single tag policy by its key. + + :param tag_key: str + + :returns: :class:`TagPolicy` + """ + + headers = { + "Accept": "application/json", + } + + res = self._api.do("GET", f"/api/2.1/tag-policies/{tag_key}", headers=headers) + return TagPolicy.from_dict(res) + + def list_tag_policies( + self, *, page_size: Optional[int] = None, page_token: Optional[str] = None + ) -> Iterator[TagPolicy]: + """Lists all tag policies in the account. + + :param page_size: int (optional) + The maximum number of results to return in this request. Fewer results may be returned than + requested. If unspecified or set to 0, this defaults to 1000. The maximum value is 1000; values + above 1000 will be coerced down to 1000. + :param page_token: str (optional) + An optional page token received from a previous list tag policies call. + + :returns: Iterator over :class:`TagPolicy` + """ + + 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", "/api/2.1/tag-policies", query=query, headers=headers) + if "tag_policies" in json: + for v in json["tag_policies"]: + yield TagPolicy.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_tag_policy(self, tag_key: str, tag_policy: TagPolicy, update_mask: str) -> TagPolicy: + """Updates an existing tag policy. + + :param tag_key: str + :param tag_policy: :class:`TagPolicy` + :param update_mask: str + The field mask must be a single string, with multiple fields separated by commas (no spaces). The + field path is relative to the resource object, using a dot (`.`) to navigate sub-fields (e.g., + `author.given_name`). Specification of elements in sequence or map fields is not allowed, as only + the entire collection field can be specified. Field names must exactly match the resource field + names. + + A field mask of `*` indicates full replacement. It’s recommended to always explicitly list the + fields being updated and avoid using `*` wildcards, as it can lead to unintended results if the API + changes in the future. + + :returns: :class:`TagPolicy` + """ + body = tag_policy.as_dict() + query = {} + if update_mask is not None: + query["update_mask"] = update_mask + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do("PATCH", f"/api/2.1/tag-policies/{tag_key}", query=query, body=body, headers=headers) + return TagPolicy.from_dict(res) diff --git a/databricks/sdk/service/vectorsearch.py b/databricks/sdk/service/vectorsearch.py index fb8ea8f94..da89959a0 100755 --- a/databricks/sdk/service/vectorsearch.py +++ b/databricks/sdk/service/vectorsearch.py @@ -377,7 +377,10 @@ def from_dict(cls, d: Dict[str, Any]) -> DirectAccessVectorIndexSpec: @dataclass class EmbeddingSourceColumn: embedding_model_endpoint_name: Optional[str] = None - """Name of the embedding model endpoint""" + """Name of the embedding model endpoint, used by default for both ingestion and querying.""" + + model_endpoint_name_for_query: Optional[str] = None + """Name of the embedding model endpoint which, if specified, is used for querying (not ingestion).""" name: Optional[str] = None """Name of the column""" @@ -387,6 +390,8 @@ def as_dict(self) -> dict: body = {} if self.embedding_model_endpoint_name is not None: body["embedding_model_endpoint_name"] = self.embedding_model_endpoint_name + if self.model_endpoint_name_for_query is not None: + body["model_endpoint_name_for_query"] = self.model_endpoint_name_for_query if self.name is not None: body["name"] = self.name return body @@ -396,6 +401,8 @@ def as_shallow_dict(self) -> dict: body = {} if self.embedding_model_endpoint_name is not None: body["embedding_model_endpoint_name"] = self.embedding_model_endpoint_name + if self.model_endpoint_name_for_query is not None: + body["model_endpoint_name_for_query"] = self.model_endpoint_name_for_query if self.name is not None: body["name"] = self.name return body @@ -403,7 +410,11 @@ def as_shallow_dict(self) -> dict: @classmethod def from_dict(cls, d: Dict[str, Any]) -> EmbeddingSourceColumn: """Deserializes the EmbeddingSourceColumn from a dictionary.""" - return cls(embedding_model_endpoint_name=d.get("embedding_model_endpoint_name", None), name=d.get("name", None)) + return cls( + embedding_model_endpoint_name=d.get("embedding_model_endpoint_name", None), + model_endpoint_name_for_query=d.get("model_endpoint_name_for_query", None), + name=d.get("name", None), + ) @dataclass