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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/.rush/temp/package-deps_build.json +45 -0
  2. package/.rush/temp/shrinkwrap-deps.json +72 -0
  3. package/CHANGELOG.md +3 -0
  4. package/dist/buildClient.js +198 -0
  5. package/dist/buildClient.js.map +1 -0
  6. package/dist/buildClientDefinitions.js +160 -0
  7. package/dist/buildClientDefinitions.js.map +1 -0
  8. package/dist/buildIndexFile.js +150 -0
  9. package/dist/buildIndexFile.js.map +1 -0
  10. package/dist/buildIsUnexpectedHelper.js +219 -0
  11. package/dist/buildIsUnexpectedHelper.js.map +1 -0
  12. package/dist/buildMethodShortcuts.js +50 -0
  13. package/dist/buildMethodShortcuts.js.map +1 -0
  14. package/dist/buildObjectTypes.js +250 -0
  15. package/dist/buildObjectTypes.js.map +1 -0
  16. package/dist/buildPaginateHelper.js +30 -0
  17. package/dist/buildPaginateHelper.js.map +1 -0
  18. package/dist/buildParameterTypes.js +287 -0
  19. package/dist/buildParameterTypes.js.map +1 -0
  20. package/dist/buildPollingHelper.js +21 -0
  21. package/dist/buildPollingHelper.js.map +1 -0
  22. package/dist/buildResponseTypes.js +122 -0
  23. package/dist/buildResponseTypes.js.map +1 -0
  24. package/dist/buildSchemaType.js +44 -0
  25. package/dist/buildSchemaType.js.map +1 -0
  26. package/dist/buildTopLevelIndexFile.js +45 -0
  27. package/dist/buildTopLevelIndexFile.js.map +1 -0
  28. package/dist/helpers/nameConstructors.js +41 -0
  29. package/dist/helpers/nameConstructors.js.map +1 -0
  30. package/dist/helpers/nameUtils.js +196 -0
  31. package/dist/helpers/nameUtils.js.map +1 -0
  32. package/dist/helpers/operationHelpers.js +83 -0
  33. package/dist/helpers/operationHelpers.js.map +1 -0
  34. package/dist/helpers/schemaHelpers.js +27 -0
  35. package/dist/helpers/schemaHelpers.js.map +1 -0
  36. package/dist/helpers/shortcutMethods.js +46 -0
  37. package/dist/helpers/shortcutMethods.js.map +1 -0
  38. package/dist/index.js +44 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/interfaces.js +18 -0
  41. package/dist/interfaces.js.map +1 -0
  42. package/dist/metadata/buildApiExtractorConfig.js +56 -0
  43. package/dist/metadata/buildApiExtractorConfig.js.map +1 -0
  44. package/dist/metadata/buildESLintConfig.js +33 -0
  45. package/dist/metadata/buildESLintConfig.js.map +1 -0
  46. package/dist/metadata/buildLicenseFile.js +41 -0
  47. package/dist/metadata/buildLicenseFile.js.map +1 -0
  48. package/dist/metadata/buildPackageFile.js +232 -0
  49. package/dist/metadata/buildPackageFile.js.map +1 -0
  50. package/dist/metadata/buildReadmeFile.js +170 -0
  51. package/dist/metadata/buildReadmeFile.js.map +1 -0
  52. package/dist/metadata/buildRollupConfig.js +144 -0
  53. package/dist/metadata/buildRollupConfig.js.map +1 -0
  54. package/dist/metadata/buildTsConfig.js +72 -0
  55. package/dist/metadata/buildTsConfig.js.map +1 -0
  56. package/dist/package.json +1 -0
  57. package/dist/static/paginateContent.js +214 -0
  58. package/dist/static/paginateContent.js.map +1 -0
  59. package/dist/static/pollingContent.js +78 -0
  60. package/dist/static/pollingContent.js.map +1 -0
  61. package/dist/test/buildEnvFile.js +31 -0
  62. package/dist/test/buildEnvFile.js.map +1 -0
  63. package/dist/test/buildKarmaConfig.js +19 -0
  64. package/dist/test/buildKarmaConfig.js.map +1 -0
  65. package/dist/test/buildRecordedClient.js +22 -0
  66. package/dist/test/buildRecordedClient.js.map +1 -0
  67. package/dist/test/buildSampleTest.js +19 -0
  68. package/dist/test/buildSampleTest.js.map +1 -0
  69. package/dist/test/template.js +192 -0
  70. package/dist/test/template.js.map +1 -0
  71. package/dist-esm/buildClient.js +191 -0
  72. package/dist-esm/buildClient.js.map +1 -0
  73. package/dist-esm/buildClientDefinitions.js +155 -0
  74. package/dist-esm/buildClientDefinitions.js.map +1 -0
  75. package/dist-esm/buildIndexFile.js +145 -0
  76. package/dist-esm/buildIndexFile.js.map +1 -0
  77. package/dist-esm/buildIsUnexpectedHelper.js +215 -0
  78. package/dist-esm/buildIsUnexpectedHelper.js.map +1 -0
  79. package/dist-esm/buildMethodShortcuts.js +46 -0
  80. package/dist-esm/buildMethodShortcuts.js.map +1 -0
  81. package/dist-esm/buildObjectTypes.js +249 -0
  82. package/dist-esm/buildObjectTypes.js.map +1 -0
  83. package/dist-esm/buildPaginateHelper.js +26 -0
  84. package/dist-esm/buildPaginateHelper.js.map +1 -0
  85. package/dist-esm/buildParameterTypes.js +288 -0
  86. package/dist-esm/buildParameterTypes.js.map +1 -0
  87. package/dist-esm/buildPollingHelper.js +17 -0
  88. package/dist-esm/buildPollingHelper.js.map +1 -0
  89. package/dist-esm/buildResponseTypes.js +127 -0
  90. package/dist-esm/buildResponseTypes.js.map +1 -0
  91. package/dist-esm/buildSchemaType.js +39 -0
  92. package/dist-esm/buildSchemaType.js.map +1 -0
  93. package/dist-esm/buildTopLevelIndexFile.js +41 -0
  94. package/dist-esm/buildTopLevelIndexFile.js.map +1 -0
  95. package/dist-esm/helpers/nameConstructors.js +34 -0
  96. package/dist-esm/helpers/nameConstructors.js.map +1 -0
  97. package/dist-esm/helpers/nameUtils.js +187 -0
  98. package/dist-esm/helpers/nameUtils.js.map +1 -0
  99. package/dist-esm/helpers/operationHelpers.js +72 -0
  100. package/dist-esm/helpers/operationHelpers.js.map +1 -0
  101. package/dist-esm/helpers/schemaHelpers.js +21 -0
  102. package/dist-esm/helpers/schemaHelpers.js.map +1 -0
  103. package/dist-esm/helpers/shortcutMethods.js +42 -0
  104. package/dist-esm/helpers/shortcutMethods.js.map +1 -0
  105. package/dist-esm/index.js +28 -0
  106. package/dist-esm/index.js.map +1 -0
  107. package/dist-esm/interfaces.js +15 -0
  108. package/dist-esm/interfaces.js.map +1 -0
  109. package/dist-esm/metadata/buildApiExtractorConfig.js +51 -0
  110. package/dist-esm/metadata/buildApiExtractorConfig.js.map +1 -0
  111. package/dist-esm/metadata/buildESLintConfig.js +28 -0
  112. package/dist-esm/metadata/buildESLintConfig.js.map +1 -0
  113. package/dist-esm/metadata/buildLicenseFile.js +36 -0
  114. package/dist-esm/metadata/buildLicenseFile.js.map +1 -0
  115. package/dist-esm/metadata/buildPackageFile.js +234 -0
  116. package/dist-esm/metadata/buildPackageFile.js.map +1 -0
  117. package/dist-esm/metadata/buildReadmeFile.js +167 -0
  118. package/dist-esm/metadata/buildReadmeFile.js.map +1 -0
  119. package/dist-esm/metadata/buildRollupConfig.js +139 -0
  120. package/dist-esm/metadata/buildRollupConfig.js.map +1 -0
  121. package/dist-esm/metadata/buildTsConfig.js +67 -0
  122. package/dist-esm/metadata/buildTsConfig.js.map +1 -0
  123. package/dist-esm/package.json +1 -0
  124. package/dist-esm/static/paginateContent.js +211 -0
  125. package/dist-esm/static/paginateContent.js.map +1 -0
  126. package/dist-esm/static/pollingContent.js +75 -0
  127. package/dist-esm/static/pollingContent.js.map +1 -0
  128. package/dist-esm/test/buildEnvFile.js +24 -0
  129. package/dist-esm/test/buildEnvFile.js.map +1 -0
  130. package/dist-esm/test/buildKarmaConfig.js +14 -0
  131. package/dist-esm/test/buildKarmaConfig.js.map +1 -0
  132. package/dist-esm/test/buildRecordedClient.js +17 -0
  133. package/dist-esm/test/buildRecordedClient.js.map +1 -0
  134. package/dist-esm/test/buildSampleTest.js +14 -0
  135. package/dist-esm/test/buildSampleTest.js.map +1 -0
  136. package/dist-esm/test/template.js +189 -0
  137. package/dist-esm/test/template.js.map +1 -0
  138. package/package.json +40 -0
  139. package/publishPackage.js +11 -0
  140. package/rlc-common.build.log +2 -0
  141. package/src/buildClient.ts +251 -0
  142. package/src/buildClientDefinitions.ts +231 -0
  143. package/src/buildIndexFile.ts +172 -0
  144. package/src/buildIsUnexpectedHelper.ts +240 -0
  145. package/src/buildMethodShortcuts.ts +75 -0
  146. package/src/buildObjectTypes.ts +393 -0
  147. package/src/buildPaginateHelper.ts +33 -0
  148. package/src/buildParameterTypes.ts +435 -0
  149. package/src/buildPollingHelper.ts +18 -0
  150. package/src/buildResponseTypes.ts +169 -0
  151. package/src/buildSchemaType.ts +56 -0
  152. package/src/buildTopLevelIndexFile.ts +46 -0
  153. package/src/helpers/nameConstructors.ts +93 -0
  154. package/src/helpers/nameUtils.ts +227 -0
  155. package/src/helpers/operationHelpers.ts +103 -0
  156. package/src/helpers/schemaHelpers.ts +25 -0
  157. package/src/helpers/shortcutMethods.ts +60 -0
  158. package/src/index.ts +28 -0
  159. package/src/interfaces.ts +212 -0
  160. package/src/metadata/buildApiExtractorConfig.ts +59 -0
  161. package/src/metadata/buildESLintConfig.ts +34 -0
  162. package/src/metadata/buildLicenseFile.ts +39 -0
  163. package/src/metadata/buildPackageFile.ts +271 -0
  164. package/src/metadata/buildReadmeFile.ts +231 -0
  165. package/src/metadata/buildRollupConfig.ts +147 -0
  166. package/src/metadata/buildTsConfig.ts +79 -0
  167. package/src/static/paginateContent.ts +210 -0
  168. package/src/static/pollingContent.ts +74 -0
  169. package/src/test/buildEnvFile.ts +26 -0
  170. package/src/test/buildKarmaConfig.ts +15 -0
  171. package/src/test/buildRecordedClient.ts +18 -0
  172. package/src/test/buildSampleTest.ts +15 -0
  173. package/src/test/template.ts +192 -0
  174. package/tsconfig-cjs.json +9 -0
  175. package/tsconfig-common.json +13 -0
  176. package/tsconfig.json +13 -0
  177. package/types/buildClient.d.ts +2 -0
  178. package/types/buildClientDefinitions.d.ts +5 -0
  179. package/types/buildIndexFile.d.ts +5 -0
  180. package/types/buildIsUnexpectedHelper.d.ts +5 -0
  181. package/types/buildMethodShortcuts.d.ts +4 -0
  182. package/types/buildObjectTypes.d.ts +14 -0
  183. package/types/buildPaginateHelper.d.ts +5 -0
  184. package/types/buildParameterTypes.d.ts +5 -0
  185. package/types/buildPollingHelper.d.ts +5 -0
  186. package/types/buildResponseTypes.d.ts +5 -0
  187. package/types/buildSchemaType.d.ts +19 -0
  188. package/types/buildTopLevelIndexFile.d.ts +5 -0
  189. package/types/helpers/nameConstructors.d.ts +28 -0
  190. package/types/helpers/nameUtils.d.ts +25 -0
  191. package/types/helpers/operationHelpers.d.ts +9 -0
  192. package/types/helpers/schemaHelpers.d.ts +4 -0
  193. package/types/helpers/shortcutMethods.d.ts +3 -0
  194. package/types/index.d.ts +25 -0
  195. package/types/interfaces.d.ts +186 -0
  196. package/types/metadata/buildApiExtractorConfig.d.ts +5 -0
  197. package/types/metadata/buildESLintConfig.d.ts +5 -0
  198. package/types/metadata/buildLicenseFile.d.ts +5 -0
  199. package/types/metadata/buildPackageFile.d.ts +5 -0
  200. package/types/metadata/buildReadmeFile.d.ts +5 -0
  201. package/types/metadata/buildRollupConfig.d.ts +5 -0
  202. package/types/metadata/buildTsConfig.d.ts +5 -0
  203. package/types/static/paginateContent.d.ts +1 -0
  204. package/types/static/pollingContent.d.ts +1 -0
  205. package/types/test/buildEnvFile.d.ts +9 -0
  206. package/types/test/buildKarmaConfig.d.ts +5 -0
  207. package/types/test/buildRecordedClient.d.ts +5 -0
  208. package/types/test/buildSampleTest.d.ts +5 -0
  209. package/types/test/template.d.ts +5 -0
