diff --git a/.codegen/_openapi_sha b/.codegen/_openapi_sha index 37b41dbfe..1a48101bf 100755 --- a/.codegen/_openapi_sha +++ b/.codegen/_openapi_sha @@ -1 +1 @@ -c23b88b906c9d262245e50038211199037d2ee9a \ No newline at end of file +b2acebf0af39a39d83fdac76ae48d761001e7052 \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index 66e361e5b..5dcaf1795 100755 --- a/.gitattributes +++ b/.gitattributes @@ -11,6 +11,7 @@ databricks/sdk/service/dashboards.py linguist-generated=true databricks/sdk/service/database.py linguist-generated=true databricks/sdk/service/dataclassification.py linguist-generated=true databricks/sdk/service/dataquality.py linguist-generated=true +databricks/sdk/service/disasterrecovery.py linguist-generated=true databricks/sdk/service/environments.py linguist-generated=true databricks/sdk/service/files.py linguist-generated=true databricks/sdk/service/httpcallv2.py linguist-generated=true diff --git a/.github/workflows/tagging.yml b/.github/workflows/tagging.yml index e22f794e0..f8d421a08 100755 --- a/.github/workflows/tagging.yml +++ b/.github/workflows/tagging.yml @@ -4,7 +4,12 @@ name: tagging on: # Manual dispatch. workflow_dispatch: - # No inputs are required for the manual dispatch. + inputs: + packages: + description: 'Comma-separated list of packages to tag (e.g. "pkg1,pkg2"). Leave empty to tag all packages with pending releases.' + required: false + type: string + default: '' # NOTE: Temporarily disable automated releases. # @@ -61,7 +66,13 @@ jobs: env: GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }} GITHUB_REPOSITORY: ${{ github.repository }} - run: uv run --locked tagging.py + PACKAGES: ${{ inputs.packages }} + run: | + if [ -n "$PACKAGES" ]; then + uv run --locked tagging.py --package "$PACKAGES" + else + uv run --locked tagging.py + fi - name: Upload created tags artifact if: always() diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index 150c60b2e..481123e05 100755 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -29,4 +29,14 @@ * Add `delete_time` and `purge_time` fields for `databricks.sdk.service.postgres.Project`. * Add `uc_connection` field for `databricks.sdk.service.supervisoragents.Tool`. * Change `name` field for `databricks.sdk.service.supervisoragents.Connection` to no longer be required. -* [Breaking] Change `name` field for `databricks.sdk.service.supervisoragents.Connection` to no longer be required. \ No newline at end of file +* [Breaking] Change `name` field for `databricks.sdk.service.supervisoragents.Connection` to no longer be required. +* Add `databricks.sdk.service.disasterrecovery` package. +* Add [a.disaster_recovery](https://databricks-sdk-py.readthedocs.io/en/latest/account/disasterrecovery/disaster_recovery.html) account-level service. +* Add `create_example()`, `delete_example()`, `get_example()`, `list_examples()` and `update_example()` methods for [w.knowledge_assistants](https://databricks-sdk-py.readthedocs.io/en/latest/workspace/knowledgeassistants/knowledge_assistants.html) workspace-level service. +* Add `delta_table_name` field for `databricks.sdk.service.ml.BackfillSource`. +* Add `confluence_options` field for `databricks.sdk.service.pipelines.ConnectorOptions`. +* Add `confluence` enum value for `databricks.sdk.service.catalog.ConnectionType`. +* Add `confluence` enum value for `databricks.sdk.service.pipelines.IngestionSourceType`. +* Change `description` field for `databricks.sdk.service.supervisoragents.SupervisorAgent` to no longer be required. +* [Breaking] Change `description` field for `databricks.sdk.service.supervisoragents.SupervisorAgent` to no longer be required. +* [Breaking] Remove `connection` field for `databricks.sdk.service.supervisoragents.Tool`. \ No newline at end of file diff --git a/databricks/sdk/__init__.py b/databricks/sdk/__init__.py index 74765b5c4..d0117185a 100755 --- a/databricks/sdk/__init__.py +++ b/databricks/sdk/__init__.py @@ -25,6 +25,7 @@ from databricks.sdk.service import database as pkg_database from databricks.sdk.service import dataclassification as pkg_dataclassification from databricks.sdk.service import dataquality as pkg_dataquality +from databricks.sdk.service import disasterrecovery as pkg_disasterrecovery from databricks.sdk.service import environments as pkg_environments from databricks.sdk.service import files as pkg_files from databricks.sdk.service import iam as pkg_iam @@ -93,6 +94,7 @@ from databricks.sdk.service.database import DatabaseAPI from databricks.sdk.service.dataclassification import DataClassificationAPI from databricks.sdk.service.dataquality import DataQualityAPI +from databricks.sdk.service.disasterrecovery import DisasterRecoveryAPI from databricks.sdk.service.environments import EnvironmentsAPI from databricks.sdk.service.files import DbfsAPI, FilesAPI from databricks.sdk.service.iam import (AccessControlAPI, @@ -1178,6 +1180,7 @@ def __init__( self._budgets = pkg_billing.BudgetsAPI(self._api_client) self._credentials = pkg_provisioning.CredentialsAPI(self._api_client) self._custom_app_integration = pkg_oauth2.CustomAppIntegrationAPI(self._api_client) + self._disaster_recovery = pkg_disasterrecovery.DisasterRecoveryAPI(self._api_client) self._encryption_keys = pkg_provisioning.EncryptionKeysAPI(self._api_client) self._endpoints = pkg_networking.EndpointsAPI(self._api_client) self._federation_policy = pkg_oauth2.AccountFederationPolicyAPI(self._api_client) @@ -1248,6 +1251,11 @@ def custom_app_integration(self) -> pkg_oauth2.CustomAppIntegrationAPI: """These APIs enable administrators to manage custom OAuth app integrations, which is required for adding/using Custom OAuth App Integration like Tableau Cloud for Databricks in AWS cloud.""" return self._custom_app_integration + @property + def disaster_recovery(self) -> pkg_disasterrecovery.DisasterRecoveryAPI: + """Manage disaster recovery configurations and execute failover operations.""" + return self._disaster_recovery + @property def encryption_keys(self) -> pkg_provisioning.EncryptionKeysAPI: """These APIs manage encryption key configurations for this workspace (optional).""" diff --git a/databricks/sdk/service/catalog.py b/databricks/sdk/service/catalog.py index 9441e345d..8f6b78fb5 100755 --- a/databricks/sdk/service/catalog.py +++ b/databricks/sdk/service/catalog.py @@ -1831,9 +1831,10 @@ def from_dict(cls, d: Dict[str, Any]) -> ConnectionInfo: class ConnectionType(Enum): - """Next Id: 123""" + """Next Id: 124""" BIGQUERY = "BIGQUERY" + CONFLUENCE = "CONFLUENCE" DATABRICKS = "DATABRICKS" GA4_RAW_DATA = "GA4_RAW_DATA" GLUE = "GLUE" @@ -9247,7 +9248,7 @@ def from_dict(cls, d: Dict[str, Any]) -> Securable: class SecurableKind(Enum): - """Latest kind: TOOLSET_EXTERNAL_MCP = 318; Next id: 319""" + """Latest kind: CONNECTION_SLACK_ACCESS_AND_INTEGRATION_LOGS_OAUTH_U2M = 319; Next id: 320""" TABLE_DB_STORAGE = "TABLE_DB_STORAGE" TABLE_DELTA = "TABLE_DELTA" diff --git a/databricks/sdk/service/disasterrecovery.py b/databricks/sdk/service/disasterrecovery.py new file mode 100755 index 000000000..a2d69bc29 --- /dev/null +++ b/databricks/sdk/service/disasterrecovery.py @@ -0,0 +1,762 @@ +# 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 google.protobuf.timestamp_pb2 import Timestamp + +from databricks.sdk.common.types.fieldmask import FieldMask +from databricks.sdk.service._internal import (_enum, _from_dict, + _repeated_dict, _timestamp) + +_LOG = logging.getLogger("databricks.sdk") + + +# all definitions in this file are in alphabetical order + + +class FailoverFailoverGroupRequestFailoverType(Enum): + """The type of failover to perform.""" + + FORCED = "FORCED" + + +@dataclass +class FailoverGroup: + """A failover group manages disaster recovery across workspace sets, coordinating UCDR and CPDR + replication.""" + + regions: List[str] + """List of all regions participating in this failover group.""" + + workspace_sets: List[WorkspaceSet] + """Workspace sets, each containing workspaces that replicate to each other.""" + + initial_primary_region: str + """Initial primary region. Used only in Create requests to set the starting primary region. Not + returned in responses.""" + + create_time: Optional[Timestamp] = None + """Time at which this failover group was created.""" + + effective_primary_region: Optional[str] = None + """Current effective primary region. Replication flows FROM workspaces in this region. Changes + after a successful failover.""" + + etag: Optional[str] = None + """Opaque version string for optimistic locking. Server-generated, returned in responses. Must be + provided on Update requests to prevent concurrent modifications.""" + + name: Optional[str] = None + """Fully qualified resource name in the format + accounts/{account_id}/failover-groups/{failover_group_id}.""" + + replication_point: Optional[Timestamp] = None + """The latest point in time to which data has been replicated.""" + + state: Optional[FailoverGroupState] = None + """Aggregate state of the failover group.""" + + unity_catalog_assets: Optional[UcReplicationConfig] = None + """Unity Catalog replication configuration.""" + + update_time: Optional[Timestamp] = None + """Time at which this failover group was last modified.""" + + def as_dict(self) -> dict: + """Serializes the FailoverGroup into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.create_time is not None: + body["create_time"] = self.create_time.ToJsonString() + if self.effective_primary_region is not None: + body["effective_primary_region"] = self.effective_primary_region + if self.etag is not None: + body["etag"] = self.etag + if self.initial_primary_region is not None: + body["initial_primary_region"] = self.initial_primary_region + if self.name is not None: + body["name"] = self.name + if self.regions: + body["regions"] = [v for v in self.regions] + if self.replication_point is not None: + body["replication_point"] = self.replication_point.ToJsonString() + if self.state is not None: + body["state"] = self.state.value + if self.unity_catalog_assets: + body["unity_catalog_assets"] = self.unity_catalog_assets.as_dict() + if self.update_time is not None: + body["update_time"] = self.update_time.ToJsonString() + if self.workspace_sets: + body["workspace_sets"] = [v.as_dict() for v in self.workspace_sets] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the FailoverGroup into a shallow dictionary of its immediate attributes.""" + body = {} + if self.create_time is not None: + body["create_time"] = self.create_time + if self.effective_primary_region is not None: + body["effective_primary_region"] = self.effective_primary_region + if self.etag is not None: + body["etag"] = self.etag + if self.initial_primary_region is not None: + body["initial_primary_region"] = self.initial_primary_region + if self.name is not None: + body["name"] = self.name + if self.regions: + body["regions"] = self.regions + if self.replication_point is not None: + body["replication_point"] = self.replication_point + if self.state is not None: + body["state"] = self.state + if self.unity_catalog_assets: + body["unity_catalog_assets"] = self.unity_catalog_assets + if self.update_time is not None: + body["update_time"] = self.update_time + if self.workspace_sets: + body["workspace_sets"] = self.workspace_sets + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> FailoverGroup: + """Deserializes the FailoverGroup from a dictionary.""" + return cls( + create_time=_timestamp(d, "create_time"), + effective_primary_region=d.get("effective_primary_region", None), + etag=d.get("etag", None), + initial_primary_region=d.get("initial_primary_region", None), + name=d.get("name", None), + regions=d.get("regions", None), + replication_point=_timestamp(d, "replication_point"), + state=_enum(d, "state", FailoverGroupState), + unity_catalog_assets=_from_dict(d, "unity_catalog_assets", UcReplicationConfig), + update_time=_timestamp(d, "update_time"), + workspace_sets=_repeated_dict(d, "workspace_sets", WorkspaceSet), + ) + + +class FailoverGroupState(Enum): + """The aggregate state of a FailoverGroup.""" + + ACTIVE = "ACTIVE" + CREATING = "CREATING" + CREATION_FAILED = "CREATION_FAILED" + DELETING = "DELETING" + DELETION_FAILED = "DELETION_FAILED" + FAILING_OVER = "FAILING_OVER" + FAILOVER_FAILED = "FAILOVER_FAILED" + INITIAL_REPLICATION = "INITIAL_REPLICATION" + + +@dataclass +class ListFailoverGroupsResponse: + """Response for listing failover groups.""" + + failover_groups: Optional[List[FailoverGroup]] = None + """The failover groups for this account.""" + + next_page_token: Optional[str] = None + """A token that can be sent as page_token to retrieve the next page. If omitted, there are no + subsequent pages.""" + + def as_dict(self) -> dict: + """Serializes the ListFailoverGroupsResponse into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.failover_groups: + body["failover_groups"] = [v.as_dict() for v in self.failover_groups] + 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 ListFailoverGroupsResponse into a shallow dictionary of its immediate attributes.""" + body = {} + if self.failover_groups: + body["failover_groups"] = self.failover_groups + 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]) -> ListFailoverGroupsResponse: + """Deserializes the ListFailoverGroupsResponse from a dictionary.""" + return cls( + failover_groups=_repeated_dict(d, "failover_groups", FailoverGroup), + next_page_token=d.get("next_page_token", None), + ) + + +@dataclass +class ListStableUrlsResponse: + """Response for listing stable URLs.""" + + next_page_token: Optional[str] = None + """A token that can be sent as page_token to retrieve the next page. If omitted, there are no + subsequent pages.""" + + stable_urls: Optional[List[StableUrl]] = None + """The stable URLs for this account.""" + + def as_dict(self) -> dict: + """Serializes the ListStableUrlsResponse 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.stable_urls: + body["stable_urls"] = [v.as_dict() for v in self.stable_urls] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the ListStableUrlsResponse 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.stable_urls: + body["stable_urls"] = self.stable_urls + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> ListStableUrlsResponse: + """Deserializes the ListStableUrlsResponse from a dictionary.""" + return cls( + next_page_token=d.get("next_page_token", None), stable_urls=_repeated_dict(d, "stable_urls", StableUrl) + ) + + +@dataclass +class LocationMapping: + """A location mapping identified by a name, with URIs per region. The system derives replication + direction from effective_primary_region.""" + + name: str + """Resource name for this location.""" + + uri_by_region: List[LocationMappingEntry] + """URI for each region. Each entry maps a region name to a storage URI.""" + + def as_dict(self) -> dict: + """Serializes the LocationMapping into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.name is not None: + body["name"] = self.name + if self.uri_by_region: + body["uri_by_region"] = [v.as_dict() for v in self.uri_by_region] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the LocationMapping into a shallow dictionary of its immediate attributes.""" + body = {} + if self.name is not None: + body["name"] = self.name + if self.uri_by_region: + body["uri_by_region"] = self.uri_by_region + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> LocationMapping: + """Deserializes the LocationMapping from a dictionary.""" + return cls(name=d.get("name", None), uri_by_region=_repeated_dict(d, "uri_by_region", LocationMappingEntry)) + + +@dataclass +class LocationMappingEntry: + """A single entry in a location mapping, mapping a region to a storage URI. Used instead of + map for proto2 compatibility.""" + + region: str + """The region name.""" + + uri: str + """The storage URI for this region.""" + + def as_dict(self) -> dict: + """Serializes the LocationMappingEntry into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.region is not None: + body["region"] = self.region + if self.uri is not None: + body["uri"] = self.uri + return body + + def as_shallow_dict(self) -> dict: + """Serializes the LocationMappingEntry into a shallow dictionary of its immediate attributes.""" + body = {} + if self.region is not None: + body["region"] = self.region + if self.uri is not None: + body["uri"] = self.uri + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> LocationMappingEntry: + """Deserializes the LocationMappingEntry from a dictionary.""" + return cls(region=d.get("region", None), uri=d.get("uri", None)) + + +@dataclass +class StableUrl: + """A stable URL provides a failover-aware endpoint for accessing a workspace. Its lifecycle is + independent of any failover group.""" + + initial_workspace_id: str + """The workspace this stable URL is initially bound to. Used only in Create requests to associate + the stable URL with a workspace. Not returned in responses. Mirrors + FailoverGroup.initial_primary_region semantics.""" + + name: Optional[str] = None + """Fully qualified resource name. Format: accounts/{account_id}/stable-urls/{stable_url_id}.""" + + url: Optional[str] = None + """The stable URL endpoint. Generated by the backend on creation and immutable thereafter. For + non-Private-Link workspaces this is `https:///?c=`. For Private-Link + workspaces this is the per-connection hostname.""" + + def as_dict(self) -> dict: + """Serializes the StableUrl into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.initial_workspace_id is not None: + body["initial_workspace_id"] = self.initial_workspace_id + if self.name is not None: + body["name"] = self.name + if self.url is not None: + body["url"] = self.url + return body + + def as_shallow_dict(self) -> dict: + """Serializes the StableUrl into a shallow dictionary of its immediate attributes.""" + body = {} + if self.initial_workspace_id is not None: + body["initial_workspace_id"] = self.initial_workspace_id + if self.name is not None: + body["name"] = self.name + if self.url is not None: + body["url"] = self.url + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> StableUrl: + """Deserializes the StableUrl from a dictionary.""" + return cls( + initial_workspace_id=d.get("initial_workspace_id", None), name=d.get("name", None), url=d.get("url", None) + ) + + +@dataclass +class UcCatalog: + """A Unity Catalog catalog to replicate.""" + + name: str + """The name of the UC catalog to replicate.""" + + def as_dict(self) -> dict: + """Serializes the UcCatalog 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 UcCatalog 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]) -> UcCatalog: + """Deserializes the UcCatalog from a dictionary.""" + return cls(name=d.get("name", None)) + + +@dataclass +class UcReplicationConfig: + """Unity Catalog replication configuration (top-level, not per-set).""" + + catalogs: List[UcCatalog] + """UC catalogs to replicate.""" + + data_replication_workspace_set: str + """The workspace set whose workspaces will be used for data replication of all UC catalogs' + underlying storage.""" + + location_mappings: Optional[List[LocationMapping]] = None + """Location mappings - storage URI per region for each location.""" + + def as_dict(self) -> dict: + """Serializes the UcReplicationConfig into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.catalogs: + body["catalogs"] = [v.as_dict() for v in self.catalogs] + if self.data_replication_workspace_set is not None: + body["data_replication_workspace_set"] = self.data_replication_workspace_set + if self.location_mappings: + body["location_mappings"] = [v.as_dict() for v in self.location_mappings] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the UcReplicationConfig into a shallow dictionary of its immediate attributes.""" + body = {} + if self.catalogs: + body["catalogs"] = self.catalogs + if self.data_replication_workspace_set is not None: + body["data_replication_workspace_set"] = self.data_replication_workspace_set + if self.location_mappings: + body["location_mappings"] = self.location_mappings + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> UcReplicationConfig: + """Deserializes the UcReplicationConfig from a dictionary.""" + return cls( + catalogs=_repeated_dict(d, "catalogs", UcCatalog), + data_replication_workspace_set=d.get("data_replication_workspace_set", None), + location_mappings=_repeated_dict(d, "location_mappings", LocationMapping), + ) + + +@dataclass +class WorkspaceSet: + """A set of workspaces that replicate to each other across regions.""" + + name: str + """Resource name for this workspace set.""" + + workspace_ids: List[str] + """Workspace IDs in this set. The system derives and validates regions. EA: exactly 2 workspaces + (one per region).""" + + replicate_workspace_assets: bool + """Whether to enable control plane DR (notebooks, jobs, clusters, etc.) for this set. Requires all + workspaces in the set to be Mission Critical tier.""" + + stable_url_names: Optional[List[str]] = None + """Resource names of stable URLs associated with this workspace set. Format: + accounts/{account_id}/stable-urls/{stable_url_id}. The referenced stable URLs must already exist + (via CreateStableUrl).""" + + def as_dict(self) -> dict: + """Serializes the WorkspaceSet into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.name is not None: + body["name"] = self.name + if self.replicate_workspace_assets is not None: + body["replicate_workspace_assets"] = self.replicate_workspace_assets + if self.stable_url_names: + body["stable_url_names"] = [v for v in self.stable_url_names] + if self.workspace_ids: + body["workspace_ids"] = [v for v in self.workspace_ids] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the WorkspaceSet into a shallow dictionary of its immediate attributes.""" + body = {} + if self.name is not None: + body["name"] = self.name + if self.replicate_workspace_assets is not None: + body["replicate_workspace_assets"] = self.replicate_workspace_assets + if self.stable_url_names: + body["stable_url_names"] = self.stable_url_names + if self.workspace_ids: + body["workspace_ids"] = self.workspace_ids + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> WorkspaceSet: + """Deserializes the WorkspaceSet from a dictionary.""" + return cls( + name=d.get("name", None), + replicate_workspace_assets=d.get("replicate_workspace_assets", None), + stable_url_names=d.get("stable_url_names", None), + workspace_ids=d.get("workspace_ids", None), + ) + + +class DisasterRecoveryAPI: + """Manage disaster recovery configurations and execute failover operations.""" + + def __init__(self, api_client): + self._api = api_client + + def create_failover_group( + self, + parent: str, + failover_group: FailoverGroup, + failover_group_id: str, + *, + validate_only: Optional[bool] = None, + ) -> FailoverGroup: + """Create a new failover group. + + :param parent: str + The parent resource. Format: accounts/{account_id}. + :param failover_group: :class:`FailoverGroup` + The failover group to create. + :param failover_group_id: str + Client-provided identifier for the failover group. Used to construct the resource name as + {parent}/failover-groups/{failover_group_id}. + :param validate_only: bool (optional) + When true, validates the request without creating the failover group. + + :returns: :class:`FailoverGroup` + """ + + body = failover_group.as_dict() + query = {} + if failover_group_id is not None: + query["failover_group_id"] = failover_group_id + if validate_only is not None: + query["validate_only"] = validate_only + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do( + "POST", f"/api/disaster-recovery/v1/{parent}/failover-groups", query=query, body=body, headers=headers + ) + return FailoverGroup.from_dict(res) + + def create_stable_url( + self, parent: str, stable_url: StableUrl, stable_url_id: str, *, validate_only: Optional[bool] = None + ) -> StableUrl: + """Create a new stable URL. + + :param parent: str + The parent resource. Format: accounts/{account_id}. + :param stable_url: :class:`StableUrl` + The stable URL to create. + :param stable_url_id: str + Client-provided identifier for the stable URL. Used to construct the resource name as + {parent}/stable-urls/{stable_url_id}. + :param validate_only: bool (optional) + When true, validates the request without creating the stable URL. + + :returns: :class:`StableUrl` + """ + + body = stable_url.as_dict() + query = {} + if stable_url_id is not None: + query["stable_url_id"] = stable_url_id + if validate_only is not None: + query["validate_only"] = validate_only + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do( + "POST", f"/api/disaster-recovery/v1/{parent}/stable-urls", query=query, body=body, headers=headers + ) + return StableUrl.from_dict(res) + + def delete_failover_group(self, name: str, *, etag: Optional[str] = None): + """Delete a failover group. + + :param name: str + The fully qualified resource name of the failover group to delete. Format: + accounts/{account_id}/failover-groups/{failover_group_id}. + :param etag: str (optional) + Opaque version string for optimistic locking. If provided, must match the current etag. If omitted, + the delete proceeds without an etag check. + + + """ + + query = {} + if etag is not None: + query["etag"] = etag + headers = { + "Accept": "application/json", + } + + self._api.do("DELETE", f"/api/disaster-recovery/v1/{name}", query=query, headers=headers) + + def delete_stable_url(self, name: str): + """Delete a stable URL. + + :param name: str + The fully qualified resource name. Format: accounts/{account_id}/stable-urls/{stable_url_id}. + + + """ + + headers = { + "Accept": "application/json", + } + + self._api.do("DELETE", f"/api/disaster-recovery/v1/{name}", headers=headers) + + def failover_failover_group( + self, + name: str, + target_primary_region: str, + failover_type: FailoverFailoverGroupRequestFailoverType, + *, + etag: Optional[str] = None, + ) -> FailoverGroup: + """Initiate a failover to a new primary region. + + :param name: str + The fully qualified resource name of the failover group to failover. Format: + accounts/{account_id}/failover-groups/{failover_group_id}. + :param target_primary_region: str + The target primary region. Must be one of the derived regions and different from the current + effective_primary_region. Serves as an idempotency check. + :param failover_type: :class:`FailoverFailoverGroupRequestFailoverType` + The type of failover to perform. + :param etag: str (optional) + Opaque version string for optimistic locking. If provided, must match the current etag. If omitted, + the failover proceeds regardless of current state. + + :returns: :class:`FailoverGroup` + """ + + body = {} + if etag is not None: + body["etag"] = etag + if failover_type is not None: + body["failover_type"] = failover_type.value + if target_primary_region is not None: + body["target_primary_region"] = target_primary_region + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do("POST", f"/api/disaster-recovery/v1/{name}/failover", body=body, headers=headers) + return FailoverGroup.from_dict(res) + + def get_failover_group(self, name: str) -> FailoverGroup: + """Get a failover group. + + :param name: str + The fully qualified resource name of the failover group. Format: + accounts/{account_id}/failover-groups/{failover_group_id}. + + :returns: :class:`FailoverGroup` + """ + + headers = { + "Accept": "application/json", + } + + res = self._api.do("GET", f"/api/disaster-recovery/v1/{name}", headers=headers) + return FailoverGroup.from_dict(res) + + def get_stable_url(self, name: str) -> StableUrl: + """Get a stable URL. + + :param name: str + The fully qualified resource name. Format: accounts/{account_id}/stable-urls/{stable_url_id}. + + :returns: :class:`StableUrl` + """ + + headers = { + "Accept": "application/json", + } + + res = self._api.do("GET", f"/api/disaster-recovery/v1/{name}", headers=headers) + return StableUrl.from_dict(res) + + def list_failover_groups( + self, parent: str, *, page_size: Optional[int] = None, page_token: Optional[str] = None + ) -> Iterator[FailoverGroup]: + """List failover groups. + + :param parent: str + The parent resource. Format: accounts/{account_id}. + :param page_size: int (optional) + Maximum number of failover groups to return per page. Default: 50, maximum: 100. + :param page_token: str (optional) + Page token received from a previous ListFailoverGroups call. Provide this to retrieve the subsequent + page. + + :returns: Iterator over :class:`FailoverGroup` + """ + + 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/disaster-recovery/v1/{parent}/failover-groups", query=query, headers=headers + ) + if "failover_groups" in json: + for v in json["failover_groups"]: + yield FailoverGroup.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 list_stable_urls( + self, parent: str, *, page_size: Optional[int] = None, page_token: Optional[str] = None + ) -> Iterator[StableUrl]: + """List stable URLs for an account. + + :param parent: str + The parent resource. Format: accounts/{account_id}. + :param page_size: int (optional) + Maximum number of stable URLs to return per page. Default: 50, maximum: 100. + :param page_token: str (optional) + Page token received from a previous ListStableUrls call. Provide this to retrieve the subsequent + page. + + :returns: Iterator over :class:`StableUrl` + """ + + 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/disaster-recovery/v1/{parent}/stable-urls", query=query, headers=headers) + if "stable_urls" in json: + for v in json["stable_urls"]: + yield StableUrl.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_failover_group(self, name: str, failover_group: FailoverGroup, update_mask: FieldMask) -> FailoverGroup: + """Update a failover group. + + :param name: str + Fully qualified resource name in the format + accounts/{account_id}/failover-groups/{failover_group_id}. + :param failover_group: :class:`FailoverGroup` + The failover group with updated fields. The name field identifies the resource and is populated from + the URL path. + :param update_mask: FieldMask + Comma-separated list of fields to update. + + :returns: :class:`FailoverGroup` + """ + + body = failover_group.as_dict() + query = {} + if update_mask is not None: + query["update_mask"] = update_mask.ToJsonString() + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do("PATCH", f"/api/disaster-recovery/v1/{name}", query=query, body=body, headers=headers) + return FailoverGroup.from_dict(res) diff --git a/databricks/sdk/service/knowledgeassistants.py b/databricks/sdk/service/knowledgeassistants.py index ea7d3ccef..e75f0103f 100755 --- a/databricks/sdk/service/knowledgeassistants.py +++ b/databricks/sdk/service/knowledgeassistants.py @@ -19,6 +19,76 @@ # all definitions in this file are in alphabetical order +@dataclass +class Example: + """An example associated with a Knowledge Assistant. Contains a question and guidelines for how the + assistant should respond.""" + + question: str + """The example question.""" + + guidelines: List[str] + """Guidelines for answering the question.""" + + create_time: Optional[Timestamp] = None + """Timestamp when this example was created.""" + + example_id: Optional[str] = None + """The universally unique identifier (UUID) of the example.""" + + name: Optional[str] = None + """Full resource name: knowledge-assistants/{knowledge_assistant_id}/examples/{example_id}""" + + update_time: Optional[Timestamp] = None + """Timestamp when this example was last updated.""" + + def as_dict(self) -> dict: + """Serializes the Example into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.create_time is not None: + body["create_time"] = self.create_time.ToJsonString() + if self.example_id is not None: + body["example_id"] = self.example_id + if self.guidelines: + body["guidelines"] = [v for v in self.guidelines] + if self.name is not None: + body["name"] = self.name + if self.question is not None: + body["question"] = self.question + if self.update_time is not None: + body["update_time"] = self.update_time.ToJsonString() + return body + + def as_shallow_dict(self) -> dict: + """Serializes the Example into a shallow dictionary of its immediate attributes.""" + body = {} + if self.create_time is not None: + body["create_time"] = self.create_time + if self.example_id is not None: + body["example_id"] = self.example_id + if self.guidelines: + body["guidelines"] = self.guidelines + if self.name is not None: + body["name"] = self.name + if self.question is not None: + body["question"] = self.question + if self.update_time is not None: + body["update_time"] = self.update_time + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> Example: + """Deserializes the Example from a dictionary.""" + return cls( + create_time=_timestamp(d, "create_time"), + example_id=d.get("example_id", None), + guidelines=d.get("guidelines", None), + name=d.get("name", None), + question=d.get("question", None), + update_time=_timestamp(d, "update_time"), + ) + + @dataclass class FileTableSpec: """FileTableSpec specifies a file table source configuration.""" @@ -620,6 +690,38 @@ class KnowledgeSourceState(Enum): UPDATING = "UPDATING" +@dataclass +class ListExamplesResponse: + """A list of Knowledge Assistant examples.""" + + examples: Optional[List[Example]] = None + + next_page_token: Optional[str] = None + + def as_dict(self) -> dict: + """Serializes the ListExamplesResponse into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.examples: + body["examples"] = [v.as_dict() for v in self.examples] + 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 ListExamplesResponse into a shallow dictionary of its immediate attributes.""" + body = {} + if self.examples: + body["examples"] = self.examples + 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]) -> ListExamplesResponse: + """Deserializes the ListExamplesResponse from a dictionary.""" + return cls(examples=_repeated_dict(d, "examples", Example), next_page_token=d.get("next_page_token", None)) + + @dataclass class ListKnowledgeAssistantsResponse: """A list of Knowledge Assistants.""" @@ -696,6 +798,31 @@ class KnowledgeAssistantsAPI: def __init__(self, api_client): self._api = api_client + def create_example(self, parent: str, example: Example) -> Example: + """Creates an example for a Knowledge Assistant. + + :param parent: str + Parent resource where this example will be created. Format: + knowledge-assistants/{knowledge_assistant_id} + :param example: :class:`Example` + The example to create under the parent Knowledge Assistant. + + :returns: :class:`Example` + """ + + body = example.as_dict() + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + cfg = self._api._cfg + if cfg.workspace_id: + headers["X-Databricks-Org-Id"] = cfg.workspace_id + + res = self._api.do("POST", f"/api/2.1/{parent}/examples", body=body, headers=headers) + return Example.from_dict(res) + def create_knowledge_assistant(self, knowledge_assistant: KnowledgeAssistant) -> KnowledgeAssistant: """Creates a Knowledge Assistant. @@ -742,6 +869,26 @@ def create_knowledge_source(self, parent: str, knowledge_source: KnowledgeSource res = self._api.do("POST", f"/api/2.1/{parent}/knowledge-sources", body=body, headers=headers) return KnowledgeSource.from_dict(res) + def delete_example(self, name: str): + """Deletes an example from a Knowledge Assistant. + + :param name: str + The resource name of the example to delete. Format: + knowledge-assistants/{knowledge_assistant_id}/examples/{example_id} + + + """ + + headers = { + "Accept": "application/json", + } + + cfg = self._api._cfg + if cfg.workspace_id: + headers["X-Databricks-Org-Id"] = cfg.workspace_id + + self._api.do("DELETE", f"/api/2.1/{name}", headers=headers) + def delete_knowledge_assistant(self, name: str): """Deletes a Knowledge Assistant. @@ -782,6 +929,27 @@ def delete_knowledge_source(self, name: str): self._api.do("DELETE", f"/api/2.1/{name}", headers=headers) + def get_example(self, name: str) -> Example: + """Gets an example from a Knowledge Assistant. + + :param name: str + The resource name of the example. Format: + knowledge-assistants/{knowledge_assistant_id}/examples/{example_id} + + :returns: :class:`Example` + """ + + headers = { + "Accept": "application/json", + } + + cfg = self._api._cfg + if cfg.workspace_id: + headers["X-Databricks-Org-Id"] = cfg.workspace_id + + res = self._api.do("GET", f"/api/2.1/{name}", headers=headers) + return Example.from_dict(res) + def get_knowledge_assistant(self, name: str) -> KnowledgeAssistant: """Gets a Knowledge Assistant. @@ -870,6 +1038,45 @@ def get_permissions(self, knowledge_assistant_id: str) -> KnowledgeAssistantPerm ) return KnowledgeAssistantPermissions.from_dict(res) + def list_examples( + self, parent: str, *, page_size: Optional[int] = None, page_token: Optional[str] = None + ) -> Iterator[Example]: + """Lists examples under a Knowledge Assistant. + + :param parent: str + Parent resource to list from. Format: knowledge-assistants/{knowledge_assistant_id} + :param page_size: int (optional) + The maximum number of examples to return. If unspecified, at most 100 examples will be returned. The + maximum value is 100; values above 100 will be coerced to 100. + :param page_token: str (optional) + A page token, received from a previous `ListExamples` call. Provide this to retrieve the subsequent + page. If unspecified, the first page will be returned. + + :returns: Iterator over :class:`Example` + """ + + 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", + } + + cfg = self._api._cfg + if cfg.workspace_id: + headers["X-Databricks-Org-Id"] = cfg.workspace_id + + while True: + json = self._api.do("GET", f"/api/2.1/{parent}/examples", query=query, headers=headers) + if "examples" in json: + for v in json["examples"]: + yield Example.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 list_knowledge_assistants( self, *, page_size: Optional[int] = None, page_token: Optional[str] = None ) -> Iterator[KnowledgeAssistant]: @@ -995,6 +1202,36 @@ def sync_knowledge_sources(self, name: str): self._api.do("POST", f"/api/2.1/{name}/knowledge-sources:sync", headers=headers) + def update_example(self, name: str, example: Example, update_mask: FieldMask) -> Example: + """Updates an example in a Knowledge Assistant. + + :param name: str + The resource name of the example to update. Format: + knowledge-assistants/{knowledge_assistant_id}/examples/{example_id} + :param example: :class:`Example` + :param update_mask: FieldMask + Comma-delimited list of fields to update on the example. Allowed values: `question`, `guidelines`. + Examples: - `question` - `question,guidelines` + + :returns: :class:`Example` + """ + + body = example.as_dict() + query = {} + if update_mask is not None: + query["update_mask"] = update_mask.ToJsonString() + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + cfg = self._api._cfg + if cfg.workspace_id: + headers["X-Databricks-Org-Id"] = cfg.workspace_id + + res = self._api.do("PATCH", f"/api/2.1/{name}", query=query, body=body, headers=headers) + return Example.from_dict(res) + def update_knowledge_assistant( self, name: str, knowledge_assistant: KnowledgeAssistant, update_mask: FieldMask ) -> KnowledgeAssistant: diff --git a/databricks/sdk/service/ml.py b/databricks/sdk/service/ml.py index 3b48657d4..19d20bcd6 100755 --- a/databricks/sdk/service/ml.py +++ b/databricks/sdk/service/ml.py @@ -455,14 +455,20 @@ def from_dict(cls, d: Dict[str, Any]) -> AvgFunction: @dataclass class BackfillSource: + delta_table_name: Optional[str] = None + """The full three-part name (catalog, schema, name) of the Delta table containing the historical + data to backfill.""" + delta_table_source: Optional[DeltaTableSource] = None - """The Delta table source containing the historic data to backfill. Only the delta table name is - used for backfill, the entity columns and timeseries column are ignored as they are defined by - the associated KafkaSource.""" + """Deprecated: Use delta_table_name instead. Kept for backwards compatibility. The Delta table + source containing the historical data to backfill. Only the delta table name is used for + backfill, other fields are ignored.""" def as_dict(self) -> dict: """Serializes the BackfillSource into a dictionary suitable for use as a JSON request body.""" body = {} + if self.delta_table_name is not None: + body["delta_table_name"] = self.delta_table_name if self.delta_table_source: body["delta_table_source"] = self.delta_table_source.as_dict() return body @@ -470,6 +476,8 @@ def as_dict(self) -> dict: def as_shallow_dict(self) -> dict: """Serializes the BackfillSource into a shallow dictionary of its immediate attributes.""" body = {} + if self.delta_table_name is not None: + body["delta_table_name"] = self.delta_table_name if self.delta_table_source: body["delta_table_source"] = self.delta_table_source return body @@ -477,7 +485,10 @@ def as_shallow_dict(self) -> dict: @classmethod def from_dict(cls, d: Dict[str, Any]) -> BackfillSource: """Deserializes the BackfillSource from a dictionary.""" - return cls(delta_table_source=_from_dict(d, "delta_table_source", DeltaTableSource)) + return cls( + delta_table_name=d.get("delta_table_name", None), + delta_table_source=_from_dict(d, "delta_table_source", DeltaTableSource), + ) @dataclass diff --git a/databricks/sdk/service/pipelines.py b/databricks/sdk/service/pipelines.py index 36d55e7e6..5d9ea0fe2 100755 --- a/databricks/sdk/service/pipelines.py +++ b/databricks/sdk/service/pipelines.py @@ -107,6 +107,33 @@ def from_dict(cls, d: Dict[str, Any]) -> ClonePipelineResponse: return cls(pipeline_id=d.get("pipeline_id", None)) +@dataclass +class ConfluenceConnectorOptions: + """Confluence specific options for ingestion""" + + include_confluence_spaces: Optional[List[str]] = None + """(Optional) Spaces to filter Confluence data on""" + + def as_dict(self) -> dict: + """Serializes the ConfluenceConnectorOptions into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.include_confluence_spaces: + body["include_confluence_spaces"] = [v for v in self.include_confluence_spaces] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the ConfluenceConnectorOptions into a shallow dictionary of its immediate attributes.""" + body = {} + if self.include_confluence_spaces: + body["include_confluence_spaces"] = self.include_confluence_spaces + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> ConfluenceConnectorOptions: + """Deserializes the ConfluenceConnectorOptions from a dictionary.""" + return cls(include_confluence_spaces=d.get("include_confluence_spaces", None)) + + @dataclass class ConnectionParameters: source_catalog: Optional[str] = None @@ -138,6 +165,8 @@ def from_dict(cls, d: Dict[str, Any]) -> ConnectionParameters: class ConnectorOptions: """Wrapper message for source-specific options to support multiple connector types""" + confluence_options: Optional[ConfluenceConnectorOptions] = None + gdrive_options: Optional[GoogleDriveOptions] = None google_ads_options: Optional[GoogleAdsOptions] = None @@ -155,6 +184,8 @@ class ConnectorOptions: def as_dict(self) -> dict: """Serializes the ConnectorOptions into a dictionary suitable for use as a JSON request body.""" body = {} + if self.confluence_options: + body["confluence_options"] = self.confluence_options.as_dict() if self.gdrive_options: body["gdrive_options"] = self.gdrive_options.as_dict() if self.google_ads_options: @@ -174,6 +205,8 @@ def as_dict(self) -> dict: def as_shallow_dict(self) -> dict: """Serializes the ConnectorOptions into a shallow dictionary of its immediate attributes.""" body = {} + if self.confluence_options: + body["confluence_options"] = self.confluence_options if self.gdrive_options: body["gdrive_options"] = self.gdrive_options if self.google_ads_options: @@ -194,6 +227,7 @@ def as_shallow_dict(self) -> dict: def from_dict(cls, d: Dict[str, Any]) -> ConnectorOptions: """Deserializes the ConnectorOptions from a dictionary.""" return cls( + confluence_options=_from_dict(d, "confluence_options", ConfluenceConnectorOptions), gdrive_options=_from_dict(d, "gdrive_options", GoogleDriveOptions), google_ads_options=_from_dict(d, "google_ads_options", GoogleAdsOptions), jira_options=_from_dict(d, "jira_options", JiraConnectorOptions), @@ -1432,6 +1466,7 @@ def from_dict(cls, d: Dict[str, Any]) -> IngestionPipelineDefinitionWorkdayRepor class IngestionSourceType(Enum): BIGQUERY = "BIGQUERY" + CONFLUENCE = "CONFLUENCE" DYNAMICS365 = "DYNAMICS365" FOREIGN_CATALOG = "FOREIGN_CATALOG" GA4_RAW_DATA = "GA4_RAW_DATA" diff --git a/databricks/sdk/service/postgres.py b/databricks/sdk/service/postgres.py index 71b0ac4b5..f1df29842 100755 --- a/databricks/sdk/service/postgres.py +++ b/databricks/sdk/service/postgres.py @@ -123,7 +123,8 @@ def from_dict(cls, d: Dict[str, Any]) -> BranchOperationMetadata: @dataclass class BranchSpec: expire_time: Optional[Timestamp] = None - """Absolute expiration timestamp. When set, the branch will expire at this time.""" + """Absolute expiration timestamp. When set, the branch will expire at this time. Mutually exclusive + with `ttl` and `no_expiry`. When updating, use `spec.expiration` in the update_mask.""" is_protected: Optional[bool] = None """When set to true, protects the branch from deletion and reset. Associated compute endpoints and @@ -131,7 +132,8 @@ class BranchSpec: no_expiry: Optional[bool] = None """Explicitly disable expiration. When set to true, the branch will not expire. If set to false, - the request is invalid; provide either ttl or expire_time instead.""" + the request is invalid; provide either ttl or expire_time instead. Mutually exclusive with + `expire_time` and `ttl`. When updating, use `spec.expiration` in the update_mask.""" source_branch: Optional[str] = None """The name of the source branch from which this branch was created (data lineage for point-in-time @@ -145,7 +147,9 @@ class BranchSpec: """The point in time on the source branch from which this branch was created.""" ttl: Optional[Duration] = None - """Relative time-to-live duration. When set, the branch will expire at creation_time + ttl.""" + """Relative time-to-live duration. When set, the branch will expire at creation_time + ttl. + Mutually exclusive with `expire_time` and `no_expiry`. When updating, use `spec.expiration` in + the update_mask.""" def as_dict(self) -> dict: """Serializes the BranchSpec into a dictionary suitable for use as a JSON request body.""" @@ -1114,13 +1118,15 @@ class EndpointSpec: no_suspension: Optional[bool] = None """When set to true, explicitly disables automatic suspension (never suspend). Should be set to - true when provided.""" + true when provided. Mutually exclusive with `suspend_timeout_duration`. When updating, use + `spec.suspension` in the update_mask.""" settings: Optional[EndpointSettings] = None suspend_timeout_duration: Optional[Duration] = None """Duration of inactivity after which the compute endpoint is automatically suspended. If specified - should be between 60s and 604800s (1 minute to 1 week).""" + should be between 60s and 604800s (1 minute to 1 week). Mutually exclusive with `no_suspension`. + When updating, use `spec.suspension` in the update_mask.""" def as_dict(self) -> dict: """Serializes the EndpointSpec into a dictionary suitable for use as a JSON request body.""" @@ -1703,7 +1709,7 @@ class Project: otherwise set to a timestamp in the past.""" initial_endpoint_spec: Optional[InitialEndpointSpec] = None - """Configuration settings for the initial Read/Write endpoint created inside the default branch for + """Configuration settings for the initial Read/Write endpoint created inside the initial branch for a newly created project. If omitted, the initial endpoint created will have default settings, without high availability configured. This field does not apply to any endpoints created after project creation. Use spec.default_endpoint_settings to configure default settings for endpoints @@ -1835,14 +1841,16 @@ class ProjectDefaultEndpointSettings: no_suspension: Optional[bool] = None """When set to true, explicitly disables automatic suspension (never suspend). Should be set to - true when provided.""" + true when provided. Mutually exclusive with `suspend_timeout_duration`. When updating, use + `spec.project_default_settings.suspension` in the update_mask.""" pg_settings: Optional[Dict[str, str]] = None """A raw representation of Postgres settings.""" suspend_timeout_duration: Optional[Duration] = None """Duration of inactivity after which the compute endpoint is automatically suspended. If specified - should be between 60s and 604800s (1 minute to 1 week).""" + should be between 60s and 604800s (1 minute to 1 week). Mutually exclusive with `no_suspension`. + When updating, use `spec.project_default_settings.suspension` in the update_mask.""" def as_dict(self) -> dict: """Serializes the ProjectDefaultEndpointSettings into a dictionary suitable for use as a JSON request body.""" @@ -2124,7 +2132,7 @@ class ProvisioningInfoState(Enum): class ProvisioningPhase(Enum): - """Copied from database_table_statuses.proto to decouple SDK packages.""" + """The current phase of the data synchronization pipeline.""" PROVISIONING_PHASE_INDEX_SCAN = "PROVISIONING_PHASE_INDEX_SCAN" PROVISIONING_PHASE_INDEX_SORT = "PROVISIONING_PHASE_INDEX_SORT" @@ -2172,6 +2180,7 @@ class RequestedClaimsPermissionSet(Enum): @dataclass class RequestedResource: table_name: Optional[str] = None + """The full Unity Catalog table name.""" unspecified_resource_name: Optional[str] = None @@ -2361,7 +2370,18 @@ class RoleRoleSpec: """The desired API-exposed Postgres role attribute to associate with the role. Optional.""" auth_method: Optional[RoleAuthMethod] = None - """If auth_method is left unspecified, a meaningful authentication method is derived from the + """Controls how the Postgres role authenticates when a client opens a database connection. + Supported values: + + * LAKEBASE_OAUTH_V1: the role authenticates by presenting a Databricks OAuth access token + derived from the backing managed identity (the Databricks user, service principal, or group + named by the role's `postgres_role`). No static password exists for roles using this method. * + PG_PASSWORD_SCRAM_SHA_256: the role authenticates with a Postgres password verified server-side + using the SCRAM-SHA-256 mechanism. Lakebase generates a password for the role. * NO_LOGIN: the + role cannot open a Postgres session at all. Useful for roles that exist only to own objects or + to aggregate privileges that are then granted to other, loginable roles. + + If auth_method is left unspecified, a meaningful authentication method is derived from the identity_type: * For the managed identities, OAUTH is used. * For the regular postgres roles, authentication based on postgres passwords is used. @@ -2698,7 +2718,7 @@ def from_dict(cls, d: Dict[str, Any]) -> SyncedTablePosition: class SyncedTableState(Enum): - """The state of a synced table. Copied from database_table_statuses.proto to decouple SDK packages.""" + """The state of a synced table.""" SYNCED_TABLE_OFFLINE = "SYNCED_TABLE_OFFLINE" SYNCED_TABLE_OFFLINE_FAILED = "SYNCED_TABLE_OFFLINE_FAILED" @@ -3550,8 +3570,8 @@ def get_synced_table(self, name: str) -> SyncedTable: """Get a Synced Table. :param name: str - Format: "synced_tables/{catalog}.{schema}.{table}", where (catalog, schema, table) are the entity - names in the Unity Catalog. + The Full resource name of the synced table. Format: "synced_tables/{catalog}.{schema}.{table}", + where (catalog, schema, table) are the entity names in the Unity Catalog. :returns: :class:`SyncedTable` """ diff --git a/databricks/sdk/service/serving.py b/databricks/sdk/service/serving.py index 6104be8a2..24efa2c3c 100755 --- a/databricks/sdk/service/serving.py +++ b/databricks/sdk/service/serving.py @@ -580,6 +580,9 @@ def from_dict(cls, d: Dict[str, Any]) -> ApiKeyAuth: @dataclass class AutoCaptureConfigInput: + """Deprecated: legacy inference table configuration. Please use AI Gateway inference tables + instead. See https://docs.databricks.com/aws/en/ai-gateway/inference-tables.""" + catalog_name: Optional[str] = None """The name of the catalog in Unity Catalog. NOTE: On update, you cannot change the catalog name if the inference table is already enabled.""" @@ -634,6 +637,9 @@ def from_dict(cls, d: Dict[str, Any]) -> AutoCaptureConfigInput: @dataclass class AutoCaptureConfigOutput: + """Deprecated: legacy inference table configuration. Please use AI Gateway inference tables + instead. See https://docs.databricks.com/aws/en/ai-gateway/inference-tables.""" + catalog_name: Optional[str] = None """The name of the catalog in Unity Catalog. NOTE: On update, you cannot change the catalog name if the inference table is already enabled.""" @@ -1127,10 +1133,9 @@ class EndpointCoreConfigInput: """The name of the serving endpoint to update. This field is required.""" auto_capture_config: Optional[AutoCaptureConfigInput] = None - """Configuration for Inference Tables which automatically logs requests and responses to Unity - Catalog. Note: this field is deprecated for creating new provisioned throughput endpoints, or - updating existing provisioned throughput endpoints that never have inference table configured; - in these cases please use AI Gateway to manage inference tables.""" + """Configuration for legacy Inference Tables which automatically log requests and responses to + Unity Catalog. Deprecated: please use AI Gateway inference tables instead. See + https://docs.databricks.com/aws/en/ai-gateway/inference-tables.""" served_entities: Optional[List[ServedEntityInput]] = None """The list of served entities under the serving endpoint config.""" @@ -1187,10 +1192,9 @@ def from_dict(cls, d: Dict[str, Any]) -> EndpointCoreConfigInput: @dataclass class EndpointCoreConfigOutput: auto_capture_config: Optional[AutoCaptureConfigOutput] = None - """Configuration for Inference Tables which automatically logs requests and responses to Unity - Catalog. Note: this field is deprecated for creating new provisioned throughput endpoints, or - updating existing provisioned throughput endpoints that never have inference table configured; - in these cases please use AI Gateway to manage inference tables.""" + """Configuration for legacy Inference Tables which automatically log requests and responses to + Unity Catalog. Deprecated: please use AI Gateway inference tables instead. See + https://docs.databricks.com/aws/en/ai-gateway/inference-tables.""" config_version: Optional[int] = None """The config version that the serving endpoint is currently serving.""" @@ -1286,10 +1290,9 @@ def from_dict(cls, d: Dict[str, Any]) -> EndpointCoreConfigSummary: @dataclass class EndpointPendingConfig: auto_capture_config: Optional[AutoCaptureConfigOutput] = None - """Configuration for Inference Tables which automatically logs requests and responses to Unity - Catalog. Note: this field is deprecated for creating new provisioned throughput endpoints, or - updating existing provisioned throughput endpoints that never have inference table configured; - in these cases please use AI Gateway to manage inference tables.""" + """Configuration for legacy Inference Tables which automatically log requests and responses to + Unity Catalog. Deprecated: please use AI Gateway inference tables instead. See + https://docs.databricks.com/aws/en/ai-gateway/inference-tables.""" config_version: Optional[int] = None """The config version that the serving endpoint is currently serving.""" @@ -4809,10 +4812,9 @@ def update_config( :param name: str The name of the serving endpoint to update. This field is required. :param auto_capture_config: :class:`AutoCaptureConfigInput` (optional) - Configuration for Inference Tables which automatically logs requests and responses to Unity Catalog. - Note: this field is deprecated for creating new provisioned throughput endpoints, or updating - existing provisioned throughput endpoints that never have inference table configured; in these cases - please use AI Gateway to manage inference tables. + Configuration for legacy Inference Tables which automatically log requests and responses to Unity + Catalog. Deprecated: please use AI Gateway inference tables instead. See + https://docs.databricks.com/aws/en/ai-gateway/inference-tables. :param served_entities: List[:class:`ServedEntityInput`] (optional) The list of served entities under the serving endpoint config. :param served_models: List[:class:`ServedModelInput`] (optional) diff --git a/databricks/sdk/service/supervisoragents.py b/databricks/sdk/service/supervisoragents.py index d87937662..5b5acae10 100755 --- a/databricks/sdk/service/supervisoragents.py +++ b/databricks/sdk/service/supervisoragents.py @@ -45,33 +45,6 @@ def from_dict(cls, d: Dict[str, Any]) -> App: return cls(name=d.get("name", None)) -@dataclass -class Connection: - """Deprecated: Use UcConnection instead. Databricks connection. Supported connection: external mcp - server.""" - - name: Optional[str] = None - - def as_dict(self) -> dict: - """Serializes the Connection 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 Connection 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]) -> Connection: - """Deserializes the Connection from a dictionary.""" - return cls(name=d.get("name", None)) - - @dataclass class GenieSpace: id: str @@ -202,15 +175,15 @@ class SupervisorAgent: display_name: str """The display name of the Supervisor Agent, unique at workspace level.""" - description: str - """Description of what this agent can do (user-facing).""" - create_time: Optional[Timestamp] = None """Creation timestamp.""" creator: Optional[str] = None """The creator of the Supervisor Agent.""" + description: Optional[str] = None + """Description of what this agent can do (user-facing).""" + endpoint_name: Optional[str] = None """The name of the supervisor agent's serving endpoint.""" @@ -307,9 +280,6 @@ class Tool: app: Optional[App] = None - connection: Optional[Connection] = None - """Deprecated: Use uc_connection instead.""" - genie_space: Optional[GenieSpace] = None id: Optional[str] = None @@ -334,8 +304,6 @@ def as_dict(self) -> dict: body = {} if self.app: body["app"] = self.app.as_dict() - if self.connection: - body["connection"] = self.connection.as_dict() if self.description is not None: body["description"] = self.description if self.genie_space: @@ -363,8 +331,6 @@ def as_shallow_dict(self) -> dict: body = {} if self.app: body["app"] = self.app - if self.connection: - body["connection"] = self.connection if self.description is not None: body["description"] = self.description if self.genie_space: @@ -392,7 +358,6 @@ def from_dict(cls, d: Dict[str, Any]) -> Tool: """Deserializes the Tool from a dictionary.""" return cls( app=_from_dict(d, "app", App), - connection=_from_dict(d, "connection", Connection), description=d.get("description", None), genie_space=_from_dict(d, "genie_space", GenieSpace), id=d.get("id", None), diff --git a/docs/account/disasterrecovery/disaster_recovery.rst b/docs/account/disasterrecovery/disaster_recovery.rst new file mode 100755 index 000000000..517726ff1 --- /dev/null +++ b/docs/account/disasterrecovery/disaster_recovery.rst @@ -0,0 +1,151 @@ +``a.disaster_recovery``: DisasterRecovery.v1 +============================================ +.. currentmodule:: databricks.sdk.service.disasterrecovery + +.. py:class:: DisasterRecoveryAPI + + Manage disaster recovery configurations and execute failover operations. + + .. py:method:: create_failover_group(parent: str, failover_group: FailoverGroup, failover_group_id: str [, validate_only: Optional[bool]]) -> FailoverGroup + + Create a new failover group. + + :param parent: str + The parent resource. Format: accounts/{account_id}. + :param failover_group: :class:`FailoverGroup` + The failover group to create. + :param failover_group_id: str + Client-provided identifier for the failover group. Used to construct the resource name as + {parent}/failover-groups/{failover_group_id}. + :param validate_only: bool (optional) + When true, validates the request without creating the failover group. + + :returns: :class:`FailoverGroup` + + + .. py:method:: create_stable_url(parent: str, stable_url: StableUrl, stable_url_id: str [, validate_only: Optional[bool]]) -> StableUrl + + Create a new stable URL. + + :param parent: str + The parent resource. Format: accounts/{account_id}. + :param stable_url: :class:`StableUrl` + The stable URL to create. + :param stable_url_id: str + Client-provided identifier for the stable URL. Used to construct the resource name as + {parent}/stable-urls/{stable_url_id}. + :param validate_only: bool (optional) + When true, validates the request without creating the stable URL. + + :returns: :class:`StableUrl` + + + .. py:method:: delete_failover_group(name: str [, etag: Optional[str]]) + + Delete a failover group. + + :param name: str + The fully qualified resource name of the failover group to delete. Format: + accounts/{account_id}/failover-groups/{failover_group_id}. + :param etag: str (optional) + Opaque version string for optimistic locking. If provided, must match the current etag. If omitted, + the delete proceeds without an etag check. + + + + + .. py:method:: delete_stable_url(name: str) + + Delete a stable URL. + + :param name: str + The fully qualified resource name. Format: accounts/{account_id}/stable-urls/{stable_url_id}. + + + + + .. py:method:: failover_failover_group(name: str, target_primary_region: str, failover_type: FailoverFailoverGroupRequestFailoverType [, etag: Optional[str]]) -> FailoverGroup + + Initiate a failover to a new primary region. + + :param name: str + The fully qualified resource name of the failover group to failover. Format: + accounts/{account_id}/failover-groups/{failover_group_id}. + :param target_primary_region: str + The target primary region. Must be one of the derived regions and different from the current + effective_primary_region. Serves as an idempotency check. + :param failover_type: :class:`FailoverFailoverGroupRequestFailoverType` + The type of failover to perform. + :param etag: str (optional) + Opaque version string for optimistic locking. If provided, must match the current etag. If omitted, + the failover proceeds regardless of current state. + + :returns: :class:`FailoverGroup` + + + .. py:method:: get_failover_group(name: str) -> FailoverGroup + + Get a failover group. + + :param name: str + The fully qualified resource name of the failover group. Format: + accounts/{account_id}/failover-groups/{failover_group_id}. + + :returns: :class:`FailoverGroup` + + + .. py:method:: get_stable_url(name: str) -> StableUrl + + Get a stable URL. + + :param name: str + The fully qualified resource name. Format: accounts/{account_id}/stable-urls/{stable_url_id}. + + :returns: :class:`StableUrl` + + + .. py:method:: list_failover_groups(parent: str [, page_size: Optional[int], page_token: Optional[str]]) -> Iterator[FailoverGroup] + + List failover groups. + + :param parent: str + The parent resource. Format: accounts/{account_id}. + :param page_size: int (optional) + Maximum number of failover groups to return per page. Default: 50, maximum: 100. + :param page_token: str (optional) + Page token received from a previous ListFailoverGroups call. Provide this to retrieve the subsequent + page. + + :returns: Iterator over :class:`FailoverGroup` + + + .. py:method:: list_stable_urls(parent: str [, page_size: Optional[int], page_token: Optional[str]]) -> Iterator[StableUrl] + + List stable URLs for an account. + + :param parent: str + The parent resource. Format: accounts/{account_id}. + :param page_size: int (optional) + Maximum number of stable URLs to return per page. Default: 50, maximum: 100. + :param page_token: str (optional) + Page token received from a previous ListStableUrls call. Provide this to retrieve the subsequent + page. + + :returns: Iterator over :class:`StableUrl` + + + .. py:method:: update_failover_group(name: str, failover_group: FailoverGroup, update_mask: FieldMask) -> FailoverGroup + + Update a failover group. + + :param name: str + Fully qualified resource name in the format + accounts/{account_id}/failover-groups/{failover_group_id}. + :param failover_group: :class:`FailoverGroup` + The failover group with updated fields. The name field identifies the resource and is populated from + the URL path. + :param update_mask: FieldMask + Comma-separated list of fields to update. + + :returns: :class:`FailoverGroup` + \ No newline at end of file diff --git a/docs/account/disasterrecovery/index.rst b/docs/account/disasterrecovery/index.rst new file mode 100755 index 000000000..af4dcff19 --- /dev/null +++ b/docs/account/disasterrecovery/index.rst @@ -0,0 +1,10 @@ + +Disaster Recovery +================= + +Manage disaster recovery configurations and execute failover operations. + +.. toctree:: + :maxdepth: 1 + + disaster_recovery \ No newline at end of file diff --git a/docs/account/index.rst b/docs/account/index.rst old mode 100644 new mode 100755 index a0db95c4d..23b262482 --- a/docs/account/index.rst +++ b/docs/account/index.rst @@ -9,6 +9,7 @@ These APIs are available from AccountClient billing/index catalog/index + disasterrecovery/index iam/index iamv2/index networking/index diff --git a/docs/dbdataclasses/catalog.rst b/docs/dbdataclasses/catalog.rst index f34f26920..0aaac9b29 100755 --- a/docs/dbdataclasses/catalog.rst +++ b/docs/dbdataclasses/catalog.rst @@ -283,11 +283,14 @@ These dataclasses are used in the SDK to represent API requests and responses fo .. py:class:: ConnectionType - Next Id: 123 + Next Id: 124 .. py:attribute:: BIGQUERY :value: "BIGQUERY" + .. py:attribute:: CONFLUENCE + :value: "CONFLUENCE" + .. py:attribute:: DATABRICKS :value: "DATABRICKS" @@ -1527,7 +1530,7 @@ These dataclasses are used in the SDK to represent API requests and responses fo .. py:class:: SecurableKind - Latest kind: TOOLSET_EXTERNAL_MCP = 318; Next id: 319 + Latest kind: CONNECTION_SLACK_ACCESS_AND_INTEGRATION_LOGS_OAUTH_U2M = 319; Next id: 320 .. py:attribute:: TABLE_DB_STORAGE :value: "TABLE_DB_STORAGE" diff --git a/docs/dbdataclasses/disasterrecovery.rst b/docs/dbdataclasses/disasterrecovery.rst new file mode 100755 index 000000000..7fb8f2cc8 --- /dev/null +++ b/docs/dbdataclasses/disasterrecovery.rst @@ -0,0 +1,76 @@ +Disaster Recovery +================= + +These dataclasses are used in the SDK to represent API requests and responses for services in the ``databricks.sdk.service.disasterrecovery`` module. + +.. py:currentmodule:: databricks.sdk.service.disasterrecovery +.. py:class:: FailoverFailoverGroupRequestFailoverType + + The type of failover to perform. + + .. py:attribute:: FORCED + :value: "FORCED" + +.. autoclass:: FailoverGroup + :members: + :undoc-members: + +.. py:class:: FailoverGroupState + + The aggregate state of a FailoverGroup. + + .. py:attribute:: ACTIVE + :value: "ACTIVE" + + .. py:attribute:: CREATING + :value: "CREATING" + + .. py:attribute:: CREATION_FAILED + :value: "CREATION_FAILED" + + .. py:attribute:: DELETING + :value: "DELETING" + + .. py:attribute:: DELETION_FAILED + :value: "DELETION_FAILED" + + .. py:attribute:: FAILING_OVER + :value: "FAILING_OVER" + + .. py:attribute:: FAILOVER_FAILED + :value: "FAILOVER_FAILED" + + .. py:attribute:: INITIAL_REPLICATION + :value: "INITIAL_REPLICATION" + +.. autoclass:: ListFailoverGroupsResponse + :members: + :undoc-members: + +.. autoclass:: ListStableUrlsResponse + :members: + :undoc-members: + +.. autoclass:: LocationMapping + :members: + :undoc-members: + +.. autoclass:: LocationMappingEntry + :members: + :undoc-members: + +.. autoclass:: StableUrl + :members: + :undoc-members: + +.. autoclass:: UcCatalog + :members: + :undoc-members: + +.. autoclass:: UcReplicationConfig + :members: + :undoc-members: + +.. autoclass:: WorkspaceSet + :members: + :undoc-members: diff --git a/docs/dbdataclasses/index.rst b/docs/dbdataclasses/index.rst index 0bf3b115e..619db5c8a 100755 --- a/docs/dbdataclasses/index.rst +++ b/docs/dbdataclasses/index.rst @@ -15,6 +15,7 @@ Dataclasses database dataclassification dataquality + disasterrecovery environments files iam diff --git a/docs/dbdataclasses/knowledgeassistants.rst b/docs/dbdataclasses/knowledgeassistants.rst index f69c68a7e..7980311a7 100755 --- a/docs/dbdataclasses/knowledgeassistants.rst +++ b/docs/dbdataclasses/knowledgeassistants.rst @@ -4,6 +4,10 @@ Knowledge Assistants These dataclasses are used in the SDK to represent API requests and responses for services in the ``databricks.sdk.service.knowledgeassistants`` module. .. py:currentmodule:: databricks.sdk.service.knowledgeassistants +.. autoclass:: Example + :members: + :undoc-members: + .. autoclass:: FileTableSpec :members: :undoc-members: @@ -80,6 +84,10 @@ These dataclasses are used in the SDK to represent API requests and responses fo .. py:attribute:: UPDATING :value: "UPDATING" +.. autoclass:: ListExamplesResponse + :members: + :undoc-members: + .. autoclass:: ListKnowledgeAssistantsResponse :members: :undoc-members: diff --git a/docs/dbdataclasses/pipelines.rst b/docs/dbdataclasses/pipelines.rst index 2dd2355d5..1490968a8 100755 --- a/docs/dbdataclasses/pipelines.rst +++ b/docs/dbdataclasses/pipelines.rst @@ -23,6 +23,10 @@ These dataclasses are used in the SDK to represent API requests and responses fo :members: :undoc-members: +.. autoclass:: ConfluenceConnectorOptions + :members: + :undoc-members: + .. autoclass:: ConnectionParameters :members: :undoc-members: @@ -256,6 +260,9 @@ These dataclasses are used in the SDK to represent API requests and responses fo .. py:attribute:: BIGQUERY :value: "BIGQUERY" + .. py:attribute:: CONFLUENCE + :value: "CONFLUENCE" + .. py:attribute:: DYNAMICS365 :value: "DYNAMICS365" diff --git a/docs/dbdataclasses/postgres.rst b/docs/dbdataclasses/postgres.rst index c5ad9a0fa..488dbce4f 100755 --- a/docs/dbdataclasses/postgres.rst +++ b/docs/dbdataclasses/postgres.rst @@ -466,7 +466,7 @@ These dataclasses are used in the SDK to represent API requests and responses fo .. py:class:: ProvisioningPhase - Copied from database_table_statuses.proto to decouple SDK packages. + The current phase of the data synchronization pipeline. .. py:attribute:: PROVISIONING_PHASE_INDEX_SCAN :value: "PROVISIONING_PHASE_INDEX_SCAN" @@ -561,7 +561,7 @@ These dataclasses are used in the SDK to represent API requests and responses fo .. py:class:: SyncedTableState - The state of a synced table. Copied from database_table_statuses.proto to decouple SDK packages. + The state of a synced table. .. py:attribute:: SYNCED_TABLE_OFFLINE :value: "SYNCED_TABLE_OFFLINE" diff --git a/docs/dbdataclasses/supervisoragents.rst b/docs/dbdataclasses/supervisoragents.rst index 116fc82e4..ff02cd17c 100755 --- a/docs/dbdataclasses/supervisoragents.rst +++ b/docs/dbdataclasses/supervisoragents.rst @@ -8,10 +8,6 @@ These dataclasses are used in the SDK to represent API requests and responses fo :members: :undoc-members: -.. autoclass:: Connection - :members: - :undoc-members: - .. autoclass:: GenieSpace :members: :undoc-members: diff --git a/docs/packages.py b/docs/packages.py index 48b0331ef..0c4fbae46 100755 --- a/docs/packages.py +++ b/docs/packages.py @@ -102,4 +102,9 @@ class Package: "Environments", "Manage workspace-level environments including base environments for serverless notebooks and jobs.", ), + Package( + "disasterrecovery", + "Disaster Recovery", + "Manage disaster recovery configurations and execute failover operations.", + ), ] \ No newline at end of file diff --git a/docs/workspace/knowledgeassistants/knowledge_assistants.rst b/docs/workspace/knowledgeassistants/knowledge_assistants.rst index b6dd30101..63caa6c70 100755 --- a/docs/workspace/knowledgeassistants/knowledge_assistants.rst +++ b/docs/workspace/knowledgeassistants/knowledge_assistants.rst @@ -6,6 +6,19 @@ Manage Knowledge Assistants and related resources. + .. py:method:: create_example(parent: str, example: Example) -> Example + + Creates an example for a Knowledge Assistant. + + :param parent: str + Parent resource where this example will be created. Format: + knowledge-assistants/{knowledge_assistant_id} + :param example: :class:`Example` + The example to create under the parent Knowledge Assistant. + + :returns: :class:`Example` + + .. py:method:: create_knowledge_assistant(knowledge_assistant: KnowledgeAssistant) -> KnowledgeAssistant Creates a Knowledge Assistant. @@ -28,6 +41,17 @@ :returns: :class:`KnowledgeSource` + .. py:method:: delete_example(name: str) + + Deletes an example from a Knowledge Assistant. + + :param name: str + The resource name of the example to delete. Format: + knowledge-assistants/{knowledge_assistant_id}/examples/{example_id} + + + + .. py:method:: delete_knowledge_assistant(name: str) Deletes a Knowledge Assistant. @@ -50,6 +74,17 @@ + .. py:method:: get_example(name: str) -> Example + + Gets an example from a Knowledge Assistant. + + :param name: str + The resource name of the example. Format: + knowledge-assistants/{knowledge_assistant_id}/examples/{example_id} + + :returns: :class:`Example` + + .. py:method:: get_knowledge_assistant(name: str) -> KnowledgeAssistant Gets a Knowledge Assistant. @@ -92,6 +127,22 @@ :returns: :class:`KnowledgeAssistantPermissions` + .. py:method:: list_examples(parent: str [, page_size: Optional[int], page_token: Optional[str]]) -> Iterator[Example] + + Lists examples under a Knowledge Assistant. + + :param parent: str + Parent resource to list from. Format: knowledge-assistants/{knowledge_assistant_id} + :param page_size: int (optional) + The maximum number of examples to return. If unspecified, at most 100 examples will be returned. The + maximum value is 100; values above 100 will be coerced to 100. + :param page_token: str (optional) + A page token, received from a previous `ListExamples` call. Provide this to retrieve the subsequent + page. If unspecified, the first page will be returned. + + :returns: Iterator over :class:`Example` + + .. py:method:: list_knowledge_assistants( [, page_size: Optional[int], page_token: Optional[str]]) -> Iterator[KnowledgeAssistant] List Knowledge Assistants @@ -140,6 +191,21 @@ + .. py:method:: update_example(name: str, example: Example, update_mask: FieldMask) -> Example + + Updates an example in a Knowledge Assistant. + + :param name: str + The resource name of the example to update. Format: + knowledge-assistants/{knowledge_assistant_id}/examples/{example_id} + :param example: :class:`Example` + :param update_mask: FieldMask + Comma-delimited list of fields to update on the example. Allowed values: `question`, `guidelines`. + Examples: - `question` - `question,guidelines` + + :returns: :class:`Example` + + .. py:method:: update_knowledge_assistant(name: str, knowledge_assistant: KnowledgeAssistant, update_mask: FieldMask) -> KnowledgeAssistant Updates a Knowledge Assistant. diff --git a/docs/workspace/postgres/postgres.rst b/docs/workspace/postgres/postgres.rst index ffcc0b993..7e36f06c0 100755 --- a/docs/workspace/postgres/postgres.rst +++ b/docs/workspace/postgres/postgres.rst @@ -322,8 +322,8 @@ Get a Synced Table. :param name: str - Format: "synced_tables/{catalog}.{schema}.{table}", where (catalog, schema, table) are the entity - names in the Unity Catalog. + The Full resource name of the synced table. Format: "synced_tables/{catalog}.{schema}.{table}", + where (catalog, schema, table) are the entity names in the Unity Catalog. :returns: :class:`SyncedTable` diff --git a/docs/workspace/serving/serving_endpoints.rst b/docs/workspace/serving/serving_endpoints.rst old mode 100644 new mode 100755 index c9a51f48d..c8f23bb88 --- a/docs/workspace/serving/serving_endpoints.rst +++ b/docs/workspace/serving/serving_endpoints.rst @@ -368,10 +368,9 @@ :param name: str The name of the serving endpoint to update. This field is required. :param auto_capture_config: :class:`AutoCaptureConfigInput` (optional) - Configuration for Inference Tables which automatically logs requests and responses to Unity Catalog. - Note: this field is deprecated for creating new provisioned throughput endpoints, or updating - existing provisioned throughput endpoints that never have inference table configured; in these cases - please use AI Gateway to manage inference tables. + Configuration for legacy Inference Tables which automatically log requests and responses to Unity + Catalog. Deprecated: please use AI Gateway inference tables instead. See + https://docs.databricks.com/aws/en/ai-gateway/inference-tables. :param served_entities: List[:class:`ServedEntityInput`] (optional) The list of served entities under the serving endpoint config. :param served_models: List[:class:`ServedModelInput`] (optional) diff --git a/tagging.py b/tagging.py old mode 100644 new mode 100755 index 79f2894c6..c19028923 --- a/tagging.py +++ b/tagging.py @@ -7,7 +7,7 @@ import re import argparse from typing import Optional, List, Callable -from dataclasses import dataclass +from dataclasses import dataclass, replace import subprocess import time import json @@ -29,6 +29,106 @@ """ +@dataclass(frozen=True) +class Version: + """ + A semver 2.0.0-compliant version (https://semver.org). + + Mirrors the API of the `semver` PyPI package so this implementation can be + swapped for that library if it is ever added to the wheelhouse. Supports + parsing, stringification, and the two bumps we need: minor (for stable + releases) and prerelease (for release trains). + """ + + # Permissive pattern for locating a semver version string inside larger + # text (e.g. a changelog header). Callers use it in f-strings; strict + # validation happens via Version.parse. + PATTERN = r"\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?" + + # Strict anchored regex per https://semver.org. Rejects leading zeros in + # numeric identifiers and invalid pre-release/build identifier charsets. + _PARSE_REGEX = re.compile( + r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)" + r"(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)" + r"(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?" + r"(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$" + ) + + major: int + minor: int + patch: int + prerelease: str = "" + build: str = "" + + @classmethod + def parse(cls, text: str) -> "Version": + """Parse a semver string, raising ValueError on malformed input.""" + match = cls._PARSE_REGEX.match(text) + if not match: + raise ValueError(f"Invalid semver version: {text!r}") + major, minor, patch, prerelease, build = match.groups() + return cls( + major=int(major), + minor=int(minor), + patch=int(patch), + prerelease=prerelease or "", + build=build or "", + ) + + def __str__(self) -> str: + result = f"{self.major}.{self.minor}.{self.patch}" + if self.prerelease: + result += f"-{self.prerelease}" + if self.build: + result += f"+{self.build}" + return result + + def bump_minor(self) -> "Version": + """ + Bump the minor version and reset patch. + + Per semver item 9, a pre-release version has lower precedence than + the same MAJOR.MINOR.PATCH, so bumping to a new minor drops any + pre-release and build metadata. + """ + return Version(major=self.major, minor=self.minor + 1, patch=0) + + def bump_prerelease(self) -> "Version": + """ + Increment the rightmost numeric identifier in the pre-release. + + Matches the npm `prerelease` bump semantics: + 0.0.0-alpha.1 -> 0.0.0-alpha.2 + 0.0.0-alpha -> 0.0.0-alpha.1 + 0.0.0-rc.1.2 -> 0.0.0-rc.1.3 + + Raises ValueError if the version has no pre-release to bump. + Build metadata is dropped since it does not affect precedence. + """ + if not self.prerelease: + raise ValueError(f"Cannot bump prerelease of {self}: no pre-release component") + parts = self.prerelease.split(".") + for i in range(len(parts) - 1, -1, -1): + if parts[i].isdigit(): + parts[i] = str(int(parts[i]) + 1) + return replace(self, prerelease=".".join(parts), build="") + # No numeric identifier exists; append ".1" to start a counter. + return replace(self, prerelease=f"{self.prerelease}.1", build="") + + def next_release_version(self) -> "Version": + """ + Default next version for the changelog after this one is released. + + If on a pre-release track, stay on it by bumping the pre-release + identifier (npm convention). Otherwise, bump the minor version, + the script's historical default for stable releases. Teams can + override the default in the release PR. + """ + if self.prerelease: + return self.bump_prerelease() + return self.bump_minor() + + # GitHub does not support signing commits for GitHub Apps directly. # This class replaces usages for git commands such as "git add", "git commit", and "git push". @dataclass @@ -170,11 +270,11 @@ def update_version_references(tag_info: TagInfo) -> None: print("`version` not found in .codegen.json. Nothing to update.") return - # Update the versions + # Update the versions. for filename, pattern in version.items(): loc = os.path.join(os.getcwd(), tag_info.package.path, filename) - previous_version = re.sub(r"\$VERSION", r"\\d+\\.\\d+\\.\\d+", pattern) - new_version = re.sub(r"\$VERSION", tag_info.version, pattern) + previous_version = pattern.replace("$VERSION", Version.PATTERN) + new_version = pattern.replace("$VERSION", tag_info.version) with open(loc, "r") as file: content = file.read() @@ -197,23 +297,21 @@ def clean_next_changelog(package_path: str) -> None: with open(file_path, "r") as file: content = file.read() - # Remove content between ### sections + # Remove content between ### sections. cleaned_content = re.sub(r"(### [^\n]+\n)(?:.*?\n?)*?(?=###|$)", r"\1", content) - # Ensure there is exactly one empty line before each section + # Ensure there is exactly one empty line before each section. cleaned_content = re.sub(r"(\n*)(###[^\n]+)", r"\n\n\2", cleaned_content) - # Find the version number - version_match = re.search(r"Release v(\d+)\.(\d+)\.(\d+)", cleaned_content) + # Find the version number and compute the default next release version. + # Teams can adjust the version in the PR if the default is not desired. + # For stable versions, bump minor (historical default since minor releases + # are more common than patch or major). For pre-release versions, stay on + # the same track by bumping the pre-release identifier (npm convention). + version_match = re.search(rf"Release v({Version.PATTERN})", cleaned_content) if not version_match: raise Exception("Version not found in the changelog") - major, minor, patch = map(int, version_match.groups()) - # Prepare next release version. - # When doing a PR, teams can adjust the version. - # By default, we increase a minor version, since minor versions releases - # are more common than patch or major version releases. - minor += 1 - patch = 0 - new_version = f"Release v{major}.{minor}.{patch}" - cleaned_content = cleaned_content.replace(version_match.group(0), new_version) + current = Version.parse(version_match.group(1)) + new_header = f"Release v{current.next_release_version()}" + cleaned_content = cleaned_content.replace(version_match.group(0), new_header) # Update file with cleaned content gh.add_file(file_path, cleaned_content) @@ -229,20 +327,26 @@ def get_previous_tag_info(package: Package) -> Optional[TagInfo]: with open(changelog_path, "r") as f: changelog = f.read() - # Extract the latest release section using regex - match = re.search(r"## (\[Release\] )?Release v[\d\.]+.*?(?=\n## (\[Release\] )?Release v|\Z)", changelog, re.S) + # Extract the latest release section using regex. + match = re.search( + rf"## (\[Release\] )?Release v{Version.PATTERN}.*?(?=\n## (\[Release\] )?Release v|\Z)", + changelog, + re.S, + ) # E.g., for new packages. if not match: return None latest_release = match.group(0) - version_match = re.search(r"## (\[Release\] )?Release v(\d+\.\d+\.\d+)", latest_release) + version_match = re.search(rf"## (\[Release\] )?Release v({Version.PATTERN})", latest_release) if not version_match: raise Exception("Version not found in the changelog") - return TagInfo(package=package, version=version_match.group(2), content=latest_release) + # Validate the extracted string is spec-compliant; fail loudly otherwise. + version = str(Version.parse(version_match.group(2))) + return TagInfo(package=package, version=version, content=latest_release) def get_next_tag_info(package: Package) -> Optional[TagInfo]: @@ -267,12 +371,14 @@ def get_next_tag_info(package: Package) -> Optional[TagInfo]: print("All sections are empty. No changes will be made to the changelog.") return None - version_match = re.search(r"## Release v(\d+\.\d+\.\d+)", next_changelog) + version_match = re.search(rf"## Release v({Version.PATTERN})", next_changelog) if not version_match: raise Exception("Version not found in the changelog") - return TagInfo(package=package, version=version_match.group(1), content=next_changelog) + # Validate the extracted string is spec-compliant; fail loudly otherwise. + version = str(Version.parse(version_match.group(1))) + return TagInfo(package=package, version=version, content=next_changelog) def write_changelog(tag_info: TagInfo) -> None: @@ -283,10 +389,12 @@ def write_changelog(tag_info: TagInfo) -> None: with open(changelog_path, "r") as f: changelog = f.read() - # Add current date to the release header + # Add current date to the release header. current_date = datetime.now(tz=timezone.utc).strftime("%Y-%m-%d") content_with_date = re.sub( - r"## Release v(\d+\.\d+\.\d+)", rf"## Release v\1 ({current_date})", tag_info.content.strip() + rf"## Release v({Version.PATTERN})", + rf"## Release v\1 ({current_date})", + tag_info.content.strip(), ) updated_changelog = re.sub(r"(# Version changelog\n\n)", f"\\1{content_with_date}\n\n\n", changelog) @@ -519,15 +627,26 @@ def pull_last_release_commit() -> None: reset_repository(commit_hash) -def get_package_from_args() -> Optional[str]: +def get_packages_from_args() -> List[str]: """ - Retrieves an optional package - python3 ./tagging.py --package + Retrieves the list of packages to tag. + + python3 ./tagging.py --package # single package + python3 ./tagging.py --package , # multiple packages + + Returns an empty list when --package is omitted, which means all packages + with pending releases will be tagged. """ parser = argparse.ArgumentParser(description="Update changelogs and tag the release.") - parser.add_argument("--package", "-p", type=str, help="Tag a single package") + parser.add_argument( + "--package", + "-p", + type=str, + default="", + help="Comma-separated list of packages to tag. Leave empty to tag all packages with pending releases.", + ) args = parser.parse_args() - return args.package + return [name.strip() for name in args.package.split(",") if name.strip()] def init_github(): @@ -553,15 +672,15 @@ def process(): If any tag are pending from an early process, it will skip updating the CHANGELOG.md files and only apply the tags. """ - package_name = get_package_from_args() + package_names = get_packages_from_args() pending_tags = find_pending_tags() # pending_tags is non-empty only when the tagging process previously failed or interrupted. # We must complete the interrupted tagging process before starting a new one to avoid inconsistent states and missing changelog entries. - # Therefore, we don't support specifying the package until the previously started process has been successfully completed. - if pending_tags and package_name: + # Therefore, we don't support specifying packages until the previously started process has been successfully completed. + if pending_tags and package_names: pending_packages = [tag.package.name for tag in pending_tags] - raise Exception(f"Cannot release package {package_name}. Pending release for {pending_packages}") + raise Exception(f"Cannot release packages {package_names}. Pending release for {pending_packages}") if pending_tags: print("Found pending tags from previous executions, entering recovery mode.") @@ -570,9 +689,9 @@ def process(): return packages = find_packages() - # If a package is specified as an argument, only process that package - if package_name: - packages = [package for package in packages if package.name == package_name] + # If packages are specified as an argument, only process those packages. + if package_names: + packages = [package for package in packages if package.name in package_names] pending_tags = retry_function(func=lambda: update_changelogs(packages), cleanup=reset_repository) push_tags(pending_tags)