@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.
Files changed (93) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/src/index.d.ts.map +1 -1
  3. package/dist/src/index.js +8 -4
  4. package/dist/src/index.js.map +1 -1
  5. package/dist/src/lib.d.ts +28 -1
  6. package/dist/src/lib.d.ts.map +1 -1
  7. package/dist/src/lib.js +18 -0
  8. package/dist/src/lib.js.map +1 -1
  9. package/dist/src/modular/buildClassicalClient.js +9 -0
  10. package/dist/src/modular/buildClassicalClient.js.map +1 -1
  11. package/dist/src/modular/buildClassicalOperationGroups.d.ts.map +1 -1
  12. package/dist/src/modular/buildClassicalOperationGroups.js +5 -1
  13. package/dist/src/modular/buildClassicalOperationGroups.js.map +1 -1
  14. package/dist/src/modular/buildCodeModel.d.ts.map +1 -1
  15. package/dist/src/modular/buildCodeModel.js +81 -65
  16. package/dist/src/modular/buildCodeModel.js.map +1 -1
  17. package/dist/src/modular/buildOperations.d.ts +1 -0
  18. package/dist/src/modular/buildOperations.d.ts.map +1 -1
  19. package/dist/src/modular/buildOperations.js +30 -13
  20. package/dist/src/modular/buildOperations.js.map +1 -1
  21. package/dist/src/modular/buildPagingFiles.d.ts +4 -0
  22. package/dist/src/modular/buildPagingFiles.d.ts.map +1 -0
  23. package/dist/src/modular/buildPagingFiles.js +333 -0
  24. package/dist/src/modular/buildPagingFiles.js.map +1 -0
  25. package/dist/src/modular/buildProjectFiles.d.ts.map +1 -1
  26. package/dist/src/modular/buildProjectFiles.js +15 -8
  27. package/dist/src/modular/buildProjectFiles.js.map +1 -1
  28. package/dist/src/modular/buildSubpathIndex.d.ts.map +1 -1
  29. package/dist/src/modular/buildSubpathIndex.js +10 -1
  30. package/dist/src/modular/buildSubpathIndex.js.map +1 -1
  31. package/dist/src/modular/emitModels.d.ts +21 -2
  32. package/dist/src/modular/emitModels.d.ts.map +1 -1
  33. package/dist/src/modular/emitModels.js +26 -5
  34. package/dist/src/modular/emitModels.js.map +1 -1
  35. package/dist/src/modular/helpers/operationHelpers.d.ts +7 -4
  36. package/dist/src/modular/helpers/operationHelpers.d.ts.map +1 -1
  37. package/dist/src/modular/helpers/operationHelpers.js +135 -99
  38. package/dist/src/modular/helpers/operationHelpers.js.map +1 -1
  39. package/dist/src/modular/helpers/typeHelpers.d.ts.map +1 -1
  40. package/dist/src/modular/helpers/typeHelpers.js +2 -1
  41. package/dist/src/modular/helpers/typeHelpers.js.map +1 -1
  42. package/dist/src/modular/modularCodeModel.d.ts +2 -1
  43. package/dist/src/modular/modularCodeModel.d.ts.map +1 -1
  44. package/dist/src/transform/transformApiVersionInfo.d.ts.map +1 -1
  45. package/dist/src/transform/transformApiVersionInfo.js +16 -10
  46. package/dist/src/transform/transformApiVersionInfo.js.map +1 -1
  47. package/dist/src/transform/transformHelperFunctionDetails.d.ts +1 -1
  48. package/dist/src/transform/transformHelperFunctionDetails.d.ts.map +1 -1
  49. package/dist/src/transform/transformHelperFunctionDetails.js +11 -16
  50. package/dist/src/transform/transformHelperFunctionDetails.js.map +1 -1
  51. package/dist/src/transform/transformParameters.d.ts.map +1 -1
  52. package/dist/src/transform/transformParameters.js +19 -14
  53. package/dist/src/transform/transformParameters.js.map +1 -1
  54. package/dist/src/transform/transformResponses.js +5 -3
  55. package/dist/src/transform/transformResponses.js.map +1 -1
  56. package/dist/src/transform/transformSchemas.d.ts.map +1 -1
  57. package/dist/src/transform/transformSchemas.js +32 -17
  58. package/dist/src/transform/transformSchemas.js.map +1 -1
  59. package/dist/src/transform/transfromRLCOptions.js.map +1 -1
  60. package/dist/src/utils/emitUtil.js +1 -1
  61. package/dist/src/utils/emitUtil.js.map +1 -1
  62. package/dist/src/utils/modelUtils.d.ts +1 -0
  63. package/dist/src/utils/modelUtils.d.ts.map +1 -1
  64. package/dist/src/utils/modelUtils.js +103 -57
  65. package/dist/src/utils/modelUtils.js.map +1 -1
  66. package/dist/src/utils/operationUtil.d.ts +3 -1
  67. package/dist/src/utils/operationUtil.d.ts.map +1 -1
  68. package/dist/src/utils/operationUtil.js +21 -7
  69. package/dist/src/utils/operationUtil.js.map +1 -1
  70. package/dist/tsconfig.tsbuildinfo +1 -1
  71. package/package.json +18 -19
  72. package/src/index.ts +20 -4
  73. package/src/lib.ts +18 -0
  74. package/src/modular/buildClassicalClient.ts +15 -0
  75. package/src/modular/buildClassicalOperationGroups.ts +17 -1
  76. package/src/modular/buildCodeModel.ts +100 -77
  77. package/src/modular/buildOperations.ts +55 -13
  78. package/src/modular/buildPagingFiles.ts +356 -0
  79. package/src/modular/buildProjectFiles.ts +17 -15
  80. package/src/modular/buildSubpathIndex.ts +12 -1
  81. package/src/modular/emitModels.ts +29 -5
  82. package/src/modular/helpers/operationHelpers.ts +130 -119
  83. package/src/modular/helpers/typeHelpers.ts +8 -6
  84. package/src/modular/modularCodeModel.ts +2 -1
  85. package/src/transform/transformApiVersionInfo.ts +25 -15
  86. package/src/transform/transformHelperFunctionDetails.ts +19 -21
  87. package/src/transform/transformParameters.ts +33 -14
  88. package/src/transform/transformResponses.ts +8 -3
  89. package/src/transform/transformSchemas.ts +33 -17
  90. package/src/transform/transfromRLCOptions.ts +2 -2
  91. package/src/utils/emitUtil.ts +1 -1
  92. package/src/utils/modelUtils.ts +119 -63
  93. 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.19.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": "~2.7.1",
