Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .chronus/changes/tcgc-fixMarkAsLro-2025-9-23-16-23-7.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: fix
packages:
- "@azure-tools/typespec-client-generator-core"
---

Fix usage of `@markAsLro` for lro operations with nullable overall response types
7 changes: 7 additions & 0 deletions .chronus/changes/tcgc-fixMarkAsLro-2025-9-24-13-16-5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: internal
packages:
- "@azure-tools/azure-http-specs"
---

merge into release branch
17 changes: 15 additions & 2 deletions packages/typespec-client-generator-core/src/internal-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -917,11 +917,24 @@ export function getTcgcLroMetadata<TServiceOperation extends SdkServiceOperation
if (getMarkAsLro(context, operation)) {
// we guard against this in the setting of `@markAsLro`
const sdkMethod = ignoreDiagnostics(getSdkBasicServiceMethod(context, operation, client));
const returnType = sdkMethod.response.type!.__raw! as Model;
let returnType: Model;
const sdkMethodResponseType = sdkMethod.response.type!;
switch (sdkMethodResponseType.kind) {
case "nullable":
returnType = sdkMethodResponseType.type.__raw! as Model;
break;
case "model":
returnType = sdkMethodResponseType.__raw! as Model;
break;
default:
throw new Error(
`LRO method ${operation.name} with @markAsLro must have a model return type.`,
);
}
return {
operation,
logicalResult: returnType,
finalStateVia: FinalStateValue.originalUri, // doesn't really matter, but for get LRO
finalStateVia: FinalStateValue.location,
pollingInfo: {
kind: "pollingOperationStep",
responseModel: returnType,
Expand Down
8 changes: 5 additions & 3 deletions packages/typespec-client-generator-core/src/methods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,9 +389,11 @@ function getServiceMethodLroMetadata<TServiceOperation extends SdkServiceOperati
finalResponse: getFinalResponse(),
finalStep: getSdkLroServiceFinalStep(context, rawMetadata.finalStep),
pollingStep: {
responseBody: diagnostics.pipe(
getClientTypeWithDiagnostics(context, rawMetadata.pollingInfo.responseModel),
) as SdkModelType,
responseBody: rawMetadata.pollingInfo.responseModel
? (diagnostics.pipe(
getClientTypeWithDiagnostics(context, rawMetadata.pollingInfo.responseModel),
) as SdkModelType)
: undefined,
},
operation: ignoreDiagnostics(getSdkBasicServiceMethod(context, rawMetadata.operation, client))
.operation,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,8 @@ it("should work with ArmResourceRead", async () => {
}
@Azure.ClientGenerator.Core.Legacy.markAsLro
op getProductionSiteDeploymentStatus is ArmResourceRead<
Employee
>;
Employee
>;
`);

const methods = armRunner.context.sdkPackage.clients[0].methods;
Expand All @@ -201,3 +201,40 @@ it("should work with ArmResourceRead", async () => {
strictEqual(metadata.finalResponse?.result?.name, "Employee");
ok(!metadata.finalResponse?.resultSegments);
});

it("Extension.Read", async () => {
const armRunner = await createSdkTestRunner({
librariesToAdd: [AzureResourceManagerTestLibrary, AzureCoreTestLibrary, OpenAPITestLibrary],
autoUsings: ["Azure.ResourceManager", "Azure.Core"],
emitterName: "@azure-tools/typespec-python",
});
await armRunner.compileWithBuiltInAzureResourceManagerService(`
/** A ContosoProviderHub resource */
model Employee is TrackedResource<{}> {
...ResourceNameParameter<Employee>;
}
@Azure.ClientGenerator.Core.Legacy.markAsLro
op get is Extension.Read<
Extension.ScopeParameter,
Employee,
Response = ArmResponse<Employee> | ArmAcceptedResponse,
Error = ErrorResponse
>;
`);

const methods = armRunner.context.sdkPackage.clients[0].methods;
strictEqual(methods.length, 1);
const method = methods[0];
strictEqual(method.kind, "lro");
strictEqual(method.name, "get");

const metadata = method.lroMetadata;
ok(metadata);
strictEqual(metadata.finalStateVia, FinalStateValue.originalUri);
strictEqual(method.response.type?.kind, "model");
strictEqual(method.response.type?.name, "Employee");
strictEqual(metadata.envelopeResult?.name, "Employee");
strictEqual(metadata.finalResponse?.envelopeResult?.name, "Employee");
strictEqual(metadata.finalResponse?.result?.name, "Employee");
ok(!metadata.finalResponse?.resultSegments);
});
Loading