Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix duplicate response headers found in appcompliance with tsp #2550

Merged
merged 6 commits into from
Jun 3, 2024
Merged
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
31 changes: 29 additions & 2 deletions common/config/rush/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/typespec-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@
"@typespec/ts-http-runtime": "1.0.0-alpha.20240314.2",
"@azure-tools/typespec-azure-core": ">=0.42.0 <1.0.0",
"@azure-tools/typespec-client-generator-core": ">=0.42.2 <1.0.0",
"@azure-tools/typespec-azure-resource-manager": ">=0.42.1 <1.0.0",
"@azure-tools/typespec-autorest": ">=0.42.1 <1.0.0",
"@typespec/openapi": ">=0.56.0, <1.0.0",
"@typespec/compiler": ">=0.56.0 <1.0.0",
"@typespec/http": ">=0.56.0 <1.0.0",
"@typespec/rest": ">=0.56.0 <1.0.0",
Expand Down
6 changes: 3 additions & 3 deletions packages/typespec-ts/src/transform/transformResponses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ function transformHeaders(
return;
}

const rlcHeaders = [];
const rlcHeaders: Map<string, ResponseHeaderSchema> = new Map();
// Current RLC client can't represent different headers per content type.
// So we merge headers here, and report any duplicates.
// It may be possible in principle to not error for identically declared
Expand Down Expand Up @@ -152,11 +152,11 @@ function transformHeaders(
required: !value?.optional,
description: getDoc(dpgContext.program, value!)
};
rlcHeaders.push(header);
rlcHeaders.set(header.name, header);
}
}

return rlcHeaders.length ? rlcHeaders : undefined;
return rlcHeaders.size ? Array.from(rlcHeaders.values()) : undefined;
}