25
- "@azure-tools/cadl-ranch-specs": "^0.24.0",
26
- "@azure-tools/cadl-ranch-expect": "^0.8.0",
27
- "@azure-tools/cadl-ranch": "^0.9.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.4",
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.20231103.1"
37
+ "@typespec/ts-http-runtime": "1.0.0-alpha.20231129.4"
39
38
  },
40
39
  "peerDependencies": {
41
- "@azure-tools/typespec-azure-core": ">=0.35.0 <1.0.0",
42
- "@azure-tools/typespec-client-generator-core": ">=0.35.0 <1.0.0",
43
- "@typespec/compiler": ">=0.49.0 <1.0.0",
44
- "@typespec/http": ">=0.49.0 <1.0.0",
45
- "@typespec/rest": ">=0.49.0 <1.0.0",
46
- "@typespec/versioning": ">=0.49.0 <1.0.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": "^2.6.1",
48
+ "prettier": "^3.1.0",
50
49
  "tslib": "^2.3.1",
51
- "@azure-tools/rlc-common": "^0.19.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(context) as SdkContext;
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, buildPaginateHelper, rlcModels);
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 (modularCodeModel.clients.length > 1) {
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
- // it is not included in properties of typespec but needed by python codegen
207
- if (discriminatorValues.length > 0) {
208
- const discriminatorInfo = {
209
- description: `the discriminator possible values ${discriminatorValues.join(
210
- ", "
211
- )}`,
212
- isPolymorphic: true,
213
- isDiscriminator: true
214
- };
215
- return discriminatorInfo;
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
- newProperty = { ...newProperty, ...handleDiscriminator(context, model) };
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
- // need to do discriminator outside `emitModel` to avoid infinite recursion
277
- // handleDiscriminator(context, model, newValue);
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
- if (contentTypes.length !== 1) {
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
- defaultContentType,
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 paging = getPagedResult(context.program, operation);
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.itemsPath;
684
- emittedOperation["continuationTokenName"] = pagedResult.nextLinkPath;
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
- ? effectiveName
996
- : getName(context.program, type));
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: sdkType.values.map((x) => getType(context, x.__raw!)),
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
- const importSet: Map<string, Set<string>> = new Map<string, Set<string>>();
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
- if (importSet.size > 0) {
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
  */