@servicenow/sdk-build-plugins 2.0.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 (166) hide show
  1. package/dist/AttachmentPlugin.d.ts +253 -0
  2. package/dist/AttachmentPlugin.js +216 -0
  3. package/dist/AttachmentPlugin.js.map +1 -0
  4. package/dist/BusinessRulePlugin.d.ts +56 -0
  5. package/dist/BusinessRulePlugin.js +171 -0
  6. package/dist/BusinessRulePlugin.js.map +1 -0
  7. package/dist/CrossScopePrivilegePlugin.d.ts +22 -0
  8. package/dist/CrossScopePrivilegePlugin.js +42 -0
  9. package/dist/CrossScopePrivilegePlugin.js.map +1 -0
  10. package/dist/DefaultPlugin.d.ts +71 -0
  11. package/dist/DefaultPlugin.js +238 -0
  12. package/dist/DefaultPlugin.js.map +1 -0
  13. package/dist/IdPlugin.d.ts +17 -0
  14. package/dist/IdPlugin.js +45 -0
  15. package/dist/IdPlugin.js.map +1 -0
  16. package/dist/ListPlugin.d.ts +91 -0
  17. package/dist/ListPlugin.js +398 -0
  18. package/dist/ListPlugin.js.map +1 -0
  19. package/dist/PropertyPlugin.d.ts +122 -0
  20. package/dist/PropertyPlugin.js +165 -0
  21. package/dist/PropertyPlugin.js.map +1 -0
  22. package/dist/ScriptTemplatePlugin.d.ts +31 -0
  23. package/dist/ScriptTemplatePlugin.js +208 -0
  24. package/dist/ScriptTemplatePlugin.js.map +1 -0
  25. package/dist/UserPreferencePlugin.d.ts +16 -0
  26. package/dist/UserPreferencePlugin.js +30 -0
  27. package/dist/UserPreferencePlugin.js.map +1 -0
  28. package/dist/aclAndRole/AclPlugin.d.ts +117 -0
  29. package/dist/aclAndRole/AclPlugin.js +285 -0
  30. package/dist/aclAndRole/AclPlugin.js.map +1 -0
  31. package/dist/aclAndRole/RolePlugin.d.ts +58 -0
  32. package/dist/aclAndRole/RolePlugin.js +152 -0
  33. package/dist/aclAndRole/RolePlugin.js.map +1 -0
  34. package/dist/aclAndRole/Util.d.ts +3 -0
  35. package/dist/aclAndRole/Util.js +106 -0
  36. package/dist/aclAndRole/Util.js.map +1 -0
  37. package/dist/app/ApplicationMenuPlugin.d.ts +32 -0
  38. package/dist/app/ApplicationMenuPlugin.js +106 -0
  39. package/dist/app/ApplicationMenuPlugin.js.map +1 -0
  40. package/dist/atf/ATFComposer.d.ts +492 -0
  41. package/dist/atf/ATFComposer.js +2717 -0
  42. package/dist/atf/ATFComposer.js.map +1 -0
  43. package/dist/atf/TestPlugin.d.ts +31 -0
  44. package/dist/atf/TestPlugin.js +95 -0
  45. package/dist/atf/TestPlugin.js.map +1 -0
  46. package/dist/atf/index.d.ts +1 -0
  47. package/dist/atf/index.js +9 -0
  48. package/dist/atf/index.js.map +1 -0
  49. package/dist/db/ColumnPlugins.d.ts +278 -0
  50. package/dist/db/ColumnPlugins.js +112 -0
  51. package/dist/db/ColumnPlugins.js.map +1 -0
  52. package/dist/db/RecordPlugin.d.ts +208 -0
  53. package/dist/db/RecordPlugin.js +287 -0
  54. package/dist/db/RecordPlugin.js.map +1 -0
  55. package/dist/db/TablePlugin.d.ts +742 -0
  56. package/dist/db/TablePlugin.js +1249 -0
  57. package/dist/db/TablePlugin.js.map +1 -0
  58. package/dist/db/index.d.ts +3 -0
  59. package/dist/db/index.js +27 -0
  60. package/dist/db/index.js.map +1 -0
  61. package/dist/index.d.ts +16 -0
  62. package/dist/index.js +51 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/scriptedRESTAPI/RESTDeserializationUtils.d.ts +12 -0
  65. package/dist/scriptedRESTAPI/RESTDeserializationUtils.js +371 -0
  66. package/dist/scriptedRESTAPI/RESTDeserializationUtils.js.map +1 -0
  67. package/dist/scriptedRESTAPI/RESTSerializationUtils.d.ts +15 -0
  68. package/dist/scriptedRESTAPI/RESTSerializationUtils.js +177 -0
  69. package/dist/scriptedRESTAPI/RESTSerializationUtils.js.map +1 -0
  70. package/dist/scriptedRESTAPI/RestApiPlugin.d.ts +144 -0
  71. package/dist/scriptedRESTAPI/RestApiPlugin.js +318 -0
  72. package/dist/scriptedRESTAPI/RestApiPlugin.js.map +1 -0
  73. package/dist/scriptedRESTAPI/RestSchemaUtils.d.ts +190 -0
  74. package/dist/scriptedRESTAPI/RestSchemaUtils.js +53 -0
  75. package/dist/scriptedRESTAPI/RestSchemaUtils.js.map +1 -0
  76. package/dist/scriptedRESTAPI/RestUtils.d.ts +75 -0
  77. package/dist/scriptedRESTAPI/RestUtils.js +469 -0
  78. package/dist/scriptedRESTAPI/RestUtils.js.map +1 -0
  79. package/dist/scripts/ClientScriptPlugin.d.ts +43 -0
  80. package/dist/scripts/ClientScriptPlugin.js +190 -0
  81. package/dist/scripts/ClientScriptPlugin.js.map +1 -0
  82. package/dist/scripts/scriptUtils.d.ts +15 -0
  83. package/dist/scripts/scriptUtils.js +83 -0
  84. package/dist/scripts/scriptUtils.js.map +1 -0
  85. package/dist/uxf/ExperiencePlugin.d.ts +22 -0
  86. package/dist/uxf/ExperiencePlugin.js +55 -0
  87. package/dist/uxf/ExperiencePlugin.js.map +1 -0
  88. package/dist/uxf/RoutesPlugin.d.ts +22 -0
  89. package/dist/uxf/RoutesPlugin.js +176 -0
  90. package/dist/uxf/RoutesPlugin.js.map +1 -0
  91. package/dist/uxf/UxfFormulaParser/cleanUxValue.d.ts +4 -0
  92. package/dist/uxf/UxfFormulaParser/cleanUxValue.js +65 -0
  93. package/dist/uxf/UxfFormulaParser/cleanUxValue.js.map +1 -0
  94. package/dist/uxf/UxfFormulaParser/grammerParser/api.d.ts +189 -0
  95. package/dist/uxf/UxfFormulaParser/grammerParser/api.js +158 -0
  96. package/dist/uxf/UxfFormulaParser/grammerParser/api.js.map +1 -0
  97. package/dist/uxf/UxfFormulaParser/grammerParser/clientTransformMap.d.ts +13 -0
  98. package/dist/uxf/UxfFormulaParser/grammerParser/clientTransformMap.js +604 -0
  99. package/dist/uxf/UxfFormulaParser/grammerParser/clientTransformMap.js.map +1 -0
  100. package/dist/uxf/UxfFormulaParser/grammerParser/grammarParser.d.ts +12 -0
  101. package/dist/uxf/UxfFormulaParser/grammerParser/grammarParser.js +551 -0
  102. package/dist/uxf/UxfFormulaParser/grammerParser/grammarParser.js.map +1 -0
  103. package/dist/uxf/UxfFormulaParser/grammerParser/spanHelpers.d.ts +31 -0
  104. package/dist/uxf/UxfFormulaParser/grammerParser/spanHelpers.js +64 -0
  105. package/dist/uxf/UxfFormulaParser/grammerParser/spanHelpers.js.map +1 -0
  106. package/dist/uxf/UxfFormulaParser/index.d.ts +3 -0
  107. package/dist/uxf/UxfFormulaParser/index.js +11 -0
  108. package/dist/uxf/UxfFormulaParser/index.js.map +1 -0
  109. package/dist/uxf/UxfFormulaParser/parser.d.ts +8 -0
  110. package/dist/uxf/UxfFormulaParser/parser.js +87 -0
  111. package/dist/uxf/UxfFormulaParser/parser.js.map +1 -0
  112. package/dist/uxf/UxfFormulaParser/utils/getErrorMsg.d.ts +8 -0
  113. package/dist/uxf/UxfFormulaParser/utils/getErrorMsg.js +17 -0
  114. package/dist/uxf/UxfFormulaParser/utils/getErrorMsg.js.map +1 -0
  115. package/dist/uxf/constants.d.ts +2 -0
  116. package/dist/uxf/constants.js +8 -0
  117. package/dist/uxf/constants.js.map +1 -0
  118. package/dist/uxf/index.d.ts +2 -0
  119. package/dist/uxf/index.js +11 -0
  120. package/dist/uxf/index.js.map +1 -0
  121. package/dist/uxf/tectonicIdGenerator.d.ts +12 -0
  122. package/dist/uxf/tectonicIdGenerator.js +102 -0
  123. package/dist/uxf/tectonicIdGenerator.js.map +1 -0
  124. package/license +9 -0
  125. package/package.json +42 -0
  126. package/src/AttachmentPlugin.ts +262 -0
  127. package/src/BusinessRulePlugin.ts +251 -0
  128. package/src/CrossScopePrivilegePlugin.ts +54 -0
  129. package/src/DefaultPlugin.ts +272 -0
  130. package/src/IdPlugin.ts +47 -0
  131. package/src/ListPlugin.ts +497 -0
  132. package/src/PropertyPlugin.ts +218 -0
  133. package/src/ScriptTemplatePlugin.ts +223 -0
  134. package/src/UserPreferencePlugin.ts +36 -0
  135. package/src/aclAndRole/AclPlugin.ts +410 -0
  136. package/src/aclAndRole/RolePlugin.ts +225 -0
  137. package/src/aclAndRole/Util.ts +104 -0
  138. package/src/app/ApplicationMenuPlugin.ts +158 -0
  139. package/src/atf/ATFComposer.ts +3356 -0
  140. package/src/atf/TestPlugin.ts +119 -0
  141. package/src/atf/index.ts +1 -0
  142. package/src/db/ColumnPlugins.ts +117 -0
  143. package/src/db/RecordPlugin.ts +391 -0
  144. package/src/db/TablePlugin.ts +1581 -0
  145. package/src/db/index.ts +3 -0
  146. package/src/index.ts +16 -0
  147. package/src/scriptedRESTAPI/RESTDeserializationUtils.ts +410 -0
  148. package/src/scriptedRESTAPI/RESTSerializationUtils.ts +227 -0
  149. package/src/scriptedRESTAPI/RestApiPlugin.ts +438 -0
  150. package/src/scriptedRESTAPI/RestSchemaUtils.ts +72 -0
  151. package/src/scriptedRESTAPI/RestUtils.ts +507 -0
  152. package/src/scripts/ClientScriptPlugin.ts +251 -0
  153. package/src/scripts/scriptUtils.ts +81 -0
  154. package/src/uxf/ExperiencePlugin.ts +64 -0
  155. package/src/uxf/RoutesPlugin.ts +215 -0
  156. package/src/uxf/UxfFormulaParser/cleanUxValue.ts +73 -0
  157. package/src/uxf/UxfFormulaParser/grammerParser/api.js +166 -0
  158. package/src/uxf/UxfFormulaParser/grammerParser/clientTransformMap.js +606 -0
  159. package/src/uxf/UxfFormulaParser/grammerParser/grammarParser.js +551 -0
  160. package/src/uxf/UxfFormulaParser/grammerParser/spanHelpers.js +65 -0
  161. package/src/uxf/UxfFormulaParser/index.ts +4 -0
  162. package/src/uxf/UxfFormulaParser/parser.ts +64 -0
  163. package/src/uxf/UxfFormulaParser/utils/getErrorMsg.ts +13 -0
  164. package/src/uxf/constants.ts +4 -0
  165. package/src/uxf/index.ts +2 -0
  166. package/src/uxf/tectonicIdGenerator.ts +81 -0
