@azure-tools/rlc-common 1.0.0-beta.1 → 1.0.0-beta.10

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 (135) hide show
  1. package/.eslintrc.json +23 -0
  2. package/.prettierignore +1 -0
  3. package/.prettierrc +7 -0
  4. package/.rush/temp/package-deps_build.json +33 -25
  5. package/.rush/temp/shrinkwrap-deps.json +84 -9
  6. package/CHANGELOG.md +52 -1
  7. package/CONTRIBUTING.md +29 -0
  8. package/README.md +3 -0
  9. package/dist/buildClient.js +89 -19
  10. package/dist/buildClient.js.map +1 -1
  11. package/dist/buildClientDefinitions.js.map +1 -1
  12. package/dist/buildIndexFile.js +18 -0
  13. package/dist/buildIndexFile.js.map +1 -1
  14. package/dist/buildIsUnexpectedHelper.js +52 -51
  15. package/dist/buildIsUnexpectedHelper.js.map +1 -1
  16. package/dist/buildMethodShortcuts.js +1 -1
  17. package/dist/buildMethodShortcuts.js.map +1 -1
  18. package/dist/buildObjectTypes.js +47 -11
  19. package/dist/buildObjectTypes.js.map +1 -1
  20. package/dist/buildParameterTypes.js +31 -3
  21. package/dist/buildParameterTypes.js.map +1 -1
  22. package/dist/buildResponseTypes.js +19 -6
  23. package/dist/buildResponseTypes.js.map +1 -1
  24. package/dist/buildSchemaType.js +22 -1
  25. package/dist/buildSchemaType.js.map +1 -1
  26. package/dist/buildSerializeHelper.js +35 -0
  27. package/dist/buildSerializeHelper.js.map +1 -0
  28. package/dist/buildTopLevelIndexFile.js +6 -3
  29. package/dist/buildTopLevelIndexFile.js.map +1 -1
  30. package/dist/helpers/nameConstructors.js +2 -2
  31. package/dist/helpers/nameConstructors.js.map +1 -1
  32. package/dist/helpers/nameUtils.js +1 -1
  33. package/dist/helpers/nameUtils.js.map +1 -1
  34. package/dist/helpers/operationHelpers.js +23 -3
  35. package/dist/helpers/operationHelpers.js.map +1 -1
  36. package/dist/helpers/pathUtils.js +13 -0
  37. package/dist/helpers/pathUtils.js.map +1 -0
  38. package/dist/helpers/shortcutMethods.js +1 -1
  39. package/dist/helpers/shortcutMethods.js.map +1 -1
  40. package/dist/index.js +1 -0
  41. package/dist/index.js.map +1 -1
  42. package/dist/interfaces.js.map +1 -1
  43. package/dist/metadata/buildPackageFile.js +70 -28
  44. package/dist/metadata/buildPackageFile.js.map +1 -1
  45. package/dist/metadata/buildReadmeFile.js +3 -3
  46. package/dist/package.json +1 -1
  47. package/dist/static/paginateContent.js +1 -1
  48. package/dist/static/pollingContent.js +26 -6
  49. package/dist/static/pollingContent.js.map +1 -1
  50. package/dist/static/serializeHelper.js +30 -0
  51. package/dist/static/serializeHelper.js.map +1 -0
  52. package/dist/test/template.js +1 -2
  53. package/dist/test/template.js.map +1 -1
  54. package/dist-esm/buildClient.js +88 -18
  55. package/dist-esm/buildClient.js.map +1 -1
  56. package/dist-esm/buildClientDefinitions.js +1 -1
  57. package/dist-esm/buildClientDefinitions.js.map +1 -1
  58. package/dist-esm/buildIndexFile.js +19 -1
  59. package/dist-esm/buildIndexFile.js.map +1 -1
  60. package/dist-esm/buildIsUnexpectedHelper.js +52 -51
  61. package/dist-esm/buildIsUnexpectedHelper.js.map +1 -1
  62. package/dist-esm/buildMethodShortcuts.js +1 -1
  63. package/dist-esm/buildMethodShortcuts.js.map +1 -1
  64. package/dist-esm/buildObjectTypes.js +47 -8
  65. package/dist-esm/buildObjectTypes.js.map +1 -1
  66. package/dist-esm/buildParameterTypes.js +35 -2
  67. package/dist-esm/buildParameterTypes.js.map +1 -1
  68. package/dist-esm/buildResponseTypes.js +19 -6
  69. package/dist-esm/buildResponseTypes.js.map +1 -1
  70. package/dist-esm/buildSchemaType.js +23 -2
  71. package/dist-esm/buildSchemaType.js.map +1 -1
  72. package/dist-esm/buildSerializeHelper.js +31 -0
  73. package/dist-esm/buildSerializeHelper.js.map +1 -0
  74. package/dist-esm/buildTopLevelIndexFile.js +6 -3
  75. package/dist-esm/buildTopLevelIndexFile.js.map +1 -1
  76. package/dist-esm/helpers/nameConstructors.js +2 -2
  77. package/dist-esm/helpers/nameConstructors.js.map +1 -1
  78. package/dist-esm/helpers/nameUtils.js +1 -1
  79. package/dist-esm/helpers/nameUtils.js.map +1 -1
  80. package/dist-esm/helpers/operationHelpers.js +15 -3
  81. package/dist-esm/helpers/operationHelpers.js.map +1 -1
  82. package/dist-esm/helpers/pathUtils.js +9 -0
  83. package/dist-esm/helpers/pathUtils.js.map +1 -0
  84. package/dist-esm/helpers/shortcutMethods.js +1 -1
  85. package/dist-esm/helpers/shortcutMethods.js.map +1 -1
  86. package/dist-esm/index.js +1 -0
  87. package/dist-esm/index.js.map +1 -1
  88. package/dist-esm/interfaces.js.map +1 -1
  89. package/dist-esm/metadata/buildPackageFile.js +70 -28
  90. package/dist-esm/metadata/buildPackageFile.js.map +1 -1
  91. package/dist-esm/metadata/buildReadmeFile.js +3 -3
  92. package/dist-esm/package.json +1 -1
  93. package/dist-esm/static/paginateContent.js +1 -1
  94. package/dist-esm/static/pollingContent.js +26 -6
  95. package/dist-esm/static/pollingContent.js.map +1 -1
  96. package/dist-esm/static/serializeHelper.js +27 -0
  97. package/dist-esm/static/serializeHelper.js.map +1 -0
  98. package/dist-esm/test/template.js +1 -2
  99. package/dist-esm/test/template.js.map +1 -1
  100. package/package.json +10 -4
  101. package/src/buildClient.ts +121 -19
  102. package/src/buildClientDefinitions.ts +6 -2
  103. package/src/buildIndexFile.ts +28 -0
  104. package/src/buildIsUnexpectedHelper.ts +52 -52
  105. package/src/buildMethodShortcuts.ts +1 -1
  106. package/src/buildObjectTypes.ts +66 -10
  107. package/src/buildParameterTypes.ts +45 -3
  108. package/src/buildResponseTypes.ts +23 -6
  109. package/src/buildSchemaType.ts +24 -1
  110. package/src/buildSerializeHelper.ts +42 -0
  111. package/src/buildTopLevelIndexFile.ts +9 -3
  112. package/src/helpers/nameConstructors.ts +2 -2
  113. package/src/helpers/nameUtils.ts +1 -1
  114. package/src/helpers/operationHelpers.ts +19 -3
  115. package/src/helpers/pathUtils.ts +9 -0
  116. package/src/helpers/schemaHelpers.ts +1 -1
  117. package/src/helpers/shortcutMethods.ts +1 -1
  118. package/src/index.ts +1 -0
  119. package/src/interfaces.ts +16 -1
  120. package/src/metadata/buildPackageFile.ts +94 -31
  121. package/src/metadata/buildReadmeFile.ts +3 -3
  122. package/src/static/paginateContent.ts +1 -1
  123. package/src/static/pollingContent.ts +26 -6
  124. package/src/static/serializeHelper.ts +29 -0
  125. package/src/test/template.ts +1 -2
  126. package/types/buildObjectTypes.d.ts +2 -1
  127. package/types/buildParameterTypes.d.ts +9 -1
  128. package/types/buildSerializeHelper.d.ts +5 -0
  129. package/types/helpers/operationHelpers.d.ts +4 -0
  130. package/types/helpers/pathUtils.d.ts +1 -0
  131. package/types/index.d.ts +1 -0
  132. package/types/interfaces.d.ts +15 -1
  133. package/types/static/pollingContent.d.ts +1 -1
  134. package/types/static/serializeHelper.d.ts +4 -0
  135. package/types/test/template.d.ts +1 -1
