diff --git a/batch_models_patch.txt b/batch_models_patch.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/sdk/resources/azure-mgmt-resource/CHANGELOG.md b/sdk/resources/azure-mgmt-resource/CHANGELOG.md index 520c3c659038..c8a7815dea51 100644 --- a/sdk/resources/azure-mgmt-resource/CHANGELOG.md +++ b/sdk/resources/azure-mgmt-resource/CHANGELOG.md @@ -1,5 +1,22 @@ # Release History +## 25.1.0b1 (TBD) + +### Features Added + + - Added support for ARM Batch Operations API (2025-08-01-preview) + - New `BatchOperations` operation group with the following methods: + - `begin_invoke_at_subscription_scope()` - Execute batch requests at subscription scope + - `begin_invoke_at_resource_group_scope()` - Execute batch requests at resource group scope + - New models for batch operations: + - `BatchRequest` - Individual request specification within a batch + - `BatchRequests` - Container for multiple batch requests + - `BatchResponse` - Individual response from a batch request + - `BatchResponseStatus` - Overall batch operation status and results + - `BatchProvisioningState` - Enum for batch operation states + - Batch operations support Long Running Operations (LRO) with proper polling + - Both synchronous and asynchronous execution patterns supported + ## 25.0.0 (2026-02-04) ### Breaking Changes diff --git a/sdk/resources/azure-mgmt-resource/azure/mgmt/resource/resources/_resource_management_client.py b/sdk/resources/azure-mgmt-resource/azure/mgmt/resource/resources/_resource_management_client.py index 8c44056c5a8c..24dc8e375dc6 100644 --- a/sdk/resources/azure-mgmt-resource/azure/mgmt/resource/resources/_resource_management_client.py +++ b/sdk/resources/azure-mgmt-resource/azure/mgmt/resource/resources/_resource_management_client.py @@ -27,6 +27,7 @@ ResourceGroupsOperations, ResourcesOperations, TagsOperations, + BatchOperations, ) if TYPE_CHECKING: @@ -50,6 +51,8 @@ class ResourceManagementClient: :vartype resource_groups: azure.mgmt.resource.resources.operations.ResourceGroupsOperations :ivar tags: TagsOperations operations :vartype tags: azure.mgmt.resource.resources.operations.TagsOperations + :ivar batch_operations: BatchOperations operations (2025-08-01-preview) + :vartype batch_operations: azure.mgmt.resource.resources.operations.BatchOperations :param credential: Credential needed for the client to connect to Azure. Required. :type credential: ~azure.core.credentials.TokenCredential :param subscription_id: The Microsoft Azure subscription ID. Required. @@ -120,6 +123,7 @@ def __init__( self.resources = ResourcesOperations(self._client, self._config, self._serialize, self._deserialize) self.resource_groups = ResourceGroupsOperations(self._client, self._config, self._serialize, self._deserialize) self.tags = TagsOperations(self._client, self._config, self._serialize, self._deserialize) + self.batch_operations = BatchOperations(self._client, self._config, self._serialize, self._deserialize) def _send_request(self, request: HttpRequest, *, stream: bool = False, **kwargs: Any) -> HttpResponse: """Runs the network request through the client's chained policies. diff --git a/sdk/resources/azure-mgmt-resource/azure/mgmt/resource/resources/models/__init__.py b/sdk/resources/azure-mgmt-resource/azure/mgmt/resource/resources/models/__init__.py index d6079c12b31e..201d41ebf55c 100644 --- a/sdk/resources/azure-mgmt-resource/azure/mgmt/resource/resources/models/__init__.py +++ b/sdk/resources/azure-mgmt-resource/azure/mgmt/resource/resources/models/__init__.py @@ -19,6 +19,11 @@ AliasPathMetadata, AliasPattern, ApiProfile, + BatchRequest, + BatchRequests, + BatchResponse, + BatchResponseItem, + BatchResponseStatus, ErrorAdditionalInfo, ErrorResponse, ExportTemplateRequest, @@ -76,6 +81,7 @@ ResourceIdentityType, TagsPatchOperation, ) + from ._patch import __all__ as _patch_all from ._patch import * from ._patch import patch_sdk as _patch_sdk @@ -86,6 +92,11 @@ "AliasPathMetadata", "AliasPattern", "ApiProfile", + "BatchRequest", + "BatchRequests", + "BatchResponse", + "BatchResponseItem", + "BatchResponseStatus", "ErrorAdditionalInfo", "ErrorResponse", "ExportTemplateRequest", diff --git a/sdk/resources/azure-mgmt-resource/azure/mgmt/resource/resources/models/_models_py3.py b/sdk/resources/azure-mgmt-resource/azure/mgmt/resource/resources/models/_models_py3.py index da8c8b8dca74..68a9cd6aac87 100644 --- a/sdk/resources/azure-mgmt-resource/azure/mgmt/resource/resources/models/_models_py3.py +++ b/sdk/resources/azure-mgmt-resource/azure/mgmt/resource/resources/models/_models_py3.py @@ -11,6 +11,7 @@ import datetime from typing import Any, Optional, TYPE_CHECKING, Union +from azure.core import CaseInsensitiveEnumMeta from .._utils import serialization as _serialization if TYPE_CHECKING: @@ -2101,3 +2102,178 @@ def __init__(self, *, location: Optional[str] = None, zones: Optional[list[str]] super().__init__(**kwargs) self.location = location self.zones = zones + + +# Batch operation models for Azure Resource Manager (API version 2025-04-01) + + +class BatchRequest(_serialization.Model): + """Batch request definition. + + :ivar http_method: The HTTP method for the request. Required. + :vartype http_method: str + :ivar relative_url: The relative URL for the request. Required. + :vartype relative_url: str + :ivar name: Optional name to identify this request within the batch. Optional. + :vartype name: str + :ivar body: The request body (for PUT/PATCH/POST operations). Optional. + :vartype body: any + :ivar headers: Request headers. Optional. + :vartype headers: dict[str, str] + :ivar depends_on: List of request names that this request depends on. Optional. + :vartype depends_on: list[str] + """ + + _attribute_map = { + "http_method": {"key": "httpMethod", "type": "str"}, + "relative_url": {"key": "relativeUrl", "type": "str"}, + "name": {"key": "name", "type": "str"}, + "body": {"key": "body", "type": "object"}, + "headers": {"key": "headers", "type": "{str}"}, + "depends_on": {"key": "dependsOn", "type": "[str]"}, + } + + def __init__( + self, + *, + http_method: str, + relative_url: str, + name: Optional[str] = None, + body: Optional[Any] = None, + headers: Optional[dict[str, str]] = None, + depends_on: Optional[list[str]] = None, + **kwargs: Any + ) -> None: + """ + :keyword http_method: The HTTP method for the request. Required. + :paramtype http_method: str + :keyword relative_url: The relative URL for the request. Required. + :paramtype relative_url: str + :keyword name: Optional name to identify this request within the batch. Optional. + :paramtype name: str + :keyword body: The request body (for PUT/PATCH/POST operations). Optional. + :paramtype body: any + :keyword headers: Request headers. Optional. + :paramtype headers: dict[str, str] + :keyword depends_on: List of request names that this request depends on. Optional. + :paramtype depends_on: list[str] + """ + super().__init__(**kwargs) + self.http_method = http_method + self.relative_url = relative_url + self.name = name + self.body = body + self.headers = headers + self.depends_on = depends_on + + +class BatchRequests(_serialization.Model): + """Collection of batch requests. + + :ivar requests: The batch requests to execute. Required. + :vartype requests: list[~azure.mgmt.resource.resources.models.BatchRequest] + """ + + _attribute_map = { + "requests": {"key": "requests", "type": "[BatchRequest]"}, + } + + def __init__(self, *, requests: list["BatchRequest"], **kwargs: Any) -> None: + """ + :keyword requests: The batch requests to execute. Required. + :paramtype requests: list[~azure.mgmt.resource.resources.models.BatchRequest] + """ + super().__init__(**kwargs) + self.requests = requests + + +class BatchResponseItem(_serialization.Model): + """Individual response within a batch response. + + :ivar name: The name of the request (if provided in the batch request). Optional. + :vartype name: str + :ivar status_code: The HTTP status code of the response. Required. + :vartype status_code: int + :ivar status: The status of the batch response item. Required. + :vartype status: str or ~azure.mgmt.resource.resources.models.BatchResponseStatus + :ivar body: The response body. Optional. + :vartype body: any + :ivar headers: Response headers. Optional. + :vartype headers: dict[str, str] + """ + + _attribute_map = { + "name": {"key": "name", "type": "str"}, + "status_code": {"key": "statusCode", "type": "int"}, + "status": {"key": "status", "type": "str"}, + "body": {"key": "body", "type": "object"}, + "headers": {"key": "headers", "type": "{str}"}, + } + + def __init__( + self, + *, + status_code: int, + status: Union[str, "BatchResponseStatus"], + name: Optional[str] = None, + body: Optional[Any] = None, + headers: Optional[dict[str, str]] = None, + **kwargs: Any + ) -> None: + """ + :keyword status_code: The HTTP status code of the response. Required. + :paramtype status_code: int + :keyword status: The status of the batch response item. Required. + :paramtype status: str or ~azure.mgmt.resource.resources.models.BatchResponseStatus + :keyword name: The name of the request (if provided in the batch request). Optional. + :paramtype name: str + :keyword body: The response body. Optional. + :paramtype body: any + :keyword headers: Response headers. Optional. + :paramtype headers: dict[str, str] + """ + super().__init__(**kwargs) + self.name = name + self.status_code = status_code + self.status = status + self.body = body + self.headers = headers + + +class BatchResponse(_serialization.Model): + """Batch operation response. + + :ivar responses: Individual responses within the batch. Required. + :vartype responses: list[~azure.mgmt.resource.resources.models.BatchResponseItem] + """ + + _attribute_map = { + "responses": {"key": "responses", "type": "[BatchResponseItem]"}, + } + + def __init__(self, *, responses: list["BatchResponseItem"], **kwargs: Any) -> None: + """ + :keyword responses: Individual responses within the batch. Required. + :paramtype responses: list[~azure.mgmt.resource.resources.models.BatchResponseItem] + """ + super().__init__(**kwargs) + self.responses = responses + + +class BatchResponseStatus(str, metaclass=CaseInsensitiveEnumMeta): + """Batch response status enumeration. + + :cvar SUCCEEDED: The request succeeded. + :vartype SUCCEEDED: str + :cvar FAILED: The request failed. + :vartype FAILED: str + :cvar VALIDATION_FAILED: The request was processed but failed validation. + :vartype VALIDATION_FAILED: str + :cvar SKIPPED: The request was skipped due to dependency failure. + :vartype SKIPPED: str + """ + + SUCCEEDED = "Succeeded" + FAILED = "Failed" + VALIDATION_FAILED = "ValidationFailed" + SKIPPED = "Skipped" diff --git a/sdk/resources/azure-mgmt-resource/azure/mgmt/resource/resources/operations/__init__.py b/sdk/resources/azure-mgmt-resource/azure/mgmt/resource/resources/operations/__init__.py index c95b954380a9..c46dcb489a72 100644 --- a/sdk/resources/azure-mgmt-resource/azure/mgmt/resource/resources/operations/__init__.py +++ b/sdk/resources/azure-mgmt-resource/azure/mgmt/resource/resources/operations/__init__.py @@ -18,6 +18,7 @@ from ._operations import ResourcesOperations # type: ignore from ._operations import ResourceGroupsOperations # type: ignore from ._operations import TagsOperations # type: ignore +from ._batch_operations import BatchOperations # type: ignore from ._patch import __all__ as _patch_all from ._patch import * @@ -30,6 +31,7 @@ "ResourcesOperations", "ResourceGroupsOperations", "TagsOperations", + "BatchOperations", ] __all__.extend([p for p in _patch_all if p not in __all__]) # pyright: ignore _patch_sdk() diff --git a/sdk/resources/azure-mgmt-resource/azure/mgmt/resource/resources/operations/_batch_operations.py b/sdk/resources/azure-mgmt-resource/azure/mgmt/resource/resources/operations/_batch_operations.py new file mode 100644 index 000000000000..109246bcd2d8 --- /dev/null +++ b/sdk/resources/azure-mgmt-resource/azure/mgmt/resource/resources/operations/_batch_operations.py @@ -0,0 +1,363 @@ +# pylint: disable=too-many-lines +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- + +from typing import Any, Callable, Dict, IO, Optional, TypeVar, Union, cast + +from azure.core.exceptions import ( + ClientAuthenticationError, + HttpResponseError, + ResourceExistsError, + ResourceNotFoundError, + ResourceNotModifiedError, + map_error, +) +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import HttpResponse +from azure.core.polling import LROPoller, NoPolling, PollingMethod +from azure.core.rest import HttpRequest +from azure.core.tracing.decorator import distributed_trace +from azure.core.utils import case_insensitive_dict +from azure.mgmt.core.exceptions import ARMErrorFormat +from azure.mgmt.core.polling.arm_polling import ARMPolling +from msrest import Serializer + +from .. import models as _models + +T = TypeVar("T") +ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, Dict[str, Any]], Any]] + +_SERIALIZER = Serializer() +_SERIALIZER.client_side_validation = False + + +class BatchOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.mgmt.resource.ResourceManagementClient`'s + :attr:`batch_operations` attribute. + """ + + models = _models + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client = input_args.pop(0) if input_args else kwargs.pop("client") + self._config = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + @distributed_trace + def begin_invoke_at_subscription_scope( + self, + subscription_id: str, + batch_requests: "_models.BatchRequests", + **kwargs: Any + ) -> LROPoller["_models.BatchResponseStatus"]: + """Invokes a batch of individual Azure Resource Manager requests at a subscription scope. + + The batch API can be used to invoke multiple Azure Resource Manager requests at once. Batches + are processed asynchronously and allow for more efficient handling of many concurrent requests. + + :param subscription_id: The ID of the target subscription. Required. + :type subscription_id: str + :param batch_requests: Specifications for all of the requests that will be invoked as part of + the batch. Required. + :type batch_requests: ~azure.mgmt.resource.models.BatchRequests + :keyword callable cls: A custom type or function that will be passed the direct response + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: By default, your polling method will be ARMPolling. Pass in False for this + operation to not poll, or pass in your own initialized polling object for a personal polling + strategy. + :paramtype polling: bool or ~azure.core.polling.PollingMethod + :keyword int polling_interval: Default waiting time between two polls for LRO operations if no + Retry-After header is present. + :return: An instance of LROPoller that returns either BatchResponseStatus or the result of + cls(response) + :rtype: ~azure.core.polling.LROPoller[~azure.mgmt.resource.models.BatchResponseStatus] + :raises ~azure.core.exceptions.HttpResponseError: + """ + api_version = kwargs.pop("api_version", "2025-08-01-preview") + content_type = kwargs.pop("content_type", "application/json") + cls = kwargs.pop("cls", None) + polling = kwargs.pop("polling", True) + lro_delay = kwargs.pop("polling_interval", self._config.polling_interval) + cont_token = kwargs.pop("continuation_token", None) + + if cont_token is None: + raw_result = self._invoke_at_subscription_scope_initial( + subscription_id=subscription_id, + batch_requests=batch_requests, + api_version=api_version, + content_type=content_type, + cls=lambda x, y, z: x, + **kwargs + ) + kwargs.pop("error_map", None) + + def get_long_running_output(pipeline_response): + response = pipeline_response.http_response + deserialized = self._deserialize("BatchResponseStatus", pipeline_response) + if cls: + return cls(pipeline_response, deserialized, {}) + return deserialized + + if polling is True: + polling_method = cast(PollingMethod, ARMPolling(lro_delay, **kwargs)) + elif polling is False: + polling_method = cast(PollingMethod, NoPolling()) + else: + polling_method = polling + if cont_token: + return LROPoller.from_continuation_token( + polling_method=polling_method, + continuation_token=cont_token, + client=self._client, + deserialization_callback=get_long_running_output, + ) + return LROPoller(self._client, raw_result, get_long_running_output, polling_method) + + def _invoke_at_subscription_scope_initial( + self, + subscription_id: str, + batch_requests: "_models.BatchRequests", + **kwargs: Any + ) -> Optional["_models.BatchResponseStatus"]: + error_map = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + api_version = kwargs.pop("api_version", "2025-08-01-preview") + content_type = kwargs.pop("content_type", "application/json") + cls = kwargs.pop("cls", None) + + _json = self._serialize.body(batch_requests, "BatchRequests") + + request = build_invoke_at_subscription_scope_request( + subscription_id=subscription_id, + api_version=api_version, + content_type=content_type, + json=_json, + **kwargs + ) + request.url = self._client.format_url(request.url) + + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200, 202]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CloudError, pipeline_response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + deserialized = None + response_headers = {} + if response.status_code == 200: + deserialized = self._deserialize("BatchResponseStatus", pipeline_response) + + if response.status_code == 202: + deserialized = self._deserialize("BatchResponseStatus", pipeline_response) + response_headers["Location"] = self._deserialize("str", response.headers.get("Location")) + + if cls: + return cls(pipeline_response, deserialized, response_headers) + + return deserialized + + _invoke_at_subscription_scope_initial.metadata = {"url": "/subscriptions/{subscriptionId}/providers/Microsoft.Resources/invokeAtSubscriptionScope"} + + @distributed_trace + def begin_invoke_at_resource_group_scope( + self, + subscription_id: str, + resource_group_name: str, + batch_requests: "_models.BatchRequests", + **kwargs: Any + ) -> LROPoller["_models.BatchResponseStatus"]: + """Invokes a batch of individual Azure Resource Manager requests at a resource group scope. + + The batch API can be used to invoke multiple Azure Resource Manager requests at once. Batches + are processed asynchronously and allow for more efficient handling of many concurrent requests. + + :param subscription_id: The ID of the target subscription. Required. + :type subscription_id: str + :param resource_group_name: The name of the resource group. Required. + :type resource_group_name: str + :param batch_requests: Specifications for all of the requests that will be invoked as part of + the batch. Required. + :type batch_requests: ~azure.mgmt.resource.models.BatchRequests + :keyword callable cls: A custom type or function that will be passed the direct response + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: By default, your polling method will be ARMPolling. Pass in False for this + operation to not poll, or pass in your own initialized polling object for a personal polling + strategy. + :paramtype polling: bool or ~azure.core.polling.PollingMethod + :keyword int polling_interval: Default waiting time between two polls for LRO operations if no + Retry-After header is present. + :return: An instance of LROPoller that returns either BatchResponseStatus or the result of + cls(response) + :rtype: ~azure.core.polling.LROPoller[~azure.mgmt.resource.models.BatchResponseStatus] + :raises ~azure.core.exceptions.HttpResponseError: + """ + api_version = kwargs.pop("api_version", "2025-08-01-preview") + content_type = kwargs.pop("content_type", "application/json") + cls = kwargs.pop("cls", None) + polling = kwargs.pop("polling", True) + lro_delay = kwargs.pop("polling_interval", self._config.polling_interval) + cont_token = kwargs.pop("continuation_token", None) + + if cont_token is None: + raw_result = self._invoke_at_resource_group_scope_initial( + subscription_id=subscription_id, + resource_group_name=resource_group_name, + batch_requests=batch_requests, + api_version=api_version, + content_type=content_type, + cls=lambda x, y, z: x, + **kwargs + ) + kwargs.pop("error_map", None) + + def get_long_running_output(pipeline_response): + response = pipeline_response.http_response + deserialized = self._deserialize("BatchResponseStatus", pipeline_response) + if cls: + return cls(pipeline_response, deserialized, {}) + return deserialized + + if polling is True: + polling_method = cast(PollingMethod, ARMPolling(lro_delay, **kwargs)) + elif polling is False: + polling_method = cast(PollingMethod, NoPolling()) + else: + polling_method = polling + if cont_token: + return LROPoller.from_continuation_token( + polling_method=polling_method, + continuation_token=cont_token, + client=self._client, + deserialization_callback=get_long_running_output, + ) + return LROPoller(self._client, raw_result, get_long_running_output, polling_method) + + def _invoke_at_resource_group_scope_initial( + self, + subscription_id: str, + resource_group_name: str, + batch_requests: "_models.BatchRequests", + **kwargs: Any + ) -> Optional["_models.BatchResponseStatus"]: + error_map = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + api_version = kwargs.pop("api_version", "2025-08-01-preview") + content_type = kwargs.pop("content_type", "application/json") + cls = kwargs.pop("cls", None) + + _json = self._serialize.body(batch_requests, "BatchRequests") + + request = build_invoke_at_resource_group_scope_request( + subscription_id=subscription_id, + resource_group_name=resource_group_name, + api_version=api_version, + content_type=content_type, + json=_json, + **kwargs + ) + request.url = self._client.format_url(request.url) + + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200, 202]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CloudError, pipeline_response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + deserialized = None + response_headers = {} + if response.status_code == 200: + deserialized = self._deserialize("BatchResponseStatus", pipeline_response) + + if response.status_code == 202: + deserialized = self._deserialize("BatchResponseStatus", pipeline_response) + response_headers["Location"] = self._deserialize("str", response.headers.get("Location")) + + if cls: + return cls(pipeline_response, deserialized, response_headers) + + return deserialized + + _invoke_at_resource_group_scope_initial.metadata = {"url": "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Resources/invokeAtResourceGroupScope"} + + +def build_invoke_at_subscription_scope_request( + subscription_id: str, **kwargs: Any +) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version = kwargs.pop("api_version", "2025-08-01-preview") + content_type = kwargs.pop("content_type", _headers.pop("Content-Type", "application/json")) + accept = _headers.pop("Accept", "application/json") + + # Construct URL + _url = "/subscriptions/{subscriptionId}/providers/Microsoft.Resources/invokeAtSubscriptionScope" + path_format_arguments = { + "subscriptionId": _SERIALIZER.url("subscription_id", subscription_id, "str"), + } + + _url = _url.format(**path_format_arguments) + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_invoke_at_resource_group_scope_request( + subscription_id: str, resource_group_name: str, **kwargs: Any +) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version = kwargs.pop("api_version", "2025-08-01-preview") + content_type = kwargs.pop("content_type", _headers.pop("Content-Type", "application/json")) + accept = _headers.pop("Accept", "application/json") + + # Construct URL + _url = "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Resources/invokeAtResourceGroupScope" + path_format_arguments = { + "subscriptionId": _SERIALIZER.url("subscription_id", subscription_id, "str"), + "resourceGroupName": _SERIALIZER.url("resource_group_name", resource_group_name, "str"), + } + + _url = _url.format(**path_format_arguments) + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) \ No newline at end of file diff --git a/sdk/resources/azure-mgmt-resource/tests/test_resource_management_batch_operations_test.py b/sdk/resources/azure-mgmt-resource/tests/test_resource_management_batch_operations_test.py new file mode 100644 index 000000000000..b3a01c5dabcc --- /dev/null +++ b/sdk/resources/azure-mgmt-resource/tests/test_resource_management_batch_operations_test.py @@ -0,0 +1,53 @@ +""" +Integration tests for batch operations in azure-mgmt-resource. +""" + +from azure.mgmt.resource.resources import ResourceManagementClient +from azure.mgmt.resource.resources.operations import BatchOperations +from azure.mgmt.resource.resources.models import ( + BatchRequest, + BatchRequests, + BatchResponse, + BatchResponseStatus, +) + + +def test_batch_integration(): + """Test that batch operations are properly integrated into ResourceManagementClient.""" + # Verify key types are importable + assert ResourceManagementClient is not None + assert BatchOperations is not None + assert BatchRequest is not None + assert BatchRequests is not None + assert BatchResponse is not None + assert BatchResponseStatus is not None + + # Test creating batch models (using correct property names from TypeSpec) + batch_request = BatchRequest( + http_method="POST", + relative_url=( + "/subscriptions/test-sub-id/resourceGroups/test-rg/" + "providers/Microsoft.Compute/virtualMachines" + ), + name="test-batch-request", + body={"test": "data"}, + ) + + batch_requests = BatchRequests(requests=[batch_request]) + assert batch_requests.requests[0] is batch_request + + # Test enum values (using correct enum from TypeSpec) + assert BatchResponseStatus.SUCCEEDED == "Succeeded" + assert BatchResponseStatus.FAILED == "Failed" + assert BatchResponseStatus.VALIDATION_FAILED == "ValidationFailed" + assert BatchResponseStatus.SKIPPED == "Skipped" + + +def test_sdk_structure(): + """Test that the SDK has the expected structure for batch operations.""" + # Ensure import paths work + import azure.mgmt.resource.resources.models # pylint: disable=unused-import + import azure.mgmt.resource.resources.operations # pylint: disable=unused-import + + # Verify the client exposes batch_operations in its public surface + assert hasattr(ResourceManagementClient, "batch_operations") \ No newline at end of file