@@ -0,0 +1,507 @@
1
+ /* eslint-disable no-case-declarations */
2
+ import * as ts from 'ts-morph'
3
+ import {
4
+ Context,
5
+ Document,
6
+ Result,
7
+ DocumentPointer,
8
+ removeNode,
9
+ getCallExpressionName,
10
+ Arranged,
11
+ UnlinkedDocument,
12
+ getNodeId,
13
+ } from '@servicenow/sdk-build-core'
14
+ import { isEqual } from 'lodash'
15
+ import { RestApi } from '@servicenow/sdk-core/runtime/rest'
16
+ import { RecordPlugin } from '../db/RecordPlugin'
17
+
18
+ export const restTables = [
19
+ 'sys_ws_definition',
20
+ 'sys_ws_operation',
21
+ 'sys_ws_version',
22
+ 'sys_ws_header',
23
+ 'sys_ws_query_parameter',
24
+ 'sys_ws_header_map',
25
+ 'sys_ws_query_parameter_map',
26
+ ]
27
+
28
+ let restDocumentMap: any = undefined
29
+
30
+ /** checks if a given query_parameter or header doc is consumed by the given rest or not in current transformation*/
31
+ const isConsumedByRest = (restId: string, restDocMap: any, document: Document) => {
32
+ if (restDocMap[restId] && restDocMap[restId].route_attrs_map) {
33
+ const found = Object.values(restDocMap[restId].route_attrs_map).find(
34
+ (doc: any) =>
35
+ doc.data['data'].web_service_query_parameter === document.guid ||
36
+ doc.data['data'].web_service_header === document.guid
37
+ )
38
+ return found ? true : false
39
+ }
40
+ return false
41
+ }
42
+
43
+ export function getRouteAttrMapResult(document: Document, restDocMap: any): Result<DocumentPointer | undefined> {
44
+ if (document.action === 'DELETE') {
45
+ return { handled: false }
46
+ }
47
+ /**
48
+ * if the given attribute map record references query parameter or routes record
49
+ * not part of the current app we make it a record call.
50
+ */
51
+ const isUnHandled = restDocMap['unHandled_records'].find((doc) => doc.id === document.guid)
52
+ if (isUnHandled) {
53
+ return { handled: false }
54
+ }
55
+
56
+ const routeId = document.data!['data']['web_service_operation']
57
+ const paramField =
58
+ document.data!['table'] === 'sys_ws_header_map' ? 'web_service_header' : 'web_service_query_parameter'
59
+ potentialRecordCallRouteParams.delete(document.data!['data'][paramField])
60
+ /** if the attribute map record is consumed by any rest then associated appropriate parent */
61
+ return { handled: true, result: { kind: 'record', guid: routeId } }
62
+ }
63
+
64
+ /** utility function to get the args node of a call expression node*/
65
+ export const getArgs = (document: Document) => {
66
+ const node = document.node!.asKindOrThrow(ts.SyntaxKind.CallExpression)
67
+ return node.getArguments()[0]!.asKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression)
68
+ }
69
+
70
+ /**
71
+ * utility function that iterates over all the headers or parameters of all routes
72
+ * inside a rest api and calls the provided callback function.
73
+ */
74
+ export function iterateAllRouteAttrNode(
75
+ document: Document,
76
+ eleAttrName: string,
77
+ callback: (
78
+ expression: ts.ArrayLiteralExpression,
79
+ element: ts.Expression,
80
+ compositeKey: { attr: string; route: string; rest: string }
81
+ ) => void
82
+ ) {
83
+ const argsNode = getArgs(document)
84
+ const restId = getNodeId(argsNode)!
85
+ argsNode
86
+ .getProperty('routes')
87
+ ?.asKind(ts.ts.SyntaxKind.PropertyAssignment)
88
+ ?.getInitializerIfKindOrThrow(ts.ts.SyntaxKind.ArrayLiteralExpression)
89
+ ?.getElements()
90
+ .forEach((element) => {
91
+ const routeId = getNodeId(element.asKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression))!
92
+ const arrayExpression = element
93
+ .asKind(ts.ts.SyntaxKind.ObjectLiteralExpression)
94
+ ?.getProperty(eleAttrName)
95
+ ?.asKind(ts.ts.SyntaxKind.PropertyAssignment)
96
+ ?.getInitializerIfKindOrThrow(ts.ts.SyntaxKind.ArrayLiteralExpression)
97
+
98
+ arrayExpression?.getElements().forEach((element) => {
99
+ const attrId = getNodeId(element.asKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression))!
100
+ callback(arrayExpression, element, {
101
+ attr: attrId,
102
+ route: routeId,
103
+ rest: restId,
104
+ })
105
+ })
106
+ })
107
+ }
108
+
109
+ /**
110
+ * keeps track of all those params records associated with a rest node,
111
+ * so if some m2m records removes all its associations then we would create record call.
112
+ */
113
+ export const potentialRecordCallRouteParams = new Set<string>()
114
+ /**
115
+ * There are some of the completely unassociated param records generated when an
116
+ * m2m record swtiches from one rest to another rest entity. we track them to generate records calls
117
+ * if required.
118
+ */
119
+ export const completelyUnassociatedParamIds = new Set<string>()
120
+ /**
121
+ * Ids of records which are going to be a record call so need not handle them in rest generator.
122
+ */
123
+ export const unHandledIds: string[] = []
124
+
125
+ /** Based on the result it adds the document id to unHandledIds which is later on used by generator
126
+ * to avoid generating nodes for them since it's expected to be handled by record plugin.
127
+ */
128
+ export function handleAndReturnResult(result: Result<DocumentPointer | undefined>, document: Document) {
129
+ if (!result.handled) {
130
+ unHandledIds.push(document.guid)
131
+ }
132
+ return result
133
+ }
134
+
135
+ /**
136
+ * Performs the following operations:
137
+ * 1. If the documents associated rest node is the same as the referenced rest in data, let the rest node handle it.
138
+ * 2. If it refernces a different rest node, then clean up the current rest node and its keys. If the referenced rest node
139
+ * has a mapping record to consume this parameter or header let it handle, else make it a record call.
140
+ */
141
+ export function getRouteAttrWithRestNodeResult(
142
+ document: Document,
143
+ context: Context,
144
+ restDocMap: any
145
+ ): Result<DocumentPointer | undefined> {
146
+ const restId = document.data!['data']['web_service_definition']
147
+ const nodeId = getNodeId(getArgs(document))!
148
+ const nodeRestId = context.keys.explicit[nodeId]!.id
149
+ if (document.action === 'DELETE') {
150
+ return { handled: true, result: { kind: 'record', guid: nodeRestId } }
151
+ }
152
+ if (restId === nodeRestId) {
153
+ return { handled: true, result: { kind: 'record', guid: restId } }
154
+ }
155
+ const attrName = document.data!['table'] === 'sys_ws_header' ? 'headers' : 'parameters'
156
+ let deleteAttrKey: string | undefined = undefined
157
+ iterateAllRouteAttrNode(document, attrName, (arrayExp, element, compKey) => {
158
+ const attrSysId = context.keys.explicit[compKey.attr]!.id
159
+ if (attrSysId === document.guid) {
160
+ deleteAttrKey = compKey.attr
161
+ const idx = context.keys.composite.findIndex((k) => isEqual(k.key, compKey))
162
+ if (idx !== -1) {
163
+ context.keys.composite.splice(idx, 1)
164
+ }
165
+ arrayExp.removeElement(element)
166
+ }
167
+ })
168
+ if (deleteAttrKey) {
169
+ delete context.keys.explicit[deleteAttrKey]
170
+ }
171
+ potentialRecordCallRouteParams.delete(document.guid)
172
+ deleteDocNode(document, context)
173
+ document['changedData'] = { data: document.data!['data'] }
174
+ if (!isConsumed(document, restDocMap)) {
175
+ return { handled: false }
176
+ }
177
+ return { handled: true, result: { kind: 'record', guid: restId } }
178
+ }
179
+
180
+ function isConsumed(document: Document, restDocMap: any) {
181
+ const restId = document.data!['data']['web_service_definition']
182
+ const isUnHandled = restDocMap['unHandled_records'].find((doc) => doc.id === document.guid)
183
+ if (isUnHandled) {
184
+ return false
185
+ }
186
+
187
+ return isConsumedByRest(restId, restDocMap, document)
188
+ }
189
+
190
+ /**
191
+ * Deletes node reference from document and updates in context docs as well to keep it updated.
192
+ */
193
+ const deleteDocNode = (document: Document, context: Context) => {
194
+ const foundDoc = context.getAllDocuments().find((doc) => doc.guid === document.guid)
195
+ delete foundDoc?.node
196
+ delete document.node
197
+ }
198
+
199
+ /**
200
+ * checks to see if the document is consumed by any rest api. If consumed then
201
+ * remove that record call expression and its associated keys and let rest handle
202
+ * it, else update the record call.
203
+ */
204
+ export function getRouteAttrWithRecordNodeResult(
205
+ document: Document,
206
+ context: Context,
207
+ restDocMap: any
208
+ ): Result<DocumentPointer | undefined> {
209
+ if (document.action === 'DELETE') {
210
+ return { handled: false }
211
+ }
212
+ if (!isConsumed(document, restDocMap)) {
213
+ return { handled: false }
214
+ }
215
+ const recordId = getNodeId(getArgs(document))!
216
+ removeNode(document.node!)
217
+ deleteDocNode(document, context)
218
+ delete context.keys.explicit[recordId]
219
+ document['changedData'] = { data: document.data!['data'] }
220
+ return { handled: true, result: { kind: 'record', guid: document.data!['data']['web_service_definition'] } }
221
+ }
222
+
223
+ /** Gets the rest id for the given route id.*/
224
+ const getRestIdForRoute = (routeId: string, restDocMap: any) => {
225
+ let restId: string | undefined = undefined
226
+ Object.keys(restDocMap)
227
+ .filter((key) => key !== 'unHanlded_records' && key !== 'delete_records')
228
+ .forEach((key) => {
229
+ if (restDocMap[key].routes) {
230
+ const foundRoute = Object.values(restDocMap[key].routes).find((doc: any) => doc.id === routeId)
231
+ if (foundRoute) {
232
+ restId = key
233
+ }
234
+ }
235
+ })
236
+ return restId
237
+ }
238
+
239
+ /** cleans the mapping node from given array and keys file*/
240
+ const cleanMapDocInNode = (
241
+ arrayExp: ts.ArrayLiteralExpression,
242
+ element: ts.Expression,
243
+ compKey: { attr: string; route: string; rest: string },
244
+ context: Context,
245
+ document: Document
246
+ ) => {
247
+ const idx = context.keys.composite.findIndex((k) => isEqual(k.key, compKey))
248
+ if (idx !== -1) {
249
+ const compObj = context.keys.composite[idx]
250
+ if (compObj?.id === document.guid) {
251
+ arrayExp.removeElement(element)
252
+ context.keys.composite.splice(idx, 1)
253
+ return context.keys.explicit[compKey.attr]?.id
254
+ }
255
+ }
256
+ return undefined
257
+ }
258
+
259
+ /**
260
+ * Performs the following operations:
261
+ * 1. If the mapping record references a differnt route or query param not part of the app
262
+ * then clean its presence from current rest node and create a record call for it.
263
+ * 2. If if references a different route and query param in same app, the clean current rest
264
+ * and update the new rest node.
265
+ * 3. If references same rest then update it.
266
+ *
267
+ * In addition to that, it also tracks the params unassociated to create record call if needed.
268
+ */
269
+ export function getRouteAttrMapWithRestNodeResult(
270
+ document: Document,
271
+ context: Context,
272
+ restDocMap: any
273
+ ): Result<DocumentPointer | undefined> {
274
+ const restNodeId = getNodeId(getArgs(document))
275
+ const restNodeSysId = context.keys.explicit[restNodeId!]!.id
276
+ const routeId = document.data!['data']['web_service_operation']
277
+ if (document.action === 'DELETE') {
278
+ return { handled: true, result: { kind: 'record', guid: routeId } }
279
+ }
280
+ const isUnHandled = restDocMap['unHandled_records'].find((doc) => doc.id === document.guid)
281
+ const attrName = document.data!['table'] === 'sys_ws_header_map' ? 'headers' : 'parameters'
282
+ if (isUnHandled) {
283
+ let unAssociatedId: string | undefined = undefined
284
+ document['changedData'] = { data: document.data!['data'] }
285
+ iterateAllRouteAttrNode(document, attrName, (arrayExp, element, compKey) => {
286
+ unAssociatedId = unAssociatedId ?? cleanMapDocInNode(arrayExp, element, compKey, context, document)
287
+ })
288
+ if (unAssociatedId) {
289
+ if (!isParamAssociated(document, attrName, context, unAssociatedId)) {
290
+ completelyUnassociatedParamIds.add(unAssociatedId)
291
+ }
292
+ }
293
+ deleteDocNode(document, context)
294
+ return { handled: false }
295
+ }
296
+ const paramField =
297
+ document.data!['table'] === 'sys_ws_header_map' ? 'web_service_header' : 'web_service_query_parameter'
298
+ potentialRecordCallRouteParams.delete(document.data!['data'][paramField])
299
+ const restId = getRestIdForRoute(routeId, restDocMap)!
300
+ if (restId !== restNodeSysId) {
301
+ let unAssociatedId: string | undefined = undefined
302
+ document['changedData'] = { data: document.data!['data'] }
303
+ iterateAllRouteAttrNode(document, attrName, (arrayExp, element, compKey) => {
304
+ unAssociatedId = unAssociatedId ?? cleanMapDocInNode(arrayExp, element, compKey, context, document)
305
+ })
306
+ if (unAssociatedId) {
307
+ if (!isParamAssociated(document, attrName, context, unAssociatedId)) {
308
+ completelyUnassociatedParamIds.add(unAssociatedId)
309
+ }
310
+ }
311
+ deleteDocNode(document, context)
312
+ }
313
+ return { handled: true, result: { kind: 'record', guid: routeId } }
314
+ }
315
+
316
+ /**
317
+ * Performs following operation
318
+ * 1. If given mapping record references some routes or param not in app update the record call.
319
+ * 2. If references some existing route and param, then remove the current record call and its keys.
320
+ * Update the new rest node.
321
+ */
322
+ export function getRouteAttrMapWithRecordNodeResult(
323
+ document: Document,
324
+ context: Context,
325
+ restDocMap: any
326
+ ): Result<DocumentPointer | undefined> {
327
+ if (document.action === 'DELETE') {
328
+ return { handled: false }
329
+ }
330
+ const routeId = document.data!['data']['web_service_operation']
331
+ const isUnHandled = restDocMap['unHandled_records'].find((doc) => doc.id === document.guid)
332
+ if (isUnHandled) {
333
+ return { handled: false }
334
+ }
335
+ document['changedData'] = { data: document.data!['data'] }
336
+ const recordId = getNodeId(getArgs(document))!
337
+ const paramField =
338
+ document.data!['table'] === 'sys_ws_header_map' ? 'web_service_header' : 'web_service_query_parameter'
339
+ potentialRecordCallRouteParams.delete(document.data!['data'][paramField])
340
+ removeNode(document.node!)
341
+ deleteDocNode(document, context)
342
+ delete context.keys.explicit[recordId]
343
+ return { handled: true, result: { kind: 'record', guid: routeId } }
344
+ }
345
+
346
+ /**
347
+ * If the incoming parameter or header record is consumed by rest associated it to the rest node, else make it a record call.
348
+ */
349
+ export function getRouteAttrResult(document: Document, restDocMap: any): Result<DocumentPointer | undefined> {
350
+ if (document.action === 'DELETE') {
351
+ return { handled: false }
352
+ }
353
+ const restId = document.data!['data']['web_service_definition']
354
+ if (isConsumedByRest(restId, restDocMap, document)) {
355
+ return { handled: true, result: { kind: 'record', guid: restId } }
356
+ }
357
+ return { handled: false }
358
+ }
359
+
360
+ const findRecInDocs = (docs: any[], id: string, table: string) => {
361
+ return docs?.find((doc) => doc.data!['table'] === table && doc.id == id)
362
+ }
363
+
364
+ export function getRestDocumentMap(context: Context) {
365
+ if (restDocumentMap) {
366
+ return restDocumentMap
367
+ }
368
+ /**gets all rest related docs */
369
+ const allRestDocs = context.getAllDocuments().filter((doc) => restTables.includes(doc.data!['table']))
370
+ allRestDocs.sort((doc1, doc2) => {
371
+ const doc1Table = doc1.data!['table']
372
+ const doc2Table = doc2.data!['table']
373
+ return restTables.indexOf(doc1Table) - restTables.indexOf(doc2Table)
374
+ })
375
+ const unHandledDocs: any[] = []
376
+ /** filters and gets all delete records and insert_or_update records */
377
+ const delRestDocs = allRestDocs.filter((doc) => doc.action === 'DELETE')
378
+ const existingDocs = allRestDocs.filter((doc) => doc.action !== 'DELETE')
379
+ existingDocs.map((doc) => {
380
+ const table = doc.data!['table']
381
+ if (
382
+ (table === 'sys_ws_query_parameter' || table === 'sys_ws_header') &&
383
+ doc.node &&
384
+ getCallExpressionName(doc.node as ts.CallExpression) === RestApi.name
385
+ ) {
386
+ potentialRecordCallRouteParams.add(doc.guid)
387
+ }
388
+ })
389
+ /**
390
+ * Generates the rest document map with insert or udpate records
391
+ * and includes only mapping records that are being transformed (this is needed to determine if an
392
+ * parameter or header record will be consumed by rest in current transformation)
393
+ */
394
+ restDocumentMap = existingDocs.reduce((docMap, doc) => {
395
+ switch (doc.data!['table']) {
396
+ case 'sys_ws_definition':
397
+ docMap[doc.guid] = {
398
+ restDef: doc,
399
+ }
400
+ break
401
+ case 'sys_ws_operation':
402
+ const op_restId = doc.data!['data']['web_service_definition']
403
+ if (docMap[op_restId]) {
404
+ ;(docMap[op_restId].routes ??= {})[doc.guid] = doc
405
+ }
406
+ break
407
+ case 'sys_ws_version':
408
+ const v_restId = doc.data!['data']['web_service_definition']
409
+ if (docMap[v_restId]) {
410
+ ;(docMap[v_restId].versions ??= {})[doc.guid] = doc
411
+ }
412
+ break
413
+ case 'sys_ws_query_parameter':
414
+ case 'sys_ws_header':
415
+ const qp_restId = doc.data!['data']['web_service_definition']
416
+ if (docMap[qp_restId]) {
417
+ ;(docMap[qp_restId].route_attrs ??= {})[doc.guid] = doc
418
+ } else {
419
+ //throw error if the transforming doc references a deleted record
420
+ if (doc.changedData) {
421
+ const found = findRecInDocs(delRestDocs, qp_restId, 'sys_ws_definition')
422
+ if (found) {
423
+ throw Error(
424
+ `Found invalid rest route param record: ${doc.guid} referencing deleted rest record: ${qp_restId}`
425
+ )
426
+ }
427
+ unHandledDocs.push(doc)
428
+ }
429
+ }
430
+ break
431
+ case 'sys_ws_query_parameter_map':
432
+ case 'sys_ws_header_map':
433
+ if (!doc.changedData) {
434
+ break
435
+ }
436
+ const attrTable =
437
+ doc.data!['table'] === 'sys_ws_header_map' ? 'sys_ws_header' : 'sys_ws_query_parameter'
438
+ const attr =
439
+ doc.data!['table'] === 'sys_ws_header_map' ? 'web_service_header' : 'web_service_query_parameter'
440
+ const qpm_routeId = doc.data!['data']['web_service_operation']
441
+ const qpm_paramId = doc.data!['data'][attr]
442
+ const delOperationFound = findRecInDocs(delRestDocs, qpm_routeId, 'sys_ws_operation')
443
+ const delParamFound = findRecInDocs(delRestDocs, qpm_paramId, attrTable)
444
+ if (delOperationFound || delParamFound) {
445
+ throw Error(
446
+ `Found invalid rest map record: ${doc.guid} referencing deleted record: ${
447
+ delOperationFound ? delOperationFound : delParamFound
448
+ }`
449
+ )
450
+ }
451
+ const routeRec = findRecInDocs(existingDocs, qpm_routeId, 'sys_ws_operation')
452
+ const paramRec = findRecInDocs(existingDocs, qpm_paramId, attrTable)
453
+ /**if both route and parameter are part of same rest then it will be consumed by rest */
454
+ if (routeRec && paramRec) {
455
+ const routeRestId = routeRec.data!['data']['web_service_definition']
456
+ const paramRestId = paramRec.data!['data']['web_service_definition']
457
+ if (routeRestId !== paramRestId) {
458
+ throw Error(
459
+ `Found invalid rest mapping record: ${doc.guid} which has param: ${qpm_paramId} and route: ${qpm_routeId} pointing to different rest definition`
460
+ )
461
+ }
462
+ ;(docMap[routeRestId].route_attrs_map ??= {})[doc.guid] = doc
463
+ } else if (paramRec) {
464
+ const paramRestId = paramRec.data!['data']['web_service_definition']
465
+ const foundRestRec = findRecInDocs(existingDocs, paramRestId, 'sys_ws_definition')
466
+ /**In mapping records resources and paramter should point to same rest*/
467
+ if (foundRestRec) {
468
+ throw Error(
469
+ `Found invalid rest mapping record: ${doc.guid} where the resource references different rest entity than the rest attr.`
470
+ )
471
+ }
472
+ unHandledDocs.push(doc)
473
+ } else {
474
+ unHandledDocs.push(doc)
475
+ }
476
+ }
477
+ return docMap
478
+ }, {})
479
+ restDocumentMap['delete_records'] = delRestDocs ?? []
480
+ restDocumentMap['unHandled_records'] = unHandledDocs ?? []
481
+ return restDocumentMap
482
+ }
483
+
484
+ export function isParamAssociated(document: Document, eleAttrName: string, context: Context, paramSysId: string) {
485
+ let isAssociated = false
486
+ iterateAllRouteAttrNode(document, eleAttrName, (_expression, _element, compKey) => {
487
+ if (context.keys.explicit[compKey.attr]?.id === paramSysId) {
488
+ isAssociated = true
489
+ }
490
+ })
491
+ return isAssociated
492
+ }
493
+
494
+ /**
495
+ * Helper function that creates a record call for the document passed.
496
+ */
497
+ export function createRecordCall(paramSysId: string, context: Context) {
498
+ const paramKey = context.keys.findExplicitKeyById(paramSysId)
499
+ if (paramKey) {
500
+ delete context.keys.explicit[paramKey]
501
+ }
502
+ const document = context.getDocument(paramSysId)!
503
+ document['changedData'] = { data: document.data!['data'] }
504
+ delete document?.node
505
+ const linkedDoc = RecordPlugin.generators.record(document as UnlinkedDocument<'record'> & Arranged, context)
506
+ RecordPlugin.transformers.record.CallExpression(linkedDoc)
507
+ }