Skip to content

Commit f228b86

Browse files
RenzoPrettoMSCopilotCopilot
authored
Fix TypeSpec LRO operations: remove @extension, use @useFinalStateVia (#43308)
* Fix TypeSpec LRO: remove @extension, use @useFinalStateVia, GET returns 200+202 Root cause of the original bug: @extension("x-ms-long-running-operation", true) only affects the OpenAPI document. Combined with @pollingOperation pointing at a non-status-monitor endpoint, the SDK generators did not detect ScenarioConfigurations.execute and ScenarioRuns.cancel as LROs, emitting them as plain void methods. This change: * Removes @extension and @pollingOperation from execute/cancel. * Adds @Azure.Core.useFinalStateVia("location") on the LRO templates in lro-helpers.tsp so SDK generators emit proper SyncPoller/ArmOperation methods that follow the Location header to poll for completion. * Keeps the GET on ScenarioRun returning both 200 (final) and 202 (in progress, with body + Location/Retry-After headers). The Location- header polling protocol REQUIRES 202 while in progress and 200 on completion; @lroStatus on the status field is only consulted by the Azure-AsyncOperation status-monitor pattern, not by Location-header polling. Uses ArmResourceRead's Response template variable per reviewer's suggestion to express the 200|202 union. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address ScenarioRun polling response review comments Use the built-in TypeSpec url scalar for the Location header and add explicit bounds for the Retry-After header on the in-progress polling response. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address Chaos .NET SDK API review naming comments Add csharp client-name customizations for new public Chaos SDK model/resource names and model ARM ID fields as armResourceIdentifier so the generated .NET SDK uses ResourceIdentifier. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Format Chaos TypeSpec client customizations Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 03635f9 commit f228b86

10 files changed

Lines changed: 88 additions & 66 deletions

File tree

specification/chaos/resource-manager/Microsoft.Chaos/Chaos/client.tsp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ using Microsoft.Chaos;
99
@@clientName(Scenario, "ChaosScenario", "csharp");
1010
@@clientName(ScenarioRun, "ChaosScenarioRun", "csharp");
1111
@@clientName(ScenarioConfiguration, "ChaosScenarioConfiguration", "csharp");
12+
@@clientName(DiscoveredResource, "ChaosDiscovered", "csharp");
13+
@@clientName(PrivateAccess, "ChaosPrivateAccess", "csharp");
14+
@@clientName(PrivateAccessPatch, "ChaosPrivateAccessPatch", "csharp");
15+
@@clientName(PrivateAccessProperties, "ChaosPrivateAccessProperties", "csharp");
1216
@@clientName(ExperimentExecution, "ChaosExperimentExecution", "csharp");
1317
@@clientName(Experiment, "ChaosExperiment", "csharp");
1418
@@clientName(ExperimentExecution, "ChaosExperimentExecution", "csharp");
@@ -52,6 +56,25 @@ using Microsoft.Chaos;
5256
@@clientName(Action, "ChaosAction", "csharp");
5357
@@clientName(ActionVersion, "ChaosActionVersion", "csharp");
5458
@@clientName(ActionKind, "ChaosActionKind", "csharp");
59+
@@clientName(
60+
ConfigurationExclusions,
61+
"ChaosScenarioConfigurationExclusions",
62+
"csharp"
63+
);
64+
@@clientName(
65+
ConfigurationFilters,
66+
"ChaosScenarioConfigurationFilters",
67+
"csharp"
68+
);
69+
@@clientName(OperationError, "ChaosOperationError", "csharp");
70+
@@clientName(ScenarioParameter.required, "IsRequired", "csharp");
71+
@@clientName(ParameterType, "ChaosScenarioParameterType", "csharp");
72+
@@clientName(FixResourcePermissionsRequest.whatIf, "IsWhatIf", "csharp");
73+
@@clientName(PermissionsFix, "ChaosPermissionsFixData", "csharp");
74+
@@clientName(PermissionsFixProperties.whatIfMode, "IsWhatIfMode", "csharp");
75+
@@clientName(Recommendation, "ChaosScenarioRecommendation", "csharp");
76+
@@clientName(Validation, "ChaosScenarioValidationData", "csharp");
77+
@@clientName(WorkspaceEvaluation, "ChaosWorkspaceEvaluationData", "csharp");
5578
@@clientName(
5679
ActionSupportedTargetType,
5780
"ChaosActionSupportedTargetType",

specification/chaos/resource-manager/Microsoft.Chaos/Chaos/lro-helpers.tsp

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@ import "@azure-tools/typespec-azure-core";
22
import "@azure-tools/typespec-azure-resource-manager";
33
import "@typespec/http";
44
import "@typespec/rest";
5-
import "@typespec/openapi";
65

76
using Azure.ResourceManager;
87
using Azure.Core;
98
using TypeSpec.Http;
109
using TypeSpec.Rest;
11-
using TypeSpec.OpenAPI;
1210

1311
namespace Microsoft.Chaos;
1412

@@ -24,15 +22,10 @@ namespace Microsoft.Chaos;
2422
#suppress "@azure-tools/typespec-azure-core/documentation-required" "template"
2523
#suppress "@azure-tools/typespec-azure-resource-manager/arm-resource-operation" "template"
2624
#suppress "@azure-tools/typespec-azure-resource-manager/arm-post-operation-response-codes" "Custom LRO operation with specific response codes"
27-
#suppress "@azure-tools/typespec-azure-core/no-openapi" "Required for LRO extensions in OpenAPI output"
2825
@autoRoute
2926
@armResourceAction(Resource)
3027
@post
31-
@extension("x-ms-long-running-operation", true)
32-
@extension(
33-
"x-ms-long-running-operation-options",
34-
#{ `final-state-via`: "location" }
35-
)
28+
@Azure.Core.useFinalStateVia("location")
3629
op ArmResourceActionAsyncWithLocation<
3730
Resource extends Azure.ResourceManager.Foundations.Resource,
3831
Request extends TypeSpec.Reflection.Model | void,
@@ -67,15 +60,10 @@ op ArmResourceActionAsyncWithLocation<
6760
#suppress "@azure-tools/typespec-azure-core/documentation-required" "template"
6861
#suppress "@azure-tools/typespec-azure-resource-manager/arm-resource-operation" "template"
6962
#suppress "@azure-tools/typespec-azure-resource-manager/arm-post-operation-response-codes" "Custom LRO operation with specific response codes"
70-
#suppress "@azure-tools/typespec-azure-core/no-openapi" "Required for LRO extensions in OpenAPI output"
7163
@autoRoute
7264
@armResourceAction(Resource)
7365
@post
74-
@extension("x-ms-long-running-operation", true)
75-
@extension(
76-
"x-ms-long-running-operation-options",
77-
#{ `final-state-via`: "location" }
78-
)
66+
@Azure.Core.useFinalStateVia("location")
7967
op ArmResourceActionNoContentAsyncWithLocationResult<
8068
Resource extends Azure.ResourceManager.Foundations.Resource,
8169
Request extends TypeSpec.Reflection.Model | void,
@@ -107,15 +95,10 @@ op ArmResourceActionNoContentAsyncWithLocationResult<
10795
#suppress "@azure-tools/typespec-azure-core/documentation-required" "template"
10896
#suppress "@azure-tools/typespec-azure-resource-manager/arm-resource-operation" "template"
10997
#suppress "@azure-tools/typespec-azure-resource-manager/arm-post-operation-response-codes" "Custom LRO operation with specific response codes"
110-
#suppress "@azure-tools/typespec-azure-core/no-openapi" "Required for LRO extensions in OpenAPI output"
11198
@autoRoute
11299
@armResourceAction(Resource)
113100
@post
114-
@extension("x-ms-long-running-operation", true)
115-
@extension(
116-
"x-ms-long-running-operation-options",
117-
#{ `final-state-via`: "location" }
118-
)
101+
@Azure.Core.useFinalStateVia("location")
119102
op ArmResourceActionNoBodyAsyncWithLocation<
120103
Resource extends Azure.ResourceManager.Foundations.Resource,
121104
Request extends TypeSpec.Reflection.Model | void,

specification/chaos/resource-manager/Microsoft.Chaos/Chaos/preview/2024-11-01-preview/openapi.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1290,7 +1290,7 @@
12901290
"tags": [
12911291
"Experiments"
12921292
],
1293-
"description": "The operation to update an experiment.",
1293+
"description": "Update an experiment.",
12941294
"parameters": [
12951295
{
12961296
"$ref": "../../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter"

specification/chaos/resource-manager/Microsoft.Chaos/Chaos/preview/2026-05-01-preview/openapi.json

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,10 @@
8585
"name": "Scenarios"
8686
},
8787
{
88-
"name": "ScenarioRuns"
88+
"name": "ScenarioConfigurations"
8989
},
9090
{
91-
"name": "ScenarioConfigurations"
91+
"name": "ScenarioRuns"
9292
}
9393
],
9494
"paths": {
@@ -3836,8 +3836,7 @@
38363836
"tags": [
38373837
"ScenarioRuns"
38383838
],
3839-
"summary": "Get a scenario run.",
3840-
"description": "Get a scenario run.",
3839+
"description": "Get a scenario run.\n\nThis endpoint is also the polling target for ScenarioConfigurations.execute\nand ScenarioRuns.cancel (final-state-via: location). While the run is in\nprogress the service returns 202 with a Location header pointing back to\nthis URL; clients must keep polling until they receive 200, which carries\nthe final ScenarioRun body.",
38413840
"parameters": [
38423841
{
38433842
"$ref": "../../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter"
@@ -3883,19 +3882,22 @@
38833882
}
38843883
},
38853884
"202": {
3886-
"description": "The request has been accepted for processing, but processing has not yet completed.",
3885+
"description": "Indicates the scenario run is still in progress. Clients polling via the\nLocation header should continue polling until a 200 response is returned.\nThe body reflects the current run state at the time of the poll.",
38873886
"schema": {
38883887
"$ref": "#/definitions/ScenarioRun"
38893888
},
38903889
"headers": {
38913890
"Location": {
38923891
"type": "string",
3893-
"description": "The Location header contains the URL where the status of the long running operation can be checked."
3892+
"format": "uri",
3893+
"description": "The URL where the status of the long-running operation can be polled."
38943894
},
38953895
"Retry-After": {
38963896
"type": "integer",
38973897
"format": "int32",
3898-
"description": "The Retry-After header can indicate how long the client should wait before polling the operation status."
3898+
"description": "The number of seconds the client should wait before polling again.",
3899+
"minimum": 0,
3900+
"maximum": 2147483647
38993901
}
39003902
}
39013903
},
@@ -5456,6 +5458,7 @@
54565458
"properties": {
54575459
"resourceId": {
54585460
"type": "string",
5461+
"format": "arm-id",
54595462
"description": "The resource id for the affected resource.",
54605463
"readOnly": true
54615464
},
@@ -6091,6 +6094,7 @@
60916094
"properties": {
60926095
"resourceId": {
60936096
"type": "string",
6097+
"format": "arm-id",
60946098
"description": "The resource id for the affected resource.",
60956099
"readOnly": true
60966100
},
@@ -6144,6 +6148,7 @@
61446148
"properties": {
61456149
"targetResourceId": {
61466150
"type": "string",
6151+
"format": "arm-id",
61476152
"description": "The target Azure resource ID.",
61486153
"readOnly": true
61496154
},
@@ -6174,6 +6179,7 @@
61746179
},
61756180
"roleAssignmentId": {
61766181
"type": "string",
6182+
"format": "arm-id",
61776183
"description": "The created role assignment resource ID (null if failed or what-if mode).",
61786184
"readOnly": true
61796185
},
@@ -7345,6 +7351,7 @@
73457351
},
73467352
"workspaceId": {
73477353
"type": "string",
7354+
"format": "arm-id",
73487355
"description": "The workspace ID this evaluation belongs to.",
73497356
"readOnly": true
73507357
},

specification/chaos/resource-manager/Microsoft.Chaos/Chaos/scenarioConfiguration.models.tsp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ model PermissionError {
223223
* The resource id for the affected resource.
224224
*/
225225
@visibility(Lifecycle.Read)
226-
resourceId: string;
226+
resourceId: Azure.Core.armResourceIdentifier;
227227

228228
/**
229229
* The missing permissions.
@@ -275,7 +275,7 @@ model ResourceStateError {
275275
* The resource id for the affected resource.
276276
*/
277277
@visibility(Lifecycle.Read)
278-
resourceId: string;
278+
resourceId: Azure.Core.armResourceIdentifier;
279279

280280
/**
281281
* The error code.
@@ -418,7 +418,7 @@ model RoleAssignmentResult {
418418
* The target Azure resource ID.
419419
*/
420420
@visibility(Lifecycle.Read)
421-
targetResourceId: string;
421+
targetResourceId: Azure.Core.armResourceIdentifier;
422422

423423
/**
424424
* The managed identity principal ID.
@@ -454,7 +454,7 @@ model RoleAssignmentResult {
454454
* The created role assignment resource ID (null if failed or what-if mode).
455455
*/
456456
@visibility(Lifecycle.Read)
457-
roleAssignmentId?: string;
457+
roleAssignmentId?: Azure.Core.armResourceIdentifier;
458458

459459
/**
460460
* Error details if the assignment failed.

specification/chaos/resource-manager/Microsoft.Chaos/Chaos/scenarioConfiguration.tsp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,6 @@ interface ScenarioConfigurations {
4848
/**
4949
* Execute the scenario execution with the given scenario configuration.
5050
*/
51-
#suppress "@azure-tools/typespec-azure-resource-manager/arm-post-operation-response-codes" "LRO POST returns 202 with Location header for polling; final result obtained by polling the Location URL"
52-
@pollingOperation(ScenarioRuns.get)
5351
execute is ArmResourceActionNoContentAsyncWithLocationResult<
5452
ScenarioConfiguration,
5553
void,
@@ -59,9 +57,7 @@ interface ScenarioConfigurations {
5957
/**
6058
* Cancel the currently running scenario execution.
6159
*/
62-
#suppress "@azure-tools/typespec-azure-resource-manager/arm-post-operation-response-codes" "LRO POST returns 202 with Location header for polling; final result obtained by polling the Location URL"
6360
@removed(Microsoft.Chaos.Versions.v2026_05_01_preview)
64-
@pollingOperation(ScenarioRuns.get)
6561
cancel is ArmResourceActionNoContentAsyncWithLocationResult<
6662
ScenarioConfiguration,
6763
void,
@@ -71,7 +67,6 @@ interface ScenarioConfigurations {
7167
/**
7268
* Validate the given scenario configuration.
7369
*/
74-
#suppress "@azure-tools/typespec-azure-resource-manager/arm-post-operation-response-codes" "LRO POST returns 202 with Location header for polling; final result obtained by polling the Location URL"
7570
validate is ArmResourceActionNoContentAsyncWithLocationResult<
7671
ScenarioConfiguration,
7772
void,
@@ -81,7 +76,6 @@ interface ScenarioConfigurations {
8176
/**
8277
* Fixes resource permissions for the given scenario configuration.
8378
*/
84-
#suppress "@azure-tools/typespec-azure-resource-manager/arm-post-operation-response-codes" "LRO POST returns 202 with Location header for polling; final result obtained by polling the Location URL"
8579
fixResourcePermissions is ArmResourceActionNoContentAsyncWithLocationResult<
8680
ScenarioConfiguration,
8781
FixResourcePermissionsRequest,

specification/chaos/resource-manager/Microsoft.Chaos/Chaos/scenarioRun.models.tsp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,41 @@ import "@azure-tools/typespec-azure-resource-manager";
55
import "./scenario.models.tsp";
66

77
using TypeSpec.Rest;
8+
using TypeSpec.Http;
89
using Azure.ResourceManager;
910
using Azure.Core;
1011
using Azure.Core.Traits;
1112

1213
namespace Microsoft.Chaos;
1314

15+
/**
16+
* Indicates the scenario run is still in progress. Clients polling via the
17+
* Location header should continue polling until a 200 response is returned.
18+
* The body reflects the current run state at the time of the poll.
19+
*/
20+
model ScenarioRunInProgressResponse {
21+
...AcceptedResponse;
22+
23+
/**
24+
* The URL where the status of the long-running operation can be polled.
25+
*/
26+
@header("Location")
27+
location: url;
28+
29+
/**
30+
* The number of seconds the client should wait before polling again.
31+
*/
32+
@minValue(0)
33+
@maxValue(2147483647)
34+
@header("Retry-After")
35+
retryAfter?: int32;
36+
37+
/**
38+
* The current scenario run state at the time of the poll.
39+
*/
40+
@bodyRoot body: ScenarioRun;
41+
}
42+
1443
/**
1544
* Model that represents the scenario run.
1645
*/

specification/chaos/resource-manager/Microsoft.Chaos/Chaos/scenarioRun.tsp

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import "@azure-tools/typespec-azure-core";
22
import "@azure-tools/typespec-azure-resource-manager";
33
import "@typespec/rest";
44
import "@typespec/http";
5-
import "@typespec/openapi";
65
import "./scenarioRun.models.tsp";
76
import "./lro-helpers.tsp";
87

@@ -11,37 +10,28 @@ using Azure.Core;
1110
using TypeSpec.Versioning;
1211
using TypeSpec.Http;
1312
using TypeSpec.Rest;
14-
using TypeSpec.OpenAPI;
1513

1614
namespace Microsoft.Chaos;
1715

1816
@added(Microsoft.Chaos.Versions.v2026_05_01_preview)
17+
@removed(Microsoft.Chaos.Versions.v2025_01_01)
1918
@armResourceOperations
2019
interface ScenarioRuns {
2120
/**
2221
* Get a scenario run.
22+
*
23+
* This endpoint is also the polling target for ScenarioConfigurations.execute
24+
* and ScenarioRuns.cancel (final-state-via: location). While the run is in
25+
* progress the service returns 202 with a Location header pointing back to
26+
* this URL; clients must keep polling until they receive 200, which carries
27+
* the final ScenarioRun body.
2328
*/
24-
#suppress "@azure-tools/typespec-azure-resource-manager/arm-resource-operation" "LRO polling endpoint that returns body in both 200 and 202"
25-
#suppress "@azure-tools/typespec-azure-resource-manager/no-response-body" "LRO polling endpoint returns body in 202 to reflect current status"
26-
#suppress "@azure-tools/typespec-azure-core/polling-operation-no-status-monitor" "LRO uses final-state-via:location pattern, not status monitor"
27-
@summary("Get a scenario run.")
28-
@autoRoute
29-
@armResourceRead(ScenarioRun)
30-
@get
31-
get(...ResourceInstanceParameters<ScenarioRun>):
32-
| ArmResponse<ScenarioRun>
33-
| (AcceptedResponse & {
34-
@doc("The Location header contains the URL where the status of the long running operation can be checked.")
35-
@header("Location")
36-
location: string;
37-
38-
@doc("The Retry-After header can indicate how long the client should wait before polling the operation status.")
39-
@header("Retry-After")
40-
retryAfter?: int32;
41-
42-
@bodyRoot body: ScenarioRun;
43-
})
44-
| ErrorResponse;
29+
#suppress "@azure-tools/typespec-azure-resource-manager/arm-resource-operation" "LRO polling endpoint that returns body in both 200 and 202 responses."
30+
#suppress "@azure-tools/typespec-azure-resource-manager/no-response-body" "LRO polling endpoint returns body in 202 to reflect the current run state."
31+
get is ArmResourceRead<
32+
ScenarioRun,
33+
Response = ArmResponse<ScenarioRun> | ScenarioRunInProgressResponse
34+
>;
4535

4636
/**
4737
* Get a list of scenario runs.
@@ -54,10 +44,7 @@ interface ScenarioRuns {
5444
/**
5545
* Cancel the currently running scenario execution.
5646
*/
57-
#suppress "@azure-tools/typespec-azure-resource-manager/arm-post-operation-response-codes" "LRO POST returns 202 with Location header for polling; final result obtained by polling the Location URL"
58-
#suppress "@azure-tools/typespec-azure-core/polling-operation-no-status-monitor" "LRO uses final-state-via:location pattern"
5947
@added(Microsoft.Chaos.Versions.v2026_05_01_preview)
60-
@pollingOperation(ScenarioRuns.get)
6148
cancel is ArmResourceActionNoContentAsyncWithLocationResult<
6249
ScenarioRun,
6350
void,

specification/chaos/resource-manager/Microsoft.Chaos/Chaos/workspace.tsp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ interface Workspaces {
7171
/**
7272
* Refreshes recommendation status for all scenarios in a given workspace.
7373
*/
74-
#suppress "@azure-tools/typespec-azure-resource-manager/arm-post-operation-response-codes" "Async LRO POST returning 202 + Location, not a synchronous POST"
7574
refreshRecommendations is ArmResourceActionNoContentAsyncWithLocationResult<
7675
Workspace,
7776
void,

specification/chaos/resource-manager/Microsoft.Chaos/Chaos/workspaceEvaluation.models.tsp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ model WorkspaceEvaluationProperties {
6060
* The workspace ID this evaluation belongs to.
6161
*/
6262
@visibility(Lifecycle.Read)
63-
workspaceId: string;
63+
workspaceId: Azure.Core.armResourceIdentifier;
6464

6565
/**
6666
* The number of scenarios to evaluate.

0 commit comments

Comments
 (0)