@azure-tools/rlc-common 0.11.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 (232) 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 +53 -0
  5. package/.rush/temp/shrinkwrap-deps.json +147 -0
  6. package/CHANGELOG.md +61 -0
  7. package/CONTRIBUTING.md +30 -0
  8. package/README.md +3 -0
  9. package/dist/buildClient.js +268 -0
  10. package/dist/buildClient.js.map +1 -0
  11. package/dist/buildClientDefinitions.js +160 -0
  12. package/dist/buildClientDefinitions.js.map +1 -0
  13. package/dist/buildIndexFile.js +170 -0
  14. package/dist/buildIndexFile.js.map +1 -0
  15. package/dist/buildIsUnexpectedHelper.js +220 -0
  16. package/dist/buildIsUnexpectedHelper.js.map +1 -0
  17. package/dist/buildMethodShortcuts.js +50 -0
  18. package/dist/buildMethodShortcuts.js.map +1 -0
  19. package/dist/buildObjectTypes.js +286 -0
  20. package/dist/buildObjectTypes.js.map +1 -0
  21. package/dist/buildPaginateHelper.js +30 -0
  22. package/dist/buildPaginateHelper.js.map +1 -0
  23. package/dist/buildParameterTypes.js +315 -0
  24. package/dist/buildParameterTypes.js.map +1 -0
  25. package/dist/buildPollingHelper.js +21 -0
  26. package/dist/buildPollingHelper.js.map +1 -0
  27. package/dist/buildResponseTypes.js +135 -0
  28. package/dist/buildResponseTypes.js.map +1 -0
  29. package/dist/buildSchemaType.js +65 -0
  30. package/dist/buildSchemaType.js.map +1 -0
  31. package/dist/buildSerializeHelper.js +35 -0
  32. package/dist/buildSerializeHelper.js.map +1 -0
  33. package/dist/buildTopLevelIndexFile.js +48 -0
  34. package/dist/buildTopLevelIndexFile.js.map +1 -0
  35. package/dist/helpers/nameConstructors.js +41 -0
  36. package/dist/helpers/nameConstructors.js.map +1 -0
  37. package/dist/helpers/nameUtils.js +196 -0
  38. package/dist/helpers/nameUtils.js.map +1 -0
  39. package/dist/helpers/operationHelpers.js +103 -0
  40. package/dist/helpers/operationHelpers.js.map +1 -0
  41. package/dist/helpers/pathUtils.js +13 -0
  42. package/dist/helpers/pathUtils.js.map +1 -0
  43. package/dist/helpers/schemaHelpers.js +27 -0
  44. package/dist/helpers/schemaHelpers.js.map +1 -0
  45. package/dist/helpers/shortcutMethods.js +46 -0
  46. package/dist/helpers/shortcutMethods.js.map +1 -0
  47. package/dist/index.js +45 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/interfaces.js +18 -0
  50. package/dist/interfaces.js.map +1 -0
  51. package/dist/metadata/buildApiExtractorConfig.js +56 -0
  52. package/dist/metadata/buildApiExtractorConfig.js.map +1 -0
  53. package/dist/metadata/buildESLintConfig.js +33 -0
  54. package/dist/metadata/buildESLintConfig.js.map +1 -0
  55. package/dist/metadata/buildLicenseFile.js +41 -0
  56. package/dist/metadata/buildLicenseFile.js.map +1 -0
  57. package/dist/metadata/buildPackageFile.js +274 -0
  58. package/dist/metadata/buildPackageFile.js.map +1 -0
  59. package/dist/metadata/buildReadmeFile.js +170 -0
  60. package/dist/metadata/buildReadmeFile.js.map +1 -0
  61. package/dist/metadata/buildRollupConfig.js +144 -0
  62. package/dist/metadata/buildRollupConfig.js.map +1 -0
  63. package/dist/metadata/buildTsConfig.js +72 -0
  64. package/dist/metadata/buildTsConfig.js.map +1 -0
  65. package/dist/package.json +1 -0
  66. package/dist/static/paginateContent.js +214 -0
  67. package/dist/static/paginateContent.js.map +1 -0
  68. package/dist/static/pollingContent.js +98 -0
  69. package/dist/static/pollingContent.js.map +1 -0
  70. package/dist/static/serializeHelper.js +30 -0
  71. package/dist/static/serializeHelper.js.map +1 -0
  72. package/dist/test/buildEnvFile.js +31 -0
  73. package/dist/test/buildEnvFile.js.map +1 -0
  74. package/dist/test/buildKarmaConfig.js +19 -0
  75. package/dist/test/buildKarmaConfig.js.map +1 -0
  76. package/dist/test/buildRecordedClient.js +22 -0
  77. package/dist/test/buildRecordedClient.js.map +1 -0
  78. package/dist/test/buildSampleTest.js +19 -0
  79. package/dist/test/buildSampleTest.js.map +1 -0
  80. package/dist/test/template.js +191 -0
  81. package/dist/test/template.js.map +1 -0
  82. package/dist-esm/buildClient.js +261 -0
  83. package/dist-esm/buildClient.js.map +1 -0
  84. package/dist-esm/buildClientDefinitions.js +155 -0
  85. package/dist-esm/buildClientDefinitions.js.map +1 -0
  86. package/dist-esm/buildIndexFile.js +165 -0
  87. package/dist-esm/buildIndexFile.js.map +1 -0
  88. package/dist-esm/buildIsUnexpectedHelper.js +216 -0
  89. package/dist-esm/buildIsUnexpectedHelper.js.map +1 -0
  90. package/dist-esm/buildMethodShortcuts.js +46 -0
  91. package/dist-esm/buildMethodShortcuts.js.map +1 -0
  92. package/dist-esm/buildObjectTypes.js +288 -0
  93. package/dist-esm/buildObjectTypes.js.map +1 -0
  94. package/dist-esm/buildPaginateHelper.js +26 -0
  95. package/dist-esm/buildPaginateHelper.js.map +1 -0
  96. package/dist-esm/buildParameterTypes.js +321 -0
  97. package/dist-esm/buildParameterTypes.js.map +1 -0
  98. package/dist-esm/buildPollingHelper.js +17 -0
  99. package/dist-esm/buildPollingHelper.js.map +1 -0
  100. package/dist-esm/buildResponseTypes.js +140 -0
  101. package/dist-esm/buildResponseTypes.js.map +1 -0
  102. package/dist-esm/buildSchemaType.js +60 -0
  103. package/dist-esm/buildSchemaType.js.map +1 -0
  104. package/dist-esm/buildSerializeHelper.js +31 -0
  105. package/dist-esm/buildSerializeHelper.js.map +1 -0
  106. package/dist-esm/buildTopLevelIndexFile.js +44 -0
  107. package/dist-esm/buildTopLevelIndexFile.js.map +1 -0
  108. package/dist-esm/helpers/nameConstructors.js +34 -0
  109. package/dist-esm/helpers/nameConstructors.js.map +1 -0
  110. package/dist-esm/helpers/nameUtils.js +187 -0
  111. package/dist-esm/helpers/nameUtils.js.map +1 -0
  112. package/dist-esm/helpers/operationHelpers.js +84 -0
  113. package/dist-esm/helpers/operationHelpers.js.map +1 -0
  114. package/dist-esm/helpers/pathUtils.js +9 -0
  115. package/dist-esm/helpers/pathUtils.js.map +1 -0
  116. package/dist-esm/helpers/schemaHelpers.js +21 -0
  117. package/dist-esm/helpers/schemaHelpers.js.map +1 -0
  118. package/dist-esm/helpers/shortcutMethods.js +42 -0
  119. package/dist-esm/helpers/shortcutMethods.js.map +1 -0
  120. package/dist-esm/index.js +29 -0
  121. package/dist-esm/index.js.map +1 -0
  122. package/dist-esm/interfaces.js +15 -0
  123. package/dist-esm/interfaces.js.map +1 -0
  124. package/dist-esm/metadata/buildApiExtractorConfig.js +51 -0
  125. package/dist-esm/metadata/buildApiExtractorConfig.js.map +1 -0
  126. package/dist-esm/metadata/buildESLintConfig.js +28 -0
  127. package/dist-esm/metadata/buildESLintConfig.js.map +1 -0
  128. package/dist-esm/metadata/buildLicenseFile.js +36 -0
  129. package/dist-esm/metadata/buildLicenseFile.js.map +1 -0
  130. package/dist-esm/metadata/buildPackageFile.js +276 -0
  131. package/dist-esm/metadata/buildPackageFile.js.map +1 -0
  132. package/dist-esm/metadata/buildReadmeFile.js +167 -0
  133. package/dist-esm/metadata/buildReadmeFile.js.map +1 -0
  134. package/dist-esm/metadata/buildRollupConfig.js +139 -0
  135. package/dist-esm/metadata/buildRollupConfig.js.map +1 -0
  136. package/dist-esm/metadata/buildTsConfig.js +67 -0
  137. package/dist-esm/metadata/buildTsConfig.js.map +1 -0
  138. package/dist-esm/package.json +1 -0
  139. package/dist-esm/static/paginateContent.js +211 -0
  140. package/dist-esm/static/paginateContent.js.map +1 -0
  141. package/dist-esm/static/pollingContent.js +95 -0
  142. package/dist-esm/static/pollingContent.js.map +1 -0
  143. package/dist-esm/static/serializeHelper.js +27 -0
  144. package/dist-esm/static/serializeHelper.js.map +1 -0
  145. package/dist-esm/test/buildEnvFile.js +24 -0
  146. package/dist-esm/test/buildEnvFile.js.map +1 -0
  147. package/dist-esm/test/buildKarmaConfig.js +14 -0
  148. package/dist-esm/test/buildKarmaConfig.js.map +1 -0
  149. package/dist-esm/test/buildRecordedClient.js +17 -0
  150. package/dist-esm/test/buildRecordedClient.js.map +1 -0
  151. package/dist-esm/test/buildSampleTest.js +14 -0
  152. package/dist-esm/test/buildSampleTest.js.map +1 -0
  153. package/dist-esm/test/template.js +188 -0
  154. package/dist-esm/test/template.js.map +1 -0
  155. package/package.json +46 -0
  156. package/publishPackage.js +11 -0
  157. package/rlc-common.build.log +2 -0
  158. package/src/buildClient.ts +353 -0
  159. package/src/buildClientDefinitions.ts +235 -0
  160. package/src/buildIndexFile.ts +202 -0
  161. package/src/buildIsUnexpectedHelper.ts +240 -0
  162. package/src/buildMethodShortcuts.ts +75 -0
  163. package/src/buildObjectTypes.ts +449 -0
  164. package/src/buildPaginateHelper.ts +33 -0
  165. package/src/buildParameterTypes.ts +477 -0
  166. package/src/buildPollingHelper.ts +18 -0
  167. package/src/buildResponseTypes.ts +186 -0
  168. package/src/buildSchemaType.ts +85 -0
  169. package/src/buildSerializeHelper.ts +42 -0
  170. package/src/buildTopLevelIndexFile.ts +52 -0
  171. package/src/helpers/nameConstructors.ts +93 -0
  172. package/src/helpers/nameUtils.ts +227 -0
  173. package/src/helpers/operationHelpers.ts +119 -0
  174. package/src/helpers/pathUtils.ts +9 -0
  175. package/src/helpers/schemaHelpers.ts +25 -0
  176. package/src/helpers/shortcutMethods.ts +60 -0
  177. package/src/index.ts +29 -0
  178. package/src/interfaces.ts +227 -0
  179. package/src/metadata/buildApiExtractorConfig.ts +59 -0
  180. package/src/metadata/buildESLintConfig.ts +34 -0
  181. package/src/metadata/buildLicenseFile.ts +39 -0
  182. package/src/metadata/buildPackageFile.ts +334 -0
  183. package/src/metadata/buildReadmeFile.ts +231 -0
  184. package/src/metadata/buildRollupConfig.ts +147 -0
  185. package/src/metadata/buildTsConfig.ts +79 -0
  186. package/src/static/paginateContent.ts +210 -0
  187. package/src/static/pollingContent.ts +94 -0
  188. package/src/static/serializeHelper.ts +29 -0
  189. package/src/test/buildEnvFile.ts +26 -0
  190. package/src/test/buildKarmaConfig.ts +15 -0
  191. package/src/test/buildRecordedClient.ts +18 -0
  192. package/src/test/buildSampleTest.ts +15 -0
  193. package/src/test/template.ts +191 -0
  194. package/tsconfig-cjs.json +9 -0
  195. package/tsconfig-common.json +13 -0
  196. package/tsconfig.json +13 -0
  197. package/types/buildClient.d.ts +2 -0
  198. package/types/buildClientDefinitions.d.ts +5 -0
  199. package/types/buildIndexFile.d.ts +5 -0
  200. package/types/buildIsUnexpectedHelper.d.ts +5 -0
  201. package/types/buildMethodShortcuts.d.ts +4 -0
  202. package/types/buildObjectTypes.d.ts +15 -0
  203. package/types/buildPaginateHelper.d.ts +5 -0
  204. package/types/buildParameterTypes.d.ts +13 -0
  205. package/types/buildPollingHelper.d.ts +5 -0
  206. package/types/buildResponseTypes.d.ts +5 -0
  207. package/types/buildSchemaType.d.ts +19 -0
  208. package/types/buildSerializeHelper.d.ts +5 -0
  209. package/types/buildTopLevelIndexFile.d.ts +5 -0
  210. package/types/helpers/nameConstructors.d.ts +28 -0
  211. package/types/helpers/nameUtils.d.ts +25 -0
  212. package/types/helpers/operationHelpers.d.ts +13 -0
  213. package/types/helpers/pathUtils.d.ts +1 -0
  214. package/types/helpers/schemaHelpers.d.ts +4 -0
  215. package/types/helpers/shortcutMethods.d.ts +3 -0
  216. package/types/index.d.ts +26 -0
  217. package/types/interfaces.d.ts +200 -0
  218. package/types/metadata/buildApiExtractorConfig.d.ts +5 -0
  219. package/types/metadata/buildESLintConfig.d.ts +5 -0
  220. package/types/metadata/buildLicenseFile.d.ts +5 -0
  221. package/types/metadata/buildPackageFile.d.ts +5 -0
  222. package/types/metadata/buildReadmeFile.d.ts +5 -0
  223. package/types/metadata/buildRollupConfig.d.ts +5 -0
  224. package/types/metadata/buildTsConfig.d.ts +5 -0
  225. package/types/static/paginateContent.d.ts +1 -0
  226. package/types/static/pollingContent.d.ts +1 -0
  227. package/types/static/serializeHelper.d.ts +4 -0
  228. package/types/test/buildEnvFile.d.ts +9 -0
  229. package/types/test/buildKarmaConfig.d.ts +5 -0
  230. package/types/test/buildRecordedClient.d.ts +5 -0
  231. package/types/test/buildSampleTest.d.ts +5 -0
  232. package/types/test/template.d.ts +5 -0
