diff --git a/.codegen.json b/.codegen.json index 65077c1cc..5a2be72a5 100644 --- a/.codegen.json +++ b/.codegen.json @@ -14,10 +14,7 @@ "pip install '.[dev]'" ], "post_generate": [ - "make fmt", - "pytest -m 'not integration' --cov=databricks --cov-report html tests", - "pip install .", - "python3.12 docs/gen-client-docs.py" + "make fmt" ] } } diff --git a/.codegen/_openapi_sha b/.codegen/_openapi_sha index 8af35ea49..c3d9494de 100644 --- a/.codegen/_openapi_sha +++ b/.codegen/_openapi_sha @@ -1 +1 @@ -b95c2c6e21bec9551ec7d7d51ddf2dfe390b4522 \ No newline at end of file +file:/home/hector.castejon/universe/bazel-bin/openapi/all-internal.json \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index e26b931e3..efdffc8e2 100755 --- a/.gitattributes +++ b/.gitattributes @@ -10,8 +10,11 @@ databricks/sdk/service/compute.py linguist-generated=true databricks/sdk/service/dashboards.py linguist-generated=true databricks/sdk/service/database.py linguist-generated=true databricks/sdk/service/files.py linguist-generated=true +databricks/sdk/service/httpcallv2.py linguist-generated=true databricks/sdk/service/iam.py linguist-generated=true +databricks/sdk/service/iamv2.py linguist-generated=true databricks/sdk/service/jobs.py linguist-generated=true +databricks/sdk/service/jsonmarshallv2.py linguist-generated=true databricks/sdk/service/marketplace.py linguist-generated=true databricks/sdk/service/ml.py linguist-generated=true databricks/sdk/service/oauth2.py linguist-generated=true @@ -26,3 +29,5 @@ 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 +test_http_call.py linguist-generated=true +test_json_marshall.py linguist-generated=true diff --git a/databricks/sdk/__init__.py b/databricks/sdk/__init__.py index a426eebfb..ff834e5b5 100755 --- a/databricks/sdk/__init__.py +++ b/databricks/sdk/__init__.py @@ -23,6 +23,7 @@ from databricks.sdk.service import database as pkg_database from databricks.sdk.service import files as pkg_files from databricks.sdk.service import iam as pkg_iam +from databricks.sdk.service import iamv2 as pkg_iamv2 from databricks.sdk.service import jobs as pkg_jobs from databricks.sdk.service import marketplace as pkg_marketplace from databricks.sdk.service import ml as pkg_ml @@ -88,6 +89,7 @@ GroupsAPI, PermissionMigrationAPI, PermissionsAPI, ServicePrincipalsAPI, UsersAPI, WorkspaceAssignmentAPI) +from databricks.sdk.service.iamv2 import AccountIamV2API, WorkspaceIamV2API from databricks.sdk.service.jobs import JobsAPI, PolicyComplianceForJobsAPI from databricks.sdk.service.marketplace import ( ConsumerFulfillmentsAPI, ConsumerInstallationsAPI, ConsumerListingsAPI, @@ -371,6 +373,7 @@ def __init__( 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) + self._workspace_iam_v2 = pkg_iamv2.WorkspaceIamV2API(self._api_client) @property def config(self) -> client.Config: @@ -896,7 +899,7 @@ def tables(self) -> pkg_catalog.TablesAPI: @property def tag_policies(self) -> pkg_tags.TagPoliciesAPI: - """The Tag Policy API allows you to manage tag policies in Databricks.""" + """The Tag Policy API allows you to manage policies for governed tags in Databricks.""" return self._tag_policies @property @@ -969,6 +972,11 @@ def forecasting(self) -> pkg_ml.ForecastingAPI: """The Forecasting API allows you to create and get serverless forecasting experiments.""" return self._forecasting + @property + def workspace_iam_v2(self) -> pkg_iamv2.WorkspaceIamV2API: + """These APIs are used to manage identities and the workspace access of these identities in .""" + return self._workspace_iam_v2 + def get_workspace_id(self) -> int: """Get the workspace ID of the workspace that this client is connected to.""" response = self._api_client.do("GET", "/api/2.0/preview/scim/v2/Me", response_headers=["X-Databricks-Org-Id"]) @@ -1074,6 +1082,7 @@ def __init__( self._workspace_assignment = pkg_iam.WorkspaceAssignmentAPI(self._api_client) self._workspace_network_configuration = pkg_settings.WorkspaceNetworkConfigurationAPI(self._api_client) self._workspaces = pkg_provisioning.WorkspacesAPI(self._api_client) + self._iam_v2 = pkg_iamv2.AccountIamV2API(self._api_client) self._budgets = pkg_billing.BudgetsAPI(self._api_client) @property @@ -1239,6 +1248,11 @@ def workspaces(self) -> pkg_provisioning.WorkspacesAPI: """These APIs manage workspaces for this account.""" return self._workspaces + @property + def iam_v2(self) -> pkg_iamv2.AccountIamV2API: + """These APIs are used to manage identities and the workspace access of these identities in .""" + return self._iam_v2 + @property def budgets(self) -> pkg_billing.BudgetsAPI: """These APIs manage budget configurations for this account.""" diff --git a/databricks/sdk/service/agentbricks.py b/databricks/sdk/service/agentbricks.py index 25175acf0..b2a560e31 100755 --- a/databricks/sdk/service/agentbricks.py +++ b/databricks/sdk/service/agentbricks.py @@ -7,7 +7,7 @@ from enum import Enum from typing import Any, Dict, List, Optional -from ._internal import _enum, _from_dict, _repeated_dict +from databricks.sdk.service._internal import _enum, _from_dict, _repeated_dict _LOG = logging.getLogger("databricks.sdk") diff --git a/databricks/sdk/service/apps.py b/databricks/sdk/service/apps.py index aeedb7146..3a84dfff7 100755 --- a/databricks/sdk/service/apps.py +++ b/databricks/sdk/service/apps.py @@ -10,8 +10,10 @@ from enum import Enum from typing import Any, Callable, Dict, Iterator, List, Optional +from databricks.sdk.service._internal import (Wait, _enum, _from_dict, + _repeated_dict) + from ..errors import OperationFailed -from ._internal import Wait, _enum, _from_dict, _repeated_dict _LOG = logging.getLogger("databricks.sdk") diff --git a/databricks/sdk/service/billing.py b/databricks/sdk/service/billing.py index 2e118457a..46562a5a8 100755 --- a/databricks/sdk/service/billing.py +++ b/databricks/sdk/service/billing.py @@ -7,7 +7,7 @@ from enum import Enum from typing import Any, BinaryIO, Dict, Iterator, List, Optional -from ._internal import _enum, _from_dict, _repeated_dict +from databricks.sdk.service._internal import _enum, _from_dict, _repeated_dict _LOG = logging.getLogger("databricks.sdk") diff --git a/databricks/sdk/service/catalog.py b/databricks/sdk/service/catalog.py index 3db28a23e..c30003416 100755 --- a/databricks/sdk/service/catalog.py +++ b/databricks/sdk/service/catalog.py @@ -10,8 +10,10 @@ from enum import Enum from typing import Any, Callable, Dict, Iterator, List, Optional +from databricks.sdk.service._internal import (Wait, _enum, _from_dict, + _repeated_dict, _repeated_enum) + from ..errors import OperationFailed -from ._internal import Wait, _enum, _from_dict, _repeated_dict, _repeated_enum _LOG = logging.getLogger("databricks.sdk") @@ -3652,7 +3654,8 @@ class ExternalLocationInfo: sufficient.""" file_event_queue: Optional[FileEventQueue] = None - """File event queue settings.""" + """File event queue settings. If `enable_file_events` is `true`, must be defined and have exactly + one of the documented properties.""" isolation_mode: Optional[IsolationMode] = None @@ -11945,7 +11948,8 @@ def create( enabled, the access to the location falls back to cluster credentials if UC credentials are not sufficient. :param file_event_queue: :class:`FileEventQueue` (optional) - File event queue settings. + File event queue settings. If `enable_file_events` is `true`, must be defined and have exactly one + of the documented properties. :param read_only: bool (optional) Indicates whether the external location is read-only. :param skip_validation: bool (optional) @@ -12107,7 +12111,8 @@ def update( enabled, the access to the location falls back to cluster credentials if UC credentials are not sufficient. :param file_event_queue: :class:`FileEventQueue` (optional) - File event queue settings. + File event queue settings. If `enable_file_events` is `true`, must be defined and have exactly one + of the documented properties. :param force: bool (optional) Force update even if changing url invalidates dependent external tables or mounts. :param isolation_mode: :class:`IsolationMode` (optional) diff --git a/databricks/sdk/service/cleanrooms.py b/databricks/sdk/service/cleanrooms.py index 57ea7e961..fb4cc75fa 100755 --- a/databricks/sdk/service/cleanrooms.py +++ b/databricks/sdk/service/cleanrooms.py @@ -10,7 +10,8 @@ from enum import Enum from typing import Any, Callable, Dict, Iterator, List, Optional -from ._internal import Wait, _enum, _from_dict, _repeated_dict +from databricks.sdk.service._internal import (Wait, _enum, _from_dict, + _repeated_dict) _LOG = logging.getLogger("databricks.sdk") diff --git a/databricks/sdk/service/compute.py b/databricks/sdk/service/compute.py index c7ca04416..adab145ed 100755 --- a/databricks/sdk/service/compute.py +++ b/databricks/sdk/service/compute.py @@ -10,8 +10,10 @@ from enum import Enum from typing import Any, Callable, Dict, Iterator, List, Optional +from databricks.sdk.service._internal import (Wait, _enum, _from_dict, + _repeated_dict, _repeated_enum) + from ..errors import OperationFailed -from ._internal import Wait, _enum, _from_dict, _repeated_dict, _repeated_enum _LOG = logging.getLogger("databricks.sdk") @@ -3136,6 +3138,9 @@ class Environment: version and a set of Python packages. The version is a string, consisting of an integer.""" jar_dependencies: Optional[List[str]] = None + """Use `java_dependencies` instead.""" + + java_dependencies: Optional[List[str]] = None """List of jar dependencies, should be string representing volume paths. For example: `/Volumes/path/to/test.jar`.""" @@ -3150,6 +3155,8 @@ def as_dict(self) -> dict: body["environment_version"] = self.environment_version if self.jar_dependencies: body["jar_dependencies"] = [v for v in self.jar_dependencies] + if self.java_dependencies: + body["java_dependencies"] = [v for v in self.java_dependencies] return body def as_shallow_dict(self) -> dict: @@ -3163,6 +3170,8 @@ def as_shallow_dict(self) -> dict: body["environment_version"] = self.environment_version if self.jar_dependencies: body["jar_dependencies"] = self.jar_dependencies + if self.java_dependencies: + body["java_dependencies"] = self.java_dependencies return body @classmethod @@ -3173,6 +3182,7 @@ def from_dict(cls, d: Dict[str, Any]) -> Environment: dependencies=d.get("dependencies", None), environment_version=d.get("environment_version", None), jar_dependencies=d.get("jar_dependencies", None), + java_dependencies=d.get("java_dependencies", None), ) @@ -7151,6 +7161,7 @@ class TerminationReasonCode(Enum): NETWORK_CHECK_STORAGE_FAILURE = "NETWORK_CHECK_STORAGE_FAILURE" NETWORK_CONFIGURATION_FAILURE = "NETWORK_CONFIGURATION_FAILURE" NFS_MOUNT_FAILURE = "NFS_MOUNT_FAILURE" + NO_ACTIVATED_K8S = "NO_ACTIVATED_K8S" NO_MATCHED_K8S = "NO_MATCHED_K8S" NO_MATCHED_K8S_TESTING_TAG = "NO_MATCHED_K8S_TESTING_TAG" NPIP_TUNNEL_SETUP_FAILURE = "NPIP_TUNNEL_SETUP_FAILURE" @@ -7189,6 +7200,7 @@ class TerminationReasonCode(Enum): UNKNOWN = "UNKNOWN" UNSUPPORTED_INSTANCE_TYPE = "UNSUPPORTED_INSTANCE_TYPE" UPDATE_INSTANCE_PROFILE_FAILURE = "UPDATE_INSTANCE_PROFILE_FAILURE" + USAGE_POLICY_ENTITLEMENT_DENIED = "USAGE_POLICY_ENTITLEMENT_DENIED" USER_INITIATED_VM_TERMINATION = "USER_INITIATED_VM_TERMINATION" USER_REQUEST = "USER_REQUEST" WORKER_SETUP_FAILURE = "WORKER_SETUP_FAILURE" diff --git a/databricks/sdk/service/dashboards.py b/databricks/sdk/service/dashboards.py index b71f2c866..7ac8103a0 100755 --- a/databricks/sdk/service/dashboards.py +++ b/databricks/sdk/service/dashboards.py @@ -10,8 +10,10 @@ from enum import Enum from typing import Any, Callable, Dict, Iterator, List, Optional +from databricks.sdk.service._internal import (Wait, _enum, _from_dict, + _repeated_dict) + from ..errors import OperationFailed -from ._internal import Wait, _enum, _from_dict, _repeated_dict _LOG = logging.getLogger("databricks.sdk") @@ -413,6 +415,40 @@ def from_dict(cls, d: Dict[str, Any]) -> GenieConversationSummary: ) +@dataclass +class GenieFeedback: + """Feedback containing rating and optional comment""" + + comment: Optional[str] = None + """Optional feedback comment text""" + + rating: Optional[GenieFeedbackRating] = None + """The feedback rating""" + + def as_dict(self) -> dict: + """Serializes the GenieFeedback into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.comment is not None: + body["comment"] = self.comment + if self.rating is not None: + body["rating"] = self.rating.value + return body + + def as_shallow_dict(self) -> dict: + """Serializes the GenieFeedback into a shallow dictionary of its immediate attributes.""" + body = {} + if self.comment is not None: + body["comment"] = self.comment + if self.rating is not None: + body["rating"] = self.rating + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> GenieFeedback: + """Deserializes the GenieFeedback from a dictionary.""" + return cls(comment=d.get("comment", None), rating=_enum(d, "rating", GenieFeedbackRating)) + + class GenieFeedbackRating(Enum): """Feedback rating for Genie messages""" @@ -572,6 +608,9 @@ class GenieMessage: error: Optional[MessageError] = None """Error message if Genie failed to respond to the message""" + feedback: Optional[GenieFeedback] = None + """User feedback for the message if provided""" + last_updated_timestamp: Optional[int] = None """Timestamp when the message was last updated""" @@ -597,6 +636,8 @@ def as_dict(self) -> dict: body["created_timestamp"] = self.created_timestamp if self.error: body["error"] = self.error.as_dict() + if self.feedback: + body["feedback"] = self.feedback.as_dict() if self.id is not None: body["id"] = self.id if self.last_updated_timestamp is not None: @@ -626,6 +667,8 @@ def as_shallow_dict(self) -> dict: body["created_timestamp"] = self.created_timestamp if self.error: body["error"] = self.error + if self.feedback: + body["feedback"] = self.feedback if self.id is not None: body["id"] = self.id if self.last_updated_timestamp is not None: @@ -651,6 +694,7 @@ def from_dict(cls, d: Dict[str, Any]) -> GenieMessage: conversation_id=d.get("conversation_id", None), created_timestamp=d.get("created_timestamp", None), error=_from_dict(d, "error", MessageError), + feedback=_from_dict(d, "feedback", GenieFeedback), id=d.get("id", None), last_updated_timestamp=d.get("last_updated_timestamp", None), message_id=d.get("message_id", None), @@ -1921,8 +1965,8 @@ def list_conversations( :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. + Include all conversations in the space across all users. Requires at least CAN MANAGE permission on + the space. :param page_size: int (optional) Maximum number of conversations to return per page :param page_token: str (optional) diff --git a/databricks/sdk/service/database.py b/databricks/sdk/service/database.py index 01c65349f..5845b86cf 100755 --- a/databricks/sdk/service/database.py +++ b/databricks/sdk/service/database.py @@ -10,7 +10,8 @@ from enum import Enum from typing import Any, Callable, Dict, Iterator, List, Optional -from ._internal import Wait, _enum, _from_dict, _repeated_dict +from databricks.sdk.service._internal import (Wait, _enum, _from_dict, + _repeated_dict) _LOG = logging.getLogger("databricks.sdk") @@ -125,6 +126,9 @@ class DatabaseInstance: creator: Optional[str] = None """The email of the creator of the instance.""" + effective_capacity: Optional[str] = None + """The sku of the instance. Valid values are "CU_1", "CU_2", "CU_4", "CU_8".""" + effective_enable_pg_native_login: Optional[bool] = None """xref AIP-129. `enable_pg_native_login` is owned by the client, while `effective_enable_pg_native_login` is owned by the server. `enable_pg_native_login` will only be @@ -140,10 +144,8 @@ class DatabaseInstance: in all response messages (Create/Update/Get/List).""" effective_node_count: Optional[int] = None - """xref AIP-129. `node_count` is owned by the client, while `effective_node_count` is owned by the - server. `node_count` will only be set in Create/Update response messages if and only if the user - provides the field via the request. `effective_node_count` on the other hand will always bet set - in all response messages (Create/Update/Get/List).""" + """The number of nodes in the instance, composed of 1 primary and 0 or more secondaries. Defaults + to 1 primary and 0 secondaries.""" effective_retention_window_in_days: Optional[int] = None """xref AIP-129. `retention_window_in_days` is owned by the client, while @@ -153,20 +155,18 @@ class DatabaseInstance: response messages (Create/Update/Get/List).""" effective_stopped: Optional[bool] = None - """xref AIP-129. `stopped` is owned by the client, while `effective_stopped` is owned by the - server. `stopped` will only be set in Create/Update response messages if and only if the user - provides the field via the request. `effective_stopped` on the other hand will always bet set in - all response messages (Create/Update/Get/List).""" + """Whether the instance is stopped.""" enable_pg_native_login: Optional[bool] = None - """Whether the instance has PG native password login enabled. Defaults to true.""" + """Whether the instance has PG native password login enabled. Defaults to false.""" enable_readable_secondaries: Optional[bool] = None """Whether to enable secondaries to serve read-only traffic. Defaults to false.""" node_count: Optional[int] = None """The number of nodes in the instance, composed of 1 primary and 0 or more secondaries. Defaults - to 1 primary and 0 secondaries.""" + to 1 primary and 0 secondaries. This field is input only, see effective_node_count for the + output.""" parent_instance_ref: Optional[DatabaseInstanceRef] = None """The ref of the parent instance. This is only available if the instance is child instance. Input: @@ -191,7 +191,7 @@ class DatabaseInstance: """The current state of the instance.""" stopped: Optional[bool] = None - """Whether the instance is stopped.""" + """Whether to stop the instance. An input only param, see effective_stopped for the output.""" uid: Optional[str] = None """An immutable UUID identifier for the instance.""" @@ -207,6 +207,8 @@ def as_dict(self) -> dict: body["creation_time"] = self.creation_time if self.creator is not None: body["creator"] = self.creator + if self.effective_capacity is not None: + body["effective_capacity"] = self.effective_capacity if self.effective_enable_pg_native_login is not None: body["effective_enable_pg_native_login"] = self.effective_enable_pg_native_login if self.effective_enable_readable_secondaries is not None: @@ -254,6 +256,8 @@ def as_shallow_dict(self) -> dict: body["creation_time"] = self.creation_time if self.creator is not None: body["creator"] = self.creator + if self.effective_capacity is not None: + body["effective_capacity"] = self.effective_capacity if self.effective_enable_pg_native_login is not None: body["effective_enable_pg_native_login"] = self.effective_enable_pg_native_login if self.effective_enable_readable_secondaries is not None: @@ -298,6 +302,7 @@ def from_dict(cls, d: Dict[str, Any]) -> DatabaseInstance: child_instance_refs=_repeated_dict(d, "child_instance_refs", DatabaseInstanceRef), creation_time=d.get("creation_time", None), creator=d.get("creator", None), + effective_capacity=d.get("effective_capacity", None), effective_enable_pg_native_login=d.get("effective_enable_pg_native_login", None), effective_enable_readable_secondaries=d.get("effective_enable_readable_secondaries", None), effective_node_count=d.get("effective_node_count", None), @@ -1611,12 +1616,8 @@ def delete_database_instance(self, name: str, *, force: Optional[bool] = None, p By default, a instance cannot be deleted if it has descendant instances created via PITR. If this flag is specified as true, all descendent instances will be deleted as well. :param purge: bool (optional) - Note purge=false is in development. If false, the database instance is soft deleted (implementation - pending). Soft deleted instances behave as if they are deleted, and cannot be used for CRUD - operations nor connected to. However they can be undeleted by calling the undelete API for a limited - time (implementation pending). If true, the database instance is hard deleted and cannot be - undeleted. For the time being, setting this value to true is required to delete an instance (soft - delete is not yet supported). + Deprecated. Omitting the field or setting it to true will result in the field being hard deleted. + Setting a value of false will throw a bad request. """ diff --git a/databricks/sdk/service/files.py b/databricks/sdk/service/files.py index 2117a09f3..e26de85ab 100755 --- a/databricks/sdk/service/files.py +++ b/databricks/sdk/service/files.py @@ -6,7 +6,8 @@ from dataclasses import dataclass from typing import Any, BinaryIO, Dict, Iterator, List, Optional -from ._internal import _escape_multi_segment_path_parameter, _repeated_dict +from databricks.sdk.service._internal import ( + _escape_multi_segment_path_parameter, _repeated_dict) _LOG = logging.getLogger("databricks.sdk") diff --git a/databricks/sdk/service/iam.py b/databricks/sdk/service/iam.py index 2991a890f..a55a1a513 100755 --- a/databricks/sdk/service/iam.py +++ b/databricks/sdk/service/iam.py @@ -7,7 +7,8 @@ from enum import Enum from typing import Any, Dict, Iterator, List, Optional -from ._internal import _enum, _from_dict, _repeated_dict, _repeated_enum +from databricks.sdk.service._internal import (_enum, _from_dict, + _repeated_dict, _repeated_enum) _LOG = logging.getLogger("databricks.sdk") @@ -425,6 +426,7 @@ class Group: [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements""" external_id: Optional[str] = None + """external_id should be unique for identifying groups""" groups: Optional[List[ComplexValue]] = None @@ -2111,6 +2113,7 @@ def create( [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) + external_id should be unique for identifying groups :param groups: List[:class:`ComplexValue`] (optional) :param id: str (optional) Databricks group ID @@ -2308,6 +2311,7 @@ def update( [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) + external_id should be unique for identifying groups :param groups: List[:class:`ComplexValue`] (optional) :param members: List[:class:`ComplexValue`] (optional) :param meta: :class:`ResourceMeta` (optional) @@ -3026,6 +3030,7 @@ def create( [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) + external_id should be unique for identifying groups :param groups: List[:class:`ComplexValue`] (optional) :param id: str (optional) Databricks group ID @@ -3215,6 +3220,7 @@ def update( [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) + external_id should be unique for identifying groups :param groups: List[:class:`ComplexValue`] (optional) :param members: List[:class:`ComplexValue`] (optional) :param meta: :class:`ResourceMeta` (optional) diff --git a/databricks/sdk/service/iamv2.py b/databricks/sdk/service/iamv2.py new file mode 100755 index 000000000..b95605da5 --- /dev/null +++ b/databricks/sdk/service/iamv2.py @@ -0,0 +1,643 @@ +# 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, List, Optional + +from databricks.sdk.service._internal import _enum, _from_dict, _repeated_enum + +_LOG = logging.getLogger("databricks.sdk") + + +# all definitions in this file are in alphabetical order + + +@dataclass +class Group: + """The details of a Group resource.""" + + account_id: Optional[str] = None + """The parent account ID for group in Databricks.""" + + external_id: Optional[str] = None + """ExternalId of the group in the customer's IdP.""" + + group_name: Optional[str] = None + """Display name of the group.""" + + internal_id: Optional[int] = None + """Internal group ID of the group in Databricks.""" + + def as_dict(self) -> dict: + """Serializes the Group into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.account_id is not None: + body["account_id"] = self.account_id + if self.external_id is not None: + body["external_id"] = self.external_id + if self.group_name is not None: + body["group_name"] = self.group_name + if self.internal_id is not None: + body["internal_id"] = self.internal_id + return body + + def as_shallow_dict(self) -> dict: + """Serializes the Group into a shallow dictionary of its immediate attributes.""" + body = {} + if self.account_id is not None: + body["account_id"] = self.account_id + if self.external_id is not None: + body["external_id"] = self.external_id + if self.group_name is not None: + body["group_name"] = self.group_name + if self.internal_id is not None: + body["internal_id"] = self.internal_id + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> Group: + """Deserializes the Group from a dictionary.""" + return cls( + account_id=d.get("account_id", None), + external_id=d.get("external_id", None), + group_name=d.get("group_name", None), + internal_id=d.get("internal_id", None), + ) + + +class PrincipalType(Enum): + """The type of the principal (user/sp/group).""" + + GROUP = "GROUP" + SERVICE_PRINCIPAL = "SERVICE_PRINCIPAL" + USER = "USER" + + +@dataclass +class ResolveGroupResponse: + group: Optional[Group] = None + """The group that was resolved.""" + + def as_dict(self) -> dict: + """Serializes the ResolveGroupResponse into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.group: + body["group"] = self.group.as_dict() + return body + + def as_shallow_dict(self) -> dict: + """Serializes the ResolveGroupResponse into a shallow dictionary of its immediate attributes.""" + body = {} + if self.group: + body["group"] = self.group + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> ResolveGroupResponse: + """Deserializes the ResolveGroupResponse from a dictionary.""" + return cls(group=_from_dict(d, "group", Group)) + + +@dataclass +class ResolveServicePrincipalResponse: + service_principal: Optional[ServicePrincipal] = None + """The service principal that was resolved.""" + + def as_dict(self) -> dict: + """Serializes the ResolveServicePrincipalResponse into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.service_principal: + body["service_principal"] = self.service_principal.as_dict() + return body + + def as_shallow_dict(self) -> dict: + """Serializes the ResolveServicePrincipalResponse into a shallow dictionary of its immediate attributes.""" + body = {} + if self.service_principal: + body["service_principal"] = self.service_principal + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> ResolveServicePrincipalResponse: + """Deserializes the ResolveServicePrincipalResponse from a dictionary.""" + return cls(service_principal=_from_dict(d, "service_principal", ServicePrincipal)) + + +@dataclass +class ResolveUserResponse: + user: Optional[User] = None + """The user that was resolved.""" + + def as_dict(self) -> dict: + """Serializes the ResolveUserResponse into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.user: + body["user"] = self.user.as_dict() + return body + + def as_shallow_dict(self) -> dict: + """Serializes the ResolveUserResponse into a shallow dictionary of its immediate attributes.""" + body = {} + if self.user: + body["user"] = self.user + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> ResolveUserResponse: + """Deserializes the ResolveUserResponse from a dictionary.""" + return cls(user=_from_dict(d, "user", User)) + + +@dataclass +class ServicePrincipal: + """The details of a ServicePrincipal resource.""" + + account_id: Optional[str] = None + """The parent account ID for the service principal in Databricks.""" + + account_sp_status: Optional[State] = None + """The activity status of a service principal in a Databricks account.""" + + application_id: Optional[str] = None + """Application ID of the service principal.""" + + display_name: Optional[str] = None + """Display name of the service principal.""" + + external_id: Optional[str] = None + """ExternalId of the service principal in the customer's IdP.""" + + internal_id: Optional[int] = None + """Internal service principal ID of the service principal in Databricks.""" + + def as_dict(self) -> dict: + """Serializes the ServicePrincipal into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.account_id is not None: + body["account_id"] = self.account_id + if self.account_sp_status is not None: + body["account_sp_status"] = self.account_sp_status.value + if self.application_id is not None: + body["application_id"] = self.application_id + if self.display_name is not None: + body["display_name"] = self.display_name + if self.external_id is not None: + body["external_id"] = self.external_id + if self.internal_id is not None: + body["internal_id"] = self.internal_id + return body + + def as_shallow_dict(self) -> dict: + """Serializes the ServicePrincipal into a shallow dictionary of its immediate attributes.""" + body = {} + if self.account_id is not None: + body["account_id"] = self.account_id + if self.account_sp_status is not None: + body["account_sp_status"] = self.account_sp_status + if self.application_id is not None: + body["application_id"] = self.application_id + if self.display_name is not None: + body["display_name"] = self.display_name + if self.external_id is not None: + body["external_id"] = self.external_id + if self.internal_id is not None: + body["internal_id"] = self.internal_id + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> ServicePrincipal: + """Deserializes the ServicePrincipal from a dictionary.""" + return cls( + account_id=d.get("account_id", None), + account_sp_status=_enum(d, "account_sp_status", State), + application_id=d.get("application_id", None), + display_name=d.get("display_name", None), + external_id=d.get("external_id", None), + internal_id=d.get("internal_id", None), + ) + + +class State(Enum): + """The activity status of a user or service principal in a Databricks account or workspace.""" + + ACTIVE = "ACTIVE" + INACTIVE = "INACTIVE" + + +@dataclass +class User: + """The details of a User resource.""" + + username: str + """Username/email of the user.""" + + account_id: Optional[str] = None + """The accountId parent of the user in Databricks.""" + + account_user_status: Optional[State] = None + """The activity status of a user in a Databricks account.""" + + external_id: Optional[str] = None + """ExternalId of the user in the customer's IdP.""" + + internal_id: Optional[int] = None + """Internal userId of the user in Databricks.""" + + name: Optional[UserName] = None + + def as_dict(self) -> dict: + """Serializes the User into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.account_id is not None: + body["account_id"] = self.account_id + if self.account_user_status is not None: + body["account_user_status"] = self.account_user_status.value + if self.external_id is not None: + body["external_id"] = self.external_id + if self.internal_id is not None: + body["internal_id"] = self.internal_id + if self.name: + body["name"] = self.name.as_dict() + if self.username is not None: + body["username"] = self.username + return body + + def as_shallow_dict(self) -> dict: + """Serializes the User into a shallow dictionary of its immediate attributes.""" + body = {} + if self.account_id is not None: + body["account_id"] = self.account_id + if self.account_user_status is not None: + body["account_user_status"] = self.account_user_status + if self.external_id is not None: + body["external_id"] = self.external_id + if self.internal_id is not None: + body["internal_id"] = self.internal_id + if self.name: + body["name"] = self.name + if self.username is not None: + body["username"] = self.username + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> User: + """Deserializes the User from a dictionary.""" + return cls( + account_id=d.get("account_id", None), + account_user_status=_enum(d, "account_user_status", State), + external_id=d.get("external_id", None), + internal_id=d.get("internal_id", None), + name=_from_dict(d, "name", UserName), + username=d.get("username", None), + ) + + +@dataclass +class UserName: + family_name: Optional[str] = None + + given_name: Optional[str] = None + + def as_dict(self) -> dict: + """Serializes the UserName into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.family_name is not None: + body["family_name"] = self.family_name + if self.given_name is not None: + body["given_name"] = self.given_name + return body + + def as_shallow_dict(self) -> dict: + """Serializes the UserName into a shallow dictionary of its immediate attributes.""" + body = {} + if self.family_name is not None: + body["family_name"] = self.family_name + if self.given_name is not None: + body["given_name"] = self.given_name + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> UserName: + """Deserializes the UserName from a dictionary.""" + return cls(family_name=d.get("family_name", None), given_name=d.get("given_name", None)) + + +@dataclass +class WorkspaceAccessDetail: + """The details of a principal's access to a workspace.""" + + access_type: Optional[WorkspaceAccessDetailAccessType] = None + + account_id: Optional[str] = None + """The account ID parent of the workspace where the principal has access.""" + + permissions: Optional[List[WorkspacePermission]] = None + """The permissions granted to the principal in the workspace.""" + + principal_id: Optional[int] = None + """The internal ID of the principal (user/sp/group) in Databricks.""" + + principal_type: Optional[PrincipalType] = None + + status: Optional[State] = None + """The activity status of the principal in the workspace. Not applicable for groups at the moment.""" + + workspace_id: Optional[int] = None + """The workspace ID where the principal has access.""" + + def as_dict(self) -> dict: + """Serializes the WorkspaceAccessDetail into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.access_type is not None: + body["access_type"] = self.access_type.value + if self.account_id is not None: + body["account_id"] = self.account_id + if self.permissions: + body["permissions"] = [v.value for v in self.permissions] + if self.principal_id is not None: + body["principal_id"] = self.principal_id + if self.principal_type is not None: + body["principal_type"] = self.principal_type.value + if self.status is not None: + body["status"] = self.status.value + if self.workspace_id is not None: + body["workspace_id"] = self.workspace_id + return body + + def as_shallow_dict(self) -> dict: + """Serializes the WorkspaceAccessDetail into a shallow dictionary of its immediate attributes.""" + body = {} + if self.access_type is not None: + body["access_type"] = self.access_type + if self.account_id is not None: + body["account_id"] = self.account_id + if self.permissions: + body["permissions"] = self.permissions + if self.principal_id is not None: + body["principal_id"] = self.principal_id + if self.principal_type is not None: + body["principal_type"] = self.principal_type + if self.status is not None: + body["status"] = self.status + if self.workspace_id is not None: + body["workspace_id"] = self.workspace_id + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> WorkspaceAccessDetail: + """Deserializes the WorkspaceAccessDetail from a dictionary.""" + return cls( + access_type=_enum(d, "access_type", WorkspaceAccessDetailAccessType), + account_id=d.get("account_id", None), + permissions=_repeated_enum(d, "permissions", WorkspacePermission), + principal_id=d.get("principal_id", None), + principal_type=_enum(d, "principal_type", PrincipalType), + status=_enum(d, "status", State), + workspace_id=d.get("workspace_id", None), + ) + + +class WorkspaceAccessDetailAccessType(Enum): + """The type of access the principal has to the workspace.""" + + DIRECT = "DIRECT" + INDIRECT = "INDIRECT" + + +class WorkspaceAccessDetailView(Enum): + """Controls what fields are returned in the GetWorkspaceAccessDetail response.""" + + BASIC = "BASIC" + FULL = "FULL" + + +class WorkspacePermission(Enum): + """The type of permission a principal has to a workspace (admin/user).""" + + ADMIN_PERMISSION = "ADMIN_PERMISSION" + USER_PERMISSION = "USER_PERMISSION" + + +class AccountIamV2API: + """These APIs are used to manage identities and the workspace access of these identities in .""" + + def __init__(self, api_client): + self._api = api_client + + def get_workspace_access_detail( + self, workspace_id: int, principal_id: int, *, view: Optional[WorkspaceAccessDetailView] = None + ) -> WorkspaceAccessDetail: + """Returns the access details for a principal in a workspace. Allows for checking access details for any + provisioned principal (user, service principal, or group) in a workspace. * Provisioned principal here + refers to one that has been synced into Databricks from the customer's IdP or added explicitly to + Databricks via SCIM/UI. Allows for passing in a "view" parameter to control what fields are returned + (BASIC by default or FULL). + + :param workspace_id: int + Required. The workspace ID for which the access details are being requested. + :param principal_id: int + Required. The internal ID of the principal (user/sp/group) for which the access details are being + requested. + :param view: :class:`WorkspaceAccessDetailView` (optional) + Controls what fields are returned. + + :returns: :class:`WorkspaceAccessDetail` + """ + + query = {} + if view is not None: + query["view"] = view.value + headers = { + "Accept": "application/json", + } + + res = self._api.do( + "GET", + f"/api/2.0/identity/accounts/{self._api.account_id}/workspaces/{workspace_id}/workspaceAccessDetails/{principal_id}", + query=query, + headers=headers, + ) + return WorkspaceAccessDetail.from_dict(res) + + def resolve_group(self, external_id: str) -> ResolveGroupResponse: + """Resolves a group with the given external ID from the customer's IdP. If the group does not exist, it + will be created in the account. If the customer is not onboarded onto Automatic Identity Management + (AIM), this will return an error. + + :param external_id: str + Required. The external ID of the group in the customer's IdP. + + :returns: :class:`ResolveGroupResponse` + """ + body = {} + if external_id is not None: + body["external_id"] = external_id + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do( + "POST", + f"/api/2.0/identity/accounts/{self._api.account_id}/groups/resolveByExternalId", + body=body, + headers=headers, + ) + return ResolveGroupResponse.from_dict(res) + + def resolve_service_principal(self, external_id: str) -> ResolveServicePrincipalResponse: + """Resolves an SP with the given external ID from the customer's IdP. If the SP does not exist, it will + be created. If the customer is not onboarded onto Automatic Identity Management (AIM), this will + return an error. + + :param external_id: str + Required. The external ID of the service principal in the customer's IdP. + + :returns: :class:`ResolveServicePrincipalResponse` + """ + body = {} + if external_id is not None: + body["external_id"] = external_id + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do( + "POST", + f"/api/2.0/identity/accounts/{self._api.account_id}/servicePrincipals/resolveByExternalId", + body=body, + headers=headers, + ) + return ResolveServicePrincipalResponse.from_dict(res) + + def resolve_user(self, external_id: str) -> ResolveUserResponse: + """Resolves a user with the given external ID from the customer's IdP. If the user does not exist, it + will be created. If the customer is not onboarded onto Automatic Identity Management (AIM), this will + return an error. + + :param external_id: str + Required. The external ID of the user in the customer's IdP. + + :returns: :class:`ResolveUserResponse` + """ + body = {} + if external_id is not None: + body["external_id"] = external_id + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do( + "POST", + f"/api/2.0/identity/accounts/{self._api.account_id}/users/resolveByExternalId", + body=body, + headers=headers, + ) + return ResolveUserResponse.from_dict(res) + + +class WorkspaceIamV2API: + """These APIs are used to manage identities and the workspace access of these identities in .""" + + def __init__(self, api_client): + self._api = api_client + + def get_workspace_access_detail_local( + self, principal_id: int, *, view: Optional[WorkspaceAccessDetailView] = None + ) -> WorkspaceAccessDetail: + """Returns the access details for a principal in the current workspace. Allows for checking access + details for any provisioned principal (user, service principal, or group) in the current workspace. * + Provisioned principal here refers to one that has been synced into Databricks from the customer's IdP + or added explicitly to Databricks via SCIM/UI. Allows for passing in a "view" parameter to control + what fields are returned (BASIC by default or FULL). + + :param principal_id: int + Required. The internal ID of the principal (user/sp/group) for which the access details are being + requested. + :param view: :class:`WorkspaceAccessDetailView` (optional) + Controls what fields are returned. + + :returns: :class:`WorkspaceAccessDetail` + """ + + query = {} + if view is not None: + query["view"] = view.value + headers = { + "Accept": "application/json", + } + + res = self._api.do( + "GET", f"/api/2.0/identity/workspaceAccessDetails/{principal_id}", query=query, headers=headers + ) + return WorkspaceAccessDetail.from_dict(res) + + def resolve_group_proxy(self, external_id: str) -> ResolveGroupResponse: + """Resolves a group with the given external ID from the customer's IdP. If the group does not exist, it + will be created in the account. If the customer is not onboarded onto Automatic Identity Management + (AIM), this will return an error. + + :param external_id: str + Required. The external ID of the group in the customer's IdP. + + :returns: :class:`ResolveGroupResponse` + """ + body = {} + if external_id is not None: + body["external_id"] = external_id + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do("POST", "/api/2.0/identity/groups/resolveByExternalId", body=body, headers=headers) + return ResolveGroupResponse.from_dict(res) + + def resolve_service_principal_proxy(self, external_id: str) -> ResolveServicePrincipalResponse: + """Resolves an SP with the given external ID from the customer's IdP. If the SP does not exist, it will + be created. If the customer is not onboarded onto Automatic Identity Management (AIM), this will + return an error. + + :param external_id: str + Required. The external ID of the service principal in the customer's IdP. + + :returns: :class:`ResolveServicePrincipalResponse` + """ + body = {} + if external_id is not None: + body["external_id"] = external_id + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do( + "POST", "/api/2.0/identity/servicePrincipals/resolveByExternalId", body=body, headers=headers + ) + return ResolveServicePrincipalResponse.from_dict(res) + + def resolve_user_proxy(self, external_id: str) -> ResolveUserResponse: + """Resolves a user with the given external ID from the customer's IdP. If the user does not exist, it + will be created. If the customer is not onboarded onto Automatic Identity Management (AIM), this will + return an error. + + :param external_id: str + Required. The external ID of the user in the customer's IdP. + + :returns: :class:`ResolveUserResponse` + """ + body = {} + if external_id is not None: + body["external_id"] = external_id + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do("POST", "/api/2.0/identity/users/resolveByExternalId", body=body, headers=headers) + return ResolveUserResponse.from_dict(res) diff --git a/databricks/sdk/service/jobs.py b/databricks/sdk/service/jobs.py index 9f023448b..c372223ee 100755 --- a/databricks/sdk/service/jobs.py +++ b/databricks/sdk/service/jobs.py @@ -10,8 +10,10 @@ from enum import Enum from typing import Any, Callable, Dict, Iterator, List, Optional +from databricks.sdk.service._internal import (Wait, _enum, _from_dict, + _repeated_dict) + from ..errors import OperationFailed -from ._internal import Wait, _enum, _from_dict, _repeated_dict _LOG = logging.getLogger("databricks.sdk") @@ -5696,9 +5698,6 @@ class RunTask: description: Optional[str] = None """An optional description for this task.""" - disabled: Optional[bool] = None - """Deprecated, field was never used in production.""" - effective_performance_target: Optional[PerformanceTarget] = None """The actual performance target used by the serverless run during execution. This can differ from the client-set performance target on the request depending on whether the performance mode is @@ -5873,8 +5872,6 @@ def as_dict(self) -> dict: body["depends_on"] = [v.as_dict() for v in self.depends_on] if self.description is not None: body["description"] = self.description - if self.disabled is not None: - body["disabled"] = self.disabled if self.effective_performance_target is not None: body["effective_performance_target"] = self.effective_performance_target.value if self.email_notifications: @@ -5972,8 +5969,6 @@ def as_shallow_dict(self) -> dict: body["depends_on"] = self.depends_on if self.description is not None: body["description"] = self.description - if self.disabled is not None: - body["disabled"] = self.disabled if self.effective_performance_target is not None: body["effective_performance_target"] = self.effective_performance_target if self.email_notifications: @@ -6061,7 +6056,6 @@ def from_dict(cls, d: Dict[str, Any]) -> RunTask: dbt_task=_from_dict(d, "dbt_task", DbtTask), depends_on=_repeated_dict(d, "depends_on", TaskDependency), description=d.get("description", None), - disabled=d.get("disabled", None), effective_performance_target=_enum(d, "effective_performance_target", PerformanceTarget), email_notifications=_from_dict(d, "email_notifications", JobEmailNotifications), end_time=d.get("end_time", None), @@ -7438,6 +7432,10 @@ class Task: disable_auto_optimization: Optional[bool] = None """An option to disable auto optimization in serverless""" + disabled: Optional[bool] = None + """An optional flag to disable the task. If set to true, the task will not run even if it is part + of a job.""" + email_notifications: Optional[TaskEmailNotifications] = None """An optional set of email addresses that is notified when runs of this task begin or complete as well as when this task is deleted. The default behavior is to not send any emails.""" @@ -7568,6 +7566,8 @@ def as_dict(self) -> dict: body["description"] = self.description if self.disable_auto_optimization is not None: body["disable_auto_optimization"] = self.disable_auto_optimization + if self.disabled is not None: + body["disabled"] = self.disabled if self.email_notifications: body["email_notifications"] = self.email_notifications.as_dict() if self.environment_key is not None: @@ -7643,6 +7643,8 @@ def as_shallow_dict(self) -> dict: body["description"] = self.description if self.disable_auto_optimization is not None: body["disable_auto_optimization"] = self.disable_auto_optimization + if self.disabled is not None: + body["disabled"] = self.disabled if self.email_notifications: body["email_notifications"] = self.email_notifications if self.environment_key is not None: @@ -7710,6 +7712,7 @@ def from_dict(cls, d: Dict[str, Any]) -> Task: depends_on=_repeated_dict(d, "depends_on", TaskDependency), description=d.get("description", None), disable_auto_optimization=d.get("disable_auto_optimization", None), + disabled=d.get("disabled", None), email_notifications=_from_dict(d, "email_notifications", TaskEmailNotifications), environment_key=d.get("environment_key", None), existing_cluster_id=d.get("existing_cluster_id", None), diff --git a/databricks/sdk/service/marketplace.py b/databricks/sdk/service/marketplace.py index a199010ab..db313f7a1 100755 --- a/databricks/sdk/service/marketplace.py +++ b/databricks/sdk/service/marketplace.py @@ -7,7 +7,8 @@ from enum import Enum from typing import Any, Dict, Iterator, List, Optional -from ._internal import _enum, _from_dict, _repeated_dict, _repeated_enum +from databricks.sdk.service._internal import (_enum, _from_dict, + _repeated_dict, _repeated_enum) _LOG = logging.getLogger("databricks.sdk") diff --git a/databricks/sdk/service/ml.py b/databricks/sdk/service/ml.py index 6ca964fd4..467219190 100755 --- a/databricks/sdk/service/ml.py +++ b/databricks/sdk/service/ml.py @@ -10,8 +10,10 @@ from enum import Enum from typing import Any, Callable, Dict, Iterator, List, Optional +from databricks.sdk.service._internal import (Wait, _enum, _from_dict, + _repeated_dict, _repeated_enum) + from ..errors import OperationFailed -from ._internal import Wait, _enum, _from_dict, _repeated_dict, _repeated_enum _LOG = logging.getLogger("databricks.sdk") diff --git a/databricks/sdk/service/oauth2.py b/databricks/sdk/service/oauth2.py index 4e762e763..32dcaf8f3 100755 --- a/databricks/sdk/service/oauth2.py +++ b/databricks/sdk/service/oauth2.py @@ -6,7 +6,7 @@ from dataclasses import dataclass from typing import Any, Dict, Iterator, List, Optional -from ._internal import _from_dict, _repeated_dict +from databricks.sdk.service._internal import _from_dict, _repeated_dict _LOG = logging.getLogger("databricks.sdk") @@ -232,11 +232,11 @@ class FederationPolicy: oidc_policy: Optional[OidcFederationPolicy] = None policy_id: Optional[str] = None - """The ID of the federation policy.""" + """The ID of the federation policy. Output only.""" service_principal_id: Optional[int] = None - """The service principal ID that this federation policy applies to. Only set for service principal - federation policies.""" + """The service principal ID that this federation policy applies to. Output only. Only set for + service principal federation policies.""" uid: Optional[str] = None """Unique, immutable id of the federation policy.""" diff --git a/databricks/sdk/service/pipelines.py b/databricks/sdk/service/pipelines.py index a9e8ee11b..745831c16 100755 --- a/databricks/sdk/service/pipelines.py +++ b/databricks/sdk/service/pipelines.py @@ -10,8 +10,10 @@ from enum import Enum from typing import Any, Callable, Dict, Iterator, List, Optional +from databricks.sdk.service._internal import (Wait, _enum, _from_dict, + _repeated_dict, _repeated_enum) + from ..errors import OperationFailed -from ._internal import Wait, _enum, _from_dict, _repeated_dict, _repeated_enum _LOG = logging.getLogger("databricks.sdk") @@ -735,6 +737,7 @@ class IngestionSourceType(Enum): BIGQUERY = "BIGQUERY" CONFLUENCE = "CONFLUENCE" DYNAMICS365 = "DYNAMICS365" + FOREIGN_CATALOG = "FOREIGN_CATALOG" GA4_RAW_DATA = "GA4_RAW_DATA" MANAGED_POSTGRESQL = "MANAGED_POSTGRESQL" META_MARKETING = "META_MARKETING" diff --git a/databricks/sdk/service/provisioning.py b/databricks/sdk/service/provisioning.py index 8e34b28f0..f1bc44230 100755 --- a/databricks/sdk/service/provisioning.py +++ b/databricks/sdk/service/provisioning.py @@ -10,8 +10,10 @@ from enum import Enum from typing import Any, Callable, Dict, Iterator, List, Optional +from databricks.sdk.service._internal import (Wait, _enum, _from_dict, + _repeated_dict, _repeated_enum) + from ..errors import OperationFailed -from ._internal import Wait, _enum, _from_dict, _repeated_dict, _repeated_enum _LOG = logging.getLogger("databricks.sdk") diff --git a/databricks/sdk/service/qualitymonitorv2.py b/databricks/sdk/service/qualitymonitorv2.py index a6fab7023..337bb86a5 100755 --- a/databricks/sdk/service/qualitymonitorv2.py +++ b/databricks/sdk/service/qualitymonitorv2.py @@ -7,7 +7,7 @@ from enum import Enum from typing import Any, Dict, Iterator, List, Optional -from ._internal import _enum, _from_dict, _repeated_dict +from databricks.sdk.service._internal import _enum, _from_dict, _repeated_dict _LOG = logging.getLogger("databricks.sdk") diff --git a/databricks/sdk/service/serving.py b/databricks/sdk/service/serving.py index ff7ce801a..8911ea960 100755 --- a/databricks/sdk/service/serving.py +++ b/databricks/sdk/service/serving.py @@ -13,8 +13,10 @@ import requests +from databricks.sdk.service._internal import (Wait, _enum, _from_dict, + _repeated_dict) + from ..errors import OperationFailed -from ._internal import Wait, _enum, _from_dict, _repeated_dict _LOG = logging.getLogger("databricks.sdk") diff --git a/databricks/sdk/service/settings.py b/databricks/sdk/service/settings.py index 3004f17da..b27e595b8 100755 --- a/databricks/sdk/service/settings.py +++ b/databricks/sdk/service/settings.py @@ -7,7 +7,8 @@ from enum import Enum from typing import Any, Dict, Iterator, List, Optional -from ._internal import _enum, _from_dict, _repeated_dict, _repeated_enum +from databricks.sdk.service._internal import (_enum, _from_dict, + _repeated_dict, _repeated_enum) _LOG = logging.getLogger("databricks.sdk") diff --git a/databricks/sdk/service/settingsv2.py b/databricks/sdk/service/settingsv2.py index 10e609f3b..1ac3519a1 100755 --- a/databricks/sdk/service/settingsv2.py +++ b/databricks/sdk/service/settingsv2.py @@ -7,7 +7,7 @@ from enum import Enum from typing import Any, Dict, Iterator, List, Optional -from ._internal import _enum, _from_dict, _repeated_dict +from databricks.sdk.service._internal import _enum, _from_dict, _repeated_dict _LOG = logging.getLogger("databricks.sdk") @@ -322,44 +322,6 @@ def from_dict(cls, d: Dict[str, Any]) -> ClusterAutoRestartMessageMaintenanceWin 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 @@ -528,12 +490,9 @@ class Setting: 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 @@ -542,8 +501,6 @@ class Setting: 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 @@ -574,8 +531,6 @@ def as_dict(self) -> dict: 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() @@ -590,8 +545,6 @@ def as_dict(self) -> 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: @@ -623,8 +576,6 @@ def as_shallow_dict(self) -> dict: 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 @@ -637,8 +588,6 @@ def as_shallow_dict(self) -> dict: 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: @@ -673,7 +622,6 @@ def from_dict(cls, d: Dict[str, Any]) -> Setting: 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 ), @@ -684,9 +632,6 @@ def from_dict(cls, d: Dict[str, Any]) -> Setting: 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( @@ -784,7 +729,8 @@ 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 + """Get a setting value at account level. See :method:settingsv2/listaccountsettingsmetadata for list of + setting available via public APIs at account level. :param name: str @@ -801,9 +747,9 @@ def get_public_account_setting(self, name: str) -> Setting: 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 + """List valid setting keys and metadata. These settings are available to be referenced via GET + :method:settingsv2/getpublicaccountsetting and PATCH :method:settingsv2/patchpublicworkspacesetting + APIs :param page_size: int (optional) The maximum number of settings to return. The service may return fewer than this value. If @@ -840,7 +786,8 @@ def list_account_settings_metadata( 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 + """Patch a setting value at account level. See :method:settingsv2/listaccountsettingsmetadata for list of + setting available via public APIs at account level. :param name: str :param setting: :class:`Setting` @@ -866,7 +813,8 @@ 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 + """Get a setting value at workspace level. See :method:settingsv2/listworkspacesettingsmetadata for list + of setting available via public APIs. :param name: str @@ -883,9 +831,9 @@ def get_public_workspace_setting(self, name: str) -> Setting: 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 + """List valid setting keys and metadata. These settings are available to be referenced via GET + :method:settingsv2/getpublicworkspacesetting and PATCH :method:settingsv2/patchpublicworkspacesetting + APIs :param page_size: int (optional) The maximum number of settings to return. The service may return fewer than this value. If @@ -920,7 +868,8 @@ def list_workspace_settings_metadata( 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 + """Patch a setting value at workspace level. See :method:settingsv2/listworkspacesettingsmetadata for + list of setting available via public APIs at workspace level. :param name: str :param setting: :class:`Setting` diff --git a/databricks/sdk/service/sharing.py b/databricks/sdk/service/sharing.py index fd8063c3d..449ec9f40 100755 --- a/databricks/sdk/service/sharing.py +++ b/databricks/sdk/service/sharing.py @@ -7,7 +7,8 @@ from enum import Enum from typing import Any, Dict, Iterator, List, Optional -from ._internal import _enum, _from_dict, _repeated_dict, _repeated_enum +from databricks.sdk.service._internal import (_enum, _from_dict, + _repeated_dict, _repeated_enum) _LOG = logging.getLogger("databricks.sdk") @@ -2307,6 +2308,9 @@ def from_dict(cls, d: Dict[str, Any]) -> Table: class TableInternalAttributes: """Internal information for D2D sharing that should not be disclosed to external users.""" + auxiliary_managed_location: Optional[str] = None + """Managed Delta Metadata location for foreign iceberg tables.""" + parent_storage_location: Optional[str] = None """Will be populated in the reconciliation response for VIEW and FOREIGN_TABLE, with the value of the parent UC entity's storage_location, following the same logic as getManagedEntityPath in @@ -2327,6 +2331,8 @@ class TableInternalAttributes: def as_dict(self) -> dict: """Serializes the TableInternalAttributes into a dictionary suitable for use as a JSON request body.""" body = {} + if self.auxiliary_managed_location is not None: + body["auxiliary_managed_location"] = self.auxiliary_managed_location if self.parent_storage_location is not None: body["parent_storage_location"] = self.parent_storage_location if self.storage_location is not None: @@ -2340,6 +2346,8 @@ def as_dict(self) -> dict: def as_shallow_dict(self) -> dict: """Serializes the TableInternalAttributes into a shallow dictionary of its immediate attributes.""" body = {} + if self.auxiliary_managed_location is not None: + body["auxiliary_managed_location"] = self.auxiliary_managed_location if self.parent_storage_location is not None: body["parent_storage_location"] = self.parent_storage_location if self.storage_location is not None: @@ -2354,6 +2362,7 @@ def as_shallow_dict(self) -> dict: def from_dict(cls, d: Dict[str, Any]) -> TableInternalAttributes: """Deserializes the TableInternalAttributes from a dictionary.""" return cls( + auxiliary_managed_location=d.get("auxiliary_managed_location", None), parent_storage_location=d.get("parent_storage_location", None), storage_location=d.get("storage_location", None), type=_enum(d, "type", TableInternalAttributesSharedTableType), @@ -2366,6 +2375,7 @@ class TableInternalAttributesSharedTableType(Enum): DELTA_ICEBERG_TABLE = "DELTA_ICEBERG_TABLE" DIRECTORY_BASED_TABLE = "DIRECTORY_BASED_TABLE" FILE_BASED_TABLE = "FILE_BASED_TABLE" + FOREIGN_ICEBERG_TABLE = "FOREIGN_ICEBERG_TABLE" FOREIGN_TABLE = "FOREIGN_TABLE" MATERIALIZED_VIEW = "MATERIALIZED_VIEW" STREAMING_TABLE = "STREAMING_TABLE" diff --git a/databricks/sdk/service/sql.py b/databricks/sdk/service/sql.py index 9a80d1f5b..76232d337 100755 --- a/databricks/sdk/service/sql.py +++ b/databricks/sdk/service/sql.py @@ -10,8 +10,10 @@ from enum import Enum from typing import Any, Callable, Dict, Iterator, List, Optional +from databricks.sdk.service._internal import (Wait, _enum, _from_dict, + _repeated_dict, _repeated_enum) + from ..errors import OperationFailed -from ._internal import Wait, _enum, _from_dict, _repeated_dict, _repeated_enum _LOG = logging.getLogger("databricks.sdk") @@ -792,7 +794,8 @@ class AlertV2Evaluation: """Operator used for comparison in alert evaluation.""" empty_result_state: Optional[AlertEvaluationState] = None - """Alert state if result is empty.""" + """Alert state if result is empty. Please avoid setting this field to be `UNKNOWN` because + `UNKNOWN` state is planned to be deprecated.""" last_evaluated_at: Optional[str] = None """Timestamp of the last evaluation.""" @@ -3914,13 +3917,18 @@ def from_dict(cls, d: Dict[str, Any]) -> ListAlertsResponseAlert: @dataclass class ListAlertsV2Response: + alerts: Optional[List[AlertV2]] = None + next_page_token: Optional[str] = None results: Optional[List[AlertV2]] = None + """Deprecated. Use `alerts` instead.""" def as_dict(self) -> dict: """Serializes the ListAlertsV2Response into a dictionary suitable for use as a JSON request body.""" body = {} + if self.alerts: + body["alerts"] = [v.as_dict() for v in self.alerts] if self.next_page_token is not None: body["next_page_token"] = self.next_page_token if self.results: @@ -3930,6 +3938,8 @@ def as_dict(self) -> dict: def as_shallow_dict(self) -> dict: """Serializes the ListAlertsV2Response into a shallow dictionary of its immediate attributes.""" body = {} + if self.alerts: + body["alerts"] = self.alerts if self.next_page_token is not None: body["next_page_token"] = self.next_page_token if self.results: @@ -3939,7 +3949,11 @@ def as_shallow_dict(self) -> dict: @classmethod def from_dict(cls, d: Dict[str, Any]) -> ListAlertsV2Response: """Deserializes the ListAlertsV2Response from a dictionary.""" - return cls(next_page_token=d.get("next_page_token", None), results=_repeated_dict(d, "results", AlertV2)) + return cls( + alerts=_repeated_dict(d, "alerts", AlertV2), + next_page_token=d.get("next_page_token", None), + results=_repeated_dict(d, "results", AlertV2), + ) class ListOrder(Enum): diff --git a/databricks/sdk/service/tags.py b/databricks/sdk/service/tags.py index 8962682fa..a46244385 100755 --- a/databricks/sdk/service/tags.py +++ b/databricks/sdk/service/tags.py @@ -6,7 +6,7 @@ from dataclasses import dataclass from typing import Any, Dict, Iterator, List, Optional -from ._internal import _repeated_dict +from databricks.sdk.service._internal import _repeated_dict _LOG = logging.getLogger("databricks.sdk") @@ -118,13 +118,13 @@ def from_dict(cls, d: Dict[str, Any]) -> Value: class TagPoliciesAPI: - """The Tag Policy API allows you to manage tag policies in Databricks.""" + """The Tag Policy API allows you to manage policies for governed tags 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. + """Creates a new tag policy, making the associated tag key governed. :param tag_policy: :class:`TagPolicy` @@ -140,7 +140,7 @@ def create_tag_policy(self, tag_policy: TagPolicy) -> TagPolicy: return TagPolicy.from_dict(res) def delete_tag_policy(self, tag_key: str): - """Deletes a tag policy by its key. + """Deletes a tag policy by its associated governed tag's key, leaving that tag key ungoverned. :param tag_key: str @@ -154,7 +154,7 @@ def delete_tag_policy(self, tag_key: str): 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. + """Gets a single tag policy by its associated governed tag's key. :param tag_key: str @@ -171,7 +171,7 @@ def get_tag_policy(self, tag_key: str) -> TagPolicy: def list_tag_policies( self, *, page_size: Optional[int] = None, page_token: Optional[str] = None ) -> Iterator[TagPolicy]: - """Lists all tag policies in the account. + """Lists the tag policies for all governed tags in the account. :param page_size: int (optional) The maximum number of results to return in this request. Fewer results may be returned than @@ -202,7 +202,7 @@ def list_tag_policies( 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. + """Updates an existing tag policy for a single governed tag. :param tag_key: str :param tag_policy: :class:`TagPolicy` diff --git a/databricks/sdk/service/vectorsearch.py b/databricks/sdk/service/vectorsearch.py index 8e706ccd6..4b75889f1 100755 --- a/databricks/sdk/service/vectorsearch.py +++ b/databricks/sdk/service/vectorsearch.py @@ -10,8 +10,10 @@ from enum import Enum from typing import Any, Callable, Dict, Iterator, List, Optional +from databricks.sdk.service._internal import (Wait, _enum, _from_dict, + _repeated_dict) + from ..errors import OperationFailed -from ._internal import Wait, _enum, _from_dict, _repeated_dict _LOG = logging.getLogger("databricks.sdk") diff --git a/databricks/sdk/service/workspace.py b/databricks/sdk/service/workspace.py index cab860d9c..43ffb9b4b 100755 --- a/databricks/sdk/service/workspace.py +++ b/databricks/sdk/service/workspace.py @@ -7,7 +7,7 @@ from enum import Enum from typing import Any, Dict, Iterator, List, Optional -from ._internal import _enum, _from_dict, _repeated_dict +from databricks.sdk.service._internal import _enum, _from_dict, _repeated_dict _LOG = logging.getLogger("databricks.sdk") diff --git a/tests/databricks/sdk/service/httpcallv2.py b/tests/databricks/sdk/service/httpcallv2.py new file mode 100755 index 000000000..efbe0c0d4 --- /dev/null +++ b/tests/databricks/sdk/service/httpcallv2.py @@ -0,0 +1,244 @@ +# 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, List, Optional + +_LOG = logging.getLogger("databricks.sdk") + + +# all definitions in this file are in alphabetical order + + +@dataclass +class ComplexQueryParam: + nested_optional_query_param: Optional[str] = None + + nested_repeated_query_param: Optional[List[str]] = None + + def as_dict(self) -> dict: + """Serializes the ComplexQueryParam into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.nested_optional_query_param is not None: + body["nested_optional_query_param"] = self.nested_optional_query_param + if self.nested_repeated_query_param: + body["nested_repeated_query_param"] = [v for v in self.nested_repeated_query_param] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the ComplexQueryParam into a shallow dictionary of its immediate attributes.""" + body = {} + if self.nested_optional_query_param is not None: + body["nested_optional_query_param"] = self.nested_optional_query_param + if self.nested_repeated_query_param: + body["nested_repeated_query_param"] = self.nested_repeated_query_param + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> ComplexQueryParam: + """Deserializes the ComplexQueryParam from a dictionary.""" + return cls( + nested_optional_query_param=d.get("nested_optional_query_param", None), + nested_repeated_query_param=d.get("nested_repeated_query_param", None), + ) + + +@dataclass +class Resource: + any_field: Optional[dict] = None + + body_field: Optional[str] = None + + nested_path_param_bool: Optional[bool] = None + + nested_path_param_int: Optional[int] = None + + nested_path_param_string: Optional[str] = None + + def as_dict(self) -> dict: + """Serializes the Resource into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.any_field: + body["any_field"] = self.any_field + if self.body_field is not None: + body["body_field"] = self.body_field + if self.nested_path_param_bool is not None: + body["nested_path_param_bool"] = self.nested_path_param_bool + if self.nested_path_param_int is not None: + body["nested_path_param_int"] = self.nested_path_param_int + if self.nested_path_param_string is not None: + body["nested_path_param_string"] = self.nested_path_param_string + return body + + def as_shallow_dict(self) -> dict: + """Serializes the Resource into a shallow dictionary of its immediate attributes.""" + body = {} + if self.any_field: + body["any_field"] = self.any_field + if self.body_field is not None: + body["body_field"] = self.body_field + if self.nested_path_param_bool is not None: + body["nested_path_param_bool"] = self.nested_path_param_bool + if self.nested_path_param_int is not None: + body["nested_path_param_int"] = self.nested_path_param_int + if self.nested_path_param_string is not None: + body["nested_path_param_string"] = self.nested_path_param_string + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> Resource: + """Deserializes the Resource from a dictionary.""" + return cls( + any_field=d.get("any_field", None), + body_field=d.get("body_field", None), + nested_path_param_bool=d.get("nested_path_param_bool", None), + nested_path_param_int=d.get("nested_path_param_int", None), + nested_path_param_string=d.get("nested_path_param_string", None), + ) + + +class HttpCallV2API: + """Lorem Ipsum""" + + def __init__(self, api_client): + self._api = api_client + + def create_resource( + self, path_param_string: str, path_param_int: int, path_param_bool: bool, *, body_field: Optional[str] = None + ) -> Resource: + """This mimics "old" style post requests which have the resource inlined. + + :param path_param_string: str + :param path_param_int: int + :param path_param_bool: bool + :param body_field: str (optional) + Body element + + :returns: :class:`Resource` + """ + body = {} + if body_field is not None: + body["body_field"] = body_field + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do( + "POST", + f"/api/2.0/http-call/{path_param_string}/{path_param_int}/{path_param_bool}", + body=body, + headers=headers, + ) + return Resource.from_dict(res) + + def get_resource( + self, + path_param_string: str, + path_param_int: int, + path_param_bool: bool, + *, + field_mask: Optional[str] = None, + optional_complex_query_param: Optional[ComplexQueryParam] = None, + query_param_bool: Optional[bool] = None, + query_param_int: Optional[int] = None, + query_param_string: Optional[str] = None, + repeated_complex_query_param: Optional[List[ComplexQueryParam]] = None, + repeated_query_param: Optional[List[str]] = None, + ) -> Resource: + + query = {} + if field_mask is not None: + query["field_mask"] = field_mask + if optional_complex_query_param is not None: + query["optional_complex_query_param"] = optional_complex_query_param.as_dict() + if query_param_bool is not None: + query["query_param_bool"] = query_param_bool + if query_param_int is not None: + query["query_param_int"] = query_param_int + if query_param_string is not None: + query["query_param_string"] = query_param_string + if repeated_complex_query_param is not None: + query["repeated_complex_query_param"] = [v.as_dict() for v in repeated_complex_query_param] + if repeated_query_param is not None: + query["repeated_query_param"] = [v for v in repeated_query_param] + headers = { + "Accept": "application/json", + } + + res = self._api.do( + "GET", + f"/api/2.0/http-call/{path_param_string}/{path_param_int}/{path_param_bool}", + query=query, + headers=headers, + ) + return Resource.from_dict(res) + + def update_resource( + self, + nested_path_param_string: str, + nested_path_param_int: int, + nested_path_param_bool: bool, + resource: Resource, + *, + field_mask: Optional[str] = None, + optional_complex_query_param: Optional[ComplexQueryParam] = None, + query_param_bool: Optional[bool] = None, + query_param_int: Optional[int] = None, + query_param_string: Optional[str] = None, + repeated_complex_query_param: Optional[List[ComplexQueryParam]] = None, + repeated_query_param: Optional[List[str]] = None, + ) -> Resource: + """This mimics "new" style post requests which have a body field. + + :param nested_path_param_string: str + :param nested_path_param_int: int + :param nested_path_param_bool: bool + :param resource: :class:`Resource` + Body element + :param field_mask: str (optional) + 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. + :param optional_complex_query_param: :class:`ComplexQueryParam` (optional) + :param query_param_bool: bool (optional) + :param query_param_int: int (optional) + :param query_param_string: str (optional) + :param repeated_complex_query_param: List[:class:`ComplexQueryParam`] (optional) + :param repeated_query_param: List[str] (optional) + + :returns: :class:`Resource` + """ + body = resource.as_dict() + query = {} + if field_mask is not None: + query["field_mask"] = field_mask + if optional_complex_query_param is not None: + query["optional_complex_query_param"] = optional_complex_query_param.as_dict() + if query_param_bool is not None: + query["query_param_bool"] = query_param_bool + if query_param_int is not None: + query["query_param_int"] = query_param_int + if query_param_string is not None: + query["query_param_string"] = query_param_string + if repeated_complex_query_param is not None: + query["repeated_complex_query_param"] = [v.as_dict() for v in repeated_complex_query_param] + if repeated_query_param is not None: + query["repeated_query_param"] = [v for v in repeated_query_param] + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + } + + res = self._api.do( + "PATCH", + f"/api/2.0/http-call/{nested_path_param_string}/{nested_path_param_int}/{nested_path_param_bool}", + query=query, + body=body, + headers=headers, + ) + return Resource.from_dict(res) diff --git a/tests/databricks/sdk/service/jsonmarshallv2.py b/tests/databricks/sdk/service/jsonmarshallv2.py new file mode 100755 index 000000000..996e7ce68 --- /dev/null +++ b/tests/databricks/sdk/service/jsonmarshallv2.py @@ -0,0 +1,478 @@ +# 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, List, Optional + +from databricks.sdk.service._internal import (_enum, _from_dict, + _repeated_dict, _repeated_enum) + +_LOG = logging.getLogger("databricks.sdk") + + +# all definitions in this file are in alphabetical order + + +@dataclass +class NestedMessage: + optional_duration: Optional[str] = None + + optional_string: Optional[str] = None + + optional_timestamp: Optional[str] = None + + def as_dict(self) -> dict: + """Serializes the NestedMessage into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.optional_duration is not None: + body["optional_duration"] = self.optional_duration + if self.optional_string is not None: + body["optional_string"] = self.optional_string + if self.optional_timestamp is not None: + body["optional_timestamp"] = self.optional_timestamp + return body + + def as_shallow_dict(self) -> dict: + """Serializes the NestedMessage into a shallow dictionary of its immediate attributes.""" + body = {} + if self.optional_duration is not None: + body["optional_duration"] = self.optional_duration + if self.optional_string is not None: + body["optional_string"] = self.optional_string + if self.optional_timestamp is not None: + body["optional_timestamp"] = self.optional_timestamp + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> NestedMessage: + """Deserializes the NestedMessage from a dictionary.""" + return cls( + optional_duration=d.get("optional_duration", None), + optional_string=d.get("optional_string", None), + optional_timestamp=d.get("optional_timestamp", None), + ) + + +@dataclass +class OptionalFields: + duration: Optional[str] = None + + field_mask: Optional[str] = None + """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.""" + + legacy_duration: Optional[str] = None + """Legacy Well Known types""" + + legacy_field_mask: Optional[str] = None + """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.""" + + legacy_timestamp: Optional[str] = None + + list_value: Optional[List[any]] = None + + map: Optional[Dict[str, str]] = None + """Lint disable reason: This is a dummy field used to test SDK Generation logic.""" + + optional_bool: Optional[bool] = None + + optional_int32: Optional[int] = None + + optional_int64: Optional[int] = None + + optional_message: Optional[NestedMessage] = None + + optional_string: Optional[str] = None + + struct: Optional[Dict[str, any]] = None + + test_enum: Optional[TestEnum] = None + + timestamp: Optional[str] = None + + value: Optional[any] = None + + def as_dict(self) -> dict: + """Serializes the OptionalFields into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.duration is not None: + body["duration"] = self.duration + if self.field_mask is not None: + body["field_mask"] = self.field_mask + if self.legacy_duration is not None: + body["legacy_duration"] = self.legacy_duration + if self.legacy_field_mask is not None: + body["legacy_field_mask"] = self.legacy_field_mask + if self.legacy_timestamp is not None: + body["legacy_timestamp"] = self.legacy_timestamp + if self.list_value: + body["list_value"] = [v for v in self.list_value] + if self.map: + body["map"] = self.map + if self.optional_bool is not None: + body["optional_bool"] = self.optional_bool + if self.optional_int32 is not None: + body["optional_int32"] = self.optional_int32 + if self.optional_int64 is not None: + body["optional_int64"] = self.optional_int64 + if self.optional_message: + body["optional_message"] = self.optional_message.as_dict() + if self.optional_string is not None: + body["optional_string"] = self.optional_string + if self.struct: + body["struct"] = self.struct + if self.test_enum is not None: + body["test_enum"] = self.test_enum.value + if self.timestamp is not None: + body["timestamp"] = self.timestamp + if self.value: + body["value"] = self.value + return body + + def as_shallow_dict(self) -> dict: + """Serializes the OptionalFields into a shallow dictionary of its immediate attributes.""" + body = {} + if self.duration is not None: + body["duration"] = self.duration + if self.field_mask is not None: + body["field_mask"] = self.field_mask + if self.legacy_duration is not None: + body["legacy_duration"] = self.legacy_duration + if self.legacy_field_mask is not None: + body["legacy_field_mask"] = self.legacy_field_mask + if self.legacy_timestamp is not None: + body["legacy_timestamp"] = self.legacy_timestamp + if self.list_value: + body["list_value"] = self.list_value + if self.map: + body["map"] = self.map + if self.optional_bool is not None: + body["optional_bool"] = self.optional_bool + if self.optional_int32 is not None: + body["optional_int32"] = self.optional_int32 + if self.optional_int64 is not None: + body["optional_int64"] = self.optional_int64 + if self.optional_message: + body["optional_message"] = self.optional_message + if self.optional_string is not None: + body["optional_string"] = self.optional_string + if self.struct: + body["struct"] = self.struct + if self.test_enum is not None: + body["test_enum"] = self.test_enum + if self.timestamp is not None: + body["timestamp"] = self.timestamp + if self.value: + body["value"] = self.value + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> OptionalFields: + """Deserializes the OptionalFields from a dictionary.""" + return cls( + duration=d.get("duration", None), + field_mask=d.get("field_mask", None), + legacy_duration=d.get("legacy_duration", None), + legacy_field_mask=d.get("legacy_field_mask", None), + legacy_timestamp=d.get("legacy_timestamp", None), + list_value=d.get("list_value", None), + map=d.get("map", None), + optional_bool=d.get("optional_bool", None), + optional_int32=d.get("optional_int32", None), + optional_int64=d.get("optional_int64", None), + optional_message=_from_dict(d, "optional_message", NestedMessage), + optional_string=d.get("optional_string", None), + struct=d.get("struct", None), + test_enum=_enum(d, "test_enum", TestEnum), + timestamp=d.get("timestamp", None), + value=d.get("value", None), + ) + + +@dataclass +class RepeatedFields: + repeated_bool: Optional[List[bool]] = None + + repeated_duration: Optional[List[str]] = None + + repeated_field_mask: Optional[List[str]] = None + + repeated_int32: Optional[List[int]] = None + + repeated_int64: Optional[List[int]] = None + + repeated_list_value: Optional[List[List[any]]] = None + + repeated_message: Optional[List[NestedMessage]] = None + + repeated_string: Optional[List[str]] = None + + repeated_struct: Optional[List[Dict[str, any]]] = None + + repeated_timestamp: Optional[List[str]] = None + + repeated_value: Optional[List[any]] = None + + test_repeated_enum: Optional[List[TestEnum]] = None + + def as_dict(self) -> dict: + """Serializes the RepeatedFields into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.repeated_bool: + body["repeated_bool"] = [v for v in self.repeated_bool] + if self.repeated_duration: + body["repeated_duration"] = [v for v in self.repeated_duration] + if self.repeated_field_mask: + body["repeated_field_mask"] = [v for v in self.repeated_field_mask] + if self.repeated_int32: + body["repeated_int32"] = [v for v in self.repeated_int32] + if self.repeated_int64: + body["repeated_int64"] = [v for v in self.repeated_int64] + if self.repeated_list_value: + body["repeated_list_value"] = [v for v in self.repeated_list_value] + if self.repeated_message: + body["repeated_message"] = [v.as_dict() for v in self.repeated_message] + if self.repeated_string: + body["repeated_string"] = [v for v in self.repeated_string] + if self.repeated_struct: + body["repeated_struct"] = [v for v in self.repeated_struct] + if self.repeated_timestamp: + body["repeated_timestamp"] = [v for v in self.repeated_timestamp] + if self.repeated_value: + body["repeated_value"] = [v for v in self.repeated_value] + if self.test_repeated_enum: + body["test_repeated_enum"] = [v.value for v in self.test_repeated_enum] + return body + + def as_shallow_dict(self) -> dict: + """Serializes the RepeatedFields into a shallow dictionary of its immediate attributes.""" + body = {} + if self.repeated_bool: + body["repeated_bool"] = self.repeated_bool + if self.repeated_duration: + body["repeated_duration"] = self.repeated_duration + if self.repeated_field_mask: + body["repeated_field_mask"] = self.repeated_field_mask + if self.repeated_int32: + body["repeated_int32"] = self.repeated_int32 + if self.repeated_int64: + body["repeated_int64"] = self.repeated_int64 + if self.repeated_list_value: + body["repeated_list_value"] = self.repeated_list_value + if self.repeated_message: + body["repeated_message"] = self.repeated_message + if self.repeated_string: + body["repeated_string"] = self.repeated_string + if self.repeated_struct: + body["repeated_struct"] = self.repeated_struct + if self.repeated_timestamp: + body["repeated_timestamp"] = self.repeated_timestamp + if self.repeated_value: + body["repeated_value"] = self.repeated_value + if self.test_repeated_enum: + body["test_repeated_enum"] = self.test_repeated_enum + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> RepeatedFields: + """Deserializes the RepeatedFields from a dictionary.""" + return cls( + repeated_bool=d.get("repeated_bool", None), + repeated_duration=d.get("repeated_duration", None), + repeated_field_mask=d.get("repeated_field_mask", None), + repeated_int32=d.get("repeated_int32", None), + repeated_int64=d.get("repeated_int64", None), + repeated_list_value=d.get("repeated_list_value", None), + repeated_message=_repeated_dict(d, "repeated_message", NestedMessage), + repeated_string=d.get("repeated_string", None), + repeated_struct=d.get("repeated_struct", None), + repeated_timestamp=d.get("repeated_timestamp", None), + repeated_value=d.get("repeated_value", None), + test_repeated_enum=_repeated_enum(d, "test_repeated_enum", TestEnum), + ) + + +@dataclass +class RequiredFields: + required_string: str + + required_int32: int + + required_int64: int + + required_bool: bool + + required_message: NestedMessage + + test_required_enum: TestEnum + + required_duration: str + + required_field_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.""" + + required_timestamp: str + + required_value: any + + required_list_value: List[any] + + required_struct: Dict[str, any] + + def as_dict(self) -> dict: + """Serializes the RequiredFields into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.required_bool is not None: + body["required_bool"] = self.required_bool + if self.required_duration is not None: + body["required_duration"] = self.required_duration + if self.required_field_mask is not None: + body["required_field_mask"] = self.required_field_mask + if self.required_int32 is not None: + body["required_int32"] = self.required_int32 + if self.required_int64 is not None: + body["required_int64"] = self.required_int64 + if self.required_list_value: + body["required_list_value"] = [v for v in self.required_list_value] + if self.required_message: + body["required_message"] = self.required_message.as_dict() + if self.required_string is not None: + body["required_string"] = self.required_string + if self.required_struct: + body["required_struct"] = self.required_struct + if self.required_timestamp is not None: + body["required_timestamp"] = self.required_timestamp + if self.required_value: + body["required_value"] = self.required_value + if self.test_required_enum is not None: + body["test_required_enum"] = self.test_required_enum.value + return body + + def as_shallow_dict(self) -> dict: + """Serializes the RequiredFields into a shallow dictionary of its immediate attributes.""" + body = {} + if self.required_bool is not None: + body["required_bool"] = self.required_bool + if self.required_duration is not None: + body["required_duration"] = self.required_duration + if self.required_field_mask is not None: + body["required_field_mask"] = self.required_field_mask + if self.required_int32 is not None: + body["required_int32"] = self.required_int32 + if self.required_int64 is not None: + body["required_int64"] = self.required_int64 + if self.required_list_value: + body["required_list_value"] = self.required_list_value + if self.required_message: + body["required_message"] = self.required_message + if self.required_string is not None: + body["required_string"] = self.required_string + if self.required_struct: + body["required_struct"] = self.required_struct + if self.required_timestamp is not None: + body["required_timestamp"] = self.required_timestamp + if self.required_value: + body["required_value"] = self.required_value + if self.test_required_enum is not None: + body["test_required_enum"] = self.test_required_enum + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> RequiredFields: + """Deserializes the RequiredFields from a dictionary.""" + return cls( + required_bool=d.get("required_bool", None), + required_duration=d.get("required_duration", None), + required_field_mask=d.get("required_field_mask", None), + required_int32=d.get("required_int32", None), + required_int64=d.get("required_int64", None), + required_list_value=d.get("required_list_value", None), + required_message=_from_dict(d, "required_message", NestedMessage), + required_string=d.get("required_string", None), + required_struct=d.get("required_struct", None), + required_timestamp=d.get("required_timestamp", None), + required_value=d.get("required_value", None), + test_required_enum=_enum(d, "test_required_enum", TestEnum), + ) + + +@dataclass +class Resource: + """We separate this into 3 submessages to simplify test cases. E.g., any required top level field + needs to be included in the expected json for each test case.""" + + optional_fields: Optional[OptionalFields] = None + + repeated_fields: Optional[RepeatedFields] = None + + required_fields: Optional[RequiredFields] = None + + def as_dict(self) -> dict: + """Serializes the Resource into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.optional_fields: + body["optional_fields"] = self.optional_fields.as_dict() + if self.repeated_fields: + body["repeated_fields"] = self.repeated_fields.as_dict() + if self.required_fields: + body["required_fields"] = self.required_fields.as_dict() + return body + + def as_shallow_dict(self) -> dict: + """Serializes the Resource into a shallow dictionary of its immediate attributes.""" + body = {} + if self.optional_fields: + body["optional_fields"] = self.optional_fields + if self.repeated_fields: + body["repeated_fields"] = self.repeated_fields + if self.required_fields: + body["required_fields"] = self.required_fields + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> Resource: + """Deserializes the Resource from a dictionary.""" + return cls( + optional_fields=_from_dict(d, "optional_fields", OptionalFields), + repeated_fields=_from_dict(d, "repeated_fields", RepeatedFields), + required_fields=_from_dict(d, "required_fields", RequiredFields), + ) + + +class TestEnum(Enum): + + TEST_ENUM_ONE = "TEST_ENUM_ONE" + TEST_ENUM_TWO = "TEST_ENUM_TWO" + + +class JsonMarshallV2API: + """Lorem Ipsum""" + + def __init__(self, api_client): + self._api = api_client + + def get_resource(self, name: str, resource: Resource) -> Resource: + + query = {} + if resource is not None: + query["resource"] = resource.as_dict() + headers = { + "Accept": "application/json", + } + + res = self._api.do("GET", f"/api/2.0/json-marshall/{name}", query=query, headers=headers) + return Resource.from_dict(res) diff --git a/tests/generated/test_http_call.py b/tests/generated/test_http_call.py new file mode 100755 index 000000000..9c6ea67f8 --- /dev/null +++ b/tests/generated/test_http_call.py @@ -0,0 +1,447 @@ +# Code generated by Databricks SDK Generator. DO NOT EDIT. + +import json + +import databricks.sdk.core as client +from tests.databricks.sdk.service.httpcallv2 import (ComplexQueryParam, + HttpCallV2API, Resource) + + +def test_http_call_legacy_http_post_no_query_params_no_body(config, requests_mock): + requests_mock.post( + "http://localhost/api/2.0/http-call/string_val/123/true", + request_headers={"User-Agent": config.user_agent}, + ) + + api_client = client.ApiClient(config) + c = HttpCallV2API(api_client) + c.create_resource( + path_param_string="string_val", + path_param_int=123, + path_param_bool=True, + ) + + assert requests_mock.call_count == 1 + assert requests_mock.called + + assert requests_mock.last_request.json() == {} + + +def test_http_call_legacy_http_post_with_body(config, requests_mock): + requests_mock.post( + "http://localhost/api/2.0/http-call/test_string/456/false", + request_headers={"User-Agent": config.user_agent}, + ) + + api_client = client.ApiClient(config) + c = HttpCallV2API(api_client) + c.create_resource( + body_field="request_body_content", + path_param_string="test_string", + path_param_int=456, + path_param_bool=False, + ) + + assert requests_mock.call_count == 1 + assert requests_mock.called + + assert requests_mock.last_request.json() == { + "body_field": "request_body_content", + } + + +def test_http_call_update_resource_no_query_params_no_body(config, requests_mock): + requests_mock.patch( + "http://localhost/api/2.0/http-call/update_string/789/true", + request_headers={"User-Agent": config.user_agent}, + ) + + api_client = client.ApiClient(config) + c = HttpCallV2API(api_client) + c.update_resource( + resource=Resource( + any_field=json.loads('{"key": "value"}'), + nested_path_param_bool=True, + nested_path_param_int=789, + nested_path_param_string="update_string", + ), + nested_path_param_string="update_string", + nested_path_param_int=789, + nested_path_param_bool=True, + ) + + assert requests_mock.call_count == 1 + assert requests_mock.called + + assert requests_mock.last_request.json() == { + "nested_path_param_string": "update_string", + "nested_path_param_int": 789, + "nested_path_param_bool": True, + "any_field": {"key": "value"}, + } + + +def test_http_call_update_resource_with_body(config, requests_mock): + requests_mock.patch( + "http://localhost/api/2.0/http-call/update_string/789/true", + request_headers={"User-Agent": config.user_agent}, + ) + + api_client = client.ApiClient(config) + c = HttpCallV2API(api_client) + c.update_resource( + resource=Resource( + body_field="request_body_content", + nested_path_param_bool=True, + nested_path_param_int=789, + nested_path_param_string="update_string", + ), + nested_path_param_string="update_string", + nested_path_param_int=789, + nested_path_param_bool=True, + ) + + assert requests_mock.call_count == 1 + assert requests_mock.called + + assert requests_mock.last_request.json() == { + "nested_path_param_string": "update_string", + "nested_path_param_int": 789, + "nested_path_param_bool": True, + "body_field": "request_body_content", + } + + +def test_http_call_update_resource_with_simple_query_params(config, requests_mock): + requests_mock.patch( + "http://localhost/api/2.0/http-call/update_string/789/true?field_mask=field.mask.value&query_param_bool=true&query_param_int=999&query_param_string=query_string_val", + request_headers={"User-Agent": config.user_agent}, + ) + + api_client = client.ApiClient(config) + c = HttpCallV2API(api_client) + c.update_resource( + resource=Resource( + nested_path_param_bool=True, + nested_path_param_int=789, + nested_path_param_string="update_string", + ), + nested_path_param_string="update_string", + nested_path_param_int=789, + nested_path_param_bool=True, + query_param_string="query_string_val", + query_param_int=999, + query_param_bool=True, + field_mask="field.mask.value", + ) + + assert requests_mock.call_count == 1 + assert requests_mock.called + + assert requests_mock.last_request.json() == { + "nested_path_param_string": "update_string", + "nested_path_param_int": 789, + "nested_path_param_bool": True, + } + + +def test_http_call_update_resource_with_one_nested_query_param(config, requests_mock): + requests_mock.patch( + "http://localhost/api/2.0/http-call/update_string/789/True?optional_complex_query_param.nested_optional_query_param=nested_optional", + request_headers={"User-Agent": config.user_agent}, + ) + + api_client = client.ApiClient(config) + c = HttpCallV2API(api_client) + c.update_resource( + resource=Resource( + nested_path_param_bool=True, + nested_path_param_int=789, + nested_path_param_string="update_string", + ), + nested_path_param_string="update_string", + nested_path_param_int=789, + nested_path_param_bool=True, + optional_complex_query_param=ComplexQueryParam( + nested_optional_query_param="nested_optional", + ), + ) + + assert requests_mock.call_count == 1 + assert requests_mock.called + + assert requests_mock.last_request.json() == { + "nested_path_param_string": "update_string", + "nested_path_param_int": 789, + "nested_path_param_bool": True, + } + + +def test_http_call_update_resource_with_repeated_query_param(config, requests_mock): + requests_mock.patch( + "http://localhost/api/2.0/http-call/update_string/789/True?repeated_query_param=item1&repeated_query_param=item2&repeated_query_param=item3", + request_headers={"User-Agent": config.user_agent}, + ) + + api_client = client.ApiClient(config) + c = HttpCallV2API(api_client) + c.update_resource( + resource=Resource( + nested_path_param_bool=True, + nested_path_param_int=789, + nested_path_param_string="update_string", + ), + nested_path_param_string="update_string", + nested_path_param_int=789, + nested_path_param_bool=True, + repeated_query_param=[ + "item1", + "item2", + "item3", + ], + ) + + assert requests_mock.call_count == 1 + assert requests_mock.called + + assert requests_mock.last_request.json() == { + "nested_path_param_string": "update_string", + "nested_path_param_int": 789, + "nested_path_param_bool": True, + } + + +def test_http_call_update_resource_with_repeated_nested_query_param(config, requests_mock): + requests_mock.patch( + "http://localhost/api/2.0/http-call/update_string/789/True?optional_complex_query_param.nested_repeated_query_param=item1&optional_complex_query_param.nested_repeated_query_param=item2&optional_complex_query_param.nested_repeated_query_param=item3", + request_headers={"User-Agent": config.user_agent}, + ) + + api_client = client.ApiClient(config) + c = HttpCallV2API(api_client) + c.update_resource( + resource=Resource( + nested_path_param_bool=True, + nested_path_param_int=789, + nested_path_param_string="update_string", + ), + nested_path_param_string="update_string", + nested_path_param_int=789, + nested_path_param_bool=True, + optional_complex_query_param=ComplexQueryParam( + nested_repeated_query_param=[ + "item1", + "item2", + "item3", + ], + ), + ) + + assert requests_mock.call_count == 1 + assert requests_mock.called + + assert requests_mock.last_request.json() == { + "nested_path_param_string": "update_string", + "nested_path_param_int": 789, + "nested_path_param_bool": True, + } + + +def test_http_call_update_resource_with_double_repeated_nested_query_param(config, requests_mock): + requests_mock.patch( + "http://localhost/api/2.0/http-call/update_string/789/True?repeated_complex_query_param=nested_repeated_query_param&repeated_complex_query_param=nested_repeated_query_param", + request_headers={"User-Agent": config.user_agent}, + ) + + api_client = client.ApiClient(config) + c = HttpCallV2API(api_client) + c.update_resource( + resource=Resource( + nested_path_param_bool=True, + nested_path_param_int=789, + nested_path_param_string="update_string", + ), + nested_path_param_string="update_string", + nested_path_param_int=789, + nested_path_param_bool=True, + repeated_complex_query_param=[ + ComplexQueryParam( + nested_repeated_query_param=[ + "item1", + "item2", + "item3", + ], + ), + ComplexQueryParam( + nested_repeated_query_param=[ + "item4", + "item5", + "item6", + ], + ), + ], + ) + + assert requests_mock.call_count == 1 + assert requests_mock.called + + assert requests_mock.last_request.json() == { + "nested_path_param_string": "update_string", + "nested_path_param_int": 789, + "nested_path_param_bool": True, + } + + +def test_http_call_get_resource_no_query_params(config, requests_mock): + requests_mock.get( + "http://localhost/api/2.0/http-call/get_string/123/true?", + request_headers={"User-Agent": config.user_agent}, + ) + + api_client = client.ApiClient(config) + c = HttpCallV2API(api_client) + c.get_resource( + path_param_string="get_string", + path_param_int=123, + path_param_bool=True, + ) + + assert requests_mock.call_count == 1 + assert requests_mock.called + + assert requests_mock.last_request.body is None + + +def test_http_call_get_resource_with_simple_query_params(config, requests_mock): + requests_mock.get( + "http://localhost/api/2.0/http-call/get_string/456/false?field_mask=field.mask.value&query_param_bool=true&query_param_int=999&query_param_string=query_string_val", + request_headers={"User-Agent": config.user_agent}, + ) + + api_client = client.ApiClient(config) + c = HttpCallV2API(api_client) + c.get_resource( + path_param_string="get_string", + path_param_int=456, + path_param_bool=False, + query_param_string="query_string_val", + query_param_int=999, + query_param_bool=True, + field_mask="field.mask.value", + ) + + assert requests_mock.call_count == 1 + assert requests_mock.called + + assert requests_mock.last_request.body is None + + +def test_http_call_get_resource_with_one_nested_query_param(config, requests_mock): + requests_mock.get( + "http://localhost/api/2.0/http-call/get_string/789/true?optional_complex_query_param.nested_optional_query_param=nested_optional", + request_headers={"User-Agent": config.user_agent}, + ) + + api_client = client.ApiClient(config) + c = HttpCallV2API(api_client) + c.get_resource( + path_param_string="get_string", + path_param_int=789, + path_param_bool=True, + optional_complex_query_param=ComplexQueryParam( + nested_optional_query_param="nested_optional", + ), + ) + + assert requests_mock.call_count == 1 + assert requests_mock.called + + assert requests_mock.last_request.body is None + + +def test_http_call_get_resource_with_repeated_query_param(config, requests_mock): + requests_mock.get( + "http://localhost/api/2.0/http-call/get_string/101/false?repeated_query_param=item1&repeated_query_param=item2&repeated_query_param=item3", + request_headers={"User-Agent": config.user_agent}, + ) + + api_client = client.ApiClient(config) + c = HttpCallV2API(api_client) + c.get_resource( + path_param_string="get_string", + path_param_int=101, + path_param_bool=False, + repeated_query_param=[ + "item1", + "item2", + "item3", + ], + ) + + assert requests_mock.call_count == 1 + assert requests_mock.called + + assert requests_mock.last_request.body is None + + +def test_http_call_get_resource_with_repeated_nested_query_param(config, requests_mock): + requests_mock.get( + "http://localhost/api/2.0/http-call/get_string/202/true?optional_complex_query_param.nested_repeated_query_param=item1&optional_complex_query_param.nested_repeated_query_param=item2&optional_complex_query_param.nested_repeated_query_param=item3", + request_headers={"User-Agent": config.user_agent}, + ) + + api_client = client.ApiClient(config) + c = HttpCallV2API(api_client) + c.get_resource( + path_param_string="get_string", + path_param_int=202, + path_param_bool=True, + optional_complex_query_param=ComplexQueryParam( + nested_repeated_query_param=[ + "item1", + "item2", + "item3", + ], + ), + ) + + assert requests_mock.call_count == 1 + assert requests_mock.called + + assert requests_mock.last_request.body is None + + +def test_http_call_get_resource_with_double_repeated_nested_query_param(config, requests_mock): + requests_mock.get( + "http://localhost/api/2.0/http-call/get_string/303/False?repeated_complex_query_param=nested_repeated_query_param&repeated_complex_query_param=nested_repeated_query_param", + request_headers={"User-Agent": config.user_agent}, + ) + + api_client = client.ApiClient(config) + c = HttpCallV2API(api_client) + c.get_resource( + path_param_string="get_string", + path_param_int=303, + path_param_bool=False, + repeated_complex_query_param=[ + ComplexQueryParam( + nested_repeated_query_param=[ + "item1", + "item2", + "item3", + ], + ), + ComplexQueryParam( + nested_repeated_query_param=[ + "item4", + "item5", + "item6", + ], + ), + ], + ) + + assert requests_mock.call_count == 1 + assert requests_mock.called + + assert requests_mock.last_request.body is None diff --git a/tests/generated/test_json_marshall.py b/tests/generated/test_json_marshall.py new file mode 100755 index 000000000..0438d387b --- /dev/null +++ b/tests/generated/test_json_marshall.py @@ -0,0 +1,358 @@ +# Code generated by Databricks SDK Generator. DO NOT EDIT. + +import json +from typing import Any, Dict + +import pytest + +from tests.databricks.sdk.service.jsonmarshallv2 import (NestedMessage, + OptionalFields, + RepeatedFields, + RequiredFields, + TestEnum) + + +@pytest.mark.parametrize( + "from_dict_method,instance,expected_dict", + [ + ( + OptionalFields.from_dict, + OptionalFields( + optional_string="test", + ), + {"optional_string": "test"}, + ), + ( + OptionalFields.from_dict, + OptionalFields( + optional_int32=42, + ), + {"optional_int32": 42}, + ), + ( + OptionalFields.from_dict, + OptionalFields( + optional_int64=9223372036854775807, + ), + {"optional_int64": 9223372036854775807}, + ), + ( + OptionalFields.from_dict, + OptionalFields( + optional_bool=True, + ), + {"optional_bool": True}, + ), + ( + OptionalFields.from_dict, + OptionalFields( + test_enum=TestEnum.TEST_ENUM_ONE, + ), + {"test_enum": "TEST_ENUM_ONE"}, + ), + ( + OptionalFields.from_dict, + OptionalFields( + optional_message=NestedMessage( + optional_string="nested_value", + ), + ), + {"optional_message": {"optional_string": "nested_value"}}, + ), + ( + OptionalFields.from_dict, + OptionalFields( + map={ + "key": "test_key", + "value": "test_value", + }, + ), + {"map": {"key": "test_key", "value": "test_value"}}, + ), + ( + OptionalFields.from_dict, + OptionalFields( + duration="3600s", + ), + {"duration": "3600s"}, + ), + ( + OptionalFields.from_dict, + OptionalFields( + field_mask="optional_string,optional_int32", + ), + {"field_mask": "optional_string,optional_int32"}, + ), + ( + OptionalFields.from_dict, + OptionalFields( + timestamp="2023-01-01T00:00:00Z", + ), + {"timestamp": "2023-01-01T00:00:00Z"}, + ), + ( + OptionalFields.from_dict, + OptionalFields( + optional_bool=True, + optional_int32=42, + optional_string="test", + ), + {"optional_string": "test", "optional_int32": 42, "optional_bool": True}, + ), + ( + RequiredFields.from_dict, + RequiredFields( + required_bool=False, + required_duration=None, + required_field_mask="", + required_int32=0, + required_int64=0, + required_list_value=[], + required_message=NestedMessage(), + required_string="", + required_struct={}, + required_timestamp="1970-01-01T00:00:00Z", + required_value=json.loads("{}"), + test_required_enum=TestEnum.TEST_ENUM_ONE, + ), + { + "required_string": "", + "required_int32": 0, + "required_int64": 0, + "required_bool": False, + "test_required_enum": "TEST_ENUM_ONE", + "required_field_mask": "", + "required_timestamp": "1970-01-01T00:00:00Z", + "required_message": {}, + }, + ), + ( + RequiredFields.from_dict, + RequiredFields( + required_bool=True, + required_duration="7200s", + required_field_mask="required_string,required_int32", + required_int32=42, + required_int64=1234567890123456789, + required_list_value=[], + required_message=NestedMessage( + optional_string="nested_value", + ), + required_string="non_default_string", + required_struct={}, + required_timestamp="2023-12-31T23:59:59Z", + required_value=json.loads("{}"), + test_required_enum=TestEnum.TEST_ENUM_TWO, + ), + { + "required_string": "non_default_string", + "required_int32": 42, + "required_int64": 1234567890123456789, + "required_bool": True, + "test_required_enum": "TEST_ENUM_TWO", + "required_duration": "7200s", + "required_field_mask": "required_string,required_int32", + "required_timestamp": "2023-12-31T23:59:59Z", + "required_message": { + "optional_string": "nested_value", + }, + }, + ), + ( + RepeatedFields.from_dict, + RepeatedFields( + repeated_string=[ + "item1", + "item2", + "item3", + ], + ), + {"repeated_string": ["item1", "item2", "item3"]}, + ), + ( + RepeatedFields.from_dict, + RepeatedFields( + repeated_int32=[ + 1, + 2, + 3, + 4, + 5, + ], + ), + {"repeated_int32": [1, 2, 3, 4, 5]}, + ), + ( + RepeatedFields.from_dict, + RepeatedFields( + repeated_int64=[ + 1000000000000000000, + 2000000000000000000, + ], + ), + {"repeated_int64": [1000000000000000000, 2000000000000000000]}, + ), + ( + RepeatedFields.from_dict, + RepeatedFields( + repeated_bool=[ + True, + False, + True, + ], + ), + {"repeated_bool": [True, False, True]}, + ), + ( + RepeatedFields.from_dict, + RepeatedFields( + test_repeated_enum=[ + TestEnum.TEST_ENUM_ONE, + TestEnum.TEST_ENUM_TWO, + ], + ), + {"test_repeated_enum": ["TEST_ENUM_ONE", "TEST_ENUM_TWO"]}, + ), + ( + RepeatedFields.from_dict, + RepeatedFields( + repeated_message=[ + NestedMessage( + optional_string="nested1", + ), + NestedMessage( + optional_string="nested2", + ), + ], + ), + {"repeated_message": [{"optional_string": "nested1"}, {"optional_string": "nested2"}]}, + ), + ( + RepeatedFields.from_dict, + RepeatedFields( + repeated_duration=[ + "60s", + "120s", + "180s", + ], + ), + {"repeated_duration": ["60s", "120s", "180s"]}, + ), + ( + RepeatedFields.from_dict, + RepeatedFields( + repeated_field_mask=[ + "field1", + "field2,field3", + ], + ), + {"repeated_field_mask": ["field1", "field2,field3"]}, + ), + ( + RepeatedFields.from_dict, + RepeatedFields( + repeated_timestamp=[ + "2023-01-01T00:00:00Z", + "2023-01-02T00:00:00Z", + ], + ), + {"repeated_timestamp": ["2023-01-01T00:00:00Z", "2023-01-02T00:00:00Z"]}, + ), + ( + RepeatedFields.from_dict, + RepeatedFields( + repeated_bool=[ + True, + False, + ], + repeated_int32=[ + 10, + 20, + 30, + ], + repeated_string=[ + "a", + "b", + "c", + ], + ), + {"repeated_string": ["a", "b", "c"], "repeated_int32": [10, 20, 30], "repeated_bool": [True, False]}, + ), + ( + RepeatedFields.from_dict, + RepeatedFields( + repeated_string=[], + ), + {}, + ), + ( + OptionalFields.from_dict, + OptionalFields(), + {}, + ), + ( + OptionalFields.from_dict, + OptionalFields( + optional_bool=False, + optional_int32=0, + optional_int64=0, + optional_string="", + ), + {"optional_int32": 0, "optional_int64": 0, "optional_bool": False, "optional_string": ""}, + ), + ( + OptionalFields.from_dict, + OptionalFields( + legacy_duration="1s", + legacy_field_mask="legacy_duration,legacy_timestamp", + legacy_timestamp="2023-01-01T00:00:00Z", + ), + { + "legacy_duration": "1s", + "legacy_timestamp": "2023-01-01T00:00:00Z", + "legacy_field_mask": "legacy_duration,legacy_timestamp", + }, + ), + ], + ids=[ + "OptionalString", + "OptionalInt32", + "OptionalInt64", + "OptionalBool", + "OptionalEnum", + "OptionalNestedMessage", + "OptionalMap", + "OptionalDuration", + "OptionalFieldMask", + "OptionalTimestamp", + "MultipleOptionalFields", + "RequiredFieldsExplicitDefaults", + "RequiredFieldsNonDefaults", + "RepeatedString", + "RepeatedInt32", + "RepeatedInt64", + "RepeatedBool", + "RepeatedEnum", + "RepeatedNestedMessage", + "RepeatedDuration", + "RepeatedFieldMask", + "RepeatedTimestamp", + "MultipleRepeatedFields", + "EmptyRepeatedFields", + "OptionalFieldsNoInput", + "OptionalFieldsZeroValues", + "LegacyWellKnownTypes", + ], +) +def test_python_marshall(from_dict_method: any, instance: Any, expected_dict: Dict[str, Any]): + """Test Python object to dict conversion""" + + result = instance.as_dict() + + assert result == expected_dict, f"Expected {expected_dict}, but got {result}" + + recreated = from_dict_method(result) + + final_dict = recreated.as_dict() + + assert final_dict == expected_dict, f"Expected {expected_dict}, but got {final_dict}"