@servicenow/sdk-build-plugins 4.7.2 → 4.8.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.
- package/dist/alias/alias-plugin.d.ts +2 -0
- package/dist/alias/alias-plugin.js +183 -0
- package/dist/alias/alias-plugin.js.map +1 -0
- package/dist/alias/alias-template-plugin.d.ts +2 -0
- package/dist/alias/alias-template-plugin.js +232 -0
- package/dist/alias/alias-template-plugin.js.map +1 -0
- package/dist/alias/index.d.ts +3 -0
- package/dist/alias/index.js +20 -0
- package/dist/alias/index.js.map +1 -0
- package/dist/alias/retry-policy-plugin.d.ts +2 -0
- package/dist/alias/retry-policy-plugin.js +119 -0
- package/dist/alias/retry-policy-plugin.js.map +1 -0
- package/dist/arrow-function-plugin.d.ts +1 -0
- package/dist/arrow-function-plugin.js +60 -21
- package/dist/arrow-function-plugin.js.map +1 -1
- package/dist/atf/test-plugin.js +1 -1
- package/dist/atf/test-plugin.js.map +1 -1
- package/dist/basic-syntax-plugin.js +7 -7
- package/dist/basic-syntax-plugin.js.map +1 -1
- package/dist/column/index.d.ts +2 -0
- package/dist/column/index.js +13 -0
- package/dist/column/index.js.map +1 -0
- package/dist/dashboard/dashboard-plugin.js +4 -0
- package/dist/dashboard/dashboard-plugin.js.map +1 -1
- package/dist/data-lookup-plugin.d.ts +2 -0
- package/dist/data-lookup-plugin.js +159 -0
- package/dist/data-lookup-plugin.js.map +1 -0
- package/dist/flow/flow-logic/flow-logic-diagnostics.js +2 -1
- package/dist/flow/flow-logic/flow-logic-diagnostics.js.map +1 -1
- package/dist/flow/flow-logic/flow-logic-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-action-definition-plugin.js +81 -16
- package/dist/flow/plugins/flow-action-definition-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-definition-plugin.js +70 -7
- package/dist/flow/plugins/flow-definition-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-instance-plugin.d.ts +35 -1
- package/dist/flow/plugins/flow-instance-plugin.js +241 -7
- package/dist/flow/plugins/flow-instance-plugin.js.map +1 -1
- package/dist/flow/plugins/step-instance-plugin.js +61 -1
- package/dist/flow/plugins/step-instance-plugin.js.map +1 -1
- package/dist/flow/post-install.d.ts +2 -1
- package/dist/flow/post-install.js +31 -4
- package/dist/flow/post-install.js.map +1 -1
- package/dist/flow/utils/complex-object-resolver.js +4 -2
- package/dist/flow/utils/complex-object-resolver.js.map +1 -1
- package/dist/flow/utils/datapill-transformer.d.ts +5 -72
- package/dist/flow/utils/datapill-transformer.js +199 -28
- package/dist/flow/utils/datapill-transformer.js.map +1 -1
- package/dist/flow/utils/flow-constants.d.ts +7 -0
- package/dist/flow/utils/flow-constants.js +6 -1
- package/dist/flow/utils/flow-constants.js.map +1 -1
- package/dist/flow/utils/flow-io-to-record.js +24 -15
- package/dist/flow/utils/flow-io-to-record.js.map +1 -1
- package/dist/flow/utils/flow-shapes.d.ts +8 -2
- package/dist/flow/utils/flow-shapes.js +19 -0
- package/dist/flow/utils/flow-shapes.js.map +1 -1
- package/dist/flow/utils/flow-variable-processor.d.ts +6 -6
- package/dist/flow/utils/flow-variable-processor.js +8 -8
- package/dist/flow/utils/flow-variable-processor.js.map +1 -1
- package/dist/form-plugin.js +35 -24
- package/dist/form-plugin.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/now-attach-plugin.d.ts +1 -1
- package/dist/now-config-plugin.js +2 -1
- package/dist/now-config-plugin.js.map +1 -1
- package/dist/now-delete-plugin.d.ts +2 -0
- package/dist/now-delete-plugin.js +64 -0
- package/dist/now-delete-plugin.js.map +1 -0
- package/dist/record-plugin.d.ts +10 -0
- package/dist/record-plugin.js +15 -1
- package/dist/record-plugin.js.map +1 -1
- package/dist/repack/lint/Rules.js +17 -7
- package/dist/repack/lint/Rules.js.map +1 -1
- package/dist/rest-message-plugin.d.ts +2 -0
- package/dist/rest-message-plugin.js +331 -0
- package/dist/rest-message-plugin.js.map +1 -0
- package/dist/script-include-plugin.js +1 -1
- package/dist/script-include-plugin.js.map +1 -1
- package/dist/server-module-plugin/sbom-builder.js +17 -7
- package/dist/server-module-plugin/sbom-builder.js.map +1 -1
- package/dist/static-content-plugin.js +17 -7
- package/dist/static-content-plugin.js.map +1 -1
- package/package.json +7 -6
- package/src/alias/alias-plugin.ts +221 -0
- package/src/alias/alias-template-plugin.ts +271 -0
- package/src/alias/index.ts +3 -0
- package/src/alias/retry-policy-plugin.ts +138 -0
- package/src/arrow-function-plugin.ts +67 -23
- package/src/atf/test-plugin.ts +1 -1
- package/src/basic-syntax-plugin.ts +7 -7
- package/src/column/index.ts +7 -0
- package/src/dashboard/dashboard-plugin.ts +4 -0
- package/src/data-lookup-plugin.ts +191 -0
- package/src/flow/flow-logic/flow-logic-diagnostics.ts +2 -1
- package/src/flow/flow-logic/flow-logic-plugin.ts +0 -1
- package/src/flow/plugins/flow-action-definition-plugin.ts +92 -25
- package/src/flow/plugins/flow-definition-plugin.ts +114 -8
- package/src/flow/plugins/flow-instance-plugin.ts +264 -7
- package/src/flow/plugins/step-instance-plugin.ts +74 -2
- package/src/flow/post-install.ts +36 -5
- package/src/flow/utils/complex-object-resolver.ts +4 -2
- package/src/flow/utils/datapill-transformer.ts +248 -36
- package/src/flow/utils/flow-constants.ts +8 -0
- package/src/flow/utils/flow-io-to-record.ts +28 -14
- package/src/flow/utils/flow-shapes.ts +19 -0
- package/src/flow/utils/flow-variable-processor.ts +21 -10
- package/src/form-plugin.ts +47 -26
- package/src/index.ts +5 -1
- package/src/now-config-plugin.ts +2 -1
- package/src/now-delete-plugin.ts +82 -0
- package/src/record-plugin.ts +17 -2
- package/src/rest-message-plugin.ts +391 -0
- package/src/script-include-plugin.ts +4 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { CallExpressionShape, Plugin } from '@servicenow/sdk-build-core'
|
|
2
|
+
import { NowIdShape } from '../now-id-plugin'
|
|
3
|
+
|
|
4
|
+
const SYS_RETRY_POLICY = 'sys_retry_policy'
|
|
5
|
+
|
|
6
|
+
const DEFAULT_CONNECTION_TYPE = 'http_retry_conditions'
|
|
7
|
+
const DEFAULT_RETRY_STRATEGY = 'fixed_time_interval'
|
|
8
|
+
const MAX_ELAPSED_TIME_SECONDS = 86400
|
|
9
|
+
const DEFAULT_RESTRICT_TO = 'http_method,status_code,error,response_body,response_headers'
|
|
10
|
+
|
|
11
|
+
export const RetryPolicyPlugin = Plugin.create({
|
|
12
|
+
name: 'RetryPolicyPlugin',
|
|
13
|
+
|
|
14
|
+
records: {
|
|
15
|
+
[SYS_RETRY_POLICY]: {
|
|
16
|
+
async toShape(record) {
|
|
17
|
+
const restrictToRaw = record.get('restrict_to')?.ifString()?.getValue()
|
|
18
|
+
const restrictToArray =
|
|
19
|
+
restrictToRaw === undefined || restrictToRaw === DEFAULT_RESTRICT_TO
|
|
20
|
+
? undefined
|
|
21
|
+
: restrictToRaw === ''
|
|
22
|
+
? []
|
|
23
|
+
: restrictToRaw
|
|
24
|
+
.split(',')
|
|
25
|
+
.map((s) => s.trim())
|
|
26
|
+
.filter(Boolean)
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
success: true,
|
|
30
|
+
value: new CallExpressionShape({
|
|
31
|
+
source: record,
|
|
32
|
+
callee: 'RetryPolicy',
|
|
33
|
+
args: [
|
|
34
|
+
record.transform(({ $ }) => ({
|
|
35
|
+
$id: $.val(NowIdShape.from(record)),
|
|
36
|
+
name: $.def(''),
|
|
37
|
+
connectionType: $.from('connection_type').def(DEFAULT_CONNECTION_TYPE),
|
|
38
|
+
retryStrategy: $.from('retry_strategy').def(DEFAULT_RETRY_STRATEGY),
|
|
39
|
+
count: $.map((v) => v.ifString()?.ifNotEmpty()?.toNumber()),
|
|
40
|
+
interval: $.map((v) => v.ifString()?.ifNotEmpty()?.toNumber()),
|
|
41
|
+
maxElapsedTime: $.from('max_elapsed_time').map((v) =>
|
|
42
|
+
v.ifString()?.ifNotEmpty()?.toNumber()
|
|
43
|
+
),
|
|
44
|
+
condition: $.def(''),
|
|
45
|
+
restrictTo: $.val(restrictToArray),
|
|
46
|
+
protectionPolicy: $.from('sys_policy').def(''),
|
|
47
|
+
})),
|
|
48
|
+
],
|
|
49
|
+
}),
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
shapes: [
|
|
56
|
+
{
|
|
57
|
+
shape: CallExpressionShape,
|
|
58
|
+
fileTypes: ['fluent'],
|
|
59
|
+
async toRecord(callExpression, { factory, diagnostics }) {
|
|
60
|
+
if (callExpression.getCallee() !== 'RetryPolicy') {
|
|
61
|
+
return { success: false }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const arg = callExpression.getArgument(0).asObject()
|
|
65
|
+
|
|
66
|
+
// Runtime validation for maxElapsedTime upper bound (TypeScript cannot enforce numeric ranges)
|
|
67
|
+
const maxElapsedTimeShape = arg.get('maxElapsedTime')
|
|
68
|
+
const maxElapsedTime = maxElapsedTimeShape.ifNumber()?.getValue()
|
|
69
|
+
if (maxElapsedTime !== undefined && maxElapsedTime > MAX_ELAPSED_TIME_SECONDS) {
|
|
70
|
+
diagnostics.error(
|
|
71
|
+
maxElapsedTimeShape,
|
|
72
|
+
`maxElapsedTime must not exceed ${MAX_ELAPSED_TIME_SECONDS} seconds (24 hours). Received: ${maxElapsedTime}.`
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const countShape = arg.get('count')
|
|
77
|
+
const count = countShape.ifNumber()?.getValue()
|
|
78
|
+
if (count !== undefined && !Number.isInteger(count)) {
|
|
79
|
+
diagnostics.error(countShape, `count must be an integer. Received: ${count}.`)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const intervalShape = arg.get('interval')
|
|
83
|
+
const interval = intervalShape.ifNumber()?.getValue()
|
|
84
|
+
if (interval !== undefined && !Number.isInteger(interval)) {
|
|
85
|
+
diagnostics.error(intervalShape, `interval must be an integer. Received: ${interval}.`)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Convert restrictTo string[] → comma-separated string for the DB
|
|
89
|
+
const restrictToShape = arg.get('restrictTo')
|
|
90
|
+
const restrictToArrayShape = restrictToShape.ifArray()
|
|
91
|
+
const restrictToCsv = restrictToArrayShape
|
|
92
|
+
? restrictToArrayShape
|
|
93
|
+
.getElements()
|
|
94
|
+
.map((el) => el.ifString()?.getValue() ?? '')
|
|
95
|
+
.filter(Boolean)
|
|
96
|
+
.join(',')
|
|
97
|
+
: DEFAULT_RESTRICT_TO
|
|
98
|
+
|
|
99
|
+
// Validate condition only references fields in restrictTo
|
|
100
|
+
const allowedFields = (restrictToCsv || DEFAULT_RESTRICT_TO).split(',')
|
|
101
|
+
const conditionShape = arg.get('condition')
|
|
102
|
+
const conditionValue = conditionShape.ifString()?.ifNotEmpty()?.getValue()
|
|
103
|
+
if (conditionValue) {
|
|
104
|
+
for (const part of conditionValue.split('^')) {
|
|
105
|
+
const fieldPart = part.startsWith('OR') ? part.slice(2) : part
|
|
106
|
+
if (!allowedFields.some((field) => fieldPart.startsWith(field))) {
|
|
107
|
+
const fieldName = fieldPart.match(/^[a-z_]+/)?.[0] ?? fieldPart
|
|
108
|
+
diagnostics.error(
|
|
109
|
+
conditionShape,
|
|
110
|
+
`Condition references field '${fieldName}' which is not in restrictTo. Allowed fields: ${allowedFields.join(', ')}.`
|
|
111
|
+
)
|
|
112
|
+
break
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const record = await factory.createRecord({
|
|
118
|
+
source: callExpression,
|
|
119
|
+
table: SYS_RETRY_POLICY,
|
|
120
|
+
explicitId: arg.get('$id'),
|
|
121
|
+
properties: arg.transform(({ $ }) => ({
|
|
122
|
+
name: $.def(''),
|
|
123
|
+
connection_type: $.from('connectionType').def(DEFAULT_CONNECTION_TYPE),
|
|
124
|
+
retry_strategy: $.from('retryStrategy').def(DEFAULT_RETRY_STRATEGY),
|
|
125
|
+
count: $,
|
|
126
|
+
interval: $,
|
|
127
|
+
max_elapsed_time: $.from('maxElapsedTime'),
|
|
128
|
+
condition: $.def(''),
|
|
129
|
+
restrict_to: $.val(restrictToCsv),
|
|
130
|
+
sys_policy: $.from('protectionPolicy').def(''),
|
|
131
|
+
})),
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
return { success: true, value: record }
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
})
|
|
@@ -71,6 +71,10 @@ export class ArrowFunctionShape extends Shape {
|
|
|
71
71
|
return this.returnValue
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
isImplicitReturn(): boolean {
|
|
75
|
+
return this.implicitReturn
|
|
76
|
+
}
|
|
77
|
+
|
|
74
78
|
override getCode(): string {
|
|
75
79
|
const params = `(${this.getParameters()
|
|
76
80
|
.map((p) => p.getCode())
|
|
@@ -192,48 +196,88 @@ export const ArrowFunctionPlugin = Plugin.create({
|
|
|
192
196
|
{
|
|
193
197
|
shape: ArrowFunctionShape,
|
|
194
198
|
async commit(shape, target, { commit }) {
|
|
195
|
-
const
|
|
199
|
+
const originalTargetWasArrow = ts.Node.isArrowFunction(target)
|
|
200
|
+
const arrowTarget = originalTargetWasArrow
|
|
196
201
|
? target
|
|
197
202
|
: target.replaceWithText('() => {}').asKindOrThrow(ts.SyntaxKind.ArrowFunction)
|
|
198
203
|
|
|
199
|
-
const nodeStatements = arrowFunction.getStatements()
|
|
200
204
|
const shapeStatements = shape.getStatements()
|
|
201
205
|
const returnValue = shape.getReturnValue()
|
|
206
|
+
const body = arrowTarget.getBody()
|
|
202
207
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
208
|
+
// Preserve concise arrows when the shape is still an implicit return.
|
|
209
|
+
if (
|
|
210
|
+
originalTargetWasArrow &&
|
|
211
|
+
!ts.Node.isBlock(body) &&
|
|
212
|
+
shapeStatements.length === 0 &&
|
|
213
|
+
returnValue &&
|
|
214
|
+
shape.isImplicitReturn()
|
|
215
|
+
) {
|
|
216
|
+
if (returnValue.is(ObjectShape)) {
|
|
217
|
+
body.replaceWithText(`(${returnValue.getCode()})`)
|
|
218
|
+
} else {
|
|
207
219
|
await commit(returnValue, body)
|
|
208
|
-
return { success: true }
|
|
209
220
|
}
|
|
221
|
+
return { success: true }
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (!ts.Node.isBlock(body)) {
|
|
225
|
+
// ts-morph cannot grow expression-body arrows like `() => ({})` into
|
|
226
|
+
// block bodies with statement APIs, so convert the body to an empty block
|
|
227
|
+
// first and then continue through the normal commit flow below.
|
|
228
|
+
body.replaceWithText('{}')
|
|
210
229
|
}
|
|
211
230
|
|
|
212
|
-
const
|
|
213
|
-
|
|
231
|
+
const getNonReturnStatements = () =>
|
|
232
|
+
arrowTarget.getStatements().filter((statement) => !ts.Node.isReturnStatement(statement))
|
|
233
|
+
|
|
234
|
+
const existingStatements = getNonReturnStatements()
|
|
235
|
+
const excess = existingStatements.length - shapeStatements.length
|
|
214
236
|
for (let i = 1; i <= excess; i++) {
|
|
215
|
-
|
|
237
|
+
existingStatements[existingStatements.length - i]?.remove()
|
|
216
238
|
}
|
|
217
239
|
|
|
218
240
|
for (const [i, statement] of shapeStatements.entries()) {
|
|
219
|
-
const
|
|
220
|
-
|
|
241
|
+
const currentStatements = getNonReturnStatements()
|
|
242
|
+
const targetStatement = currentStatements[i]
|
|
243
|
+
if (targetStatement) {
|
|
244
|
+
await commit(statement, targetStatement)
|
|
245
|
+
} else {
|
|
246
|
+
// Insert before `return` so return-only bodies can grow safely.
|
|
247
|
+
const returnIndex = arrowTarget
|
|
248
|
+
.getStatements()
|
|
249
|
+
.findIndex((node) => ts.Node.isReturnStatement(node))
|
|
250
|
+
const inserted =
|
|
251
|
+
returnIndex >= 0
|
|
252
|
+
? arrowTarget.insertStatements(returnIndex, statement.getCode())[0]!
|
|
253
|
+
: arrowTarget.addStatements(statement.getCode())[0]!
|
|
254
|
+
await commit(statement, inserted)
|
|
255
|
+
}
|
|
221
256
|
}
|
|
222
257
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
258
|
+
let existingReturn = arrowTarget
|
|
259
|
+
.getStatements()
|
|
260
|
+
.find((statement) => ts.Node.isReturnStatement(statement))
|
|
261
|
+
if (!returnValue) {
|
|
262
|
+
existingReturn?.remove()
|
|
263
|
+
return { success: true }
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (!existingReturn) {
|
|
267
|
+
const inserted = arrowTarget.addStatements(`return ${returnValue.getCode()}`)[0]!
|
|
268
|
+
existingReturn = ts.Node.isReturnStatement(inserted)
|
|
269
|
+
? inserted
|
|
270
|
+
: arrowTarget.getStatements().find((statement) => ts.Node.isReturnStatement(statement))
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (existingReturn) {
|
|
274
|
+
const expr = existingReturn.getExpression()
|
|
275
|
+
if (expr) {
|
|
276
|
+
await commit(returnValue, expr)
|
|
232
277
|
} else {
|
|
233
|
-
|
|
278
|
+
existingReturn.replaceWithText(`return ${returnValue.getCode()}`)
|
|
234
279
|
}
|
|
235
280
|
}
|
|
236
|
-
|
|
237
281
|
return { success: true }
|
|
238
282
|
},
|
|
239
283
|
},
|
package/src/atf/test-plugin.ts
CHANGED
|
@@ -85,13 +85,13 @@ export const BasicSyntaxPlugin = Plugin.create({
|
|
|
85
85
|
const conditionalDir = NowConfig.getConditionalDirectory(config, source.path)
|
|
86
86
|
const taxonomySubdir = tableName && taxonomy.mapping[tableName] ? taxonomy.mapping[tableName] : ''
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
88
|
+
// The hosted/conditional plugin prefix (e.g. `if/com.<plugin>`) is orthogonal to
|
|
89
|
+
// the taxonomy folder (e.g. `data/table`). Compose them so records keep their
|
|
90
|
+
// taxonomy organization even when they live under a conditional/hosted plugin
|
|
91
|
+
// directory, instead of the plugin prefix overriding the taxonomy (DEF0844422).
|
|
92
|
+
// `pathModule.join` drops empty segments, so any of these may be absent.
|
|
93
|
+
const prefixDir = hostedDir ?? conditionalDir ?? ''
|
|
94
|
+
const dir = pathModule.join(generatedDir, prefixDir, taxonomySubdir)
|
|
95
95
|
|
|
96
96
|
let name = pathModule.basename(source.path).replace(RegExp(`${pathModule.extname(source.path)}$`), '')
|
|
97
97
|
if (!name) {
|
|
@@ -211,6 +211,8 @@ export const DashboardPlugin = Plugin.create({
|
|
|
211
211
|
$id: $.val(NowIdShape.from(record)),
|
|
212
212
|
name: $,
|
|
213
213
|
active: $.toBoolean().def(true),
|
|
214
|
+
description: $.def(''),
|
|
215
|
+
certified: $.toBoolean().def(false),
|
|
214
216
|
tabs: $.val(tabs).def([]),
|
|
215
217
|
visibilities: $.val(visibilities).def([]),
|
|
216
218
|
permissions: $.val(permissions).def([]),
|
|
@@ -272,6 +274,8 @@ export const DashboardPlugin = Plugin.create({
|
|
|
272
274
|
properties: arg.transform(({ $ }) => ({
|
|
273
275
|
name: $,
|
|
274
276
|
active: $.def(true),
|
|
277
|
+
description: $.def(''),
|
|
278
|
+
certified: $.def(false),
|
|
275
279
|
})),
|
|
276
280
|
})
|
|
277
281
|
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { CallExpressionShape, Plugin, isSNScope } from '@servicenow/sdk-build-core'
|
|
2
|
+
import { NowIdShape } from './now-id-plugin'
|
|
3
|
+
|
|
4
|
+
const DL_DEFINITION = 'dl_definition'
|
|
5
|
+
const DL_DEFINITION_REL_MATCH = 'dl_definition_rel_match'
|
|
6
|
+
const DL_DEFINITION_REL_SET = 'dl_definition_rel_set'
|
|
7
|
+
|
|
8
|
+
export const DataLookupPlugin = Plugin.create({
|
|
9
|
+
name: 'DataLookupPlugin',
|
|
10
|
+
|
|
11
|
+
records: {
|
|
12
|
+
[DL_DEFINITION]: {
|
|
13
|
+
relationships: {
|
|
14
|
+
[DL_DEFINITION_REL_MATCH]: {
|
|
15
|
+
via: 'dl_definition',
|
|
16
|
+
descendant: true,
|
|
17
|
+
},
|
|
18
|
+
[DL_DEFINITION_REL_SET]: {
|
|
19
|
+
via: 'dl_definition',
|
|
20
|
+
descendant: true,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
async toShape(record, { descendants }) {
|
|
25
|
+
const matchRecords = descendants
|
|
26
|
+
.query(DL_DEFINITION_REL_MATCH)
|
|
27
|
+
.filter((r) => r.get('dl_definition').equals(record.getId()))
|
|
28
|
+
|
|
29
|
+
const setRecords = descendants
|
|
30
|
+
.query(DL_DEFINITION_REL_SET)
|
|
31
|
+
.filter((r) => r.get('dl_definition').equals(record.getId()))
|
|
32
|
+
|
|
33
|
+
const matchRules = matchRecords.map((r) =>
|
|
34
|
+
r.transform(({ $ }) => ({
|
|
35
|
+
$id: $.val(NowIdShape.from(r)),
|
|
36
|
+
sourceField: $.from('source_table_field'),
|
|
37
|
+
matcherField: $.from('matcher_table_field'),
|
|
38
|
+
exactMatch: $.from('exact_match').toBoolean().def(false),
|
|
39
|
+
}))
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
const setRules = setRecords.map((r) =>
|
|
43
|
+
r.transform(({ $ }) => ({
|
|
44
|
+
$id: $.val(NowIdShape.from(r)),
|
|
45
|
+
targetField: $.from('source_table_field'),
|
|
46
|
+
matcherField: $.from('matcher_table_field'),
|
|
47
|
+
alwaysReplace: $.from('always_replace').toBoolean().def(false),
|
|
48
|
+
}))
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
success: true,
|
|
53
|
+
value: new CallExpressionShape({
|
|
54
|
+
source: record,
|
|
55
|
+
callee: 'DataLookup',
|
|
56
|
+
args: [
|
|
57
|
+
record.transform(({ $ }) => ({
|
|
58
|
+
$id: $.val(NowIdShape.from(record)),
|
|
59
|
+
name: $,
|
|
60
|
+
sourceTable: $.from('source_table'),
|
|
61
|
+
matcherTable: $.from('matcher_table'),
|
|
62
|
+
active: $.from('active').toBoolean().def(true),
|
|
63
|
+
runOnInsert: $.from('run_on_insert').toBoolean().def(true),
|
|
64
|
+
runOnUpdate: $.from('run_on_update').toBoolean().def(false),
|
|
65
|
+
runOnFormChange: $.from('run_on_form_change').toBoolean().def(true),
|
|
66
|
+
matchRules: $.val(matchRules.length ? matchRules : undefined),
|
|
67
|
+
setRules: $.val(setRules.length ? setRules : undefined),
|
|
68
|
+
protectionPolicy: $.from('sys_policy').def(''),
|
|
69
|
+
})),
|
|
70
|
+
],
|
|
71
|
+
}),
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
[DL_DEFINITION_REL_MATCH]: {
|
|
77
|
+
// Child records are pulled by the parent dl_definition toShape via descendants.query(),
|
|
78
|
+
// so they must not be transformed independently.
|
|
79
|
+
toShape() {
|
|
80
|
+
return { success: false }
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
[DL_DEFINITION_REL_SET]: {
|
|
85
|
+
// Same as dl_definition_rel_match — rendered inline by the parent's toShape.
|
|
86
|
+
toShape() {
|
|
87
|
+
return { success: false }
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
shapes: [
|
|
93
|
+
{
|
|
94
|
+
shape: CallExpressionShape,
|
|
95
|
+
fileTypes: ['fluent'],
|
|
96
|
+
async toRecord(callExpression, { factory, diagnostics, config }) {
|
|
97
|
+
if (callExpression.getCallee() !== 'DataLookup') {
|
|
98
|
+
return { success: false }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const arg = callExpression.getArgument(0).asObject()
|
|
102
|
+
|
|
103
|
+
const name = arg.get('name').ifString()?.getValue()
|
|
104
|
+
if (name && name.length > 40) {
|
|
105
|
+
diagnostics.error(
|
|
106
|
+
arg.get('name'),
|
|
107
|
+
`Data Lookup Definition name must be 40 characters or fewer (got ${name.length}).`
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const sourceTable = arg.get('sourceTable').ifString()?.getValue() ?? ''
|
|
112
|
+
const matcherTable = arg.get('matcherTable').ifString()?.getValue() ?? ''
|
|
113
|
+
|
|
114
|
+
const scope = config.scope
|
|
115
|
+
if (sourceTable && !sourceTable.startsWith(`${scope}_`) && !isSNScope(scope) && scope !== 'global') {
|
|
116
|
+
diagnostics.error(
|
|
117
|
+
arg.get('sourceTable'),
|
|
118
|
+
`sourceTable '${sourceTable}' must be in the same scope as the data lookup definition. Expected a table starting with '${scope}_'.`
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
if (matcherTable && !matcherTable.startsWith(`${scope}_`) && !isSNScope(scope) && scope !== 'global') {
|
|
122
|
+
diagnostics.error(
|
|
123
|
+
arg.get('matcherTable'),
|
|
124
|
+
`matcherTable '${matcherTable}' must be in the same scope as the data lookup definition. Expected a table starting with '${scope}_'.`
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const mainRecord = await factory.createRecord({
|
|
129
|
+
source: callExpression,
|
|
130
|
+
table: DL_DEFINITION,
|
|
131
|
+
explicitId: arg.get('$id'),
|
|
132
|
+
properties: arg.transform(({ $ }) => ({
|
|
133
|
+
name: $,
|
|
134
|
+
source_table: $.from('sourceTable'),
|
|
135
|
+
matcher_table: $.from('matcherTable'),
|
|
136
|
+
active: $.from('active').def(true),
|
|
137
|
+
run_on_insert: $.from('runOnInsert').def(true),
|
|
138
|
+
run_on_update: $.from('runOnUpdate').def(false),
|
|
139
|
+
run_on_form_change: $.from('runOnFormChange').def(true),
|
|
140
|
+
sys_policy: $.from('protectionPolicy'),
|
|
141
|
+
})),
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
const matchElements = arg.get('matchRules').ifArray()?.getElements() ?? []
|
|
145
|
+
const matchRecords = await Promise.all(
|
|
146
|
+
matchElements.map(async (el) => {
|
|
147
|
+
const rule = el.asObject()
|
|
148
|
+
return factory.createRecord({
|
|
149
|
+
source: el,
|
|
150
|
+
table: DL_DEFINITION_REL_MATCH,
|
|
151
|
+
explicitId: rule.get('$id'),
|
|
152
|
+
properties: rule.transform(({ $ }) => ({
|
|
153
|
+
dl_definition: $.val(mainRecord.getId()),
|
|
154
|
+
source_table_field: $.from('sourceField'),
|
|
155
|
+
matcher_table_field: $.from('matcherField'),
|
|
156
|
+
exact_match: $.from('exactMatch').def(false),
|
|
157
|
+
source_table: $.val(sourceTable),
|
|
158
|
+
matcher_table: $.val(matcherTable),
|
|
159
|
+
})),
|
|
160
|
+
})
|
|
161
|
+
})
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
const setElements = arg.get('setRules').ifArray()?.getElements() ?? []
|
|
165
|
+
const setRecords = await Promise.all(
|
|
166
|
+
setElements.map(async (el) => {
|
|
167
|
+
const rule = el.asObject()
|
|
168
|
+
return factory.createRecord({
|
|
169
|
+
source: el,
|
|
170
|
+
table: DL_DEFINITION_REL_SET,
|
|
171
|
+
explicitId: rule.get('$id'),
|
|
172
|
+
properties: rule.transform(({ $ }) => ({
|
|
173
|
+
dl_definition: $.val(mainRecord.getId()),
|
|
174
|
+
source_table_field: $.from('targetField'),
|
|
175
|
+
matcher_table_field: $.from('matcherField'),
|
|
176
|
+
always_replace: $.from('alwaysReplace').def(false),
|
|
177
|
+
source_table: $.val(sourceTable),
|
|
178
|
+
matcher_table: $.val(matcherTable),
|
|
179
|
+
})),
|
|
180
|
+
})
|
|
181
|
+
})
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
success: true,
|
|
186
|
+
value: mainRecord.with(...matchRecords, ...setRecords),
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
})
|
|
@@ -756,12 +756,13 @@ function validateEndFlowPlacement(expr: FlowLogicInstanceShape): string | undefi
|
|
|
756
756
|
FLOW_LOGIC.IF,
|
|
757
757
|
FLOW_LOGIC.ELSEIF,
|
|
758
758
|
FLOW_LOGIC.ELSE,
|
|
759
|
+
FLOW_LOGIC.DO_IN_PARALLEL,
|
|
759
760
|
// TODO: Add FLOW_LOGIC.DO_UNTIL when implemented
|
|
760
761
|
]
|
|
761
762
|
|
|
762
763
|
const parentBlock = findAncestorByCalleeName(node, ...END_FLOW_BLOCKS)
|
|
763
764
|
if (!parentBlock) {
|
|
764
|
-
return 'End Flow must be used within a Do Until, For Each, or If block'
|
|
765
|
+
return 'End Flow must be used within a Do In Parallel, Do Until, For Each, or If block'
|
|
765
766
|
}
|
|
766
767
|
|
|
767
768
|
return undefined
|