diff --git a/.chronus/changes/azure-http-specs-add-export-binary-lro-2026-01-19.md b/.chronus/changes/azure-http-specs-add-export-binary-lro-2026-01-19.md new file mode 100644 index 0000000000..6354c7b120 --- /dev/null +++ b/.chronus/changes/azure-http-specs-add-export-binary-lro-2026-01-19.md @@ -0,0 +1,7 @@ +--- +changeKind: feature +packages: + - "@azure-tools/azure-http-specs" +--- + +Add POST LRO test case `exportBinary` whose response type is `bytes` in the Azure Resource Manager operation templates spec. diff --git a/packages/azure-http-specs/assets/image.png b/packages/azure-http-specs/assets/image.png new file mode 100644 index 0000000000..42fe8dc145 Binary files /dev/null and b/packages/azure-http-specs/assets/image.png differ diff --git a/packages/azure-http-specs/spec-summary.md b/packages/azure-http-specs/spec-summary.md index ef9310d6ba..96ed063739 100644 --- a/packages/azure-http-specs/spec-summary.md +++ b/packages/azure-http-specs/spec-summary.md @@ -161,6 +161,72 @@ Example input: Output: None (204/empty response) +### Azure_ClientGenerator_Core_ClientDefaultValue_getHeaderParameter + +- Endpoint: `get /azure/client-generator-core/client-default-value/header-parameter` + +Test case 4: `@clientDefaultValue` for header parameters. +This scenario tests that client default values are correctly applied to header parameters. + +Expected header parameters: +Accept: "application/json;odata.metadata=none" (default) +x-custom-header: "default-value" (default) + +Expected response: 204 No Content + +### Azure_ClientGenerator_Core_ClientDefaultValue_getOperationParameter + +- Endpoint: `get /azure/client-generator-core/client-default-value/operation-parameter` + +Test case 2: `@clientDefaultValue` for operation parameter. +This scenario tests that client default values are correctly applied to operation parameters. + +Expected query parameter: +name: "test" +pageSize: 10 (default) +format: "json" (default) + +Expected response: 204 No Content + +### Azure_ClientGenerator_Core_ClientDefaultValue_getPathParameter + +- Endpoint: `get /azure/client-generator-core/client-default-value/path-parameter/{segment1}/{segment2}` + +Test case 3: `@clientDefaultValue` for first path segment. +This scenario has 2 path segments and tests client default value on the first segment. + +Expected path parameters: +segment1: "default-segment1" (default) +segment2: "segment2" + +Expected response: 204 No Content + +### Azure_ClientGenerator_Core_ClientDefaultValue_putModelProperty + +- Endpoint: `put /azure/client-generator-core/client-default-value/model-property` + +Test case 1: `@clientDefaultValue` for model property. +This scenario tests that client default values are correctly applied to model properties. + +Expected input body: + +```json +{ + "name": "test" +} +``` + +Expected response body: + +```json +{ + "name": "test", + "timeout": 30, + "tier": "standard", + "retry": true +} +``` + ### Azure_ClientGenerator_Core_ClientInitialization_HeaderParam - Endpoints: @@ -316,75 +382,9 @@ client.getStandalone(); client.deleteStandalone(); ``` -### Azure_ClientGenerator_Core_ClientDefaultValue_getOperationParameter - -- Endpoint: `get /azure/client-generator-core/client-default-value/operation-parameter` - -Test case 2: `@clientDefaultValue` for operation parameter. -This scenario tests that client default values are correctly applied to operation parameters. - -Expected query parameter: -name: "test" -pageSize: 10 (default) -format: "json" (default) +### Azure_ClientGenerator_Core_ClientLocation_MoveMethodParameterToClient_BlobOperations -Expected response: 204 No Content - -### Azure_ClientGenerator_Core_ClientDefaultValue_getHeaderParameter - -- Endpoint: `get /azure/client-generator-core/client-default-value/header-parameter` - -Test case 4: `@clientDefaultValue` for header parameters. -This scenario tests that client default values are correctly applied to header parameters. - -Expected header parameters: -Accept: "application/json;odata.metadata=none" (default) -x-custom-header: "default-value" (default) - -Expected response: 204 No Content - -### Azure_ClientGenerator_Core_ClientDefaultValue_getPathParameter - -- Endpoint: `get /azure/client-generator-core/client-default-value/path-parameter/{segment1}/{segment2}` - -Test case 3: `@clientDefaultValue` for first path segment. -This scenario has 2 path segments and tests client default value on the first segment. - -Expected path parameters: -segment1: "default-segment1" (default) -segment2: "segment2" - -Expected response: 204 No Content - -### Azure_ClientGenerator_Core_ClientDefaultValue_putModelProperty - -- Endpoint: `put /azure/client-generator-core/client-default-value/model-property` - -Test case 1: `@clientDefaultValue` for model property. -This scenario tests that client default values are correctly applied to model properties. - -Expected input body: - -```json -{ - "name": "test" -} -``` - -Expected response body: - -```json -{ - "name": "test", - "timeout": 30, - "tier": "standard", - "retry": true -} -``` - -### Azure_ClientGenerator_Core_ClientLocation_MoveMethodParameterToClient - -- Endpoint: `get /azure/client-generator-core/client-location/blob` +- Endpoint: `get /azure/client-generator-core/client-location/move-method-parameter-to-client/blob` Test moving a method parameter to client. @@ -399,12 +399,11 @@ Expected response: - Status: 200 - Body: {"id": "blob-001", "name": "testblob.txt", "size": 1024, "path": "/testcontainer/testblob.txt"} -### Azure_ClientGenerator_Core_ClientLocation_MoveToExistingSubClient +### Azure_ClientGenerator_Core_ClientLocation_MoveToExistingSubClient_UserOperations - Endpoints: - - `get /azure/client-generator-core/client-location/admin` - - `get /azure/client-generator-core/client-location/user` - - `get /azure/client-generator-core/client-location/user` + - `get /azure/client-generator-core/client-location/move-to-existing-sub-client/user` + - `get /azure/client-generator-core/client-location/move-to-existing-sub-client/user` Test moving an operation from one sub client to another existing sub client. @@ -415,11 +414,11 @@ Expected client structure: - Interface UserOperations should contain only operation `getUser` - Interface AdminOperations should contain operations `getAdminInfo` and `deleteUser` (moved from UserOperations) -### Azure_ClientGenerator_Core_ClientLocation_MoveToNewSubClient +### Azure_ClientGenerator_Core_ClientLocation_MoveToNewSubClient_ProductOperations - Endpoints: - - `get /azure/client-generator-core/client-location/products` - - `get /azure/client-generator-core/client-location/products/archive` + - `get /azure/client-generator-core/client-location/move-to-new-sub-client/products` + - `get /azure/client-generator-core/client-location/move-to-new-sub-client/products/archive` Test moving an operation to a new sub client specified by string name. @@ -430,11 +429,11 @@ Expected client structure: - Interface ProductOperations should contain only operation `listProducts` - A new sub client "ArchiveOperations" should be created containing operation `archiveProduct` -### Azure_ClientGenerator_Core_ClientLocation_MoveToRootClient +### Azure_ClientGenerator_Core_ClientLocation_MoveToRootClient_ResourceOperations - Endpoints: - - `get /azure/client-generator-core/client-location/resource` - - `get /azure/client-generator-core/client-location/health` + - `get /azure/client-generator-core/client-location/move-to-root-client/resource` + - `get /azure/client-generator-core/client-location/move-to-root-client/health` Test moving an operation to the root client. @@ -2168,6 +2167,280 @@ Expected response body: } ``` +### Azure_ResourceManager_MultiServiceOlderVersions_Compute_VirtualMachines_createOrUpdate + +- Endpoint: `put https://management.azure.com` + +Test that a client can expose operations from multiple services using older API versions. This operation should be called like this: `client.virtualMachines.createOrUpdate(...)`. + +PUT (create or update) a Virtual Machine. +Expected path: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Compute/virtualMachinesOld/vm-old1 +Expected query parameter: api-version=2024-11-01 +Expected request body: + +```json +{ + "location": "eastus", + "properties": { + "size": "Standard_D2s_v3" + } +} +``` + +Expected response body: + +```json +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Compute/virtualMachinesOld/vm-old1", + "name": "vm-old1", + "type": "Microsoft.Compute/virtualMachinesOld", + "location": "eastus", + "properties": { + "provisioningState": "Succeeded", + "size": "Standard_D2s_v3" + } +} +``` + +### Azure_ResourceManager_MultiServiceOlderVersions_Compute_VirtualMachines_get + +- Endpoint: `get https://management.azure.com` + +Test that a client can expose operations from multiple services using older API versions. This operation should be called like this: `client.virtualMachines.get(...)`. + +GET a Virtual Machine. +Expected path: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Compute/virtualMachinesOld/vm-old1 +Expected query parameter: api-version=2024-11-01 + +Expected response body: + +```json +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Compute/virtualMachinesOld/vm-old1", + "name": "vm-old1", + "type": "Microsoft.Compute/virtualMachinesOld", + "location": "eastus", + "properties": { + "provisioningState": "Succeeded", + "size": "Standard_D2s_v3" + } +} +``` + +### Azure_ResourceManager_MultiServiceOlderVersions_ComputeDisk_Disks_createOrUpdate + +- Endpoint: `put https://management.azure.com` + +Test that a client can expose operations from multiple services using older API versions. This operation should be called like this: `client.disks.createOrUpdate(...)`. + +PUT (create or update) a Disk resource. +Expected path: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Compute/disksOld/disk-old1 +Expected query parameter: api-version=2024-03-02 +Expected request body: + +```json +{ + "location": "eastus", + "properties": { + "diskSizeGB": 128 + } +} +``` + +Expected response body: + +```json +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Compute/disksOld/disk-old1", + "name": "disk-old1", + "type": "Microsoft.Compute/disksOld", + "location": "eastus", + "properties": { + "provisioningState": "Succeeded", + "diskSizeGB": 128 + } +} +``` + +### Azure_ResourceManager_MultiServiceOlderVersions_ComputeDisk_Disks_get + +- Endpoint: `get https://management.azure.com` + +Test that a client can expose operations from multiple services using older API versions. This operation should be called like this: `client.disks.get(...)`. + +GET a Disk resource. +Expected path: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Compute/disksOld/disk-old1 +Expected query parameter: api-version=2024-03-02 + +Expected response body: + +```json +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Compute/disksOld/disk-old1", + "name": "disk-old1", + "type": "Microsoft.Compute/disksOld", + "location": "eastus", + "properties": { + "provisioningState": "Succeeded", + "diskSizeGB": 128 + } +} +``` + +### Azure_ResourceManager_MultiServiceSharedModels_Compute_VirtualMachines_createOrUpdate + +- Endpoint: `put https://management.azure.com` + +Test that a client can expose operations from multiple services with shared models. This operation should be called like this: `client.virtualMachines.createOrUpdate(...)`. + +PUT (create or update) a Virtual Machine. +Expected path: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Compute/virtualMachinesShared/vm-shared1 +Expected query parameter: api-version=2025-05-01 +Expected request body: + +```json +{ + "location": "eastus", + "properties": { + "metadata": { + "createdBy": "user@example.com", + "tags": { + "environment": "production" + } + } + } +} +``` + +Expected response body: + +```json +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Compute/virtualMachinesShared/vm-shared1", + "name": "vm-shared1", + "type": "Microsoft.Compute/virtualMachinesShared", + "location": "eastus", + "properties": { + "provisioningState": "Succeeded", + "metadata": { + "createdAt": "2025-01-01T00:00:00Z", + "createdBy": "user@example.com", + "tags": { + "environment": "production" + } + } + } +} +``` + +### Azure_ResourceManager_MultiServiceSharedModels_Compute_VirtualMachines_get + +- Endpoint: `get https://management.azure.com` + +Test that a client can expose operations from multiple services with shared models. This operation should be called like this: `client.virtualMachines.get(...)`. + +GET a Virtual Machine. +Expected path: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Compute/virtualMachinesShared/vm-shared1 +Expected query parameter: api-version=2025-05-01 + +Expected response body: + +```json +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Compute/virtualMachinesShared/vm-shared1", + "name": "vm-shared1", + "type": "Microsoft.Compute/virtualMachinesShared", + "location": "eastus", + "properties": { + "provisioningState": "Succeeded", + "metadata": { + "createdAt": "2025-01-01T00:00:00Z", + "createdBy": "user@example.com", + "tags": { + "environment": "production" + } + } + } +} +``` + +### Azure_ResourceManager_MultiServiceSharedModels_Storage_StorageAccounts_createOrUpdate + +- Endpoint: `put https://management.azure.com` + +Test that a client can expose operations from multiple services with shared models. This operation should be called like this: `client.storageAccounts.createOrUpdate(...)`. + +PUT (create or update) a Storage Account. +Expected path: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Storage/storageAccounts/account1 +Expected query parameter: api-version=2025-02-01 +Expected request body: + +```json +{ + "location": "westus", + "properties": { + "metadata": { + "createdBy": "admin@example.com", + "tags": { + "department": "engineering" + } + } + } +} +``` + +Expected response body: + +```json +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Storage/storageAccounts/account1", + "name": "account1", + "type": "Microsoft.Storage/storageAccounts", + "location": "westus", + "properties": { + "provisioningState": "Succeeded", + "metadata": { + "createdAt": "2025-01-02T00:00:00Z", + "createdBy": "admin@example.com", + "tags": { + "department": "engineering" + } + } + } +} +``` + +### Azure_ResourceManager_MultiServiceSharedModels_Storage_StorageAccounts_get + +- Endpoint: `get https://management.azure.com` + +Test that a client can expose operations from multiple services with shared models. This operation should be called like this: `client.storageAccounts.get(...)`. + +GET a Storage Account. +Expected path: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Storage/storageAccounts/account1 +Expected query parameter: api-version=2025-02-01 + +Expected response body: + +```json +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Storage/storageAccounts/account1", + "name": "account1", + "type": "Microsoft.Storage/storageAccounts", + "location": "westus", + "properties": { + "provisioningState": "Succeeded", + "metadata": { + "createdAt": "2025-01-02T00:00:00Z", + "createdBy": "admin@example.com", + "tags": { + "department": "engineering" + } + } + } +} +``` + ### Azure_ResourceManager_NonResource_NonResourceOperations_create - Endpoint: `put https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.NonResource/locations/{location}/otherParameters/{parameter}` @@ -2506,6 +2779,145 @@ Expected response body: } ``` +### Azure_ResourceManager_OperationTemplates_Lro_exportBinary + +- Endpoint: `post https://management.azure.com` + +Resource POST operation that returns binary data. +Service returns both Location and Azure-AsyncOperation header on initial request. +final-state-via: location + +Expected verb: POST +Expected path: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Azure.ResourceManager.OperationTemplates/orders/order1/exportBinary +Expected query parameter: api-version=2023-12-01-preview +Expected request body: + +```json +{ + "sourceId": "source1" +} +``` + +Expected response status code: 202 +Expected response headers: + +- Azure-AsyncOperation={endpoint}/subscriptions/00000000-0000-0000-0000-000000000000/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_aao +- Location={endpoint}/subscriptions/00000000-0000-0000-0000-000000000000/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_location + Expected no response body + +Whether you do polling through AAO, Location or combined, first one will respond with provisioning state "InProgress", second one with "Succeeded". + +AAO first poll. +Expected verb: GET +Expected URL: {endpoint}/subscriptions/00000000-0000-0000-0000-000000000000/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_aao +Expected status code: 200 +Expected response body: + +```json +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_aao", + "name": "lro_post_binary_aao", + "status": "InProgress", + "startTime": "2024-11-08T01:41:53.5508583+00:00" +} +``` + +AAO second poll. +Expected verb: GET +Expected URL: {endpoint}/subscriptions/00000000-0000-0000-0000-000000000000/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_aao +Expected status code: 200 +Expected response body: + +```json +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_aao", + "name": "lro_post_binary_aao", + "status": "Succeeded", + "startTime": "2024-11-08T01:41:53.5508583+00:00", + "endTime": "2024-11-08T01:42:41.5354192+00:00" +} +``` + +Location first poll. +Expected verb: GET +Expected URL: {endpoint}/subscriptions/00000000-0000-0000-0000-000000000000/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_location +Expected status code: 202 +Expected no response body + +Location second poll. +Expected verb: GET +Expected URL: {endpoint}/subscriptions/00000000-0000-0000-0000-000000000000/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_location +Expected status code: 200 +Expected response header: Content-Type=application/octet-stream +Expected response body: binary content (image.png file) + +### Azure_ResourceManager_OperationTemplates_LroPaging_postPagingLro + +- Endpoint: `post https://management.azure.com` + +Resource POST operation that returns a LRO with paging. + +Step 1: Initial Request +Expected verb: POST +Expected path: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Azure.ResourceManager.OperationTemplates/products/default/postPagingLro +Expected query parameter: api-version=2023-12-01-preview +Expected response: 202 Accepted with Location and Retry-After headers. + +Step 2: Polling Request +Expected verb: GET +Expected path: /subscriptions/00000000-0000-0000-0000-000000000000/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_paging_post_location +Expected query parameter: api-version=2023-12-01-preview +Expected response: 202 Accepted with Location and Retry-After headers. + +Step 3: Final Result Request +Expected verb: GET +Expected path: /subscriptions/00000000-0000-0000-0000-000000000000/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_paging_post_location +Expected query parameter: api-version=2023-12-01-preview +Expected response: 200 OK with a paged result. The response body contains a "nextLink" field. +Expected response body: + +```json +{ + "value": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Azure.ResourceManager.OperationTemplates/products/product1", + "name": "product1", + "type": "Azure.ResourceManager.OperationTemplates/products", + "location": "eastus", + "properties": { + "provisioningState": "Succeeded", + "productId": "product1" + } + } + ], + "nextLink": "..." +} +``` + +Step 4: Next Page Request +Expected verb: GET +Expected path: /subscriptions/00000000-0000-0000-0000-000000000000/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_paging_post_location/nextPage +Expected query parameter: api-version=2023-12-01-preview +Expected response: 200 OK with the second page of results. +Expected response body: + +```json +{ + "value": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Azure.ResourceManager.OperationTemplates/products/product2", + "name": "product2", + "type": "Azure.ResourceManager.OperationTemplates/products", + "location": "eastus", + "properties": { + "provisioningState": "Succeeded", + "productId": "product2" + } + } + ] +} +``` + ### Azure_ResourceManager_OperationTemplates_OptionalBody_get - Endpoint: `get https://management.azure.com` diff --git a/packages/azure-http-specs/specs/azure/resource-manager/operation-templates/lro.tsp b/packages/azure-http-specs/specs/azure/resource-manager/operation-templates/lro.tsp index 6d5d6f2187..a43db5f352 100644 --- a/packages/azure-http-specs/specs/azure/resource-manager/operation-templates/lro.tsp +++ b/packages/azure-http-specs/specs/azure/resource-manager/operation-templates/lro.tsp @@ -4,6 +4,7 @@ import "@azure-tools/typespec-azure-core"; import "@azure-tools/typespec-azure-resource-manager"; import "@typespec/spector"; +using Http; using Rest; using Spector; @@ -36,6 +37,19 @@ model ExportResult { content: string; } +model ExportBinaryRequest { + @doc("Source identifier for binary export.") + sourceId: string; +} + +model ExportBinaryResult { + @header contentType: "application/octet-stream"; + + @doc("Binary content of the exported data.") + @body + data: bytes; +} + @armResourceOperations interface Lro { @scenario @@ -218,6 +232,79 @@ interface Lro { Azure.Core.Foundations.RetryAfterHeader >; + @scenario + @scenarioDoc(""" + Resource POST operation that returns binary data. + Service returns both Location and Azure-AsyncOperation header on initial request. + final-state-via: location + + Expected verb: POST + Expected path: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Azure.ResourceManager.OperationTemplates/orders/order1/exportBinary + Expected query parameter: api-version=2023-12-01-preview + Expected request body: + ```json + { + "sourceId": "source1" + } + ``` + Expected response status code: 202 + Expected response headers: + - Azure-AsyncOperation={endpoint}/subscriptions/00000000-0000-0000-0000-000000000000/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_aao + - Location={endpoint}/subscriptions/00000000-0000-0000-0000-000000000000/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_location + Expected no response body + + Whether you do polling through AAO, Location or combined, first one will respond with provisioning state "InProgress", second one with "Succeeded". + + AAO first poll. + Expected verb: GET + Expected URL: {endpoint}/subscriptions/00000000-0000-0000-0000-000000000000/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_aao + Expected status code: 200 + Expected response body: + ```json + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_aao", + "name": "lro_post_binary_aao", + "status" : "InProgress", + "startTime": "2024-11-08T01:41:53.5508583+00:00" + } + ``` + + AAO second poll. + Expected verb: GET + Expected URL: {endpoint}/subscriptions/00000000-0000-0000-0000-000000000000/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_aao + Expected status code: 200 + Expected response body: + ```json + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_aao", + "name": "lro_post_binary_aao", + "status" : "Succeeded", + "startTime": "2024-11-08T01:41:53.5508583+00:00", + "endTime": "2024-11-08T01:42:41.5354192+00:00" + } + ``` + + Location first poll. + Expected verb: GET + Expected URL: {endpoint}/subscriptions/00000000-0000-0000-0000-000000000000/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_location + Expected status code: 202 + Expected no response body + + Location second poll. + Expected verb: GET + Expected URL: {endpoint}/subscriptions/00000000-0000-0000-0000-000000000000/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_location + Expected status code: 200 + Expected response header: Content-Type=application/octet-stream + Expected response body: binary content (image.png file) + """) + exportBinary is ArmResourceActionAsync< + Order, + ExportBinaryRequest, + ExportBinaryResult, + LroHeaders = ArmCombinedLroHeaders & + Azure.Core.Foundations.RetryAfterHeader + >; + @scenario @scenarioDoc(""" Resource DELETE operation. diff --git a/packages/azure-http-specs/specs/azure/resource-manager/operation-templates/mockapi.ts b/packages/azure-http-specs/specs/azure/resource-manager/operation-templates/mockapi.ts index 4a623cc3fb..0dbe72ca74 100644 --- a/packages/azure-http-specs/specs/azure/resource-manager/operation-templates/mockapi.ts +++ b/packages/azure-http-specs/specs/azure/resource-manager/operation-templates/mockapi.ts @@ -7,6 +7,7 @@ import { ScenarioMockApi, withServiceKeys, } from "@typespec/spec-api"; +import { pngFile } from "../../../helper.js"; export const Scenarios: Record = {}; @@ -80,6 +81,7 @@ const validProductListResultPage2 = { }; let createOrReplacePollCount = 0; let postPollCount = 0; +let postBinaryPollCount = 0; let deletePollCount = 0; // operation list @@ -381,6 +383,119 @@ Scenarios.Azure_ResourceManager_OperationTemplates_Lro_export = passOnSuccess([ }, ]); +Scenarios.Azure_ResourceManager_OperationTemplates_Lro_exportBinary = passOnSuccess([ + { + // LRO POST binary initial request + uri: "/subscriptions/:subscriptionId/resourceGroups/:resourceGroup/providers/Azure.ResourceManager.OperationTemplates/orders/:orderName/exportBinary", + method: "post", + request: { + pathParams: { + subscriptionId: SUBSCRIPTION_ID_EXPECTED, + resourceGroup: RESOURCE_GROUP_EXPECTED, + orderName: "order1", + }, + query: { + "api-version": "2023-12-01-preview", + }, + body: json({ + sourceId: "source1", + }), + }, + response: { + status: 202, + headers: { + location: dyn`${dynItem("baseUrl")}/subscriptions/${SUBSCRIPTION_ID_EXPECTED}/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_location`, + "azure-asyncoperation": dyn`${dynItem("baseUrl")}/subscriptions/${SUBSCRIPTION_ID_EXPECTED}/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_aao`, + }, + }, + handler: (req: MockRequest) => { + postBinaryPollCount = 0; + return { + status: 202, + headers: { + location: `${req.baseUrl}/subscriptions/${SUBSCRIPTION_ID_EXPECTED}/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_location`, + "azure-asyncoperation": `${req.baseUrl}/subscriptions/${SUBSCRIPTION_ID_EXPECTED}/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_aao`, + }, + }; + }, + kind: "MockApiDefinition", + }, + { + // LRO POST binary poll intermediate/get final result - Location Header + uri: "/subscriptions/:subscriptionId/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_location", + method: "get", + request: { + pathParams: { + subscriptionId: SUBSCRIPTION_ID_EXPECTED, + }, + query: { + "api-version": "2023-12-01-preview", + }, + }, + response: { + status: 200, + }, + handler: (req: MockRequest) => { + const response = + // first status will be 202, second and forward be 200 with binary content + postBinaryPollCount > 0 + ? { + status: 200, + body: { + contentType: "application/octet-stream", + rawContent: pngFile, + }, + } + : { status: 202 }; + + postBinaryPollCount += 1; + return response; + }, + kind: "MockApiDefinition", + }, + { + // LRO POST binary poll intermediate/get final result - Azure-AsyncOperation Header + uri: "/subscriptions/:subscriptionId/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_aao", + method: "get", + request: { + pathParams: { + subscriptionId: SUBSCRIPTION_ID_EXPECTED, + }, + query: { + "api-version": "2023-12-01-preview", + }, + }, + response: { + status: 200, + }, + handler: (req: MockRequest) => { + const aaoResponse = { + id: `/subscriptions/${SUBSCRIPTION_ID_EXPECTED}/providers/Azure.ResourceManager.OperationTemplates/locations/eastus/operations/lro_post_binary_aao`, + name: "lro_post_binary_aao", + startTime: "2024-11-08T01:41:53.5508583+00:00", + }; + // first provisioningState will be "InProgress", second and forward be "Succeeded" + const responseBody = + postBinaryPollCount > 0 + ? { + ...aaoResponse, + status: "Succeeded", + endTime: "2024-11-08T01:42:41.5354192+00:00", + } + : { ...aaoResponse, status: "InProgress" }; + + const response = { + status: 200, // aao always returns 200 with response body + body: json(responseBody), + }; + + postBinaryPollCount += 1; + return response; + }, + kind: "MockApiDefinition", + }, +]); + Scenarios.Azure_ResourceManager_OperationTemplates_Lro_delete = passOnSuccess([ { // LRO DELETE initial request diff --git a/packages/azure-http-specs/specs/helper.ts b/packages/azure-http-specs/specs/helper.ts new file mode 100644 index 0000000000..5ea4745396 --- /dev/null +++ b/packages/azure-http-specs/specs/helper.ts @@ -0,0 +1,7 @@ +import { resolvePath } from "@typespec/compiler"; +import { readFileSync } from "fs"; +import { fileURLToPath } from "url"; + +const root = resolvePath(fileURLToPath(import.meta.url), "../../../"); + +export const pngFile = readFileSync(resolvePath(root, "assets/image.png"));