@azure-tools/typespec-ts 0.19.0 → 0.20.0
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 +11 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +8 -4
- package/dist/src/index.js.map +1 -1
- package/dist/src/lib.d.ts +28 -1
- package/dist/src/lib.d.ts.map +1 -1
- package/dist/src/lib.js +18 -0
- package/dist/src/lib.js.map +1 -1
- package/dist/src/modular/buildClassicalClient.js +9 -0
- package/dist/src/modular/buildClassicalClient.js.map +1 -1
- package/dist/src/modular/buildClassicalOperationGroups.d.ts.map +1 -1
- package/dist/src/modular/buildClassicalOperationGroups.js +5 -1
- package/dist/src/modular/buildClassicalOperationGroups.js.map +1 -1
- package/dist/src/modular/buildCodeModel.d.ts.map +1 -1
- package/dist/src/modular/buildCodeModel.js +81 -65
- package/dist/src/modular/buildCodeModel.js.map +1 -1
- package/dist/src/modular/buildOperations.d.ts +1 -0
- package/dist/src/modular/buildOperations.d.ts.map +1 -1
- package/dist/src/modular/buildOperations.js +30 -13
- package/dist/src/modular/buildOperations.js.map +1 -1
- package/dist/src/modular/buildPagingFiles.d.ts +4 -0
- package/dist/src/modular/buildPagingFiles.d.ts.map +1 -0
- package/dist/src/modular/buildPagingFiles.js +333 -0
- package/dist/src/modular/buildPagingFiles.js.map +1 -0
- package/dist/src/modular/buildProjectFiles.d.ts.map +1 -1
- package/dist/src/modular/buildProjectFiles.js +15 -8
- package/dist/src/modular/buildProjectFiles.js.map +1 -1
- package/dist/src/modular/buildSubpathIndex.d.ts.map +1 -1
- package/dist/src/modular/buildSubpathIndex.js +10 -1
- package/dist/src/modular/buildSubpathIndex.js.map +1 -1
- package/dist/src/modular/emitModels.d.ts +21 -2
- package/dist/src/modular/emitModels.d.ts.map +1 -1
- package/dist/src/modular/emitModels.js +26 -5
- package/dist/src/modular/emitModels.js.map +1 -1
- package/dist/src/modular/helpers/operationHelpers.d.ts +7 -4
- package/dist/src/modular/helpers/operationHelpers.d.ts.map +1 -1
- package/dist/src/modular/helpers/operationHelpers.js +135 -99
- package/dist/src/modular/helpers/operationHelpers.js.map +1 -1
- package/dist/src/modular/helpers/typeHelpers.d.ts.map +1 -1
- package/dist/src/modular/helpers/typeHelpers.js +2 -1
- package/dist/src/modular/helpers/typeHelpers.js.map +1 -1
- package/dist/src/modular/modularCodeModel.d.ts +2 -1
- package/dist/src/modular/modularCodeModel.d.ts.map +1 -1
- package/dist/src/transform/transformApiVersionInfo.d.ts.map +1 -1
- package/dist/src/transform/transformApiVersionInfo.js +16 -10
- package/dist/src/transform/transformApiVersionInfo.js.map +1 -1
- package/dist/src/transform/transformHelperFunctionDetails.d.ts +1 -1
- package/dist/src/transform/transformHelperFunctionDetails.d.ts.map +1 -1
- package/dist/src/transform/transformHelperFunctionDetails.js +11 -16
- package/dist/src/transform/transformHelperFunctionDetails.js.map +1 -1
- package/dist/src/transform/transformParameters.d.ts.map +1 -1
- package/dist/src/transform/transformParameters.js +19 -14
- package/dist/src/transform/transformParameters.js.map +1 -1
- package/dist/src/transform/transformResponses.js +5 -3
- package/dist/src/transform/transformResponses.js.map +1 -1
- package/dist/src/transform/transformSchemas.d.ts.map +1 -1
- package/dist/src/transform/transformSchemas.js +32 -17
- package/dist/src/transform/transformSchemas.js.map +1 -1
- package/dist/src/transform/transfromRLCOptions.js.map +1 -1
- package/dist/src/utils/emitUtil.js +1 -1
- package/dist/src/utils/emitUtil.js.map +1 -1
- package/dist/src/utils/modelUtils.d.ts +1 -0
- package/dist/src/utils/modelUtils.d.ts.map +1 -1
- package/dist/src/utils/modelUtils.js +103 -57
- package/dist/src/utils/modelUtils.js.map +1 -1
- package/dist/src/utils/operationUtil.d.ts +3 -1
- package/dist/src/utils/operationUtil.d.ts.map +1 -1
- package/dist/src/utils/operationUtil.js +21 -7
- package/dist/src/utils/operationUtil.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +18 -19
- package/src/index.ts +20 -4
- package/src/lib.ts +18 -0
- package/src/modular/buildClassicalClient.ts +15 -0
- package/src/modular/buildClassicalOperationGroups.ts +17 -1
- package/src/modular/buildCodeModel.ts +100 -77
- package/src/modular/buildOperations.ts +55 -13
- package/src/modular/buildPagingFiles.ts +356 -0
- package/src/modular/buildProjectFiles.ts +17 -15
- package/src/modular/buildSubpathIndex.ts +12 -1
- package/src/modular/emitModels.ts +29 -5
- package/src/modular/helpers/operationHelpers.ts +130 -119
- package/src/modular/helpers/typeHelpers.ts +8 -6
- package/src/modular/modularCodeModel.ts +2 -1
- package/src/transform/transformApiVersionInfo.ts +25 -15
- package/src/transform/transformHelperFunctionDetails.ts +19 -21
- package/src/transform/transformParameters.ts +33 -14
- package/src/transform/transformResponses.ts +8 -3
- package/src/transform/transformSchemas.ts +33 -17
- package/src/transform/transfromRLCOptions.ts +2 -2
- package/src/utils/emitUtil.ts +1 -1
- package/src/utils/modelUtils.ts +119 -63
- package/src/utils/operationUtil.ts +26 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@azure-tools/typespec-ts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "An experimental TypeSpec emitter for TypeScript RLC",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
"@types/chai": "^4.3.1",
|
|
11
11
|
"@types/mocha": "^9.1.1",
|
|
12
12
|
"@types/node": "^18.0.0",
|
|
13
|
-
"@types/prettier": "^2.6.0",
|
|
14
13
|
"@types/fs-extra": "^9.0.13",
|
|
15
14
|
"@typescript-eslint/eslint-plugin": "^6.8.0",
|
|
16
15
|
"@typescript-eslint/parser": "^6.8.0",
|
|
@@ -21,12 +20,12 @@
|
|
|
21
20
|
"rimraf": "^5.0.0",
|
|
22
21
|
"ts-node": "~10.9.1",
|
|
23
22
|
"typescript": "~5.2.0",
|
|
24
|
-
"prettier": "
|
|
25
|
-
"@azure-tools/cadl-ranch-specs": "^0.
|
|
26
|
-
"@azure-tools/cadl-ranch-expect": "^0.
|
|
27
|
-
"@azure-tools/cadl-ranch": "^0.
|
|
23
|
+
"prettier": "^3.1.0",
|
|
24
|
+
"@azure-tools/cadl-ranch-specs": "^0.26.2",
|
|
25
|
+
"@azure-tools/cadl-ranch-expect": "^0.9.0",
|
|
26
|
+
"@azure-tools/cadl-ranch": "^0.10.0",
|
|
28
27
|
"chalk": "^4.0.0",
|
|
29
|
-
"@azure-rest/core-client": "^1.1.
|
|
28
|
+
"@azure-rest/core-client": "^1.1.6",
|
|
30
29
|
"@azure/core-auth": "^1.3.2",
|
|
31
30
|
"cross-env": "^7.0.3",
|
|
32
31
|
"@azure/core-paging": "^1.5.0",
|
|
@@ -35,20 +34,20 @@
|
|
|
35
34
|
"@azure/logger": "^1.0.4",
|
|
36
35
|
"@azure/core-util": "^1.4.0",
|
|
37
36
|
"eslint-plugin-require-extensions": "0.1.3",
|
|
38
|
-
"@typespec/ts-http-runtime": "1.0.0-alpha.
|
|
37
|
+
"@typespec/ts-http-runtime": "1.0.0-alpha.20231129.4"
|
|
39
38
|
},
|
|
40
39
|
"peerDependencies": {
|
|
41
|
-
"@azure-tools/typespec-azure-core": ">=0.
|
|
42
|
-
"@azure-tools/typespec-client-generator-core": ">=0.
|
|
43
|
-
"@typespec/compiler": ">=0.
|
|
44
|
-
"@typespec/http": ">=0.
|
|
45
|
-
"@typespec/rest": ">=0.
|
|
46
|
-
"@typespec/versioning": ">=0.
|
|
40
|
+
"@azure-tools/typespec-azure-core": ">=0.36.0 <1.0.0",
|
|
41
|
+
"@azure-tools/typespec-client-generator-core": ">=0.36.1 <1.0.0",
|
|
42
|
+
"@typespec/compiler": ">=0.50.0 <1.0.0",
|
|
43
|
+
"@typespec/http": ">=0.50.0 <1.0.0",
|
|
44
|
+
"@typespec/rest": ">=0.50.0 <1.0.0",
|
|
45
|
+
"@typespec/versioning": ">=0.50.0 <1.0.0"
|
|
47
46
|
},
|
|
48
47
|
"dependencies": {
|
|
49
|
-
"prettier": "^
|
|
48
|
+
"prettier": "^3.1.0",
|
|
50
49
|
"tslib": "^2.3.1",
|
|
51
|
-
"@azure-tools/rlc-common": "^0.
|
|
50
|
+
"@azure-tools/rlc-common": "^0.20.0",
|
|
52
51
|
"ts-morph": "^15.1.0",
|
|
53
52
|
"fs-extra": "^11.1.0"
|
|
54
53
|
},
|
|
@@ -78,7 +77,7 @@
|
|
|
78
77
|
"format": "npm run -s prettier -- --write",
|
|
79
78
|
"check-format": "npm run prettier -- --check",
|
|
80
79
|
"prettier": "prettier --config ./.prettierrc src/**/*.ts",
|
|
81
|
-
"check:tree": "ts-node ./test/commands/check-clean-tree.ts",
|
|
80
|
+
"check:tree": "node --loader ts-node/esm ./test/commands/check-clean-tree.ts",
|
|
82
81
|
"integration-test-ci": "npm run integration-test-ci:rlc && npm run integration-test-ci:modular && npm run integration-test-ci:non-branded",
|
|
83
82
|
"integration-test-ci:rlc": "npm run start-test-server:rlc & npm run copy:typespec && npm run generate-and-run:rlc",
|
|
84
83
|
"integration-test-ci:modular": "npm run start-test-server:modular & npm run copy:typespec && npm run generate-and-run:modular",
|
|
@@ -93,8 +92,8 @@
|
|
|
93
92
|
"generate-tsp-only:rlc": "node --loader ts-node/esm ./test/commands/gen-cadl-ranch.ts --tag=rlc",
|
|
94
93
|
"generate-tsp-only:modular": "node --loader ts-node/esm ./test/commands/gen-cadl-ranch.ts --tag=modular",
|
|
95
94
|
"generate-tsp-only:non-branded": "npm run generate-tsp-only:non-branded-rlc && npm run generate-tsp-only:non-branded-modular",
|
|
96
|
-
"generate-tsp-only:non-branded-rlc": "ts-node ./test/commands/gen-cadl-ranch.ts --tag=non-branded-rlc",
|
|
97
|
-
"generate-tsp-only:non-branded-modular": "ts-node ./test/commands/gen-cadl-ranch.ts --tag=non-branded-modular",
|
|
95
|
+
"generate-tsp-only:non-branded-rlc": "node --loader ts-node/esm ./test/commands/gen-cadl-ranch.ts --tag=non-branded-rlc",
|
|
96
|
+
"generate-tsp-only:non-branded-modular": "node --loader ts-node/esm ./test/commands/gen-cadl-ranch.ts --tag=non-branded-modular",
|
|
98
97
|
"integration-test:alone": "npm run integration-test:alone:rlc && npm run integration-test:alone:modular",
|
|
99
98
|
"integration-test:alone:rlc": "cross-env TS_NODE_PROJECT=tsconfig.integration.json mocha -r ts-node/register --experimental-specifier-resolution=node --timeout 4000 ./test/integration/*.spec.ts",
|
|
100
99
|
"integration-test:alone:modular": "cross-env TS_NODE_PROJECT=tsconfig.integration.json mocha -r ts-node/register --experimental-specifier-resolution=node --timeout 4000 ./test/modularIntegration/*.spec.ts",
|
package/src/index.ts
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
buildApiExtractorConfig,
|
|
18
18
|
buildPackageFile,
|
|
19
19
|
buildPollingHelper,
|
|
20
|
-
buildPaginateHelper,
|
|
20
|
+
buildPaginateHelper as buildRLCPaginateHelper,
|
|
21
21
|
buildEsLintConfig,
|
|
22
22
|
buildKarmaConfigFile,
|
|
23
23
|
buildEnvFile,
|
|
@@ -54,6 +54,10 @@ import { GenerationDirDetail, SdkContext } from "./utils/interfaces.js";
|
|
|
54
54
|
import { transformRLCOptions } from "./transform/transfromRLCOptions.js";
|
|
55
55
|
import { ModularCodeModel } from "./modular/modularCodeModel.js";
|
|
56
56
|
import { getClientName } from "@azure-tools/rlc-common";
|
|
57
|
+
import {
|
|
58
|
+
buildPagingTypes,
|
|
59
|
+
buildPagingHelpers as buildModularPagingHelpers
|
|
60
|
+
} from "./modular/buildPagingFiles.js";
|
|
57
61
|
|
|
58
62
|
export * from "./lib.js";
|
|
59
63
|
|
|
@@ -61,7 +65,10 @@ export async function $onEmit(context: EmitContext) {
|
|
|
61
65
|
/** Shared status */
|
|
62
66
|
const program: Program = context.program;
|
|
63
67
|
const emitterOptions: RLCOptions = context.options;
|
|
64
|
-
const dpgContext = createSdkContext(
|
|
68
|
+
const dpgContext = createSdkContext(
|
|
69
|
+
context,
|
|
70
|
+
"@azure-tools/typespec-ts"
|
|
71
|
+
) as SdkContext;
|
|
65
72
|
const needUnexpectedHelper: Map<string, boolean> = new Map<string, boolean>();
|
|
66
73
|
const serviceNameToRlcModelsMap: Map<string, RLCModel> = new Map<
|
|
67
74
|
string,
|
|
@@ -136,7 +143,7 @@ export async function $onEmit(context: EmitContext) {
|
|
|
136
143
|
await emitContentByBuilder(program, buildIndexFile, rlcModels);
|
|
137
144
|
await emitContentByBuilder(program, buildLogger, rlcModels);
|
|
138
145
|
await emitContentByBuilder(program, buildTopLevelIndex, rlcModels);
|
|
139
|
-
await emitContentByBuilder(program,
|
|
146
|
+
await emitContentByBuilder(program, buildRLCPaginateHelper, rlcModels);
|
|
140
147
|
await emitContentByBuilder(program, buildPollingHelper, rlcModels);
|
|
141
148
|
await emitContentByBuilder(program, buildSerializeHelper, rlcModels);
|
|
142
149
|
await emitContentByBuilder(
|
|
@@ -170,11 +177,20 @@ export async function $onEmit(context: EmitContext) {
|
|
|
170
177
|
overwrite: true
|
|
171
178
|
}
|
|
172
179
|
);
|
|
180
|
+
|
|
181
|
+
const isMultiClients = modularCodeModel.clients.length > 1;
|
|
173
182
|
for (const subClient of modularCodeModel.clients) {
|
|
174
183
|
buildModels(modularCodeModel, subClient);
|
|
175
184
|
buildModelsOptions(modularCodeModel, subClient);
|
|
176
185
|
const hasClientUnexpectedHelper =
|
|
177
186
|
needUnexpectedHelper.get(subClient.rlcClientName) ?? false;
|
|
187
|
+
buildPagingTypes(modularCodeModel, subClient);
|
|
188
|
+
buildModularPagingHelpers(
|
|
189
|
+
modularCodeModel,
|
|
190
|
+
subClient,
|
|
191
|
+
hasClientUnexpectedHelper,
|
|
192
|
+
isMultiClients
|
|
193
|
+
);
|
|
178
194
|
buildOperationFiles(
|
|
179
195
|
dpgContext,
|
|
180
196
|
modularCodeModel,
|
|
@@ -197,7 +213,7 @@ export async function $onEmit(context: EmitContext) {
|
|
|
197
213
|
exportIndex: true,
|
|
198
214
|
interfaceOnly: true
|
|
199
215
|
});
|
|
200
|
-
if (
|
|
216
|
+
if (isMultiClients) {
|
|
201
217
|
buildSubClientIndexFile(modularCodeModel, subClient);
|
|
202
218
|
}
|
|
203
219
|
buildRootIndex(modularCodeModel, subClient, rootIndexFile);
|
package/src/lib.ts
CHANGED
|
@@ -192,6 +192,24 @@ const libDef = {
|
|
|
192
192
|
default:
|
|
193
193
|
"Required header cannot be nullable. Please remove the nullable modifier."
|
|
194
194
|
}
|
|
195
|
+
},
|
|
196
|
+
"no-paging-items-defined": {
|
|
197
|
+
severity: "warning",
|
|
198
|
+
messages: {
|
|
199
|
+
default: paramMessage`Please specify @items property for the paging operation - ${"operationName"}.`
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
"decimal-to-number": {
|
|
203
|
+
severity: "warning",
|
|
204
|
+
messages: {
|
|
205
|
+
default: paramMessage`Please note the decimal type will be converted to number. If you strongly care about precision you can use @encode to encode it as a string for the property - ${"propertyName"}.`
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
"unable-serialized-type": {
|
|
209
|
+
severity: "warning",
|
|
210
|
+
messages: {
|
|
211
|
+
default: paramMessage`Please note the header ${"type"} is not serializable.`
|
|
212
|
+
}
|
|
195
213
|
}
|
|
196
214
|
},
|
|
197
215
|
emitter: {
|
|
@@ -155,6 +155,21 @@ function importAllModels(
|
|
|
155
155
|
moduleSpecifier: `./models/options.js`,
|
|
156
156
|
namedImports: exportedOptions
|
|
157
157
|
});
|
|
158
|
+
|
|
159
|
+
const pagingTypes = project.getSourceFile(
|
|
160
|
+
`${srcPath}/${subfolder !== "" ? subfolder + "/" : ""}models/pagingTypes.ts`
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
if (!pagingTypes) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const exportedPaingTypes = [...pagingTypes.getExportedDeclarations().keys()];
|
|
168
|
+
|
|
169
|
+
clientFile.addImportDeclaration({
|
|
170
|
+
moduleSpecifier: `./models/pagingTypes.js`,
|
|
171
|
+
namedImports: exportedPaingTypes
|
|
172
|
+
});
|
|
158
173
|
}
|
|
159
174
|
|
|
160
175
|
function importPipeline(
|
|
@@ -7,7 +7,7 @@ import { NameType } from "@azure-tools/rlc-common";
|
|
|
7
7
|
import { getClassicalOperation } from "./helpers/classicalOperationHelpers.js";
|
|
8
8
|
import { getClassicalLayerPrefix } from "./helpers/namingHelpers.js";
|
|
9
9
|
import { SourceFile } from "ts-morph";
|
|
10
|
-
import { importModels } from "./buildOperations.js";
|
|
10
|
+
import { importModels, importPagingDependencies } from "./buildOperations.js";
|
|
11
11
|
|
|
12
12
|
export function buildClassicOperationFiles(
|
|
13
13
|
codeModel: ModularCodeModel,
|
|
@@ -52,6 +52,14 @@ export function buildClassicOperationFiles(
|
|
|
52
52
|
operationGroup.namespaceHierarchies.length
|
|
53
53
|
);
|
|
54
54
|
importApis(classicFile, client, codeModel, operationGroup);
|
|
55
|
+
// We need to import the paging helpers and types explicitly because ts-morph may not be able to find them.
|
|
56
|
+
importPagingDependencies(
|
|
57
|
+
srcPath,
|
|
58
|
+
classicFile,
|
|
59
|
+
codeModel.project,
|
|
60
|
+
subfolder,
|
|
61
|
+
operationGroup.namespaceHierarchies.length
|
|
62
|
+
);
|
|
55
63
|
classicFile.fixMissingImports();
|
|
56
64
|
classicFile.fixUnusedIdentifiers();
|
|
57
65
|
classicOperationFiles.set(classicOperationFileName, classicFile);
|
|
@@ -91,6 +99,14 @@ export function buildClassicOperationFiles(
|
|
|
91
99
|
// We SHOULD keep this because otherwise ts-morph will "helpfully" try to import models from the rest layer when we call fixMissingImports().
|
|
92
100
|
importModels(srcPath, classicFile, codeModel.project, subfolder, layer);
|
|
93
101
|
importApis(classicFile, client, codeModel, operationGroup, layer);
|
|
102
|
+
// We need to import the paging helpers and types explicitly because ts-morph may not be able to find them.
|
|
103
|
+
importPagingDependencies(
|
|
104
|
+
srcPath,
|
|
105
|
+
classicFile,
|
|
106
|
+
codeModel.project,
|
|
107
|
+
subfolder,
|
|
108
|
+
operationGroup.namespaceHierarchies.length
|
|
109
|
+
);
|
|
94
110
|
classicFile.fixMissingImports();
|
|
95
111
|
classicFile.fixUnusedIdentifiers();
|
|
96
112
|
classicOperationFiles.set(classicOperationFileName, classicFile);
|
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
getEffectiveModelType,
|
|
21
21
|
getDiscriminator,
|
|
22
22
|
Operation,
|
|
23
|
-
isKey,
|
|
24
23
|
Scalar,
|
|
25
24
|
IntrinsicScalarName,
|
|
26
25
|
isStringType,
|
|
@@ -69,7 +68,6 @@ import {
|
|
|
69
68
|
getClientType,
|
|
70
69
|
SdkEnumValueType
|
|
71
70
|
} from "@azure-tools/typespec-client-generator-core";
|
|
72
|
-
import { getResourceOperation } from "@typespec/rest";
|
|
73
71
|
import {
|
|
74
72
|
ModularCodeModel,
|
|
75
73
|
Client as HrlcClient,
|
|
@@ -78,9 +76,11 @@ import {
|
|
|
78
76
|
OperationGroup,
|
|
79
77
|
Response,
|
|
80
78
|
Type as HrlcType,
|
|
81
|
-
Header
|
|
79
|
+
Header,
|
|
80
|
+
Property
|
|
82
81
|
} from "./modularCodeModel.js";
|
|
83
82
|
import {
|
|
83
|
+
getBodyType,
|
|
84
84
|
getEnrichedDefaultApiVersion,
|
|
85
85
|
isAzureCoreErrorType
|
|
86
86
|
} from "../utils/modelUtils.js";
|
|
@@ -96,7 +96,9 @@ import {
|
|
|
96
96
|
getOperationName,
|
|
97
97
|
isBinaryPayload,
|
|
98
98
|
isIgnoredHeaderParam,
|
|
99
|
-
isLongRunningOperation
|
|
99
|
+
isLongRunningOperation,
|
|
100
|
+
parseItemName,
|
|
101
|
+
parseNextLinkName
|
|
100
102
|
} from "../utils/operationUtil.js";
|
|
101
103
|
import { SdkContext } from "../utils/interfaces.js";
|
|
102
104
|
import { Project } from "ts-morph";
|
|
@@ -105,6 +107,8 @@ import {
|
|
|
105
107
|
getModelNamespaceName,
|
|
106
108
|
getOperationNamespaceInterfaceName
|
|
107
109
|
} from "../utils/namespaceUtils.js";
|
|
110
|
+
import { reportDiagnostic } from "../lib.js";
|
|
111
|
+
import { getType as getTypeName } from "./helpers/typeHelpers.js";
|
|
108
112
|
|
|
109
113
|
interface HttpServerParameter {
|
|
110
114
|
type: "endpointPath";
|
|
@@ -194,8 +198,10 @@ function handleDiscriminator(context: SdkContext, type: Model) {
|
|
|
194
198
|
const discriminator = getDiscriminator(context.program, type);
|
|
195
199
|
if (discriminator) {
|
|
196
200
|
const discriminatorValues: string[] = [];
|
|
201
|
+
const aliases: string[] = [];
|
|
197
202
|
for (const childModel of type.derivedModels) {
|
|
198
203
|
const modelType = getType(context, childModel);
|
|
204
|
+
aliases.push(modelType.name);
|
|
199
205
|
for (const property of modelType.properties) {
|
|
200
206
|
if (property.restApiName === discriminator.propertyName) {
|
|
201
207
|
modelType.discriminatorValue = property.type.value;
|
|
@@ -203,17 +209,22 @@ function handleDiscriminator(context: SdkContext, type: Model) {
|
|
|
203
209
|
}
|
|
204
210
|
}
|
|
205
211
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
212
|
+
const discriminatorInfo = {
|
|
213
|
+
description:
|
|
214
|
+
discriminatorValues.length > 0
|
|
215
|
+
? `the discriminator possible values: ${discriminatorValues.join(
|
|
216
|
+
", "
|
|
217
|
+
)}`
|
|
218
|
+
: "discriminator property",
|
|
219
|
+
type: { type: "string" },
|
|
220
|
+
restApiName: discriminator.propertyName,
|
|
221
|
+
clientName: discriminator.propertyName,
|
|
222
|
+
name: discriminator.propertyName,
|
|
223
|
+
isPolymorphic: true,
|
|
224
|
+
isDiscriminator: true,
|
|
225
|
+
aliases
|
|
226
|
+
};
|
|
227
|
+
return discriminatorInfo;
|
|
217
228
|
}
|
|
218
229
|
return undefined;
|
|
219
230
|
}
|
|
@@ -260,6 +271,8 @@ function processModelProperties(
|
|
|
260
271
|
model: Model
|
|
261
272
|
) {
|
|
262
273
|
// need to do properties after insertion to avoid infinite recursion
|
|
274
|
+
const discriminatorInfo = handleDiscriminator(context, model);
|
|
275
|
+
let hasDiscriminator = false;
|
|
263
276
|
for (const property of model.properties.values()) {
|
|
264
277
|
if (!isSchemaProperty(context.program, property)) {
|
|
265
278
|
continue;
|
|
@@ -269,12 +282,24 @@ function processModelProperties(
|
|
|
269
282
|
}
|
|
270
283
|
let newProperty = emitProperty(context, property);
|
|
271
284
|
if (isDiscriminator(context, model, property.name)) {
|
|
272
|
-
|
|
285
|
+
hasDiscriminator = true;
|
|
286
|
+
newProperty = {
|
|
287
|
+
...newProperty,
|
|
288
|
+
...discriminatorInfo,
|
|
289
|
+
type: newProperty["type"]
|
|
290
|
+
};
|
|
273
291
|
}
|
|
274
292
|
newValue.properties.push(newProperty);
|
|
275
293
|
}
|
|
276
|
-
|
|
277
|
-
|
|
294
|
+
if (discriminatorInfo) {
|
|
295
|
+
if (!hasDiscriminator) {
|
|
296
|
+
newValue.properties.push({ ...discriminatorInfo });
|
|
297
|
+
}
|
|
298
|
+
newValue.alias = `${newValue.name}Parent`;
|
|
299
|
+
newValue.isPolyBaseModel = true;
|
|
300
|
+
discriminatorInfo?.aliases.push(`${newValue.alias}`);
|
|
301
|
+
newValue.aliasType = discriminatorInfo?.aliases.join(" | ");
|
|
302
|
+
}
|
|
278
303
|
}
|
|
279
304
|
|
|
280
305
|
function isEmptyAnonymousModel(type: EmitterType): boolean {
|
|
@@ -396,49 +421,10 @@ type BodyParameter = ParamBase & {
|
|
|
396
421
|
type: Type;
|
|
397
422
|
restApiName: string;
|
|
398
423
|
location: "body";
|
|
399
|
-
defaultContentType: string;
|
|
424
|
+
// defaultContentType: string;
|
|
400
425
|
isBinaryPayload: boolean;
|
|
401
426
|
};
|
|
402
427
|
|
|
403
|
-
function getBodyType(program: Program, route: HttpOperation): Type {
|
|
404
|
-
let bodyModel = route.parameters.body?.type;
|
|
405
|
-
if (bodyModel && bodyModel.kind === "Model" && route.operation) {
|
|
406
|
-
const resourceType = getResourceOperation(
|
|
407
|
-
program,
|
|
408
|
-
route.operation
|
|
409
|
-
)?.resourceType;
|
|
410
|
-
if (resourceType && route.responses && route.responses.length > 0) {
|
|
411
|
-
const resp = route.responses[0];
|
|
412
|
-
if (resp && resp.responses && resp.responses.length > 0) {
|
|
413
|
-
const responseBody = resp.responses[0]?.body;
|
|
414
|
-
if (responseBody?.type?.kind === "Model") {
|
|
415
|
-
const bodyTypeInResponse = getEffectiveSchemaType(
|
|
416
|
-
program,
|
|
417
|
-
responseBody.type
|
|
418
|
-
);
|
|
419
|
-
// response body type is reosurce type, and request body type (if templated) contains resource type
|
|
420
|
-
if (
|
|
421
|
-
bodyTypeInResponse === resourceType &&
|
|
422
|
-
bodyModel.templateMapper &&
|
|
423
|
-
bodyModel.templateMapper.args &&
|
|
424
|
-
bodyModel.templateMapper.args.some((it) => {
|
|
425
|
-
return it.kind === "Model" || it.kind === "Union"
|
|
426
|
-
? it === bodyTypeInResponse
|
|
427
|
-
: false;
|
|
428
|
-
})
|
|
429
|
-
) {
|
|
430
|
-
bodyModel = resourceType;
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
if (resourceType && bodyModel.name === "") {
|
|
436
|
-
bodyModel = resourceType;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
return bodyModel!;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
428
|
function emitBodyParameter(
|
|
443
429
|
context: SdkContext,
|
|
444
430
|
httpOperation: HttpOperation
|
|
@@ -450,25 +436,17 @@ function emitBodyParameter(
|
|
|
450
436
|
if (contentTypes.length === 0) {
|
|
451
437
|
contentTypes = ["application/json"];
|
|
452
438
|
}
|
|
453
|
-
|
|
454
|
-
throw Error("Currently only one kind of content-type!");
|
|
455
|
-
}
|
|
456
|
-
const type = getType(context, getBodyType(context.program, httpOperation), {
|
|
439
|
+
const type = getType(context, getBodyType(context.program, httpOperation)!, {
|
|
457
440
|
disableEffectiveModel: true
|
|
458
441
|
});
|
|
459
442
|
|
|
460
|
-
const defaultContentType =
|
|
461
|
-
body.parameter?.default ?? contentTypes.includes("application/json")
|
|
462
|
-
? "application/json"
|
|
463
|
-
: contentTypes[0]!;
|
|
464
443
|
return {
|
|
465
444
|
contentTypes,
|
|
466
445
|
type,
|
|
467
446
|
restApiName: body.parameter?.name ?? "body",
|
|
468
447
|
location: "body",
|
|
469
448
|
...base,
|
|
470
|
-
|
|
471
|
-
isBinaryPayload: isBinaryPayload(context, body.type, defaultContentType)
|
|
449
|
+
isBinaryPayload: isBinaryPayload(context, body.type, contentTypes)
|
|
472
450
|
};
|
|
473
451
|
}
|
|
474
452
|
|
|
@@ -639,11 +617,39 @@ function emitOperation(
|
|
|
639
617
|
operationGroupName: string,
|
|
640
618
|
rlcModels: RLCModel
|
|
641
619
|
): HrlcOperation {
|
|
620
|
+
const isBranded = rlcModels.options?.branded ?? true;
|
|
621
|
+
// Skip to extract paging and lro information for non-branded clients.
|
|
622
|
+
if (!isBranded) {
|
|
623
|
+
return emitBasicOperation(
|
|
624
|
+
context,
|
|
625
|
+
operation,
|
|
626
|
+
operationGroupName,
|
|
627
|
+
rlcModels
|
|
628
|
+
);
|
|
629
|
+
}
|
|
642
630
|
const lro = isLongRunningOperation(
|
|
643
631
|
context.program,
|
|
644
632
|
ignoreDiagnostics(getHttpOperation(context.program, operation))
|
|
645
633
|
);
|
|
646
|
-
const
|
|
634
|
+
const pagingMetadata = getPagedResult(context.program, operation);
|
|
635
|
+
// Disable the paging feature if no itemsSegments is found.
|
|
636
|
+
const paging =
|
|
637
|
+
pagingMetadata &&
|
|
638
|
+
pagingMetadata.itemsSegments &&
|
|
639
|
+
pagingMetadata.itemsSegments.length > 0;
|
|
640
|
+
if (
|
|
641
|
+
pagingMetadata &&
|
|
642
|
+
(!pagingMetadata.itemsSegments || pagingMetadata.itemsSegments.length === 0)
|
|
643
|
+
) {
|
|
644
|
+
reportDiagnostic(context.program, {
|
|
645
|
+
code: "no-paging-items-defined",
|
|
646
|
+
format: {
|
|
647
|
+
operationName: operation.name
|
|
648
|
+
},
|
|
649
|
+
target: operation
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
|
|
647
653
|
if (lro && paging) {
|
|
648
654
|
return emitLroPagingOperation(
|
|
649
655
|
context,
|
|
@@ -680,8 +686,8 @@ function addPagingInformation(
|
|
|
680
686
|
"Trying to add paging information, but not paging metadata for this operation"
|
|
681
687
|
);
|
|
682
688
|
}
|
|
683
|
-
emittedOperation["itemName"] = pagedResult
|
|
684
|
-
emittedOperation["continuationTokenName"] = pagedResult
|
|
689
|
+
emittedOperation["itemName"] = parseItemName(pagedResult);
|
|
690
|
+
emittedOperation["continuationTokenName"] = parseNextLinkName(pagedResult);
|
|
685
691
|
}
|
|
686
692
|
|
|
687
693
|
function emitLroPagingOperation(
|
|
@@ -939,8 +945,7 @@ function emitProperty(
|
|
|
939
945
|
optional: property.optional,
|
|
940
946
|
description: getDocStr(context.program, property),
|
|
941
947
|
addedOn: getAddedOnVersion(context.program, property),
|
|
942
|
-
readonly:
|
|
943
|
-
isReadOnly(context.program, property) || isKey(context.program, property),
|
|
948
|
+
readonly: isReadOnly(context.program, property),
|
|
944
949
|
clientDefaultValue: clientDefaultValue,
|
|
945
950
|
format: newProperty.format
|
|
946
951
|
};
|
|
@@ -992,8 +997,8 @@ function emitModel(context: SdkContext, type: Model): Record<string, any> {
|
|
|
992
997
|
(context.rlcOptions?.enableModelNamespace
|
|
993
998
|
? fullNamespaceName
|
|
994
999
|
: effectiveName
|
|
995
|
-
|
|
996
|
-
|
|
1000
|
+
? effectiveName
|
|
1001
|
+
: getName(context.program, type));
|
|
997
1002
|
if (
|
|
998
1003
|
!overridedModelName &&
|
|
999
1004
|
type.templateMapper &&
|
|
@@ -1312,14 +1317,32 @@ function emitUnion(context: SdkContext, type: Union): Record<string, any> {
|
|
|
1312
1317
|
}
|
|
1313
1318
|
if (sdkType.kind === "union") {
|
|
1314
1319
|
const unionName = type.name;
|
|
1320
|
+
const discriminatorPropertyName = getDiscriminator(context.program, type)
|
|
1321
|
+
?.propertyName;
|
|
1322
|
+
const variantTypes = sdkType.values.map((x) => {
|
|
1323
|
+
const valueType = getType(context, x.__raw!);
|
|
1324
|
+
if (valueType.properties && discriminatorPropertyName) {
|
|
1325
|
+
valueType.discriminatorValue = valueType.properties.filter(
|
|
1326
|
+
(p: Property) => p.clientName === discriminatorPropertyName
|
|
1327
|
+
)[0].type.value;
|
|
1328
|
+
}
|
|
1329
|
+
return valueType;
|
|
1330
|
+
});
|
|
1315
1331
|
return {
|
|
1316
1332
|
nullable: sdkType.nullable,
|
|
1317
1333
|
name: unionName,
|
|
1318
1334
|
description: `Type of ${unionName}`,
|
|
1319
1335
|
internal: true,
|
|
1320
1336
|
type: "combined",
|
|
1321
|
-
types:
|
|
1322
|
-
xmlMetadata: {}
|
|
1337
|
+
types: variantTypes,
|
|
1338
|
+
xmlMetadata: {},
|
|
1339
|
+
discriminator: discriminatorPropertyName,
|
|
1340
|
+
alias:
|
|
1341
|
+
unionName === "" || unionName === undefined ? undefined : unionName,
|
|
1342
|
+
aliasType:
|
|
1343
|
+
unionName === "" || unionName === undefined
|
|
1344
|
+
? undefined
|
|
1345
|
+
: variantTypes.map((x) => getTypeName(x).name).join(" | ")
|
|
1323
1346
|
};
|
|
1324
1347
|
} else if (sdkType.kind === "enum") {
|
|
1325
1348
|
return {
|
|
@@ -12,6 +12,8 @@ import { isRLCMultiEndpoint } from "../utils/clientUtils.js";
|
|
|
12
12
|
import { getDocsFromDescription } from "./helpers/docsHelpers.js";
|
|
13
13
|
import { SdkContext } from "../utils/interfaces.js";
|
|
14
14
|
import { getImportSpecifier } from "@azure-tools/rlc-common";
|
|
15
|
+
import { addImportsToFiles } from "@azure-tools/rlc-common";
|
|
16
|
+
import { clearImportSets } from "@azure-tools/rlc-common";
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* This function creates a file under /api for each operation group.
|
|
@@ -26,7 +28,7 @@ export function buildOperationFiles(
|
|
|
26
28
|
) {
|
|
27
29
|
const operationFiles = [];
|
|
28
30
|
for (const operationGroup of client.operationGroups) {
|
|
29
|
-
|
|
31
|
+
clearImportSets(codeModel.runtimeImports);
|
|
30
32
|
const operationFileName =
|
|
31
33
|
operationGroup.className && operationGroup.namespaceHierarchies.length > 0
|
|
32
34
|
? `${operationGroup.namespaceHierarchies
|
|
@@ -56,6 +58,15 @@ export function buildOperationFiles(
|
|
|
56
58
|
operationGroup.namespaceHierarchies.length
|
|
57
59
|
);
|
|
58
60
|
|
|
61
|
+
// We need to import the paging helpers and types explicitly because ts-morph may not be able to find them.
|
|
62
|
+
importPagingDependencies(
|
|
63
|
+
srcPath,
|
|
64
|
+
operationGroupFile,
|
|
65
|
+
codeModel.project,
|
|
66
|
+
subfolder,
|
|
67
|
+
operationGroup.namespaceHierarchies.length
|
|
68
|
+
);
|
|
69
|
+
|
|
59
70
|
const namedImports: string[] = [];
|
|
60
71
|
let clientType = "Client";
|
|
61
72
|
if (isRLCMultiEndpoint(dpgContext)) {
|
|
@@ -95,14 +106,12 @@ export function buildOperationFiles(
|
|
|
95
106
|
dpgContext,
|
|
96
107
|
o,
|
|
97
108
|
clientType,
|
|
98
|
-
importSet,
|
|
99
109
|
codeModel.runtimeImports
|
|
100
110
|
);
|
|
101
111
|
const deserializeOperationDeclaration = getDeserializePrivateFunction(
|
|
102
112
|
o,
|
|
103
113
|
isRLCMultiEndpoint(dpgContext),
|
|
104
114
|
needUnexpectedHelper,
|
|
105
|
-
importSet,
|
|
106
115
|
codeModel.runtimeImports
|
|
107
116
|
);
|
|
108
117
|
operationGroupFile.addFunctions([
|
|
@@ -124,16 +133,7 @@ export function buildOperationFiles(
|
|
|
124
133
|
]
|
|
125
134
|
}
|
|
126
135
|
]);
|
|
127
|
-
|
|
128
|
-
for (const [moduleName, imports] of importSet.entries()) {
|
|
129
|
-
operationGroupFile.addImportDeclarations([
|
|
130
|
-
{
|
|
131
|
-
moduleSpecifier: moduleName,
|
|
132
|
-
namedImports: [...imports.values()]
|
|
133
|
-
}
|
|
134
|
-
]);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
136
|
+
addImportsToFiles(codeModel.runtimeImports, operationGroupFile);
|
|
137
137
|
operationGroupFile.fixMissingImports();
|
|
138
138
|
// have to fixUnusedIdentifiers after everything get generated.
|
|
139
139
|
operationGroupFile.fixUnusedIdentifiers();
|
|
@@ -175,6 +175,48 @@ export function importModels(
|
|
|
175
175
|
// sourceFile.fixUnusedIdentifiers();
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
export function importPagingDependencies(
|
|
179
|
+
srcPath: string,
|
|
180
|
+
sourceFile: SourceFile,
|
|
181
|
+
project: Project,
|
|
182
|
+
subfolder: string = "",
|
|
183
|
+
importLayer: number = 0
|
|
184
|
+
) {
|
|
185
|
+
const pagingTypes = project.getSourceFile(
|
|
186
|
+
`${srcPath}/${subfolder !== "" ? subfolder + "/" : ""}models/pagingTypes.ts`
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
if (!pagingTypes) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const exportedPaingTypes = [...pagingTypes.getExportedDeclarations().keys()];
|
|
194
|
+
|
|
195
|
+
sourceFile.addImportDeclaration({
|
|
196
|
+
moduleSpecifier: `${"../".repeat(importLayer + 1)}models/pagingTypes.js`,
|
|
197
|
+
namedImports: exportedPaingTypes
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const pagingHelper = project.getSourceFile(
|
|
201
|
+
`${srcPath}/${subfolder !== "" ? subfolder + "/" : ""}api/pagingHelpers.ts`
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
if (!pagingHelper) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const exportedPaingHelpers = [
|
|
209
|
+
...pagingHelper.getExportedDeclarations().keys()
|
|
210
|
+
];
|
|
211
|
+
|
|
212
|
+
sourceFile.addImportDeclaration({
|
|
213
|
+
moduleSpecifier: `${
|
|
214
|
+
importLayer === 0 ? "./" : "../".repeat(importLayer)
|
|
215
|
+
}pagingHelpers.js`,
|
|
216
|
+
namedImports: exportedPaingHelpers
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
178
220
|
/**
|
|
179
221
|
* This function generates the interfaces for each operation options
|
|
180
222
|
*/
|