@@ -0,0 +1,353 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ import {
5
+ InterfaceDeclarationStructure,
6
+ OptionalKind,
7
+ Project,
8
+ StatementStructures,
9
+ StructureKind,
10
+ VariableDeclarationKind,
11
+ VariableStatementStructure,
12
+ WriterFunction
13
+ } from "ts-morph";
14
+ import * as path from "path";
15
+ import { NameType, normalizeName } from "./helpers/nameUtils.js";
16
+ import { isConstantSchema } from "./helpers/schemaHelpers.js";
17
+ import { buildMethodShortcutImplementation } from "./buildMethodShortcuts.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
+ }
43
+
44
+ export function buildClient(model: RLCModel): File | undefined {
45
+ const name = normalizeName(model.libraryName, NameType.File);
46
+ const { srcPath } = model;
47
+ const project = new Project();
48
+ const filePath = path.join(srcPath, `${name}.ts`);
49
+ const clientFile = project.createSourceFile(filePath, undefined, {
50
+ overwrite: true
51
+ });
52
+
53
+ // Get all paths
54
+ const clientName = model.libraryName;
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
+ }
78
+
79
+ if (!model.options) {
80
+ return undefined;
81
+ }
82
+ const { multiClient, batch } = model.options;
83
+ const { addCredentials, credentialScopes, credentialKeyHeaderName } =
84
+ model.options;
85
+ const credentialTypes =
86
+ credentialScopes && credentialScopes.length > 0 ? ["TokenCredential"] : [];
87
+
88
+ if (credentialKeyHeaderName) {
89
+ credentialTypes.push("KeyCredential");
90
+ }
91
+
92
+ const commonClientParams = [
93
+ ...(urlParameters ?? []),
94
+ ...(addCredentials === false ||
95
+ !isSecurityInfoDefined(credentialScopes, credentialKeyHeaderName)
96
+ ? []
97
+ : [
98
+ {
99
+ name: "credentials",
100
+ type: credentialTypes.join(" | "),
101
+ description: `uniquely identify client credential`
102
+ }
103
+ ])
104
+ ];
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
+ ];
114
+ const functionStatement = {
115
+ isExported: true,
116
+ name: `createClient`,
117
+ parameters: allClientParams,
118
+ docs: [
119
+ {
120
+ description:
121
+ `Initialize a new instance of \`${clientInterfaceName}\` \n` +
122
+ allClientParams
123
+ .map((param) => {
124
+ return `@param ${param.name} type: ${param.type
125
+ .split("=")[0]
126
+ .split(" ")
127
+ .join("")}, ${
128
+ param.description ?? "The parameter " + param.name
129
+ }`;
130
+ })
131
+ .join("\n")
132
+ }
133
+ ],
134
+ returnType: clientInterfaceName,
135
+ isDefaultExport: false,
136
+ statements: getClientFactoryBody(model, clientInterfaceName)
137
+ };
138
+
139
+ if (!multiClient || !batch || batch.length === 1) {
140
+ functionStatement.isDefaultExport = true;
141
+ }
142
+ clientFile.addFunction(functionStatement);
143
+
144
+ clientFile.addImportDeclarations([
145
+ {
146
+ namedImports: ["getClient", "ClientOptions"],
147
+ moduleSpecifier: "@azure-rest/core-client"
148
+ }
149
+ ]);
150
+
151
+ if (
152
+ addCredentials &&
153
+ isSecurityInfoDefined(credentialScopes, credentialKeyHeaderName)
154
+ ) {
155
+ clientFile.addImportDeclarations([
156
+ {
157
+ namedImports: credentialTypes,
158
+ moduleSpecifier: "@azure/core-auth"
159
+ }
160
+ ]);
161
+ }
162
+ clientFile.addImportDeclarations([
163
+ {
164
+ namedImports: [`${clientInterfaceName}`],
165
+ moduleSpecifier: "./clientDefinitions"
166
+ }
167
+ ]);
168
+ return { path: filePath, content: clientFile.getFullText() };
169
+ }
170
+
171
+ function isSecurityInfoDefined(
172
+ credentialScopes?: string[],
173
+ credentialKeyHeaderName?: string
174
+ ) {
175
+ return (
176
+ (credentialScopes && credentialScopes.length > 0) || credentialKeyHeaderName
177
+ );
178
+ }
179
+
180
+ function getClientFactoryBody(
181
+ model: RLCModel,
182
+ clientTypeName: string
183
+ ): string | WriterFunction | (string | WriterFunction | StatementStructures)[] {
184
+ if (!model.options || !model.options.packageDetails || !model.urlInfo) {
185
+ return "";
186
+ }
187
+ const { includeShortcuts, packageDetails } = model.options;
188
+ let clientPackageName = packageDetails.nameWithoutScope ?? "";
189
+ const packageVersion = packageDetails.version;
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
+
204
+ let baseUrl: string;
205
+ if (urlParameters && endpoint) {
206
+ let parsedEndpoint = endpoint;
207
+ urlParameters.forEach((urlParameter) => {
208
+ parsedEndpoint = parsedEndpoint.replace(
209
+ `{${urlParameter.name}}`,
210
+ `\${${urlParameter.name}}`
211
+ );
212
+ });
213
+
214
+ baseUrl = `options.baseUrl ?? \`${parsedEndpoint}\``;
215
+ } else {
216
+ baseUrl = `options.baseUrl ?? "${endpoint}"`;
217
+ }
218
+
219
+ const apiVersion = getApiVersionInQueryParam(model);
220
+ let apiVersionStatement: string = "";
221
+ if (apiVersion) {
222
+ apiVersionStatement = `options.apiVersion = options.apiVersion ?? "${apiVersion}"`;
223
+ }
224
+
225
+ if (!clientPackageName.endsWith("-rest")) {
226
+ clientPackageName = clientPackageName + "-rest";
227
+ }
228
+ const userAgentInfoStatement =
229
+ "const userAgentInfo = `azsdk-js-" +
230
+ clientPackageName +
231
+ "/" +
232
+ packageVersion +
233
+ "`;";
234
+ const userAgentPrefix =
235
+ "options.userAgentOptions && options.userAgentOptions.userAgentPrefix ? `${options.userAgentOptions.userAgentPrefix} ${userAgentInfo}`: `${userAgentInfo}`;";
236
+ const userAgentStatement: VariableStatementStructure = {
237
+ kind: StructureKind.VariableStatement,
238
+ declarationKind: VariableDeclarationKind.Const,
239
+ declarations: [{ name: "userAgentPrefix", initializer: userAgentPrefix }]
240
+ };
241
+
242
+ const userAgentOptionsStatement = `options = {
243
+ ...options,
244
+ userAgentOptions: {
245
+ userAgentPrefix
246
+ }
247
+ }`;
248
+
249
+ const baseUrlStatement: VariableStatementStructure = {
250
+ kind: StructureKind.VariableStatement,
251
+ declarationKind: VariableDeclarationKind.Const,
252
+ declarations: [{ name: "baseUrl", initializer: baseUrl }]
253
+ };
254
+
255
+ const { credentialScopes, credentialKeyHeaderName } = model.options;
256
+
257
+ const scopesString =
258
+ credentialScopes && credentialScopes.length
259
+ ? credentialScopes.map((cs) => `"${cs}"`).join(", ")
260
+ : "";
261
+ const scopes = scopesString ? `scopes: [${scopesString}],` : "";
262
+
263
+ const apiKeyHeaderName = credentialKeyHeaderName
264
+ ? `apiKeyHeaderName: "${credentialKeyHeaderName}",`
265
+ : "";
266
+
267
+ const credentials =
268
+ scopes || apiKeyHeaderName
269
+ ? `options = {
270
+ ...options,
271
+ credentials: {
272
+ ${scopes}
273
+ ${apiKeyHeaderName}
274
+ },
275
+ }`
276
+ : "";
277
+
278
+ const getClient = `const client = getClient(
279
+ baseUrl, ${credentials ? "credentials," : ""} options
280
+ ) as ${clientTypeName};
281
+ `;
282
+
283
+ let returnStatement = `return client;`;
284
+
285
+ if (includeShortcuts) {
286
+ const shortcutImplementations = buildMethodShortcutImplementation(
287
+ model.paths
288
+ );
289
+ const shortcutBody = Object.keys(shortcutImplementations).map((key) => {
290
+ // If the operation group has an empty name, it means its operations are client
291
+ // level operations so we need to spread the definitions. Otherwise they are
292
+ // within an operation group so we add them as key: value
293
+ return `${
294
+ key && key !== "client" ? `"${key}":` : "..."
295
+ } {${shortcutImplementations[key].join()}}`;
296
+ });
297
+ returnStatement = `return { ...client, ${shortcutBody.join()} };`;
298
+ }
299
+
300
+ return [
301
+ ...optionalUrlParameters,
302
+ baseUrlStatement,
303
+ apiVersionStatement,
304
+ credentials,
305
+ userAgentInfoStatement,
306
+ userAgentStatement,
307
+ userAgentOptionsStatement,
308
+ getClient,
309
+ returnStatement
310
+ ];
311
+ }
312
+
313
+ function getApiVersionInQueryParam(model: RLCModel): string | undefined {
314
+ if (!model.apiVersionInQueryParam) {
315
+ return undefined;
316
+ }
317
+
318
+ if (
319
+ model.apiVersionInQueryParam &&
320
+ isConstantSchema(model.apiVersionInQueryParam as Schema)
321
+ ) {
322
+ return model.apiVersionInQueryParam.default;
323
+ }
324
+
325
+ return undefined;
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
+ }
@@ -0,0 +1,235 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ import {
5
+ CallSignatureDeclarationStructure,
6
+ InterfaceDeclarationStructure,
7
+ OptionalKind,
8
+ Project,
9
+ SourceFile,
10
+ StructureKind,
11
+ Writers
12
+ } from "ts-morph";
13
+ import * as path from "path";
14
+
15
+ import {
16
+ buildMethodDefinitions,
17
+ getPathParamDefinitions
18
+ } from "./helpers/operationHelpers.js";
19
+ import { PathMetadata, Paths, RLCModel } from "./interfaces.js";
20
+ import { generateMethodShortcuts } from "./helpers/shortcutMethods.js";
21
+ import { REST_CLIENT_RESERVED } from "./buildMethodShortcuts.js";
22
+ import {
23
+ CasingConvention,
24
+ NameType,
25
+ normalizeName
26
+ } from "./helpers/nameUtils.js";
27
+ import { pascalCase } from "./helpers/nameUtils.js";
28
+
29
+ export function buildClientDefinitions(model: RLCModel) {
30
+ const options = {
31
+ importedParameters: new Set<string>(),
32
+ importedResponses: new Set<string>(),
33
+ clientImports: new Set<string>()
34
+ };
35
+ const project = new Project();
36
+ const srcPath = model.srcPath;
37
+ const filePath = path.join(srcPath, `clientDefinitions.ts`);
38
+ const clientDefinitionsFile = project.createSourceFile(filePath, undefined, {
39
+ overwrite: true
40
+ });
41
+
42
+ // Get all paths
43
+ const pathDictionary = model.paths;
44
+ let shortcuts: OptionalKind<InterfaceDeclarationStructure>[] = [];
45
+ // There may be operations without an operation group, those shortcut
46
+ // methods need to be handled differently.
47
+ let shortcutsInOperationGroup: { name: string; type: string }[] = [];
48
+
49
+ if (model.options?.includeShortcuts) {
50
+ shortcuts = generateMethodShortcuts(model.paths);
51
+ clientDefinitionsFile.addInterfaces(shortcuts);
52
+ shortcutsInOperationGroup = shortcuts
53
+ .filter((s) => s.name !== "ClientOperations")
54
+ .map((s) => getShortcutName(s.name));
55
+ }
56
+
57
+ clientDefinitionsFile.addInterface({
58
+ name: "Routes",
59
+ isExported: true,
60
+ callSignatures: getPathFirstRoutesInterfaceDefinition(
61
+ pathDictionary,
62
+ clientDefinitionsFile,
63
+ options
64
+ )
65
+ });
66
+
67
+ const clientName = model.libraryName;
68
+
69
+ const clientInterfaceName = clientName.endsWith("Client")
70
+ ? `${clientName}`
71
+ : `${clientName}Client`;
72
+
73
+ clientDefinitionsFile.addTypeAlias({
74
+ isExported: true,
75
+ name: clientInterfaceName,
76
+ type: Writers.intersectionType(
77
+ "Client",
78
+ Writers.objectType({
79
+ properties: [
80
+ { name: "path", type: "Routes" },
81
+ ...shortcutsInOperationGroup
82
+ ]
83
+ }),
84
+ // If the length of shortcuts in operation group and all shortcutsInOperationGroup
85
+ // is the same, then we don't have any operations at the client level
86
+ // Otherwise we need to make the client interface name an union with the
87
+ // definition of all client level shortcut methods
88
+ ...(shortcutsInOperationGroup.length !== shortcuts.length
89
+ ? [`ClientOperations`]
90
+ : [])
91
+ )
92
+ });
93
+
94
+ if (options.importedParameters.size) {
95
+ clientDefinitionsFile.addImportDeclaration({
96
+ namedImports: [...options.importedParameters],
97
+ moduleSpecifier: "./parameters"
98
+ });
99
+ }
100
+
101
+ if (options.importedResponses.size) {
102
+ clientDefinitionsFile.addImportDeclaration({
103
+ namedImports: [...options.importedResponses],
104
+ moduleSpecifier: "./responses"
105
+ });
106
+ }
107
+
108
+ options.clientImports.add("Client");
109
+ options.clientImports.add("StreamableMethod");
110
+ clientDefinitionsFile.addImportDeclarations([
111
+ {
112
+ namedImports: [...options.clientImports],
113
+ moduleSpecifier: "@azure-rest/core-client"
114
+ }
115
+ ]);
116
+
117
+ return { path: filePath, content: clientDefinitionsFile.getFullText() };
118
+ }
119
+
120
+ function getPathFirstRoutesInterfaceDefinition(
121
+ paths: Paths,
122
+ sourcefile: SourceFile,
123
+ options: {
124
+ importedParameters: Set<string>;
125
+ importedResponses: Set<string>;
126
+ clientImports: Set<string>;
127
+ }
128
+ ): CallSignatureDeclarationStructure[] {
129
+ const operationGroupCount = getOperationGroupCount(paths);
130
+
131
+ const signatures: CallSignatureDeclarationStructure[] = [];
132
+ for (const key of Object.keys(paths)) {
133
+ for (const verb of Object.keys(paths[key].methods)) {
134
+ for (const method of paths[key].methods[verb]) {
135
+ options.importedParameters.add(method.optionsName);
136
+ method.returnType
137
+ .split(" | ")
138
+ .forEach((item) => options.importedResponses.add(item));
139
+ }
140
+ }
141
+ generatePathFirstRouteMethodsDefinition(
142
+ paths[key],
143
+ operationGroupCount,
144
+ sourcefile
145
+ );
146
+ const pathParams = paths[key].pathParameters;
147
+ signatures.push({
148
+ docs: [
149
+ `Resource for '${key
150
+ .replace(/}/g, "\\}")
151
+ .replace(
152
+ /{/g,
153
+ "\\{"
154
+ )}' has methods for the following verbs: ${Object.keys(
155
+ paths[key].methods
156
+ ).join(", ")}`
157
+ ],
158
+ parameters: [
159
+ { name: "path", type: `"${key}"` },
160
+ ...getPathParamDefinitions(pathParams)
161
+ ],
162
+ returnType: getOperationReturnTypeName(
163
+ paths[key],
164
+ getOperationGroupCount(paths)
165
+ ),
166
+ kind: StructureKind.CallSignature
167
+ });
168
+ }
169
+ return signatures;
170
+ }
171
+
172
+ function getOperationGroupCount(paths: Paths) {
173
+ const operationGroups = Object.keys(paths)
174
+ .map((p) => paths[p].operationGroupName)
175
+ .filter((p) => p && p !== "Client");
176
+ const uniqueNames = new Set(operationGroups);
177
+
178
+ return uniqueNames.size;
179
+ }
180
+
181
+ function getOperationReturnTypeName(
182
+ { operationGroupName, name }: PathMetadata,
183
+ operationGroupCount: number
184
+ ) {
185
+ if (
186
+ operationGroupCount > 1 &&
187
+ operationGroupName &&
188
+ operationGroupName !== "Client"
189
+ ) {
190
+ return `${pascalCase(operationGroupName)}${pascalCase(name)}`;
191
+ }
192
+
193
+ return pascalCase(name);
194
+ }
195
+
196
+ function generatePathFirstRouteMethodsDefinition(
197
+ path: PathMetadata,
198
+ operationGroupCount: number,
199
+ file: SourceFile
200
+ ): void {
201
+ const methodDefinitions = buildMethodDefinitions(path.methods);
202
+ const interfaceDef = {
203
+ methods: methodDefinitions,
204
+ name: getOperationReturnTypeName(path, operationGroupCount),
205
+ isExported: true
206
+ };
207
+ file.addInterface(interfaceDef);
208
+ }
209
+
210
+ function getShortcutName(interfaceName: string) {
211
+ const endIndex = shouldKeepSuffix(interfaceName)
212
+ ? undefined
213
+ : interfaceName.length - "Operations".length;
214
+ const clientProperty = normalizeName(
215
+ interfaceName.substring(0, endIndex),
216
+ NameType.OperationGroup,
217
+ true,
218
+ REST_CLIENT_RESERVED,
219
+ CasingConvention.Camel
220
+ );
221
+
222
+ return {
223
+ name: clientProperty,
224
+ type: interfaceName
225
+ };
226
+ }
227
+
228
+ function shouldKeepSuffix(name: string) {
229
+ const reservedNames = [
230
+ "pipelineOperations",
231
+ "pathOperations",
232
+ "pathUncheckedOperations"
233
+ ];
234
+ return reservedNames.some((r) => r.toLowerCase() === name.toLowerCase());
235
+ }