function transformBody(
Expand Down
114 changes: 114 additions & 0 deletions packages/typespec-ts/test/unit/armTemplate.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { assert } from "chai";
import { emitResponsesFromTypeSpec } from "../util/emitUtil.js";
import { assertEqualContent } from "../util/testUtil.js";

describe("ARM template", () => {
it("Response with ArmProviderActionAsync", async () => {
const tspContent = `
import "@azure-tools/typespec-azure-resource-manager";
import "@typespec/versioning";

using Azure.ResourceManager;
using TypeSpec.Versioning;

@armProviderNamespace
@service({
title: "App Compliance Automation Tool for Microsoft 365",
})
@versioned(Versions)
@armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v3)
namespace Microsoft.AppComplianceAutomation;

/**
* The available API versions.
*/
enum Versions {
/**
* The 2024-06-27 API version.
*/
@useDependency(Azure.ResourceManager.Versions.v1_0_Preview_1)
@useDependency(Azure.Core.Versions.v1_0_Preview_1)
v2024_06_27: "2024-06-27",
}

alias PlaceholderResource = Azure.ResourceManager.Foundations.ProxyResource;
alias ArmProviderActionAsync<
Request extends {} | void,
Response extends {} | void,
Parameters extends {} = {},
LroHeaders extends TypeSpec.Reflection.Model = ArmLroLocationHeader &
Azure.Core.Foundations.RetryAfterHeader
> = Azure.ResourceManager.ArmResourceActionAsync<
PlaceholderResource,
Request,
Response | ArmAcceptedLroResponse<"Accepted", LroHeaders>,
BaseParameters = ApiVersionParameter,
Parameters = Parameters,
LroHeaders = ArmLroLocationHeader<Azure.Core.StatusMonitorPollingOptions<ArmOperationStatus>>
>;

@doc("Parameters for onboard operation")
model OnboardRequest {
/**
* List of subscription ids to be onboarded
*/
subscriptionIds: string[];
}

/**
* Onboard subscriptions response.
*/
@doc("Success. The response indicates given subscriptions has been onboarded.")
model OnboardResponse {
/**
* List of subscription ids that are onboarded
*/
subscriptionIds?: string[];
}

op upload is ArmProviderActionAsync<OnboardRequest, OnboardResponse>;
`;
const response = await emitResponsesFromTypeSpec(
tspContent,
false,
true,
true,
true
);
assert.ok(response);
await assertEqualContent(response!.content, `
import { RawHttpHeaders } from "@azure/core-rest-pipeline";
import { HttpResponse } from "@azure-rest/core-client";
import { OnboardResponseOutput, ErrorResponseOutput } from "./outputModels.js";

/** Azure operation completed successfully. */
export interface Upload200Response extends HttpResponse {
status: "200";
body: OnboardResponseOutput;
}

export interface Upload202Headers {
/** The Location header contains the URL where the status of the long running operation can be checked. */
"location"?: string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we don't have the fix, we will generate two location headers.

/** The Retry-After header can indicate how long the client should wait before polling the operation status. */
"retry-after"?: number;
}

/** Resource operation accepted. */
export interface Upload202Response extends HttpResponse {
status: "202";
headers: RawHttpHeaders & Upload202Headers;
}

export interface UploadDefaultResponse extends HttpResponse {
status: string;
body: ErrorResponseOutput;
}

/** The final response for long-running upload operation */
export interface UploadLogicalResponse extends HttpResponse {
status: "200";
body: OnboardResponseOutput;
}`);
});
});
8 changes: 6 additions & 2 deletions packages/typespec-ts/test/util/emitUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,14 +256,18 @@ export async function emitResponsesFromTypeSpec(
tspContent: string,
needAzureCore: boolean = false,
withRawContent: boolean = false,
needTCGC: boolean = false
needTCGC: boolean = false,
withVersionedApiVersion: boolean = false,
needArmTemplate: boolean = false
) {
const context = await rlcEmitterFor(
tspContent,
true,
needAzureCore,
needTCGC,
withRawContent
withRawContent,
withVersionedApiVersion,
needArmTemplate
);
const dpgContext = createDpgContextTestHelper(context.program);
const importSet = initInternalImports();
Expand Down
13 changes: 11 additions & 2 deletions packages/typespec-ts/test/util/testUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { HttpTestLibrary } from "@typespec/http/testing";
import { VersioningTestLibrary } from "@typespec/versioning/testing";
import { AzureCoreTestLibrary } from "@azure-tools/typespec-azure-core/testing";
import { SdkTestLibrary } from "@azure-tools/typespec-client-generator-core/testing";
import { OpenAPITestLibrary } from "@typespec/openapi/testing";
import { AutorestTestLibrary } from "@azure-tools/typespec-autorest/testing";
import { AzureResourceManagerTestLibrary } from "@azure-tools/typespec-azure-resource-manager/testing";
import { SdkContext } from "../../src/utils/interfaces.js";
import { assert } from "chai";
import { format } from "prettier";
Expand All @@ -19,7 +22,10 @@ export async function createRLCEmitterTestHost() {
RestTestLibrary,
VersioningTestLibrary,
AzureCoreTestLibrary,
SdkTestLibrary
SdkTestLibrary,
AzureResourceManagerTestLibrary,
OpenAPITestLibrary,
AutorestTestLibrary
]
});
}
Expand All @@ -30,7 +36,8 @@ export async function rlcEmitterFor(
needAzureCore: boolean = false,
needTCGC: boolean = false,
withRawContent: boolean = false,
withVersionedApiVersion: boolean = false
withVersionedApiVersion: boolean = false,
needArmTemplate: boolean = false
): Promise<TestHost> {
const host: TestHost = await createRLCEmitterTestHost();
const namespace = `
Expand All @@ -51,13 +58,15 @@ import "@typespec/rest";
import "@typespec/versioning";
${needTCGC ? 'import "@azure-tools/typespec-client-generator-core";' : ""}
${needAzureCore ? 'import "@azure-tools/typespec-azure-core";' : ""}
${needArmTemplate ? 'import "@azure-tools/typespec-azure-resource-manager";' : ""}

using TypeSpec.Rest;
using TypeSpec.Http;
using TypeSpec.Versioning;
${needTCGC ? "using Azure.ClientGenerator.Core;" : ""}
${needAzureCore ? "using Azure.Core;" : ""}
${needNamespaces ? namespace : ""}
${needArmTemplate ? 'using Azure.ResourceManager;' : ""}
${
withVersionedApiVersion && needNamespaces
? 'enum Versions { v2022_05_15_preview: "2022-05-15-preview"}'
Expand Down
Loading