Skip to content

Commit 9f296cf

Browse files
MaryGaoCopilotxirzec
authored
Add the cloudSetting option for ARM Modular SDK (#3233)
* Add the cloud setting option for ARM modular SDK * Update the idea * Build the cloud setting enum in model.ts * Update the comments * Fix the generation issues * Refactor code a little to remove duplications * Update the integration testings * Fix the thrown exception messages * Update the formats * Directly use tcgc arm option * Fix the UTs * Update packages/typespec-ts/src/modular/buildClientContext.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Regen the smoke with copilot comments * Regen with smoke testing * Move the helper into our static helper folder * Remove the baseurl * Update the un-necessary codes * Update the un-necessary codes * Fix the UTs * Fix the UTs * Update packages/typespec-ts/src/modular/buildRootIndex.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update the comments * Update the copilot suggestion * Update the git diff * Regen modular integration test * Update packages/typespec-test/test/NetworkAnalytics.Management/generated/typespec-ts/sdk/test/arm-test/src/static-helpers/cloudSettingHelpers.ts Co-authored-by: Jeff Fisher <xirzec@xirzec.com> * Update packages/typespec-test/test/NetworkAnalytics.Management/generated/typespec-ts/sdk/test/arm-test/src/static-helpers/cloudSettingHelpers.ts Co-authored-by: Jeff Fisher <xirzec@xirzec.com> * Refresh the smoke test and integration test --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Jeff Fisher <xirzec@xirzec.com>
1 parent de4d4de commit 9f296cf

17 files changed

Lines changed: 249 additions & 42 deletions

File tree

packages/typespec-test/test/NetworkAnalytics.Management/generated/typespec-ts/sdk/test/arm-test/review/arm-networkanalytics.api.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@ export interface AccountSasToken {
2828
// @public
2929
export type ActionType = string;
3030

31+
// @public
32+
export enum AzureClouds {
33+
AZURE_CHINA_CLOUD = "AZURE_CHINA_CLOUD",
34+
AZURE_PUBLIC_CLOUD = "AZURE_PUBLIC_CLOUD",
35+
AZURE_US_GOVERNMENT = "AZURE_US_GOVERNMENT"
36+
}
37+
38+
// @public
39+
export type AzureSupportedClouds = `${AzureClouds}`;
40+
3141
// @public
3242
export interface ConsumptionEndpointsProperties {
3343
readonly fileAccessResourceId?: string;
@@ -442,6 +452,7 @@ export class NetworkAnalyticsApi {
442452
// @public
443453
export interface NetworkAnalyticsApiOptionalParams extends ClientOptions {
444454
apiVersion?: string;
455+
cloudSetting?: AzureSupportedClouds;
445456
}
446457

447458
// @public

packages/typespec-test/test/NetworkAnalytics.Management/generated/typespec-ts/sdk/test/arm-test/src/api/networkAnalyticsApiContext.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33

44
import { logger } from "../logger.js";
55
import { KnownVersions } from "../models/models.js";
6+
import {
7+
AzureSupportedClouds,
8+
getArmEndpoint,
9+
} from "../static-helpers/cloudSettingHelpers.js";
610
import { Client, ClientOptions, getClient } from "@azure-rest/core-client";
711
import { TokenCredential } from "@azure/core-auth";
812

@@ -19,14 +23,19 @@ export interface NetworkAnalyticsApiOptionalParams extends ClientOptions {
1923
/** The API version to use for this operation. */
2024
/** Known values of {@link KnownVersions} that the service accepts. */
2125
apiVersion?: string;
26+
/** Specifies the Azure cloud environment for the client. */
27+
cloudSetting?: AzureSupportedClouds;
2228
}
2329

2430
export function createNetworkAnalyticsApi(
2531
credential: TokenCredential,
2632
subscriptionId: string,
2733
options: NetworkAnalyticsApiOptionalParams = {},
2834
): NetworkAnalyticsApiContext {
29-
const endpointUrl = options.endpoint ?? "https://management.azure.com";
35+
const endpointUrl =
36+
options.endpoint ??
37+
getArmEndpoint(options.cloudSetting) ??
38+
"https://management.azure.com";
3039
const prefixFromOptions = options?.userAgentOptions?.userAgentPrefix;
3140
const userAgentInfo = `azsdk-js-arm-networkanalytics/1.0.0-beta.1`;
3241
const userAgentPrefix = prefixFromOptions

packages/typespec-test/test/NetworkAnalytics.Management/generated/typespec-ts/sdk/test/arm-test/src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
import {
5+
AzureClouds,
6+
AzureSupportedClouds,
7+
} from "./static-helpers/cloudSettingHelpers.js";
48
import {
59
PageSettings,
610
ContinuablePage,
@@ -104,3 +108,4 @@ export {
104108
OperationsOperations,
105109
} from "./classic/index.js";
106110
export { PageSettings, ContinuablePage, PagedAsyncIterableIterator };
111+
export { AzureClouds, AzureSupportedClouds };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
/** An enum to describe Azure Cloud. */
5+
export enum AzureClouds {
6+
/** Azure public cloud, which is the default cloud for Azure SDKs. */
7+
AZURE_PUBLIC_CLOUD = "AZURE_PUBLIC_CLOUD",
8+
/** Azure China cloud */
9+
AZURE_CHINA_CLOUD = "AZURE_CHINA_CLOUD",
10+
/** Azure US government cloud */
11+
AZURE_US_GOVERNMENT = "AZURE_US_GOVERNMENT",
12+
}
13+
14+
/** The supported values for cloud setting as a string literal type */
15+
export type AzureSupportedClouds = `${AzureClouds}`;
16+
17+
export function getArmEndpoint(
18+
cloudSetting?: AzureSupportedClouds,
19+
): string | undefined {
20+
if (cloudSetting === undefined) {
21+
return undefined;
22+
}
23+
const cloudEndpoints: Record<keyof typeof AzureClouds, string> = {
24+
AZURE_CHINA_CLOUD: "https://management.chinacloudapi.cn/",
25+
AZURE_US_GOVERNMENT: "https://management.usgovcloudapi.net/",
26+
AZURE_PUBLIC_CLOUD: "https://management.azure.com/",
27+
};
28+
if (cloudSetting in cloudEndpoints) {
29+
return cloudEndpoints[cloudSetting];
30+
} else {
31+
throw new Error(
32+
`Unknown cloud setting: ${cloudSetting}. Please refer to the enum AzureClouds for possible values.`,
33+
);
34+
}
35+
}

packages/typespec-ts/src/framework/load-static-helpers.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { readdir, stat, readFile } from "fs/promises";
22
import * as path from "path";
33
import {
44
ClassDeclaration,
5+
EnumDeclaration,
56
FunctionDeclaration,
67
InterfaceDeclaration,
78
Project,
@@ -15,7 +16,7 @@ import { ModularEmitterOptions } from "../modular/interfaces.js";
1516
export const SourceFileSymbol = Symbol("SourceFile");
1617
export interface StaticHelperMetadata {
1718
name: string;
18-
kind: "function" | "interface" | "typeAlias" | "class";
19+
kind: "function" | "interface" | "typeAlias" | "class" | "enum";
1920
location: string;
2021
[SourceFileSymbol]?: SourceFile;
2122
}
@@ -137,6 +138,7 @@ function getDeclarationByMetadata(
137138
| FunctionDeclaration
138139
| TypeAliasDeclaration
139140
| InterfaceDeclaration
141+
| EnumDeclaration
140142
| undefined {
141143
switch (declaration.kind) {
142144
case "class":
@@ -147,6 +149,8 @@ function getDeclarationByMetadata(
147149
return file.getInterface(declaration.name);
148150
case "typeAlias":
149151
return file.getTypeAlias(declaration.name);
152+
case "enum":
153+
return file.getEnum(declaration.name);
150154
default:
151155
throw new Error(
152156
`invalid helper kind ${declaration.kind}\nAll helpers provided to loadStaticHelpers are of kind: function, interface, typeAlias, class`

packages/typespec-ts/src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
import { EmitContext, Program } from "@typespec/compiler";
1212
import { GenerationDirDetail, SdkContext } from "./utils/interfaces.js";
1313
import {
14+
CloudSettingHelpers,
1415
MultipartHelpers,
1516
PagingHelpers,
1617
PollingHelpers,
@@ -130,7 +131,8 @@ export async function $onEmit(context: EmitContext) {
130131
...PagingHelpers,
131132
...PollingHelpers,
132133
...UrlTemplateHelpers,
133-
...MultipartHelpers
134+
...MultipartHelpers,
135+
...CloudSettingHelpers
134136
},
135137
{
136138
sourcesDir: dpgContext.generationPathDetail?.modularSourcesDir,

packages/typespec-ts/src/lib.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,12 @@ const libDef = {
356356
messages: {
357357
default: paramMessage`The property name ${"propertyName"} has conflicts with others and please use @clientName to rename it.`
358358
}
359+
},
360+
"parameter-name-conflict": {
361+
severity: "warning",
362+
messages: {
363+
default: paramMessage`The parameter name ${"parameterName"} has conflicts with others and please use @clientName to rename it.`
364+
}
359365
}
360366
},
361367
emitter: {

packages/typespec-ts/src/modular/buildClientContext.ts

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ import {
3333
import { getModularClientOptions } from "../utils/clientUtils.js";
3434
import { useContext } from "../contextManager.js";
3535
import { refkey } from "../framework/refkey.js";
36+
import { reportDiagnostic } from "../lib.js";
37+
import { NoTarget } from "@typespec/compiler";
38+
import { CloudSettingHelpers } from "./static-helpers-metadata.js";
3639

3740
/**
3841
* This function gets the path of the file containing the modular client context
@@ -100,25 +103,46 @@ export function buildClientContext(
100103
})
101104
});
102105

106+
const propertiesInOptions = getClientParameters(client, dpgContext, {
107+
optionalOnly: true
108+
})
109+
.filter((p) => p.name !== "endpoint")
110+
.map((p) => {
111+
return {
112+
name: getClientParameterName(p),
113+
type:
114+
p.name.toLowerCase() === "apiversion"
115+
? "string"
116+
: getTypeExpression(dpgContext, p.type),
117+
hasQuestionToken: true,
118+
docs: getDocsWithKnownVersion(dpgContext, p)
119+
};
120+
});
121+
if (dpgContext.arm) {
122+
propertiesInOptions.push({
123+
name: "cloudSetting",
124+
type: `${resolveReference(CloudSettingHelpers.AzureSupportedClouds)}`,
125+
hasQuestionToken: true,
126+
docs: [`Specifies the Azure cloud environment for the client.`]
127+
});
128+
}
129+
// check if we have duplication options
130+
const existingOptionNames = new Set<string>();
131+
for (const property of propertiesInOptions) {
132+
if (existingOptionNames.has(property.name)) {
133+
reportDiagnostic(dpgContext.program, {
134+
code: "parameter-name-conflict",
135+
format: { parameterName: property.name },
136+
target: NoTarget
137+
});
138+
}
139+
existingOptionNames.add(property.name);
140+
}
103141
clientContextFile.addInterface({
104142
name: `${getClassicalClientName(client)}OptionalParams`,
105143
isExported: true,
106144
extends: [resolveReference(dependencies.ClientOptions)],
107-
properties: getClientParameters(client, dpgContext, {
108-
optionalOnly: true
109-
})
110-
.filter((p) => p.name !== "endpoint")
111-
.map((p) => {
112-
return {
113-
name: getClientParameterName(p),
114-
type:
115-
p.name.toLowerCase() === "apiversion"
116-
? "string"
117-
: getTypeExpression(dpgContext, p.type),
118-
hasQuestionToken: true,
119-
docs: getDocsWithKnownVersion(dpgContext, p)
120-
};
121-
}),
145+
properties: propertiesInOptions,
122146
docs: ["Optional parameters for the client."]
123147
});
124148

packages/typespec-ts/src/modular/buildRootIndex.ts

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import { Project, SourceFile } from "ts-morph";
33
import { getClassicalClientName } from "./helpers/namingHelpers.js";
44
import { ModularEmitterOptions } from "./interfaces.js";
55
import { resolveReference } from "../framework/reference.js";
6-
import { MultipartHelpers, PagingHelpers } from "./static-helpers-metadata.js";
6+
import {
7+
CloudSettingHelpers,
8+
MultipartHelpers,
9+
PagingHelpers
10+
} from "./static-helpers-metadata.js";
711
import {
812
SdkClientType,
913
SdkContext,
@@ -68,6 +72,7 @@ export function buildRootIndex(
6872

6973
exportPagingTypes(context, rootIndexFile);
7074
exportFileContentsType(context, rootIndexFile);
75+
exportAzureCloudTypes(context, rootIndexFile);
7176
}
7277

7378
function exportModels(
@@ -91,6 +96,15 @@ function exportModels(
9196
}
9297
}
9398

99+
function exportAzureCloudTypes(context: SdkContext, rootIndexFile: SourceFile) {
100+
if (context.arm) {
101+
addExportsToRootIndexFile(rootIndexFile, [
102+
resolveReference(CloudSettingHelpers.AzureClouds),
103+
resolveReference(CloudSettingHelpers.AzureSupportedClouds)
104+
]);
105+
}
106+
}
107+
94108
/**
95109
* This is a temporary solution for adding paging exports. Eventually we will have the binder generate the exports automatically.
96110
*/
@@ -99,18 +113,11 @@ function exportPagingTypes(context: SdkContext, rootIndexFile: SourceFile) {
99113
return;
100114
}
101115

102-
const existingExports = getExistingExports(rootIndexFile);
103-
const namedExports = [
116+
addExportsToRootIndexFile(rootIndexFile, [
104117
resolveReference(PagingHelpers.PageSettings),
105118
resolveReference(PagingHelpers.ContinuablePage),
106119
resolveReference(PagingHelpers.PagedAsyncIterableIterator)
107-
];
108-
109-
const newNamedExports = getNewNamedExports(namedExports, existingExports);
110-
111-
if (newNamedExports.length > 0) {
112-
addExportsToRootIndexFile(rootIndexFile, newNamedExports);
113-
}
120+
]);
114121
}
115122

116123
function hasPaging(context: SdkContext): boolean {
@@ -139,14 +146,9 @@ function exportFileContentsType(
139146
)
140147
)
141148
) {
142-
const existingExports = getExistingExports(rootIndexFile);
143-
const namedExports = [resolveReference(MultipartHelpers.FileContents)];
144-
145-
const newNamedExports = getNewNamedExports(namedExports, existingExports);
146-
147-
if (newNamedExports.length > 0) {
148-
addExportsToRootIndexFile(rootIndexFile, newNamedExports);
149-
}
149+
addExportsToRootIndexFile(rootIndexFile, [
150+
resolveReference(MultipartHelpers.FileContents)
151+
]);
150152
}
151153
}
152154

@@ -171,11 +173,15 @@ function getNewNamedExports(
171173

172174
function addExportsToRootIndexFile(
173175
rootIndexFile: SourceFile,
174-
newNamedExports: string[]
176+
namedExports: string[]
175177
) {
176-
rootIndexFile.addExportDeclaration({
177-
namedExports: newNamedExports
178-
});
178+
const existingExports = getExistingExports(rootIndexFile);
179+
const newNamedExports = getNewNamedExports(namedExports, existingExports);
180+
if (newNamedExports.length > 0) {
181+
rootIndexFile.addExportDeclaration({
182+
namedExports: newNamedExports
183+
});
184+
}
179185
}
180186

181187
function exportRestoreHelpers(

packages/typespec-ts/src/modular/helpers/clientHelpers.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import { SdkContext } from "../../utils/interfaces.js";
2020
import { getClassicalClientName } from "./namingHelpers.js";
2121
import { getTypeExpression } from "../type-expressions/get-type-expression.js";
2222
import { isCredentialType } from "./typeHelpers.js";
23+
import { CloudSettingHelpers } from "../static-helpers-metadata.js";
24+
import { resolveReference } from "../../framework/reference.js";
2325

2426
interface ClientParameterOptions {
2527
onClientOnly?: boolean;
@@ -170,8 +172,16 @@ export function buildGetClientEndpointParam(
170172
dpgContext: SdkContext,
171173
client: SdkClientType<SdkServiceOperation>
172174
): string {
173-
const coreEndpointParam = `options.endpoint`;
174-
175+
let coreEndpointParam = "";
176+
if (dpgContext.rlcOptions?.flavor === "azure") {
177+
const cloudSettingSuffix = dpgContext.arm
178+
? ` ?? ${resolveReference(CloudSettingHelpers.getArmEndpoint)}(options.cloudSetting)`
179+
: "";
180+
coreEndpointParam = `options.endpoint${cloudSettingSuffix}`;
181+
} else {
182+
// unbranded does not have the deprecated baseUrl parameter
183+
coreEndpointParam = `options.endpoint`;
184+
}
175185
// Special case: endpoint URL not defined
176186
const endpointParam = getClientParameters(client, dpgContext, {
177187
onClientOnly: true,

0 commit comments

Comments
 (0)