diff --git a/.chronus/changes/remove-lro-workaround-2024-10-19-9-4-1.md b/.chronus/changes/remove-lro-workaround-2024-10-19-9-4-1.md deleted file mode 100644 index 01b4ee5937..0000000000 --- a/.chronus/changes/remove-lro-workaround-2024-10-19-9-4-1.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking -changeKind: feature -packages: - - "@azure-tools/typespec-client-generator-core" ---- - -1. Introduce new usage: `LroInitial`, `LroPolling`, `LroFinalEnvelope`. -2. usage and access now properly propagate on polling model, final result and final envelop result of `lroMetadata`. diff --git a/packages/typespec-client-generator-core/CHANGELOG.md b/packages/typespec-client-generator-core/CHANGELOG.md index 99f0d1dabd..36e6f3b764 100644 --- a/packages/typespec-client-generator-core/CHANGELOG.md +++ b/packages/typespec-client-generator-core/CHANGELOG.md @@ -1,5 +1,19 @@ # Change Log - @azure-tools/typespec-client-generator-core +## 0.48.6 + +### Bug Fixes + +- [#1966](https://github.com/Azure/typespec-azure/pull/1966) Allow for responses without bodies to be errors, depending on presence of `@error` decorator + + +## 0.48.5 + +### Breaking Changes + +- [#1957](https://github.com/Azure/typespec-azure/pull/1957) Introduce new usage: `LroInitial`, `LroPolling`, `LroFinalEnvelope`. Usage and access now properly propagate on polling model, final result and final envelop result of `lroMetadata`. + + ## 0.48.4 ### Bug Fixes diff --git a/packages/typespec-client-generator-core/package.json b/packages/typespec-client-generator-core/package.json index 8d887f8337..0f4cae9e12 100644 --- a/packages/typespec-client-generator-core/package.json +++ b/packages/typespec-client-generator-core/package.json @@ -1,6 +1,6 @@ { "name": "@azure-tools/typespec-client-generator-core", - "version": "0.48.4", + "version": "0.48.6", "author": "Microsoft Corporation", "description": "TypeSpec Data Plane Generation library", "homepage": "https://azure.github.io/typespec-azure", diff --git a/packages/typespec-client-generator-core/src/http.ts b/packages/typespec-client-generator-core/src/http.ts index 5685d041bb..3f0465d34b 100644 --- a/packages/typespec-client-generator-core/src/http.ts +++ b/packages/typespec-client-generator-core/src/http.ts @@ -433,7 +433,6 @@ function getSdkHttpResponseAndExceptions( let body: Type | undefined; let type: SdkType | undefined; let contentTypes: string[] = []; - for (const innerResponse of response.responses) { const defaultContentType = innerResponse.body?.contentTypes.includes("application/json") ? "application/json" @@ -494,7 +493,12 @@ function getSdkHttpResponseAndExceptions( ), description: response.description, }; - if (response.statusCodes === "*" || (body && isErrorModel(context.program, body))) { + + if ( + response.statusCodes === "*" || + isErrorModel(context.program, response.type) || + (body && isErrorModel(context.program, body)) + ) { exceptions.push({ ...sdkResponse, kind: "http", diff --git a/packages/typespec-client-generator-core/test/methods/lro.test.ts b/packages/typespec-client-generator-core/test/methods/lro.test.ts index af9bf3c002..d28d4bf3fb 100644 --- a/packages/typespec-client-generator-core/test/methods/lro.test.ts +++ b/packages/typespec-client-generator-core/test/methods/lro.test.ts @@ -546,6 +546,146 @@ describe("typespec-client-generator-core: long running operation metadata", () = "polling model should not be input", ); }); + + it("LRO final envelope result correctly marked when only used in ignored polling operation", async () => { + const runnerWithCore = await createSdkTestRunner({ + librariesToAdd: [AzureCoreTestLibrary], + autoUsings: ["Azure.Core", "Azure.Core.Traits"], + emitterName: "@azure-tools/typespec-java", + }); + await runnerWithCore.compileWithCustomization( + ` + @useDependency(Versions.v1_0_Preview_2) + @server("http://localhost:3000", "endpoint") + @service() + namespace DocumentIntelligence; + @lroStatus + @doc("Operation status.") + union DocumentIntelligenceOperationStatus { + string, + @doc("The operation has not started yet.") + notStarted: "notStarted", + @doc("The operation is in progress.") + running: "running", + @doc("The operation has failed.") + @lroFailed + failed: "failed", + @doc("The operation has succeeded.") + @lroSucceeded + succeeded: "succeeded", + @doc("The operation has been canceled.") + @lroCanceled + canceled: "canceled", + @doc("The operation has been skipped.") + @lroCanceled + skipped: "skipped", + } + #suppress "@azure-tools/typespec-azure-core/long-running-polling-operation-required" "This is a template" + op DocumentIntelligenceLongRunningOperation< + TParams extends TypeSpec.Reflection.Model, + TResponse extends TypeSpec.Reflection.Model + > is Foundations.Operation< + { + ...TParams, + @doc("Unique document model name.") + @path + @pattern("^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$") + @maxLength(64) + modelId: string; + }, + AcceptedResponse & + Foundations.RetryAfterHeader & { + @pollingLocation + @header("Operation-Location") + operationLocation: ResourceLocation; + }, + {}, + {} + >; + op DocumentIntelligenceOperation< + TParams extends TypeSpec.Reflection.Model, + TResponse extends TypeSpec.Reflection.Model & Foundations.RetryAfterHeader + > is Foundations.Operation< + TParams, + TResponse, + {}, + {} + >; + @doc("Document analysis result.") + model AnalyzeResult { + @doc("API version used to produce this result.") + apiVersion: string; + @doc("Document model ID used to produce this result.") + @pattern("^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$") + modelId: string; + } + @doc("Status and result of the analyze operation.") + model AnalyzeOperation { + @doc("Operation status. notStarted, running, succeeded, or failed") + status: DocumentIntelligenceOperationStatus; + @doc("Date and time (UTC) when the analyze operation was submitted.") + createdDateTime: utcDateTime; + @doc("Date and time (UTC) when the status was last updated.") + lastUpdatedDateTime: utcDateTime; + @doc("Encountered error during document analysis.") + error?: {}; + @lroResult + @doc("Document analysis result.") + analyzeResult?: AnalyzeResult; + } + #suppress "@azure-tools/typespec-azure-core/use-standard-operations" "Doesn't fit standard ops" + @doc("Analyzes document with document model.") + @post + @pollingOperation(getAnalyzeResult) + @sharedRoute + @route("/documentModels/{modelId}:analyze") + op analyzeDocument is DocumentIntelligenceLongRunningOperation< + { + @doc("Input content type.") + @header + contentType: "application/json"; + @doc("Analyze request parameters.") + @bodyRoot + @clientName("body", "python") + analyzeRequest?: {}; + }, + AnalyzeOperation + >; + #suppress "@azure-tools/typespec-azure-core/use-standard-operations" "Doesn't fit standard ops" + @doc("Gets the result of document analysis.") + @route("/documentModels/{modelId}/analyzeResults/{resultId}") + @get + op getAnalyzeResult is DocumentIntelligenceOperation< + { + @doc("Unique document model name.") + @path + @pattern("^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$") + @maxLength(64) + modelId: string; + @doc("Analyze operation result ID.") + @path + resultId: uuid; + }, + AnalyzeOperation + >; + `, + ` + namespace ClientCustomizations; + @client({ + name: "DocumentIntelligenceClient", + service: DocumentIntelligence, + }) + interface DocumentIntelligenceClient { + analyzeDocument is DocumentIntelligence.analyzeDocument; + } + `, + ); + const models = runnerWithCore.context.sdkPackage.models; + strictEqual(models.length, 4); + const analyzeOperationModel = models.find((m) => m.name === "AnalyzeOperation"); + ok(analyzeOperationModel); + strictEqual(analyzeOperationModel.usage, UsageFlags.LroFinalEnvelope | UsageFlags.LroPolling); + }); }); describe("Arm LRO templates", () => { diff --git a/packages/typespec-client-generator-core/test/packages/responses.test.ts b/packages/typespec-client-generator-core/test/packages/responses.test.ts index a74c4ced6b..d109e7f86c 100644 --- a/packages/typespec-client-generator-core/test/packages/responses.test.ts +++ b/packages/typespec-client-generator-core/test/packages/responses.test.ts @@ -82,6 +82,26 @@ describe("typespec-client-generator-core: responses", () => { strictEqual(method.response.resultPath, undefined); }); + it("basic returning compiler NotFoundResponse error", async () => { + await runner.compileWithBuiltInService( + ` + @error + model NotFoundErrorResponse is NotFoundResponse; + @get op get(): void | NotFoundErrorResponse; + `, + ); + const sdkPackage = runner.context.sdkPackage; + const client = sdkPackage.clients[0]; + const getMethod = client.methods[0]; + strictEqual(getMethod.kind, "basic"); + const operation = getMethod.operation; + strictEqual(operation.responses.length, 1); + strictEqual(operation.responses[0].statusCodes, 204); + strictEqual(operation.exceptions.length, 1); + const exception = operation.exceptions[0]; + strictEqual(exception.statusCodes, 404); + }); + it("basic returning model", async () => { await runner.compileWithBuiltInService( `