@@ -2,6 +2,8 @@
2
2
  // Licensed under the MIT License.
3
3
 
4
4
  import {
5
+ InterfaceDeclarationStructure,
6
+ OptionalKind,
5
7
  Project,
6
8
  StatementStructures,
7
9
  StructureKind,
@@ -13,7 +15,31 @@ import * as path from "path";
13
15
  import { NameType, normalizeName } from "./helpers/nameUtils.js";
14
16
  import { isConstantSchema } from "./helpers/schemaHelpers.js";
15
17
  import { buildMethodShortcutImplementation } from "./buildMethodShortcuts.js";
16
- import { RLCModel, Schema, File } from "./interfaces.js";
18
+ import { RLCModel, Schema, File, PathParameter } from "./interfaces.js";
19
+
20
+ function getClientOptionsInterface(
21
+ clientName: string,
22
+ optionalUrlParameters?: PathParameter[]
23
+ ): OptionalKind<InterfaceDeclarationStructure> | undefined {
24
+ if (!optionalUrlParameters || optionalUrlParameters.length === 0) {
25
+ return undefined;
26
+ }
27
+
28
+ const properties = optionalUrlParameters.map((param) => {
29
+ return {
30
+ name: param.name,
31
+ type: param.type,
32
+ hasQuestionToken: true
33
+ };
34
+ });
35
+
36
+ return {
37
+ name: `${clientName}Options`,
38
+ extends: ["ClientOptions"],
39
+ isExported: true,
40
+ properties
41
+ };
42
+ }
17
43
 
18
44
  export function buildClient(model: RLCModel): File | undefined {
19
45
  const name = normalizeName(model.libraryName, NameType.File);
@@ -26,7 +52,29 @@ export function buildClient(model: RLCModel): File | undefined {
26
52
 
27
53
  // Get all paths
28
54
  const clientName = model.libraryName;
29
- const urlParameters = model?.urlInfo?.urlParameters;
55
+ const clientInterfaceName = clientName.endsWith("Client")
56
+ ? `${clientName}`
57
+ : `${clientName}Client`;
58
+
59
+ normalizeUrlInfo(model);
60
+ const urlParameters = model?.urlInfo?.urlParameters?.filter(
61
+ // Do not include parameters with constant values in the signature, these should go in the options bag
62
+ (p) => p.value === undefined
63
+ );
64
+
65
+ const optionalUrlParameters = model?.urlInfo?.urlParameters?.filter(
66
+ // Do not include parameters with constant values in the signature, these should go in the options bag
67
+ (p) => Boolean(p.value)
68
+ );
69
+
70
+ const clientOptionsInterface = getClientOptionsInterface(
71
+ clientInterfaceName,
72
+ optionalUrlParameters
73
+ );
74
+
75
+ if (clientOptionsInterface) {
76
+ clientFile.addInterface(clientOptionsInterface);
77
+ }
30
78
 
31
79
  if (!model.options) {
32
80
  return undefined;
@@ -46,26 +94,39 @@ export function buildClient(model: RLCModel): File | undefined {
46
94
  ...(addCredentials === false ||
47
95
  !isSecurityInfoDefined(credentialScopes, credentialKeyHeaderName)
48
96
  ? []
49
- : [{ name: "credentials", type: credentialTypes.join(" | ") }])
97
+ : [
98
+ {
99
+ name: "credentials",
100
+ type: credentialTypes.join(" | "),
101
+ description: `uniquely identify client credential`
102
+ }
103
+ ])
50
104
  ];
51
- const clientInterfaceName = clientName.endsWith("Client")
52
- ? `${clientName}`
53
- : `${clientName}Client`;
54
105
 
106
+ const allClientParams = [
107
+ ...commonClientParams,
108
+ {
109
+ name: "options",
110
+ type: `${clientOptionsInterface?.name ?? "ClientOptions"} = {}`,
111
+ description: "the parameter for all optional parameters"
112
+ }
113
+ ];
55
114
  const functionStatement = {
56
115
  isExported: true,
57
116
  name: `createClient`,
58
- parameters: [
59
- ...commonClientParams,
60
- { name: "options", type: "ClientOptions = {}" }
61
- ],
117
+ parameters: allClientParams,
62
118
  docs: [
63
119
  {
64
120
  description:
65
- `Initialize a new instance of the class ${clientInterfaceName} class. \n` +
66
- commonClientParams
121
+ `Initialize a new instance of \`${clientInterfaceName}\` \n` +
122
+ allClientParams
67
123
  .map((param) => {
68
- return `@param ${param.name} type: ${param.type} ${param.description ?? ""}`;
124
+ return `@param ${param.name} type: ${param.type
125
+ .split("=")[0]
126
+ .split(" ")
127
+ .join("")}, ${
128
+ param.description ?? "The parameter " + param.name
129
+ }`;
69
130
  })
70
131
  .join("\n")
71
132
  }
@@ -127,6 +188,19 @@ function getClientFactoryBody(
127
188
  let clientPackageName = packageDetails.nameWithoutScope ?? "";
128
189
  const packageVersion = packageDetails.version;
129
190
  const { endpoint, urlParameters } = model.urlInfo;
191
+
192
+ const optionalUrlParameters: string[] = [];
193
+
194
+ for (const param of urlParameters ?? []) {
195
+ if (param.value) {
196
+ const value =
197
+ typeof param.value === "string" ? `"${param.value}"` : param.value;
198
+ optionalUrlParameters.push(
199
+ `const ${param.name} = options.${param.name} ?? ${value}`
200
+ );
201
+ }
202
+ }
203
+
130
204
  let baseUrl: string;
131
205
  if (urlParameters && endpoint) {
132
206
  let parsedEndpoint = endpoint;
@@ -142,7 +216,7 @@ function getClientFactoryBody(
142
216
  baseUrl = `options.baseUrl ?? "${endpoint}"`;
143
217
  }
144
218
 
145
- const apiVersion = getApiVersion(model);
219
+ const apiVersion = getApiVersionInQueryParam(model);
146
220
  let apiVersionStatement: string = "";
147
221
  if (apiVersion) {
148
222
  apiVersionStatement = `options.apiVersion = options.apiVersion ?? "${apiVersion}"`;
@@ -224,6 +298,7 @@ function getClientFactoryBody(
224
298
  }
225
299
 
226
300
  return [
301
+ ...optionalUrlParameters,
227
302
  baseUrlStatement,
228
303
  apiVersionStatement,
229
304
  credentials,
@@ -235,17 +310,44 @@ function getClientFactoryBody(
235
310
  ];
236
311
  }
237
312
 
238
- function getApiVersion(model: RLCModel): string | undefined {
239
- if (!model.apiVersionParam) {
313
+ function getApiVersionInQueryParam(model: RLCModel): string | undefined {
314
+ if (!model.apiVersionInQueryParam) {
240
315
  return undefined;
241
316
  }
242
317
 
243
318
  if (
244
- model.apiVersionParam &&
245
- isConstantSchema(model.apiVersionParam as Schema)
319
+ model.apiVersionInQueryParam &&
320
+ isConstantSchema(model.apiVersionInQueryParam as Schema)
246
321
  ) {
247
- return model.apiVersionParam.default;
322
+ return model.apiVersionInQueryParam.default;
248
323
  }
249
324
 
250
325
  return undefined;
251
326
  }
327
+
328
+ function normalizeUrlInfo(model: RLCModel) {
329
+ if (
330
+ !model ||
331
+ !model.urlInfo ||
332
+ !model.urlInfo.endpoint ||
333
+ !model.urlInfo.urlParameters ||
334
+ model.urlInfo.urlParameters.length === 0
335
+ ) {
336
+ return;
337
+ }
338
+
339
+ let parsedEndpoint = model.urlInfo.endpoint;
340
+ const urlParameters = model.urlInfo.urlParameters;
341
+ urlParameters.forEach((urlParameter) => {
342
+ const name = urlParameter.name;
343
+ const normalizedName = normalizeName(name, NameType.Parameter);
344
+ if (name !== normalizedName) {
345
+ urlParameter.name = normalizedName;
346
+ parsedEndpoint = parsedEndpoint.replace(
347
+ `{${name}}`,
348
+ `{${normalizedName}}`
349
+ );
350
+ }
351
+ });
352
+ model.urlInfo.endpoint = parsedEndpoint;
353
+ }
@@ -18,8 +18,12 @@ import {
18
18
  } from "./helpers/operationHelpers.js";
19
19
  import { PathMetadata, Paths, RLCModel } from "./interfaces.js";
20
20
  import { generateMethodShortcuts } from "./helpers/shortcutMethods.js";
21
- import { REST_CLIENT_RESERVED } from './buildMethodShortcuts.js'
22
- import { CasingConvention, NameType, normalizeName } from "./helpers/nameUtils.js";
21
+ import { REST_CLIENT_RESERVED } from "./buildMethodShortcuts.js";
22
+ import {
23
+ CasingConvention,
24
+ NameType,
25
+ normalizeName
26
+ } from "./helpers/nameUtils.js";
23
27
  import { pascalCase } from "./helpers/nameUtils.js";
24
28
 
25
29
  export function buildClientDefinitions(model: RLCModel) {
@@ -5,9 +5,13 @@ import { Project, SourceFile } from "ts-morph";
5
5
  import { NameType, normalizeName } from "./helpers/nameUtils.js";
6
6
  import {
7
7
  hasInputModels,
8
+ hasMultiCollection,
8
9
  hasOutputModels,
9
10
  hasPagingOperations,
11
+ hasPipeCollection,
10
12
  hasPollingOperations,
13
+ hasSsvCollection,
14
+ hasTsvCollection,
11
15
  hasUnexpectedHelper
12
16
  } from "./helpers/operationHelpers.js";
13
17
  import { RLCModel } from "./interfaces.js";
@@ -89,6 +93,19 @@ function generateRLCIndexForMultiClient(file: SourceFile, model: RLCModel) {
89
93
  exports.push("PollingHelper");
90
94
  }
91
95
 
96
+ if (
97
+ hasMultiCollection(model) ||
98
+ hasSsvCollection(model) ||
99
+ hasPipeCollection(model) ||
100
+ hasTsvCollection(model)
101
+ ) {
102
+ file.addImportDeclaration({
103
+ namespaceImport: "SerializeHelper",
104
+ moduleSpecifier: "./serializeHelper"
105
+ });
106
+ exports.push("SerializeHelper");
107
+ }
108
+
92
109
  file.addExportDeclarations([
93
110
  {
94
111
  moduleSpecifier: `./${moduleName}`,
@@ -165,6 +182,17 @@ function generateRLCIndex(file: SourceFile, model: RLCModel) {
165
182
  ]);
166
183
  }
167
184
 
185
+ if (
186
+ hasMultiCollection(model) ||
187
+ hasSsvCollection(model) ||
188
+ hasPipeCollection(model) ||
189
+ hasTsvCollection(model)
190
+ ) {
191
+ file.addExportDeclarations([{
192
+ moduleSpecifier: "./serializeHelper"
193
+ }]);
194
+ }
195
+
168
196
  file.addExportAssignment({
169
197
  expression: createClientFuncName,
170
198
  isExportEquals: false
@@ -22,9 +22,9 @@ export function buildIsUnexpectedHelper(model: RLCModel) {
22
22
  });
23
23
 
24
24
  let map: Record<string, string[]> = {};
25
- let allResponseTypes: Set<string> = new Set();
26
- let allErrorTypes: Set<string> = new Set();
27
- let overloads: OptionalKind<FunctionDeclarationOverloadStructure>[] = [];
25
+ const allResponseTypes: Set<string> = new Set();
26
+ const allErrorTypes: Set<string> = new Set();
27
+ const overloads: OptionalKind<FunctionDeclarationOverloadStructure>[] = [];
28
28
  const pathDictionary = model.paths;
29
29
 
30
30
  for (const [path, details] of Object.entries(pathDictionary)) {
@@ -112,7 +112,7 @@ export function buildIsUnexpectedHelper(model: RLCModel) {
112
112
  } pathDetails = responseMap[\`\${method} \${url.pathname}\`];
113
113
  if (!pathDetails) {`,
114
114
  hasTemplate
115
- ? "pathDetails = geParametrizedPathSuccess(method, url.pathname);"
115
+ ? "pathDetails = getParametrizedPathSuccess(method, url.pathname);"
116
116
  : `return true;`,
117
117
  ` }
118
118
  return !pathDetails.includes(response.status);
@@ -122,7 +122,7 @@ export function buildIsUnexpectedHelper(model: RLCModel) {
122
122
  if (hasTemplate) {
123
123
  isErrorHelper.addFunction({
124
124
  isExported: false,
125
- name: "geParametrizedPathSuccess",
125
+ name: "getParametrizedPathSuccess",
126
126
  parameters: [
127
127
  {
128
128
  name: "method",
@@ -136,75 +136,75 @@ export function buildIsUnexpectedHelper(model: RLCModel) {
136
136
  returnType: `string[]`,
137
137
  statements: [
138
138
  `
139
- const pathParts = path.split("/");
140
-
141
- // Iterate the responseMap to find a match
142
- for (const [key, value] of Object.entries(responseMap)) {
143
- // Extracting the path from the map key which is in format
144
- // GET /path/foo
145
- if (!key.startsWith(method)) {
146
- continue;
147
- }
148
- const candidatePath = getPathFromMapKey(key);
149
- // Get each part of the url path
150
- const candidateParts = candidatePath.split("/");
151
-
152
- // If the candidate and actual paths don't match in size
153
- // we move on to the next candidate path
154
- if (
155
- candidateParts.length === pathParts.length &&
156
- hasParametrizedPath(key)
157
- ) {
139
+ const pathParts = path.split("/");
140
+
141
+ // Traverse list to match the longest candidate
142
+ // matchedLen: the length of candidate path
143
+ // matchedValue: the matched status code array
144
+ let matchedLen = -1,
145
+ matchedValue: string[] = [];
146
+
147
+ // Iterate the responseMap to find a match
148
+ for (const [key, value] of Object.entries(responseMap)) {
149
+ // Extracting the path from the map key which is in format
150
+ // GET /path/foo
151
+ if (!key.startsWith(method)) {
152
+ continue;
153
+ }
154
+ const candidatePath = getPathFromMapKey(key);
155
+ // Get each part of the url path
156
+ const candidateParts = candidatePath.split("/");
157
+
158
158
  // track if we have found a match to return the values found.
159
159
  let found = true;
160
- for (let i = 0; i < candidateParts.length; i++) {
160
+ for (
161
+ let i = candidateParts.length - 1, j = pathParts.length - 1;
162
+ i >= 1 && j >= 1;
163
+ i--, j--
164
+ ) {
161
165
  if (
162
166
  candidateParts[i]?.startsWith("{") &&
163
- candidateParts[i]?.endsWith("}")
167
+ candidateParts[i]?.indexOf("}") !== -1
164
168
  ) {
169
+ const start = candidateParts[i]!.indexOf("}") + 1,
170
+ end = candidateParts[i]?.length;
165
171
  // If the current part of the candidate is a "template" part
166
- // it is a match with the actual path part on hand
167
- // skip as the parameterized part can match anything
172
+ // Try to use the suffix of pattern to match the path
173
+ // {guid} ==> $
174
+ // {guid}:export ==> :export$
175
+ const isMatched = new RegExp(
176
+ \`\${candidateParts[i]?.slice(start, end)}\`
177
+ ).test(pathParts[j] || '');
178
+
179
+ if (!isMatched) {
180
+ found = false;
181
+ break;
182
+ }
168
183
  continue;
169
184
  }
170
-
185
+
171
186
  // If the candidate part is not a template and
172
187
  // the parts don't match mark the candidate as not found
173
188
  // to move on with the next candidate path.
174
- if (candidateParts[i] !== pathParts[i]) {
189
+ if (candidateParts[i] !== pathParts[j]) {
175
190
  found = false;
176
191
  break;
177
192
  }
178
193
  }
179
-
194
+
180
195
  // We finished evaluating the current candidate parts
181
- // if all parts matched we return the success values form
182
- // the path mapping.
183
- if (found) {
184
- return value;
196
+ // Update the matched value if and only if we found the longer pattern
197
+ if (found && candidatePath.length > matchedLen) {
198
+ matchedLen = candidatePath.length;
199
+ matchedValue = value;
185
200
  }
186
201
  }
187
- }
188
-
189
- // No match was found, return an empty array.
190
- return [];
202
+
203
+ return matchedValue;
191
204
  `
192
205
  ]
193
206
  });
194
207
 
195
- isErrorHelper.addFunction({
196
- isExported: false,
197
- name: "hasParametrizedPath",
198
- parameters: [
199
- {
200
- name: "path",
201
- type: "string"
202
- }
203
- ],
204
- returnType: `boolean`,
205
- statements: [`return path.includes("/{");`]
206
- });
207
-
208
208
  isErrorHelper.addFunction({
209
209
  isExported: false,
210
210
  name: "getPathFromMapKey",
@@ -22,7 +22,7 @@ export const REST_CLIENT_RESERVED: ReservedName[] = [
22
22
  ];
23
23
 
24
24
  export function buildMethodShortcutImplementation(paths: Paths) {
25
- let keys: Record<string, string[]> = {};
25
+ const keys: Record<string, string[]> = {};
26
26
  for (const path of Object.keys(paths)) {
27
27
  const groupName = normalizeName(
28
28
  paths[path].operationGroupName,
@@ -33,6 +33,9 @@ export function buildObjectInterfaces(
33
33
  const objectInterfaces: InterfaceDeclarationStructure[] = [];
34
34
 
35
35
  for (const objectSchema of objectSchemas) {
36
+ if (objectSchema.alias || objectSchema.outputAlias) {
37
+ continue;
38
+ }
36
39
  const baseName = getObjectBaseName(objectSchema, schemaUsage);
37
40
  const interfaceDeclaration = getObjectInterfaceDeclaration(
38
41
  baseName,
@@ -46,6 +49,40 @@ export function buildObjectInterfaces(
46
49
  return objectInterfaces;
47
50
  }
48
51
 
52
+ export function buildObjectAliases(
53
+ model: RLCModel,
54
+ importedModels: Set<string>,
55
+ schemaUsage: SchemaContext[]
56
+ ) {
57
+ const objectSchemas: ObjectSchema[] = (model.schemas ?? []).filter(
58
+ (o) =>
59
+ isObjectSchema(o) &&
60
+ (o as ObjectSchema).usage?.some((u) => schemaUsage.includes(u))
61
+ );
62
+ const objectAliases: TypeAliasDeclarationStructure[] = [];
63
+
64
+ for (const objectSchema of objectSchemas) {
65
+ if (objectSchema.alias || objectSchema.outputAlias) {
66
+ const description = objectSchema.description;
67
+ objectAliases.push({
68
+ kind: StructureKind.TypeAlias,
69
+ ...(description && { docs: [{ description }] }),
70
+ name: schemaUsage.includes(SchemaContext.Input)
71
+ ? `${objectSchema.typeName}`
72
+ : `${objectSchema.outputTypeName}`,
73
+ type: schemaUsage.includes(SchemaContext.Input)
74
+ ? `${objectSchema.alias}`
75
+ : `${objectSchema.outputAlias}`,
76
+ isExported: true
77
+ });
78
+ if (objectSchema.alias?.startsWith("Paged<")) {
79
+ importedModels.add("Paged");
80
+ }
81
+ }
82
+ }
83
+ return objectAliases;
84
+ }
85
+
49
86
  export function buildPolymorphicAliases(
50
87
  model: RLCModel,
51
88
  schemaUsage: SchemaContext[]
@@ -123,7 +160,7 @@ function getPolymorphicTypeAlias(
123
160
  }
124
161
 
125
162
  const description = objectSchema.description;
126
-
163
+
127
164
  return {
128
165
  kind: StructureKind.TypeAlias,
129
166
  ...(description && { docs: [{ description }] }),
@@ -316,7 +353,7 @@ function getImmediateParentsNames(
316
353
  return [];
317
354
  }
318
355
 
319
- let extendFrom: string[] = [];
356
+ const extendFrom: string[] = [];
320
357
 
321
358
  // If an immediate parent is a DictionarySchema, that means that the object has been marked
322
359
  // with additional properties. We need to add Record<string, unknown> to the extend list and
@@ -350,7 +387,12 @@ function getPropertySignatures(
350
387
  schemaUsage: SchemaContext[],
351
388
  importedModels: Set<string>
352
389
  ) {
353
- return Object.keys(properties).map((p) =>
390
+ let validProperties = Object.keys(properties);
391
+ const readOnlyFilter = (name: string) =>
392
+ !(schemaUsage.includes(SchemaContext.Input) && properties[name].readOnly);
393
+ const neverFilter = (name: string) => properties[name].type !== "never";
394
+ validProperties = validProperties.filter(readOnlyFilter).filter(neverFilter);
395
+ return validProperties.map((p) =>
354
396
  getPropertySignature(
355
397
  { ...properties[p], name: p },
356
398
  schemaUsage,
@@ -368,26 +410,40 @@ function getPropertySignatures(
368
410
  export function getPropertySignature(
369
411
  property: Property | Parameter,
370
412
  schemaUsage: SchemaContext[],
371
- importedModels = new Set<string>()
413
+ importedModels: Set<string>
372
414
  ): PropertySignatureStructure {
373
415
  const propertyName = property.name;
374
416
 
375
417
  const description = property.description;
376
- const type =
377
- ((schemaUsage.includes(SchemaContext.Output) &&
378
- property.usage?.includes(SchemaContext.Output)) ||
379
- (schemaUsage.includes(SchemaContext.Exception) &&
380
- property.usage?.includes(SchemaContext.Exception))) &&
381
- property.outputTypeName
418
+ let type =
419
+ generateForOutput(schemaUsage, property.usage) && property.outputTypeName
382
420
  ? property.outputTypeName
383
421
  : property.typeName
384
422
  ? property.typeName
385
423
  : property.type;
424
+ if (property.typeName && property.fromCore) {
425
+ importedModels.add(property.typeName);
426
+ type = property.typeName;
427
+ }
386
428
  return {
387
429
  name: propertyName,
388
430
  ...(description && { docs: [{ description }] }),
389
431
  hasQuestionToken: !property.required,
432
+ isReadonly:
433
+ generateForOutput(schemaUsage, property.usage) && property.readOnly,
390
434
  type,
391
435
  kind: StructureKind.PropertySignature
392
436
  };
393
437
  }
438
+
439
+ function generateForOutput(
440
+ schemaUsage: SchemaContext[],
441
+ propertyUsage?: SchemaContext[]
442
+ ) {
443
+ return (
444
+ (schemaUsage.includes(SchemaContext.Output) &&
445
+ propertyUsage?.includes(SchemaContext.Output)) ||
446
+ (schemaUsage.includes(SchemaContext.Exception) &&
447
+ propertyUsage?.includes(SchemaContext.Exception))
448
+ );
449
+ }
@@ -11,12 +11,12 @@ import {
11
11
  import * as path from "path";
12
12
  import {
13
13
  ImportKind,
14
+ ObjectSchema,
14
15
  ParameterMetadata,
15
16
  ParameterMetadatas,
16
17
  RLCModel,
17
18
  Schema
18
19
  } from "./interfaces.js";
19
- import { NameType, normalizeName } from "./helpers/nameUtils.js";
20
20
  import {
21
21
  getParameterBaseName,
22
22
  getParameterTypeName
@@ -92,6 +92,11 @@ export function buildParameterTypes(model: RLCModel) {
92
92
  i
93
93
  );
94
94
 
95
+ const bodyTypeAlias = buildBodyTypeAlias(parameter);
96
+ if (bodyTypeAlias) {
97
+ parametersFile.addTypeAlias(bodyTypeAlias);
98
+ }
99
+
95
100
  // Add interfaces for body and query parameters
96
101
  parametersFile.addInterfaces([
97
102
  ...(bodyParameterDefinition ?? []),
@@ -215,7 +220,7 @@ function getPropertyFromSchema(schema: Schema): PropertySignatureStructure {
215
220
  name: schema.name,
216
221
  ...(description && { docs: [{ description }] }),
217
222
  type: schema.type,
218
- hasQuestionToken: !Boolean(schema.required),
223
+ hasQuestionToken: !schema.required,
219
224
  kind: StructureKind.PropertySignature
220
225
  };
221
226
  }
@@ -296,8 +301,12 @@ function buildHeaderParameterDefinitions(
296
301
  baseName
297
302
  );
298
303
 
304
+ let isOptional = true;
299
305
  if (headersInterface) {
300
306
  parametersFile.addInterface(headersInterface);
307
+ isOptional = !(headersInterface.properties || []).some(
308
+ (prop) => prop.hasQuestionToken === false
309
+ );
301
310
  }
302
311
 
303
312
  internalReferences.add(headerParameterInterfaceName);
@@ -310,7 +319,8 @@ function buildHeaderParameterDefinitions(
310
319
  {
311
320
  name: "headers",
312
321
  type: `RawHttpHeadersInput & ${baseName}Headers`,
313
- kind: StructureKind.PropertySignature
322
+ kind: StructureKind.PropertySignature,
323
+ hasQuestionToken: isOptional
314
324
  }
315
325
  ]
316
326
  };
@@ -433,3 +443,35 @@ function buildBodyParametersDefinition(
433
443
  ];
434
444
  }
435
445
  }
446
+
447
+ export function buildBodyTypeAlias(parameters: ParameterMetadatas) {
448
+ const bodyParameters = parameters.body;
449
+ if (
450
+ !bodyParameters ||
451
+ !bodyParameters?.body ||
452
+ !bodyParameters?.body.length
453
+ ) {
454
+ return undefined;
455
+ }
456
+ const schema = bodyParameters.body[0] as ObjectSchema;
457
+ const headerParameters = (parameters.parameters || []).filter(
458
+ (p) => p.type === "header" && p.name === "contentType"
459
+ );
460
+ if (!headerParameters.length || headerParameters.length > 1) {
461
+ return undefined;
462
+ }
463
+
464
+ const contentType = headerParameters[0].param.type;
465
+ const description = `${schema.description}`;
466
+ const typeName = `${schema.typeName}ResourceMergeAndPatch`;
467
+ if (contentType.includes("application/merge-patch+json")) {
468
+ const type = `Partial<${schema.typeName}>`;
469
+ return {
470
+ // kind: StructureKind.TypeAlias,
471
+ ...(description && { docs: [{ description }] }),
472
+ name: `${typeName}`,
473
+ type,
474
+ isExported: true
475
+ };
476
+ }
477
+ }