@@ -0,0 +1,231 @@
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 { CasingConvention, NameType, normalizeName } from "./helpers/nameUtils.js";
23
+ import { pascalCase } from "./helpers/nameUtils.js";
24
+
25
+ export function buildClientDefinitions(model: RLCModel) {
26
+ const options = {
27
+ importedParameters: new Set<string>(),
28
+ importedResponses: new Set<string>(),
29
+ clientImports: new Set<string>()
30
+ };
31
+ const project = new Project();
32
+ const srcPath = model.srcPath;
33
+ const filePath = path.join(srcPath, `clientDefinitions.ts`);
34
+ const clientDefinitionsFile = project.createSourceFile(filePath, undefined, {
35
+ overwrite: true
36
+ });
37
+
38
+ // Get all paths
39
+ const pathDictionary = model.paths;
40
+ let shortcuts: OptionalKind<InterfaceDeclarationStructure>[] = [];
41
+ // There may be operations without an operation group, those shortcut
42
+ // methods need to be handled differently.
43
+ let shortcutsInOperationGroup: { name: string; type: string }[] = [];
44
+
45
+ if (model.options?.includeShortcuts) {
46
+ shortcuts = generateMethodShortcuts(model.paths);
47
+ clientDefinitionsFile.addInterfaces(shortcuts);
48
+ shortcutsInOperationGroup = shortcuts
49
+ .filter((s) => s.name !== "ClientOperations")
50
+ .map((s) => getShortcutName(s.name));
51
+ }
52
+
53
+ clientDefinitionsFile.addInterface({
54
+ name: "Routes",
55
+ isExported: true,
56
+ callSignatures: getPathFirstRoutesInterfaceDefinition(
57
+ pathDictionary,
58
+ clientDefinitionsFile,
59
+ options
60
+ )
61
+ });
62
+
63
+ const clientName = model.libraryName;
64
+
65
+ const clientInterfaceName = clientName.endsWith("Client")
66
+ ? `${clientName}`
67
+ : `${clientName}Client`;
68
+
69
+ clientDefinitionsFile.addTypeAlias({
70
+ isExported: true,
71
+ name: clientInterfaceName,
72
+ type: Writers.intersectionType(
73
+ "Client",
74
+ Writers.objectType({
75
+ properties: [
76
+ { name: "path", type: "Routes" },
77
+ ...shortcutsInOperationGroup
78
+ ]
79
+ }),
80
+ // If the length of shortcuts in operation group and all shortcutsInOperationGroup
81
+ // is the same, then we don't have any operations at the client level
82
+ // Otherwise we need to make the client interface name an union with the
83
+ // definition of all client level shortcut methods
84
+ ...(shortcutsInOperationGroup.length !== shortcuts.length
85
+ ? [`ClientOperations`]
86
+ : [])
87
+ )
88
+ });
89
+
90
+ if (options.importedParameters.size) {
91
+ clientDefinitionsFile.addImportDeclaration({
92
+ namedImports: [...options.importedParameters],
93
+ moduleSpecifier: "./parameters"
94
+ });
95
+ }
96
+
97
+ if (options.importedResponses.size) {
98
+ clientDefinitionsFile.addImportDeclaration({
99
+ namedImports: [...options.importedResponses],
100
+ moduleSpecifier: "./responses"
101
+ });
102
+ }
103
+
104
+ options.clientImports.add("Client");
105
+ options.clientImports.add("StreamableMethod");
106
+ clientDefinitionsFile.addImportDeclarations([
107
+ {
108
+ namedImports: [...options.clientImports],
109
+ moduleSpecifier: "@azure-rest/core-client"
110
+ }
111
+ ]);
112
+
113
+ return { path: filePath, content: clientDefinitionsFile.getFullText() };
114
+ }
115
+
116
+ function getPathFirstRoutesInterfaceDefinition(
117
+ paths: Paths,
118
+ sourcefile: SourceFile,
119
+ options: {
120
+ importedParameters: Set<string>;
121
+ importedResponses: Set<string>;
122
+ clientImports: Set<string>;
123
+ }
124
+ ): CallSignatureDeclarationStructure[] {
125
+ const operationGroupCount = getOperationGroupCount(paths);
126
+
127
+ const signatures: CallSignatureDeclarationStructure[] = [];
128
+ for (const key of Object.keys(paths)) {
129
+ for (const verb of Object.keys(paths[key].methods)) {
130
+ for (const method of paths[key].methods[verb]) {
131
+ options.importedParameters.add(method.optionsName);
132
+ method.returnType
133
+ .split(" | ")
134
+ .forEach((item) => options.importedResponses.add(item));
135
+ }
136
+ }
137
+ generatePathFirstRouteMethodsDefinition(
138
+ paths[key],
139
+ operationGroupCount,
140
+ sourcefile
141
+ );
142
+ const pathParams = paths[key].pathParameters;
143
+ signatures.push({
144
+ docs: [
145
+ `Resource for '${key
146
+ .replace(/}/g, "\\}")
147
+ .replace(
148
+ /{/g,
149
+ "\\{"
150
+ )}' has methods for the following verbs: ${Object.keys(
151
+ paths[key].methods
152
+ ).join(", ")}`
153
+ ],
154
+ parameters: [
155
+ { name: "path", type: `"${key}"` },
156
+ ...getPathParamDefinitions(pathParams)
157
+ ],
158
+ returnType: getOperationReturnTypeName(
159
+ paths[key],
160
+ getOperationGroupCount(paths)
161
+ ),
162
+ kind: StructureKind.CallSignature
163
+ });
164
+ }
165
+ return signatures;
166
+ }
167
+
168
+ function getOperationGroupCount(paths: Paths) {
169
+ const operationGroups = Object.keys(paths)
170
+ .map((p) => paths[p].operationGroupName)
171
+ .filter((p) => p && p !== "Client");
172
+ const uniqueNames = new Set(operationGroups);
173
+
174
+ return uniqueNames.size;
175
+ }
176
+
177
+ function getOperationReturnTypeName(
178
+ { operationGroupName, name }: PathMetadata,
179
+ operationGroupCount: number
180
+ ) {
181
+ if (
182
+ operationGroupCount > 1 &&
183
+ operationGroupName &&
184
+ operationGroupName !== "Client"
185
+ ) {
186
+ return `${pascalCase(operationGroupName)}${pascalCase(name)}`;
187
+ }
188
+
189
+ return pascalCase(name);
190
+ }
191
+
192
+ function generatePathFirstRouteMethodsDefinition(
193
+ path: PathMetadata,
194
+ operationGroupCount: number,
195
+ file: SourceFile
196
+ ): void {
197
+ const methodDefinitions = buildMethodDefinitions(path.methods);
198
+ const interfaceDef = {
199
+ methods: methodDefinitions,
200
+ name: getOperationReturnTypeName(path, operationGroupCount),
201
+ isExported: true
202
+ };
203
+ file.addInterface(interfaceDef);
204
+ }
205
+
206
+ function getShortcutName(interfaceName: string) {
207
+ const endIndex = shouldKeepSuffix(interfaceName)
208
+ ? undefined
209
+ : interfaceName.length - "Operations".length;
210
+ const clientProperty = normalizeName(
211
+ interfaceName.substring(0, endIndex),
212
+ NameType.OperationGroup,
213
+ true,
214
+ REST_CLIENT_RESERVED,
215
+ CasingConvention.Camel
216
+ );
217
+
218
+ return {
219
+ name: clientProperty,
220
+ type: interfaceName
221
+ };
222
+ }
223
+
224
+ function shouldKeepSuffix(name: string) {
225
+ const reservedNames = [
226
+ "pipelineOperations",
227
+ "pathOperations",
228
+ "pathUncheckedOperations"
229
+ ];
230
+ return reservedNames.some((r) => r.toLowerCase() === name.toLowerCase());
231
+ }
@@ -0,0 +1,172 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ import { Project, SourceFile } from "ts-morph";
5
+ import { NameType, normalizeName } from "./helpers/nameUtils.js";
6
+ import {
7
+ hasInputModels,
8
+ hasOutputModels,
9
+ hasPagingOperations,
10
+ hasPollingOperations,
11
+ hasUnexpectedHelper
12
+ } from "./helpers/operationHelpers.js";
13
+ import { RLCModel } from "./interfaces.js";
14
+ import * as path from "path";
15
+
16
+ export function buildIndexFile(model: RLCModel) {
17
+ const multiClient = Boolean(model.options?.multiClient),
18
+ batch = model.options?.batch;
19
+ const project = new Project();
20
+ const { srcPath } = model;
21
+ const filePath = path.join(srcPath, `index.ts`);
22
+ const indexFile = project.createSourceFile(filePath, undefined, {
23
+ overwrite: true
24
+ });
25
+
26
+ if (!multiClient || !batch || batch?.length === 1) {
27
+ // if we are generate single client package for RLC
28
+ generateRLCIndex(indexFile, model);
29
+ } else {
30
+ generateRLCIndexForMultiClient(indexFile, model);
31
+ }
32
+ return {
33
+ path: filePath,
34
+ content: indexFile.getFullText()
35
+ };
36
+ }
37
+
38
+ // to generate a index.ts for each single module inside the multi client RLC package
39
+ function generateRLCIndexForMultiClient(file: SourceFile, model: RLCModel) {
40
+ const clientName = model.libraryName;
41
+ const createClientFuncName = `createClient`;
42
+ const moduleName = normalizeName(clientName, NameType.File);
43
+
44
+ file.addImportDeclaration({
45
+ namespaceImport: "Parameters",
46
+ moduleSpecifier: "./parameters"
47
+ });
48
+
49
+ file.addImportDeclaration({
50
+ namespaceImport: "Responses",
51
+ moduleSpecifier: "./responses"
52
+ });
53
+
54
+ file.addImportDeclaration({
55
+ namespaceImport: "Client",
56
+ moduleSpecifier: "./clientDefinitions"
57
+ });
58
+
59
+ const exports = ["Parameters", "Responses", "Client"];
60
+ if (hasInputModels(model)) {
61
+ file.addImportDeclaration({
62
+ namespaceImport: "Models",
63
+ moduleSpecifier: "./models"
64
+ });
65
+ exports.push("Models");
66
+ }
67
+
68
+ if (hasOutputModels(model)) {
69
+ file.addImportDeclaration({
70
+ namespaceImport: "OutputModels",
71
+ moduleSpecifier: "./outputModels"
72
+ });
73
+ exports.push("OutputModels");
74
+ }
75
+
76
+ if (hasPagingOperations(model)) {
77
+ file.addImportDeclaration({
78
+ namespaceImport: "PaginateHelper",
79
+ moduleSpecifier: "./paginateHelper"
80
+ });
81
+ exports.push("PaginateHelper");
82
+ }
83
+
84
+ if (hasPollingOperations(model)) {
85
+ file.addImportDeclaration({
86
+ namespaceImport: "PollingHelper",
87
+ moduleSpecifier: "./pollingHelper"
88
+ });
89
+ exports.push("PollingHelper");
90
+ }
91
+
92
+ file.addExportDeclarations([
93
+ {
94
+ moduleSpecifier: `./${moduleName}`,
95
+ namedExports: [`${createClientFuncName}`]
96
+ },
97
+ {
98
+ namedExports: [...exports]
99
+ }
100
+ ]);
101
+ }
102
+
103
+ function generateRLCIndex(file: SourceFile, model: RLCModel) {
104
+ const clientName = model.libraryName;
105
+ const createClientFuncName = `${clientName}`;
106
+ const moduleName = normalizeName(clientName, NameType.File);
107
+
108
+ file.addImportDeclaration({
109
+ moduleSpecifier: `./${moduleName}`,
110
+ defaultImport: createClientFuncName
111
+ });
112
+
113
+ file.addExportDeclarations([
114
+ {
115
+ moduleSpecifier: `./${moduleName}`
116
+ },
117
+ {
118
+ moduleSpecifier: "./parameters"
119
+ },
120
+ {
121
+ moduleSpecifier: "./responses"
122
+ },
123
+ {
124
+ moduleSpecifier: "./clientDefinitions"
125
+ }
126
+ ]);
127
+
128
+ if (hasUnexpectedHelper(model)) {
129
+ file.addExportDeclarations([
130
+ {
131
+ moduleSpecifier: "./isUnexpected"
132
+ }
133
+ ]);
134
+ }
135
+
136
+ if (hasInputModels(model)) {
137
+ file.addExportDeclarations([
138
+ {
139
+ moduleSpecifier: "./models"
140
+ }
141
+ ]);
142
+ }
143
+
144
+ if (hasOutputModels(model)) {
145
+ file.addExportDeclarations([
146
+ {
147
+ moduleSpecifier: "./outputModels"
148
+ }
149
+ ]);
150
+ }
151
+
152
+ if (hasPagingOperations(model)) {
153
+ file.addExportDeclarations([
154
+ {
155
+ moduleSpecifier: "./paginateHelper"
156
+ }
157
+ ]);
158
+ }
159
+
160
+ if (hasPollingOperations(model)) {
161
+ file.addExportDeclarations([
162
+ {
163
+ moduleSpecifier: "./pollingHelper"
164
+ }
165
+ ]);
166
+ }
167
+
168
+ file.addExportAssignment({
169
+ expression: createClientFuncName,
170
+ isExportEquals: false
171
+ });
172
+ }
@@ -0,0 +1,240 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ import { Paths, RLCModel } from "./interfaces.js";
5
+ import * as path from "path";
6
+ import {
7
+ FunctionDeclarationOverloadStructure,
8
+ OptionalKind,
9
+ Project,
10
+ VariableDeclarationKind
11
+ } from "ts-morph";
12
+ import { hasUnexpectedHelper } from "./helpers/operationHelpers.js";
13
+ export function buildIsUnexpectedHelper(model: RLCModel) {
14
+ if (!hasUnexpectedHelper(model)) {
15
+ return;
16
+ }
17
+ const project = new Project();
18
+ const srcPath = model.srcPath;
19
+ const filePath = path.join(srcPath, `isUnexpected.ts`);
20
+ const isErrorHelper = project.createSourceFile(filePath, undefined, {
21
+ overwrite: true
22
+ });
23
+
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>[] = [];
28
+ const pathDictionary = model.paths;
29
+
30
+ for (const [path, details] of Object.entries(pathDictionary)) {
31
+ for (const [methodName, methodDetails] of Object.entries(details.methods)) {
32
+ const originalMethod = methodName.toUpperCase();
33
+ const operation = `${originalMethod} ${path}`;
34
+ const success = methodDetails[0].successStatus;
35
+ map = { ...map, ...{ [operation]: success } };
36
+
37
+ // LROs may call the same path but with GET
38
+ // to get the operation status.
39
+ if (
40
+ methodDetails[0].annotations?.isLongRunning &&
41
+ originalMethod !== "GET"
42
+ ) {
43
+ const operation = `GET ${path}`;
44
+ const success =
45
+ (pathDictionary[path].methods["get"] &&
46
+ pathDictionary[path].methods["get"][0]?.successStatus) ??
47
+ methodDetails[0].successStatus;
48
+ map = { ...map, ...{ [operation]: success } };
49
+ }
50
+
51
+ const successTypes = methodDetails[0].responseTypes.success;
52
+ const errorTypes = methodDetails[0].responseTypes.error;
53
+
54
+ if (!successTypes.length || !errorTypes.length || !errorTypes[0]) {
55
+ continue;
56
+ }
57
+
58
+ successTypes.forEach((t) => allResponseTypes.add(t));
59
+ errorTypes.forEach((t) => {
60
+ allResponseTypes.add(t);
61
+ allErrorTypes.add(t);
62
+ });
63
+
64
+ overloads.push({
65
+ isExported: true,
66
+ parameters: [
67
+ {
68
+ name: "response",
69
+ type: [...successTypes, ...errorTypes].join(" | ")
70
+ }
71
+ ],
72
+ returnType: `response is ${errorTypes[0]}`
73
+ });
74
+ }
75
+ }
76
+ isErrorHelper.addImportDeclaration({
77
+ namedImports: [...allResponseTypes],
78
+ moduleSpecifier: "./responses"
79
+ });
80
+
81
+ isErrorHelper.addVariableStatement({
82
+ declarations: [
83
+ {
84
+ name: "responseMap",
85
+ initializer: JSON.stringify(map),
86
+ type: "Record<string, string[]>"
87
+ }
88
+ ],
89
+ declarationKind: VariableDeclarationKind.Const
90
+ });
91
+
92
+ if (allErrorTypes.size) {
93
+ const hasTemplate = hasParametrizedPath(pathDictionary);
94
+ isErrorHelper.addFunction({
95
+ overloads,
96
+ isExported: true,
97
+ name: "isUnexpected",
98
+ parameters: [
99
+ {
100
+ name: "response",
101
+ type: [...allResponseTypes].join(" | ")
102
+ }
103
+ ],
104
+ returnType: `response is ${[...allErrorTypes].join(" | ")}`,
105
+ statements: [
106
+ `
107
+ const lroOriginal = response.headers["x-ms-original-url"];
108
+ const url = new URL(lroOriginal ?? response.request.url);
109
+ const method = response.request.method;
110
+ ${
111
+ hasTemplate ? "let" : "const"
112
+ } pathDetails = responseMap[\`\${method} \${url.pathname}\`];
113
+ if (!pathDetails) {`,
114
+ hasTemplate
115
+ ? "pathDetails = geParametrizedPathSuccess(method, url.pathname);"
116
+ : `return true;`,
117
+ ` }
118
+ return !pathDetails.includes(response.status);
119
+ `
120
+ ]
121
+ });
122
+ if (hasTemplate) {
123
+ isErrorHelper.addFunction({
124
+ isExported: false,
125
+ name: "geParametrizedPathSuccess",
126
+ parameters: [
127
+ {
128
+ name: "method",
129
+ type: "string"
130
+ },
131
+ {
132
+ name: "path",
133
+ type: "string"
134
+ }
135
+ ],
136
+ returnType: `string[]`,
137
+ statements: [
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
+ ) {
158
+ // track if we have found a match to return the values found.
159
+ let found = true;
160
+ for (let i = 0; i < candidateParts.length; i++) {
161
+ if (
162
+ candidateParts[i]?.startsWith("{") &&
163
+ candidateParts[i]?.endsWith("}")
164
+ ) {
165
+ // 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
168
+ continue;
169
+ }
170
+
171
+ // If the candidate part is not a template and
172
+ // the parts don't match mark the candidate as not found
173
+ // to move on with the next candidate path.
174
+ if (candidateParts[i] !== pathParts[i]) {
175
+ found = false;
176
+ break;
177
+ }
178
+ }
179
+
180
+ // 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;
185
+ }
186
+ }
187
+ }
188
+
189
+ // No match was found, return an empty array.
190
+ return [];
191
+ `
192
+ ]
193
+ });
194
+
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
+ isErrorHelper.addFunction({
209
+ isExported: false,
210
+ name: "getPathFromMapKey",
211
+ parameters: [
212
+ {
213
+ name: "mapKey",
214
+ type: "string"
215
+ }
216
+ ],
217
+ returnType: `string`,
218
+ statements: [
219
+ `const pathStart = mapKey.indexOf("/");
220
+ return mapKey.slice(pathStart);`
221
+ ]
222
+ });
223
+ }
224
+ }
225
+
226
+ return {
227
+ path: filePath,
228
+ content: isErrorHelper.getFullText()
229
+ };
230
+ }
231
+
232
+ function hasParametrizedPath(pathDictionary: Paths): boolean {
233
+ for (const [path] of Object.entries(pathDictionary)) {
234
+ if (path.includes("/{") && path.includes("}")) {
235
+ return true;
236
+ }
237
+ }
238
+
239
+ return false;
240
+ }
@@ -0,0 +1,75 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ import {
5
+ CasingConvention,
6
+ NameType,
7
+ normalizeName,
8
+ ReservedName
9
+ } from "./helpers/nameUtils.js";
10
+ import { Paths, PathParameter, PathMetadata } from "./interfaces.js";
11
+
12
+ export const REST_CLIENT_RESERVED: ReservedName[] = [
13
+ { name: "path", reservedFor: [NameType.Property, NameType.OperationGroup] },
14
+ {
15
+ name: "pathUnchecked",
16
+ reservedFor: [NameType.Property, NameType.OperationGroup]
17
+ },
18
+ {
19
+ name: "pipeline",
20
+ reservedFor: [NameType.Property, NameType.OperationGroup]
21
+ }
22
+ ];
23
+
24
+ export function buildMethodShortcutImplementation(paths: Paths) {
25
+ let keys: Record<string, string[]> = {};
26
+ for (const path of Object.keys(paths)) {
27
+ const groupName = normalizeName(
28
+ paths[path].operationGroupName,
29
+ NameType.OperationGroup,
30
+ true,
31
+ REST_CLIENT_RESERVED,
32
+ CasingConvention.Camel
33
+ );
34
+
35
+ if (keys[groupName]) {
36
+ keys[groupName].push(...buildOperationDeclarations(path, paths[path]));
37
+ } else {
38
+ keys[groupName] = buildOperationDeclarations(path, paths[path]);
39
+ }
40
+ }
41
+ return keys;
42
+ }
43
+
44
+ function buildOperationDeclarations(path: string, pathMetadata: PathMetadata) {
45
+ let ops: string[] = [];
46
+ for (const method of Object.keys(pathMetadata.methods)) {
47
+ for (const op of pathMetadata.methods[method]) {
48
+ const pathParams = pathMetadata?.pathParameters;
49
+ const name = normalizeName(op.operationName, NameType.Property);
50
+ const methodDefinitions = generateOperationDeclaration(
51
+ path,
52
+ name,
53
+ method,
54
+ pathParams
55
+ );
56
+ ops = [...ops, methodDefinitions];
57
+ }
58
+ }
59
+
60
+ return ops;
61
+ }
62
+
63
+ function generateOperationDeclaration(
64
+ path: string,
65
+ operationName: string,
66
+ method: string,
67
+ pathParams: PathParameter[] = []
68
+ ): string {
69
+ const pathParamNames = `${
70
+ pathParams.length > 0 ? `${pathParams.map((p) => p.name)},` : ""
71
+ }`;
72
+ return `"${operationName}": (${pathParamNames} options) => {
73
+ return client.path("${path}", ${pathParamNames}).${method}(options);
74
+ }`;
75
+ }