@azure-tools/typespec-ts 0.49.0 → 0.49.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/dist/src/framework/hooks/binder.d.ts.map +1 -1
- package/dist/src/framework/hooks/binder.js +8 -17
- package/dist/src/framework/hooks/binder.js.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +13 -11
- package/dist/src/index.js.map +1 -1
- package/dist/src/lib.d.ts +8 -0
- package/dist/src/lib.d.ts.map +1 -1
- package/dist/src/lib.js +10 -0
- package/dist/src/lib.js.map +1 -1
- package/dist/src/modular/buildOperations.d.ts.map +1 -1
- package/dist/src/modular/buildOperations.js +10 -5
- package/dist/src/modular/buildOperations.js.map +1 -1
- package/dist/src/modular/buildProjectFiles.d.ts.map +1 -1
- package/dist/src/modular/buildProjectFiles.js +13 -10
- package/dist/src/modular/buildProjectFiles.js.map +1 -1
- package/dist/src/modular/emitModels.d.ts.map +1 -1
- package/dist/src/modular/emitModels.js +43 -40
- package/dist/src/modular/emitModels.js.map +1 -1
- package/dist/src/modular/emitSamples.d.ts.map +1 -1
- package/dist/src/modular/emitSamples.js +9 -0
- package/dist/src/modular/emitSamples.js.map +1 -1
- package/dist/src/modular/helpers/clientHelpers.d.ts.map +1 -1
- package/dist/src/modular/helpers/clientHelpers.js +4 -1
- package/dist/src/modular/helpers/clientHelpers.js.map +1 -1
- package/dist/src/modular/helpers/namingHelpers.d.ts +7 -0
- package/dist/src/modular/helpers/namingHelpers.d.ts.map +1 -1
- package/dist/src/modular/helpers/namingHelpers.js +15 -0
- package/dist/src/modular/helpers/namingHelpers.js.map +1 -1
- package/dist/src/modular/helpers/operationHelpers.d.ts +19 -2
- package/dist/src/modular/helpers/operationHelpers.d.ts.map +1 -1
- package/dist/src/modular/helpers/operationHelpers.js +410 -39
- package/dist/src/modular/helpers/operationHelpers.js.map +1 -1
- package/dist/src/modular/serialization/buildDeserializerFunction.d.ts.map +1 -1
- package/dist/src/modular/serialization/buildDeserializerFunction.js +9 -3
- package/dist/src/modular/serialization/buildDeserializerFunction.js.map +1 -1
- package/dist/src/modular/serialization/buildXmlSerializerFunction.d.ts +12 -0
- package/dist/src/modular/serialization/buildXmlSerializerFunction.d.ts.map +1 -1
- package/dist/src/modular/serialization/buildXmlSerializerFunction.js +259 -18
- package/dist/src/modular/serialization/buildXmlSerializerFunction.js.map +1 -1
- package/dist/src/modular/static-helpers-metadata.d.ts +10 -0
- package/dist/src/modular/static-helpers-metadata.d.ts.map +1 -1
- package/dist/src/modular/static-helpers-metadata.js +10 -0
- package/dist/src/modular/static-helpers-metadata.js.map +1 -1
- package/dist/src/modular/type-expressions/get-model-expression.d.ts +3 -1
- package/dist/src/modular/type-expressions/get-model-expression.d.ts.map +1 -1
- package/dist/src/modular/type-expressions/get-model-expression.js +50 -9
- package/dist/src/modular/type-expressions/get-model-expression.js.map +1 -1
- package/dist/src/modular/type-expressions/get-nullable-expression.d.ts.map +1 -1
- package/dist/src/modular/type-expressions/get-nullable-expression.js +8 -0
- package/dist/src/modular/type-expressions/get-nullable-expression.js.map +1 -1
- package/dist/src/modular/type-expressions/get-type-expression.d.ts +3 -2
- package/dist/src/modular/type-expressions/get-type-expression.d.ts.map +1 -1
- package/dist/src/modular/type-expressions/get-type-expression.js.map +1 -1
- package/dist/src/transform/transform.d.ts.map +1 -1
- package/dist/src/transform/transform.js +10 -10
- package/dist/src/transform/transform.js.map +1 -1
- package/dist/src/transform/transformSchemas.d.ts.map +1 -1
- package/dist/src/transform/transformSchemas.js +4 -4
- package/dist/src/transform/transformSchemas.js.map +1 -1
- package/dist/src/transform/transfromRLCOptions.d.ts +1 -1
- package/dist/src/transform/transfromRLCOptions.d.ts.map +1 -1
- package/dist/src/transform/transfromRLCOptions.js +37 -24
- package/dist/src/transform/transfromRLCOptions.js.map +1 -1
- package/dist/src/utils/clientUtils.d.ts +1 -1
- package/dist/src/utils/clientUtils.d.ts.map +1 -1
- package/dist/src/utils/clientUtils.js +40 -17
- package/dist/src/utils/clientUtils.js.map +1 -1
- package/dist/src/utils/crossLanguageDef.d.ts.map +1 -1
- package/dist/src/utils/crossLanguageDef.js +9 -3
- package/dist/src/utils/crossLanguageDef.js.map +1 -1
- package/dist/src/utils/interfaces.d.ts +2 -1
- package/dist/src/utils/interfaces.d.ts.map +1 -1
- package/dist/src/utils/modelUtils.d.ts +2 -2
- package/dist/src/utils/modelUtils.d.ts.map +1 -1
- package/dist/src/utils/modelUtils.js +15 -16
- package/dist/src/utils/modelUtils.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +22 -22
- package/src/framework/hooks/binder.ts +12 -22
- package/src/index.ts +17 -10
- package/src/lib.ts +20 -0
- package/src/modular/buildOperations.ts +12 -4
- package/src/modular/buildProjectFiles.ts +19 -16
- package/src/modular/emitModels.ts +72 -44
- package/src/modular/emitSamples.ts +11 -1
- package/src/modular/helpers/clientHelpers.ts +5 -1
- package/src/modular/helpers/namingHelpers.ts +19 -0
- package/src/modular/helpers/operationHelpers.ts +533 -40
- package/src/modular/serialization/buildDeserializerFunction.ts +11 -2
- package/src/modular/serialization/buildXmlSerializerFunction.ts +375 -24
- package/src/modular/static-helpers-metadata.ts +10 -0
- package/src/modular/type-expressions/get-model-expression.ts +78 -13
- package/src/modular/type-expressions/get-nullable-expression.ts +9 -0
- package/src/modular/type-expressions/get-type-expression.ts +3 -1
- package/src/transform/transform.ts +5 -2
- package/src/transform/transformSchemas.ts +4 -1
- package/src/transform/transfromRLCOptions.ts +65 -24
- package/src/utils/clientUtils.ts +49 -16
- package/src/utils/crossLanguageDef.ts +8 -0
- package/src/utils/interfaces.ts +2 -1
- package/src/utils/modelUtils.ts +22 -18
- package/static/static-helpers/serialization/serialize-record.ts +3 -3
- package/static/static-helpers/serialization/xml-helpers.ts +91 -36
- package/static/static-helpers/urlTemplate.ts +5 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@azure-tools/typespec-ts",
|
|
3
|
-
"version": "0.49.
|
|
3
|
+
"version": "0.49.1",
|
|
4
4
|
"description": "An experimental TypeSpec emitter for TypeScript RLC",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -18,15 +18,15 @@
|
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@azure-rest/core-client": "^2.3.1",
|
|
21
|
-
"@typespec/http-specs": "0.1.0-alpha.
|
|
22
|
-
"@typespec/spector": "0.1.0-alpha.
|
|
21
|
+
"@typespec/http-specs": "0.1.0-alpha.33-dev.0",
|
|
22
|
+
"@typespec/spector": "0.1.0-alpha.24-dev.0",
|
|
23
23
|
"@typespec/spec-api": "0.1.0-alpha.13-dev.0",
|
|
24
|
-
"@typespec/tspd": "0.
|
|
25
|
-
"@azure-tools/azure-http-specs": "0.1.0-alpha.
|
|
26
|
-
"@azure-tools/typespec-autorest": "^0.
|
|
27
|
-
"@azure-tools/typespec-azure-core": "^0.
|
|
28
|
-
"@azure-tools/typespec-azure-resource-manager": "^0.
|
|
29
|
-
"@azure-tools/typespec-client-generator-core": "^0.
|
|
24
|
+
"@typespec/tspd": "0.74.0",
|
|
25
|
+
"@azure-tools/azure-http-specs": "0.1.0-alpha.38-dev.2",
|
|
26
|
+
"@azure-tools/typespec-autorest": "^0.65.0",
|
|
27
|
+
"@azure-tools/typespec-azure-core": "^0.65.0",
|
|
28
|
+
"@azure-tools/typespec-azure-resource-manager": "^0.65.0",
|
|
29
|
+
"@azure-tools/typespec-client-generator-core": "^0.65.1",
|
|
30
30
|
"@azure/abort-controller": "^2.1.2",
|
|
31
31
|
"@azure/core-auth": "^1.6.0",
|
|
32
32
|
"@azure/core-lro": "^3.1.0",
|
|
@@ -40,12 +40,12 @@
|
|
|
40
40
|
"@types/mocha": "^10.0.6",
|
|
41
41
|
"@typescript-eslint/eslint-plugin": "^8.28.0",
|
|
42
42
|
"@typescript-eslint/parser": "^8.28.0",
|
|
43
|
-
"@typespec/compiler": "^1.
|
|
44
|
-
"@typespec/http": "^1.
|
|
45
|
-
"@typespec/openapi": "^1.
|
|
46
|
-
"@typespec/rest": "^0.
|
|
43
|
+
"@typespec/compiler": "^1.9.0",
|
|
44
|
+
"@typespec/http": "^1.9.0",
|
|
45
|
+
"@typespec/openapi": "^1.9.0",
|
|
46
|
+
"@typespec/rest": "^0.79.0",
|
|
47
47
|
"@typespec/ts-http-runtime": "^0.1.0",
|
|
48
|
-
"@typespec/versioning": "^0.
|
|
48
|
+
"@typespec/versioning": "^0.79.0",
|
|
49
49
|
"chai": "^4.3.6",
|
|
50
50
|
"chalk": "^4.0.0",
|
|
51
51
|
"cross-env": "^7.0.3",
|
|
@@ -64,16 +64,16 @@
|
|
|
64
64
|
"js-yaml": "^4.1.0"
|
|
65
65
|
},
|
|
66
66
|
"peerDependencies": {
|
|
67
|
-
"@azure-tools/typespec-azure-core": "^0.
|
|
68
|
-
"@azure-tools/typespec-client-generator-core": "^0.
|
|
69
|
-
"@typespec/compiler": "^1.
|
|
70
|
-
"@typespec/http": "^1.
|
|
71
|
-
"@typespec/rest": "^0.
|
|
72
|
-
"@typespec/versioning": "^0.
|
|
73
|
-
"@typespec/xml": "^0.
|
|
67
|
+
"@azure-tools/typespec-azure-core": "^0.65.0",
|
|
68
|
+
"@azure-tools/typespec-client-generator-core": "^0.65.1",
|
|
69
|
+
"@typespec/compiler": "^1.9.0",
|
|
70
|
+
"@typespec/http": "^1.9.0",
|
|
71
|
+
"@typespec/rest": "^0.79.0",
|
|
72
|
+
"@typespec/versioning": "^0.79.0",
|
|
73
|
+
"@typespec/xml": "^0.79.0"
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
|
-
"@azure-tools/rlc-common": "^0.49.
|
|
76
|
+
"@azure-tools/rlc-common": "^0.49.1",
|
|
77
77
|
"fast-xml-parser": "^4.5.0",
|
|
78
78
|
"fs-extra": "^11.1.0",
|
|
79
79
|
"lodash": "^4.17.21",
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from "../load-static-helpers.js";
|
|
16
16
|
import path from "path/posix";
|
|
17
17
|
import { normalizePath } from "@typespec/compiler";
|
|
18
|
+
import { generateLocallyUniqueName } from "../../modular/helpers/namingHelpers.js";
|
|
18
19
|
|
|
19
20
|
export interface DeclarationInfo {
|
|
20
21
|
name: string;
|
|
@@ -96,7 +97,7 @@ class BinderImp implements Binder {
|
|
|
96
97
|
): string {
|
|
97
98
|
const existingNamesInFile =
|
|
98
99
|
this.symbolsBySourceFile.get(sourceFile) ?? new Set<string>();
|
|
99
|
-
return
|
|
100
|
+
return generateLocallyUniqueName(name, existingNamesInFile);
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
/**
|
|
@@ -113,33 +114,22 @@ class BinderImp implements Binder {
|
|
|
113
114
|
.flatMap((i) => i.namedImports as ImportSpecifierStructure[])
|
|
114
115
|
.map((i) => i.alias ?? i.name);
|
|
115
116
|
|
|
117
|
+
// Also check actual imports already added to the source file (e.g., manual imports)
|
|
118
|
+
const actualImports = sourceFile
|
|
119
|
+
.getImportDeclarations()
|
|
120
|
+
.flatMap((imp) => imp.getNamedImports())
|
|
121
|
+
.map(
|
|
122
|
+
(namedImp) => namedImp.getAliasNode()?.getText() ?? namedImp.getName()
|
|
123
|
+
);
|
|
124
|
+
|
|
116
125
|
const existingDeclarations =
|
|
117
126
|
this.symbolsBySourceFile.get(sourceFile) ?? new Set<string>();
|
|
118
|
-
return
|
|
127
|
+
return generateLocallyUniqueName(
|
|
119
128
|
name,
|
|
120
|
-
new Set([...existingImports, ...existingDeclarations])
|
|
129
|
+
new Set([...existingImports, ...actualImports, ...existingDeclarations])
|
|
121
130
|
);
|
|
122
131
|
}
|
|
123
132
|
|
|
124
|
-
/**
|
|
125
|
-
* Generates a locally unique name within a set of existing names.
|
|
126
|
-
* @param name - The base name.
|
|
127
|
-
* @param existingNames - A set of names already in use.
|
|
128
|
-
* @returns A unique name not present in the existing names set.
|
|
129
|
-
*/
|
|
130
|
-
private generateLocallyUniqueName(
|
|
131
|
-
name: string,
|
|
132
|
-
existingNames: Set<string>
|
|
133
|
-
): string {
|
|
134
|
-
let uniqueName = name;
|
|
135
|
-
let counter = 1;
|
|
136
|
-
while (existingNames.has(uniqueName)) {
|
|
137
|
-
uniqueName = `${name}_${counter}`;
|
|
138
|
-
counter++;
|
|
139
|
-
}
|
|
140
|
-
return uniqueName;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
133
|
/**
|
|
144
134
|
* Resolves a reference to a declaration.
|
|
145
135
|
* If a declaration is not ready yet, a placeholder is added to the source file.
|
package/src/index.ts
CHANGED
|
@@ -82,6 +82,7 @@ import { buildRestorePoller } from "./modular/buildRestorePoller.js";
|
|
|
82
82
|
import { buildSubpathIndexFile } from "./modular/buildSubpathIndex.js";
|
|
83
83
|
import {
|
|
84
84
|
createSdkContext,
|
|
85
|
+
listAllServiceNamespaces,
|
|
85
86
|
SdkClientType,
|
|
86
87
|
SdkServiceOperation
|
|
87
88
|
} from "@azure-tools/typespec-client-generator-core";
|
|
@@ -90,7 +91,11 @@ import { emitLoggerFile } from "./modular/emitLoggerFile.js";
|
|
|
90
91
|
import { emitTypes } from "./modular/emitModels.js";
|
|
91
92
|
import { existsSync } from "fs";
|
|
92
93
|
import { getModuleExports } from "./modular/buildProjectFiles.js";
|
|
93
|
-
import {
|
|
94
|
+
import {
|
|
95
|
+
getClientHierarchyMap,
|
|
96
|
+
getRLCClients,
|
|
97
|
+
getModularClientOptions
|
|
98
|
+
} from "./utils/clientUtils.js";
|
|
94
99
|
import { join } from "path";
|
|
95
100
|
import { loadStaticHelpers } from "./framework/load-static-helpers.js";
|
|
96
101
|
import { packageUsesXmlSerialization } from "./modular/serialization/buildXmlSerializerFunction.js";
|
|
@@ -192,6 +197,7 @@ export async function $onEmit(context: EmitContext) {
|
|
|
192
197
|
const generationPathDetail: GenerationDirDetail =
|
|
193
198
|
await calculateGenerationDir();
|
|
194
199
|
dpgContext.generationPathDetail = generationPathDetail;
|
|
200
|
+
dpgContext.allServiceNamespaces = listAllServiceNamespaces(dpgContext);
|
|
195
201
|
const options: RLCOptions = transformRLCOptions(emitterOptions, dpgContext);
|
|
196
202
|
emitterOptions["is-modular-library"] = options.isModularLibrary;
|
|
197
203
|
emitterOptions["generate-sample"] = options.generateSample;
|
|
@@ -261,9 +267,7 @@ export async function $onEmit(context: EmitContext) {
|
|
|
261
267
|
for (const client of clients) {
|
|
262
268
|
const rlcModels = await transformRLCModel(client, dpgContext);
|
|
263
269
|
rlcCodeModels.push(rlcModels);
|
|
264
|
-
const serviceName =
|
|
265
|
-
? (client.service[0]?.name ?? "Unknown")
|
|
266
|
-
: client.service.name;
|
|
270
|
+
const serviceName = client.services[0]?.name ?? "Unknown";
|
|
267
271
|
serviceNameToRlcModelsMap.set(serviceName, rlcModels);
|
|
268
272
|
needUnexpectedHelper.set(
|
|
269
273
|
getClientName(rlcModels),
|
|
@@ -317,7 +321,6 @@ export async function $onEmit(context: EmitContext) {
|
|
|
317
321
|
}
|
|
318
322
|
);
|
|
319
323
|
|
|
320
|
-
const isMultiClients = dpgContext.sdkPackage.clients.length > 1;
|
|
321
324
|
emitTypes(dpgContext, { sourceRoot: modularSourcesRoot });
|
|
322
325
|
buildSubpathIndexFile(modularEmitterOptions, "models", undefined, {
|
|
323
326
|
recursive: true
|
|
@@ -351,7 +354,9 @@ export async function $onEmit(context: EmitContext) {
|
|
|
351
354
|
exportIndex: true,
|
|
352
355
|
interfaceOnly: true
|
|
353
356
|
});
|
|
354
|
-
|
|
357
|
+
const { subfolder } = getModularClientOptions(subClient);
|
|
358
|
+
// Generate index file for clients with subfolders (multi-client scenarios and nested clients)
|
|
359
|
+
if (subfolder) {
|
|
355
360
|
buildSubClientIndexFile(dpgContext, subClient, modularEmitterOptions);
|
|
356
361
|
}
|
|
357
362
|
buildRootIndex(
|
|
@@ -395,14 +400,16 @@ export async function $onEmit(context: EmitContext) {
|
|
|
395
400
|
}
|
|
396
401
|
|
|
397
402
|
function buildMetadataJson() {
|
|
398
|
-
const
|
|
403
|
+
const apiVersions = dpgContext.sdkPackage.metadata.apiVersions;
|
|
399
404
|
const emitterVersion = getTypespecTsVersion(context);
|
|
400
|
-
if (
|
|
405
|
+
if (apiVersions === undefined && emitterVersion === undefined) {
|
|
401
406
|
return;
|
|
402
407
|
}
|
|
403
408
|
const content: Metadata = {};
|
|
404
|
-
if (
|
|
405
|
-
|
|
409
|
+
if (apiVersions !== undefined && apiVersions.size > 0) {
|
|
410
|
+
// Use the first/latest API version if multiple are available
|
|
411
|
+
const firstVersion = Array.from(apiVersions.values())[0];
|
|
412
|
+
content.apiVersion = firstVersion;
|
|
406
413
|
}
|
|
407
414
|
if (emitterVersion !== undefined) {
|
|
408
415
|
content.emitterVersion = emitterVersion;
|
package/src/lib.ts
CHANGED
|
@@ -15,6 +15,13 @@ import {
|
|
|
15
15
|
import { Options } from "prettier";
|
|
16
16
|
|
|
17
17
|
export interface EmitterOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Indicates whether to include response headers in the generated response type for modular operations.
|
|
20
|
+
* When set to true, modular operation responses with model or void bodies will have their headers
|
|
21
|
+
* represented as properties on the response type. Other SDK styles and response shapes are not
|
|
22
|
+
* currently affected by this option.
|
|
23
|
+
*/
|
|
24
|
+
"include-headers-in-response"?: boolean;
|
|
18
25
|
"include-shortcuts"?: boolean;
|
|
19
26
|
"multi-client"?: boolean;
|
|
20
27
|
batch?: any[];
|
|
@@ -71,12 +78,19 @@ export interface EmitterOptions {
|
|
|
71
78
|
"default-value-object"?: boolean;
|
|
72
79
|
//TODO should remove this after finish the release tool test
|
|
73
80
|
"should-use-pnpm-dep"?: boolean;
|
|
81
|
+
"ignore-nullable-on-optional"?: boolean;
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
export const RLCOptionsSchema: JSONSchemaType<EmitterOptions> = {
|
|
77
85
|
type: "object",
|
|
78
86
|
additionalProperties: true,
|
|
79
87
|
properties: {
|
|
88
|
+
"include-headers-in-response": {
|
|
89
|
+
type: "boolean",
|
|
90
|
+
nullable: true,
|
|
91
|
+
description:
|
|
92
|
+
"This option is used to indicate whether to include response headers in the generated response type. When set to true, the generated response type will include response headers as properties."
|
|
93
|
+
},
|
|
80
94
|
"include-shortcuts": {
|
|
81
95
|
type: "boolean",
|
|
82
96
|
nullable: true,
|
|
@@ -343,6 +357,12 @@ export const RLCOptionsSchema: JSONSchemaType<EmitterOptions> = {
|
|
|
343
357
|
type: "boolean",
|
|
344
358
|
nullable: true,
|
|
345
359
|
description: "Internal option for test."
|
|
360
|
+
},
|
|
361
|
+
"ignore-nullable-on-optional": {
|
|
362
|
+
type: "boolean",
|
|
363
|
+
nullable: true,
|
|
364
|
+
description:
|
|
365
|
+
"If an optional property is also marked as nullable, it will be treated as just optional. Defaults to `true` for Azure services."
|
|
346
366
|
}
|
|
347
367
|
},
|
|
348
368
|
required: []
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from "ts-morph";
|
|
8
8
|
import {
|
|
9
9
|
getDeserializePrivateFunction,
|
|
10
|
+
getDeserializeHeadersPrivateFunction,
|
|
10
11
|
getExpectedStatuses,
|
|
11
12
|
getOperationFunction,
|
|
12
13
|
getOperationOptionsName,
|
|
@@ -85,16 +86,23 @@ export function buildOperationFiles(
|
|
|
85
86
|
const sendOperationDeclaration = getSendPrivateFunction(
|
|
86
87
|
dpgContext,
|
|
87
88
|
[prefixes, op],
|
|
88
|
-
clientType
|
|
89
|
+
clientType,
|
|
90
|
+
client
|
|
89
91
|
);
|
|
90
92
|
const deserializeOperationDeclaration = getDeserializePrivateFunction(
|
|
91
93
|
dpgContext,
|
|
92
94
|
op
|
|
93
95
|
);
|
|
94
|
-
|
|
96
|
+
const deserializeHeadersDeclaration =
|
|
97
|
+
getDeserializeHeadersPrivateFunction(dpgContext, op);
|
|
98
|
+
const functionsToAdd = [
|
|
95
99
|
sendOperationDeclaration,
|
|
96
100
|
deserializeOperationDeclaration
|
|
97
|
-
]
|
|
101
|
+
];
|
|
102
|
+
if (deserializeHeadersDeclaration) {
|
|
103
|
+
functionsToAdd.push(deserializeHeadersDeclaration);
|
|
104
|
+
}
|
|
105
|
+
operationGroupFile.addFunctions(functionsToAdd);
|
|
98
106
|
addDeclaration(
|
|
99
107
|
operationGroupFile,
|
|
100
108
|
operationDeclaration,
|
|
@@ -191,7 +199,7 @@ export function buildOperationOptions(
|
|
|
191
199
|
return {
|
|
192
200
|
docs: getDocsFromDescription(p.doc),
|
|
193
201
|
hasQuestionToken: true,
|
|
194
|
-
type: getTypeExpression(context, p.type),
|
|
202
|
+
type: getTypeExpression(context, p.type, { isOptional: true }),
|
|
195
203
|
name: normalizeName(p.name, NameType.Parameter)
|
|
196
204
|
};
|
|
197
205
|
})
|
|
@@ -35,23 +35,26 @@ function buildExportsForMultiClient(
|
|
|
35
35
|
delete packageInfo.exports["./api"];
|
|
36
36
|
}
|
|
37
37
|
if (emitterOptions.options.hierarchyClient) {
|
|
38
|
-
for
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
// TODO: support api subpath exports for multi-service. Skip for now. https://github.com/Azure/autorest.typescript/issues/3717
|
|
39
|
+
if (!emitterOptions.options.isMultiService) {
|
|
40
|
+
for (const flattenedClient of clientMap) {
|
|
41
|
+
const { subfolder } = getModularClientOptions(flattenedClient);
|
|
42
|
+
const client = flattenedClient[1];
|
|
43
|
+
const methodMap = getMethodHierarchiesMap(context, client);
|
|
44
|
+
for (const [prefixKey, _] of methodMap) {
|
|
45
|
+
const prefixes = prefixKey.split("/");
|
|
46
|
+
if (prefixKey === "") {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const subApiPath = `api/${getClassicalLayerPrefix(
|
|
50
|
+
prefixes,
|
|
51
|
+
NameType.File,
|
|
52
|
+
"/"
|
|
53
|
+
)}`;
|
|
54
|
+
packageInfo.exports[
|
|
55
|
+
`./${subfolder ? subfolder + "/" : ""}${subApiPath}`
|
|
56
|
+
] = `src/${subfolder ? subfolder + "/" : ""}${subApiPath}/index.ts`;
|
|
46
57
|
}
|
|
47
|
-
const subApiPath = `api/${getClassicalLayerPrefix(
|
|
48
|
-
prefixes,
|
|
49
|
-
NameType.File,
|
|
50
|
-
"/"
|
|
51
|
-
)}`;
|
|
52
|
-
packageInfo.exports[
|
|
53
|
-
`./${subfolder ? subfolder + "/" : ""}${subApiPath}`
|
|
54
|
-
] = `src/${subfolder ? subfolder + "/" : ""}${subApiPath}/index.ts`;
|
|
55
58
|
}
|
|
56
59
|
}
|
|
57
60
|
delete packageInfo.exports["./models"];
|
|
@@ -35,7 +35,8 @@ import {
|
|
|
35
35
|
} from "@azure-tools/typespec-client-generator-core";
|
|
36
36
|
import {
|
|
37
37
|
getExternalModel,
|
|
38
|
-
getModelExpression
|
|
38
|
+
getModelExpression,
|
|
39
|
+
getMultipartFileTypeExpression
|
|
39
40
|
} from "./type-expressions/get-model-expression.js";
|
|
40
41
|
|
|
41
42
|
import { SdkContext } from "../utils/interfaces.js";
|
|
@@ -51,6 +52,8 @@ import {
|
|
|
51
52
|
import {
|
|
52
53
|
buildXmlModelSerializer,
|
|
53
54
|
buildXmlModelDeserializer,
|
|
55
|
+
buildXmlObjectModelSerializer,
|
|
56
|
+
buildXmlObjectModelDeserializer,
|
|
54
57
|
hasXmlSerialization
|
|
55
58
|
} from "./serialization/buildXmlSerializerFunction.js";
|
|
56
59
|
import path from "path";
|
|
@@ -75,8 +78,6 @@ import {
|
|
|
75
78
|
flattenPropertyModelMap,
|
|
76
79
|
getAllOperationsFromClient
|
|
77
80
|
} from "../framework/hooks/sdkTypes.js";
|
|
78
|
-
import { resolveReference } from "../framework/reference.js";
|
|
79
|
-
import { MultipartHelpers } from "./static-helpers-metadata.js";
|
|
80
81
|
import { getAllAncestors } from "./helpers/operationHelpers.js";
|
|
81
82
|
import { getAllProperties } from "./helpers/operationHelpers.js";
|
|
82
83
|
import { getDirectSubtypes } from "./helpers/typeHelpers.js";
|
|
@@ -216,6 +217,10 @@ function emitType(context: SdkContext, type: SdkType, sourceFile: SourceFile) {
|
|
|
216
217
|
return;
|
|
217
218
|
}
|
|
218
219
|
const apiVersionEnumOnly = type.usage === UsageFlags.ApiVersionEnum;
|
|
220
|
+
// Skip known api version enums for multi-service scenarios as users are not allowed to set api versions
|
|
221
|
+
if (apiVersionEnumOnly && context.rlcOptions?.isMultiService) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
219
224
|
const inputUsage = (type.usage & UsageFlags.Input) === UsageFlags.Input;
|
|
220
225
|
const outputUsage = (type.usage & UsageFlags.Output) === UsageFlags.Output;
|
|
221
226
|
const exceptionUsage =
|
|
@@ -266,6 +271,10 @@ function emitType(context: SdkContext, type: SdkType, sourceFile: SourceFile) {
|
|
|
266
271
|
}
|
|
267
272
|
|
|
268
273
|
export function getApiVersionEnum(context: SdkContext) {
|
|
274
|
+
// Skip api version enum for multi-service scenarios since each service may have different versions
|
|
275
|
+
if (context.rlcOptions?.isMultiService) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
269
278
|
const apiVersionEnum = context.sdkPackage.enums.find(
|
|
270
279
|
(e) => e.usage === UsageFlags.ApiVersionEnum
|
|
271
280
|
);
|
|
@@ -293,9 +302,6 @@ export function getModelNamespaces(
|
|
|
293
302
|
context: SdkContext,
|
|
294
303
|
model: SdkType
|
|
295
304
|
): string[] {
|
|
296
|
-
const deepestNamespace = getNamespaceFullName(
|
|
297
|
-
listAllServiceNamespaces(context)[0]!
|
|
298
|
-
);
|
|
299
305
|
if (
|
|
300
306
|
model.kind === "model" ||
|
|
301
307
|
model.kind === "enum" ||
|
|
@@ -312,6 +318,14 @@ export function getModelNamespaces(
|
|
|
312
318
|
return [];
|
|
313
319
|
}
|
|
314
320
|
const segments = model.namespace.split(".");
|
|
321
|
+
// Keep full namespace segments if multiple services are present because there isn't a root namespace to trim
|
|
322
|
+
if (context.rlcOptions?.isMultiService) {
|
|
323
|
+
return segments;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const allServiceNamespaces =
|
|
327
|
+
context.allServiceNamespaces ?? listAllServiceNamespaces(context);
|
|
328
|
+
const deepestNamespace = getNamespaceFullName(allServiceNamespaces[0]!);
|
|
315
329
|
const rootNamespace = deepestNamespace.split(".") ?? [];
|
|
316
330
|
if (segments.length > rootNamespace.length) {
|
|
317
331
|
while (segments[0] === rootNamespace[0]) {
|
|
@@ -371,6 +385,14 @@ function addSerializationFunctions(
|
|
|
371
385
|
if (typeOrProperty.kind === "model" && hasXmlSerialization(typeOrProperty)) {
|
|
372
386
|
const xmlSerializerRefKey = refkey(typeOrProperty, "xmlSerializer");
|
|
373
387
|
const xmlDeserializerRefKey = refkey(typeOrProperty, "xmlDeserializer");
|
|
388
|
+
const xmlObjectSerializerRefKey = refkey(
|
|
389
|
+
typeOrProperty,
|
|
390
|
+
"xmlObjectSerializer"
|
|
391
|
+
);
|
|
392
|
+
const xmlObjectDeserializerRefKey = refkey(
|
|
393
|
+
typeOrProperty,
|
|
394
|
+
"xmlObjectDeserializer"
|
|
395
|
+
);
|
|
374
396
|
|
|
375
397
|
const xmlSerializationFunction = buildXmlModelSerializer(
|
|
376
398
|
context,
|
|
@@ -385,6 +407,24 @@ function addSerializationFunctions(
|
|
|
385
407
|
addDeclaration(sourceFile, xmlSerializationFunction, xmlSerializerRefKey);
|
|
386
408
|
}
|
|
387
409
|
|
|
410
|
+
// Also generate XML object serializer for nested object serialization
|
|
411
|
+
const xmlObjectSerializationFunction = buildXmlObjectModelSerializer(
|
|
412
|
+
context,
|
|
413
|
+
typeOrProperty,
|
|
414
|
+
options
|
|
415
|
+
);
|
|
416
|
+
if (
|
|
417
|
+
xmlObjectSerializationFunction &&
|
|
418
|
+
typeof xmlObjectSerializationFunction !== "string" &&
|
|
419
|
+
xmlObjectSerializationFunction.name
|
|
420
|
+
) {
|
|
421
|
+
addDeclaration(
|
|
422
|
+
sourceFile,
|
|
423
|
+
xmlObjectSerializationFunction,
|
|
424
|
+
xmlObjectSerializerRefKey
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
388
428
|
const xmlDeserializationFunction = buildXmlModelDeserializer(
|
|
389
429
|
context,
|
|
390
430
|
typeOrProperty,
|
|
@@ -401,6 +441,24 @@ function addSerializationFunctions(
|
|
|
401
441
|
xmlDeserializerRefKey
|
|
402
442
|
);
|
|
403
443
|
}
|
|
444
|
+
|
|
445
|
+
// Also generate XML object deserializer for nested object deserialization
|
|
446
|
+
const xmlObjectDeserializationFunction = buildXmlObjectModelDeserializer(
|
|
447
|
+
context,
|
|
448
|
+
typeOrProperty,
|
|
449
|
+
options
|
|
450
|
+
);
|
|
451
|
+
if (
|
|
452
|
+
xmlObjectDeserializationFunction &&
|
|
453
|
+
typeof xmlObjectDeserializationFunction !== "string" &&
|
|
454
|
+
xmlObjectDeserializationFunction.name
|
|
455
|
+
) {
|
|
456
|
+
addDeclaration(
|
|
457
|
+
sourceFile,
|
|
458
|
+
xmlObjectDeserializationFunction,
|
|
459
|
+
xmlObjectDeserializerRefKey
|
|
460
|
+
);
|
|
461
|
+
}
|
|
404
462
|
}
|
|
405
463
|
}
|
|
406
464
|
|
|
@@ -777,45 +835,15 @@ function buildModelProperty(
|
|
|
777
835
|
.join(" | ");
|
|
778
836
|
}
|
|
779
837
|
// eslint-disable-next-line
|
|
780
|
-
else if (
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
typeExpression
|
|
785
|
-
|
|
786
|
-
const isContentTypeOptional =
|
|
787
|
-
multipartOptions?.contentType === undefined ||
|
|
788
|
-
multipartOptions.contentType.optional ||
|
|
789
|
-
multipartOptions.defaultContentTypes.length > 0;
|
|
790
|
-
const isFilenameOptional =
|
|
791
|
-
multipartOptions?.filename === undefined ||
|
|
792
|
-
multipartOptions.filename.optional;
|
|
793
|
-
|
|
794
|
-
const contentTypeType = multipartOptions?.contentType
|
|
795
|
-
? getTypeExpression(context, multipartOptions.contentType.type)
|
|
796
|
-
: "string";
|
|
797
|
-
const filenameType = multipartOptions?.filename
|
|
798
|
-
? getTypeExpression(context, multipartOptions.filename.type)
|
|
799
|
-
: "string";
|
|
800
|
-
|
|
801
|
-
typeExpression += `contentType${isContentTypeOptional ? "?" : ""}: ${contentTypeType};`;
|
|
802
|
-
typeExpression += `filename${isFilenameOptional ? "?" : ""}: ${filenameType};`;
|
|
803
|
-
|
|
804
|
-
typeExpression += "}";
|
|
805
|
-
|
|
806
|
-
if (isContentTypeOptional && isFilenameOptional) {
|
|
807
|
-
// Allow passing content directly if both filename and content type are optional
|
|
808
|
-
typeExpression = `(${resolveReference(MultipartHelpers.FileContents)}) | ${typeExpression}`;
|
|
809
|
-
} else {
|
|
810
|
-
// If either one is required, still accept File at the top level since it requires a filename
|
|
811
|
-
typeExpression = `File | ${typeExpression}`;
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
if (property.type.kind === "array") {
|
|
815
|
-
typeExpression = `Array<${typeExpression}>`;
|
|
816
|
-
}
|
|
838
|
+
else if (
|
|
839
|
+
property.kind === "property" &&
|
|
840
|
+
property.serializationOptions.multipart?.isFilePart
|
|
841
|
+
) {
|
|
842
|
+
typeExpression = getMultipartFileTypeExpression(context, property);
|
|
817
843
|
} else {
|
|
818
|
-
typeExpression = getTypeExpression(context, property.type
|
|
844
|
+
typeExpression = getTypeExpression(context, property.type, {
|
|
845
|
+
isOptional: property.optional
|
|
846
|
+
});
|
|
819
847
|
}
|
|
820
848
|
|
|
821
849
|
const propertyStructure: PropertySignatureStructure = {
|
|
@@ -11,7 +11,9 @@ import {
|
|
|
11
11
|
SdkHttpParameterExampleValue,
|
|
12
12
|
SdkServiceOperation,
|
|
13
13
|
SdkExampleValue,
|
|
14
|
-
SdkClientInitializationType
|
|
14
|
+
SdkClientInitializationType,
|
|
15
|
+
SdkModelPropertyType,
|
|
16
|
+
isReadOnly
|
|
15
17
|
} from "@azure-tools/typespec-client-generator-core";
|
|
16
18
|
import {
|
|
17
19
|
isAzurePackage,
|
|
@@ -449,6 +451,10 @@ function prepareExampleParameters(
|
|
|
449
451
|
if (!propExample) {
|
|
450
452
|
continue;
|
|
451
453
|
}
|
|
454
|
+
// Skip readonly properties as they cannot be set by users
|
|
455
|
+
if (isReadOnly(prop as SdkModelPropertyType)) {
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
452
458
|
result.push(
|
|
453
459
|
prepareExampleValue(
|
|
454
460
|
dpgContext,
|
|
@@ -597,6 +603,10 @@ function getParameterValue(
|
|
|
597
603
|
if (propValue === undefined || propValue === null) {
|
|
598
604
|
continue;
|
|
599
605
|
}
|
|
606
|
+
// Skip readonly properties as they cannot be set by users
|
|
607
|
+
if (property && isReadOnly(property as SdkModelPropertyType)) {
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
600
610
|
let propRetValue;
|
|
601
611
|
|
|
602
612
|
if (
|
|
@@ -105,6 +105,9 @@ export function getClientParameters(
|
|
|
105
105
|
const skipMethodParam = (p: SdkParameter) => p.kind !== "method";
|
|
106
106
|
const armSpecific = (p: SdkParameter) =>
|
|
107
107
|
!(p.kind === "endpoint" && dpgContext.arm);
|
|
108
|
+
// Skip apiVersion parameter when it's multi-service (each service has its own default apiVersion)
|
|
109
|
+
const skipApiVersionOnMultiService = (p: SdkParameter) =>
|
|
110
|
+
!(dpgContext.rlcOptions?.isMultiService && p.isApiVersionParam);
|
|
108
111
|
const filters = [
|
|
109
112
|
options.requiredOnly ? isRequired : undefined,
|
|
110
113
|
dpgContext.rlcOptions?.addCredentials === false
|
|
@@ -112,7 +115,8 @@ export function getClientParameters(
|
|
|
112
115
|
: undefined,
|
|
113
116
|
options.optionalOnly ? isOptional : undefined,
|
|
114
117
|
options.onClientOnly ? skipMethodParam : undefined,
|
|
115
|
-
options.skipArmSpecific ? undefined : armSpecific
|
|
118
|
+
options.skipArmSpecific ? undefined : armSpecific,
|
|
119
|
+
skipApiVersionOnMultiService
|
|
116
120
|
];
|
|
117
121
|
const params = clientParams.filter((p) =>
|
|
118
122
|
filters.every((filter) => !filter || filter(p))
|
|
@@ -73,3 +73,22 @@ export function getClassicalLayerPrefix(
|
|
|
73
73
|
export function isDefined<T>(thing: T | undefined | null): thing is T {
|
|
74
74
|
return typeof thing !== "undefined" && thing !== null;
|
|
75
75
|
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Generates a locally unique name within a set of existing names.
|
|
79
|
+
* @param name - The base name.
|
|
80
|
+
* @param existingNames - A set of names already in use.
|
|
81
|
+
* @returns A unique name not present in the existing names set.
|
|
82
|
+
*/
|
|
83
|
+
export function generateLocallyUniqueName(
|
|
84
|
+
name: string,
|
|
85
|
+
existingNames: Set<string>
|
|
86
|
+
): string {
|
|
87
|
+
let uniqueName = name;
|
|
88
|
+
let counter = 1;
|
|
89
|
+
while (existingNames.has(uniqueName)) {
|
|
90
|
+
uniqueName = `${name}_${counter}`;
|
|
91
|
+
counter++;
|
|
92
|
+
}
|
|
93
|
+
return uniqueName;
|
|
94
|
+
}
|