@servicenow/sdk-build-plugins 2.0.1 → 2.1.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.
- package/dist/AttachmentPlugin.d.ts +8 -6
- package/dist/AttachmentPlugin.js +4 -2
- package/dist/AttachmentPlugin.js.map +1 -1
- package/dist/BusinessRulePlugin.d.ts +5 -31
- package/dist/BusinessRulePlugin.js +45 -72
- package/dist/BusinessRulePlugin.js.map +1 -1
- package/dist/CrossScopePrivilegePlugin.d.ts +14 -1
- package/dist/CrossScopePrivilegePlugin.js +36 -1
- package/dist/CrossScopePrivilegePlugin.js.map +1 -1
- package/dist/DefaultPlugin.d.ts +60 -57
- package/dist/DefaultPlugin.js +151 -146
- package/dist/DefaultPlugin.js.map +1 -1
- package/dist/HtmlTemplatePlugin.d.ts +21 -0
- package/dist/HtmlTemplatePlugin.js +52 -0
- package/dist/HtmlTemplatePlugin.js.map +1 -0
- package/dist/IdPlugin.d.ts +18 -8
- package/dist/IdPlugin.js +55 -28
- package/dist/IdPlugin.js.map +1 -1
- package/dist/JsonPlugin.d.ts +23 -0
- package/dist/JsonPlugin.js +74 -0
- package/dist/JsonPlugin.js.map +1 -0
- package/dist/ListPlugin.d.ts +7 -13
- package/dist/ListPlugin.js +32 -15
- package/dist/ListPlugin.js.map +1 -1
- package/dist/NowConfigPlugin.d.ts +45 -0
- package/dist/NowConfigPlugin.js +90 -0
- package/dist/NowConfigPlugin.js.map +1 -0
- package/dist/PackageJsonPlugin.d.ts +20 -0
- package/dist/PackageJsonPlugin.js +48 -0
- package/dist/PackageJsonPlugin.js.map +1 -0
- package/dist/PropertyPlugin.d.ts +9 -35
- package/dist/PropertyPlugin.js +53 -32
- package/dist/PropertyPlugin.js.map +1 -1
- package/dist/ScriptTemplatePlugin.d.ts +3 -20
- package/dist/ScriptTemplatePlugin.js +20 -138
- package/dist/ScriptTemplatePlugin.js.map +1 -1
- package/dist/ServerModulePlugin.d.ts +62 -0
- package/dist/ServerModulePlugin.js +231 -0
- package/dist/ServerModulePlugin.js.map +1 -0
- package/dist/UserPreferencePlugin.d.ts +4 -1
- package/dist/UserPreferencePlugin.js +12 -10
- package/dist/UserPreferencePlugin.js.map +1 -1
- package/dist/aclAndRole/AclPlugin.d.ts +41 -79
- package/dist/aclAndRole/AclPlugin.js +50 -72
- package/dist/aclAndRole/AclPlugin.js.map +1 -1
- package/dist/aclAndRole/RolePlugin.d.ts +27 -18
- package/dist/aclAndRole/RolePlugin.js +66 -41
- package/dist/aclAndRole/RolePlugin.js.map +1 -1
- package/dist/app/ApplicationMenuPlugin.d.ts +4 -2
- package/dist/app/ApplicationMenuPlugin.js +12 -7
- package/dist/app/ApplicationMenuPlugin.js.map +1 -1
- package/dist/atf/ATFComposer.js +324 -324
- package/dist/atf/TestPlugin.d.ts +12 -10
- package/dist/atf/TestPlugin.js +14 -12
- package/dist/atf/TestPlugin.js.map +1 -1
- package/dist/db/ColumnPlugins.d.ts +559 -97
- package/dist/db/ColumnPlugins.js +28 -24
- package/dist/db/ColumnPlugins.js.map +1 -1
- package/dist/db/DBUtils.d.ts +2 -0
- package/dist/db/DBUtils.js +27 -0
- package/dist/db/DBUtils.js.map +1 -0
- package/dist/db/DocumentationPlugin.d.ts +58 -0
- package/dist/db/DocumentationPlugin.js +248 -0
- package/dist/db/DocumentationPlugin.js.map +1 -0
- package/dist/db/RecordPlugin.d.ts +15 -13
- package/dist/db/RecordPlugin.js +74 -79
- package/dist/db/RecordPlugin.js.map +1 -1
- package/dist/db/TablePlugin.d.ts +38 -105
- package/dist/db/TablePlugin.js +205 -111
- package/dist/db/TablePlugin.js.map +1 -1
- package/dist/db/index.d.ts +2 -0
- package/dist/db/index.js +4 -1
- package/dist/db/index.js.map +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/dist/scriptedRESTAPI/RESTDeserializationUtils.js +8 -6
- package/dist/scriptedRESTAPI/RESTDeserializationUtils.js.map +1 -1
- package/dist/scriptedRESTAPI/RESTSerializationUtils.js +7 -6
- package/dist/scriptedRESTAPI/RESTSerializationUtils.js.map +1 -1
- package/dist/scriptedRESTAPI/RestApiPlugin.d.ts +74 -85
- package/dist/scriptedRESTAPI/RestApiPlugin.js +71 -54
- package/dist/scriptedRESTAPI/RestApiPlugin.js.map +1 -1
- package/dist/scriptedRESTAPI/RestUtils.d.ts +2 -0
- package/dist/scriptedRESTAPI/RestUtils.js +37 -9
- package/dist/scriptedRESTAPI/RestUtils.js.map +1 -1
- package/dist/scripts/ClientScriptPlugin.d.ts +18 -16
- package/dist/scripts/ClientScriptPlugin.js +48 -45
- package/dist/scripts/ClientScriptPlugin.js.map +1 -1
- package/dist/scripts/scriptUtils.d.ts +0 -14
- package/dist/scripts/scriptUtils.js +0 -74
- package/dist/scripts/scriptUtils.js.map +1 -1
- package/dist/uxf/ExperiencePlugin.d.ts +31 -3
- package/dist/uxf/ExperiencePlugin.js +10 -4
- package/dist/uxf/ExperiencePlugin.js.map +1 -1
- package/dist/uxf/RoutesPlugin.d.ts +10 -3
- package/dist/uxf/RoutesPlugin.js +20 -14
- package/dist/uxf/RoutesPlugin.js.map +1 -1
- package/package.json +6 -6
- package/src/AttachmentPlugin.ts +10 -2
- package/src/BusinessRulePlugin.ts +79 -104
- package/src/CrossScopePrivilegePlugin.ts +65 -5
- package/src/DefaultPlugin.ts +181 -173
- package/src/HtmlTemplatePlugin.ts +31 -0
- package/src/IdPlugin.ts +59 -28
- package/src/JsonPlugin.ts +81 -0
- package/src/ListPlugin.ts +38 -19
- package/src/NowConfigPlugin.ts +72 -0
- package/src/PackageJsonPlugin.ts +24 -0
- package/src/PropertyPlugin.ts +60 -38
- package/src/ScriptTemplatePlugin.ts +22 -179
- package/src/ServerModulePlugin.ts +267 -0
- package/src/UserPreferencePlugin.ts +28 -19
- package/src/aclAndRole/AclPlugin.ts +78 -109
- package/src/aclAndRole/RolePlugin.ts +85 -58
- package/src/app/ApplicationMenuPlugin.ts +15 -11
- package/src/atf/ATFComposer.ts +1 -1
- package/src/atf/TestPlugin.ts +14 -12
- package/src/db/ColumnPlugins.ts +36 -37
- package/src/db/DBUtils.ts +36 -0
- package/src/db/DocumentationPlugin.ts +282 -0
- package/src/db/RecordPlugin.ts +96 -98
- package/src/db/TablePlugin.ts +278 -152
- package/src/db/index.ts +2 -0
- package/src/index.ts +7 -2
- package/src/scriptedRESTAPI/RESTDeserializationUtils.ts +15 -7
- package/src/scriptedRESTAPI/RESTSerializationUtils.ts +7 -6
- package/src/scriptedRESTAPI/RestApiPlugin.ts +85 -75
- package/src/scriptedRESTAPI/RestUtils.ts +44 -11
- package/src/scripts/ClientScriptPlugin.ts +64 -68
- package/src/scripts/scriptUtils.ts +0 -76
- package/src/uxf/ExperiencePlugin.ts +14 -13
- package/src/uxf/RoutesPlugin.ts +22 -27
package/src/db/TablePlugin.ts
CHANGED
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
UserRolesColumn,
|
|
33
33
|
VersionColumn,
|
|
34
34
|
StringColumn,
|
|
35
|
+
Documentation,
|
|
35
36
|
} from '@servicenow/sdk-core/runtime/db'
|
|
36
37
|
import {
|
|
37
38
|
Context,
|
|
@@ -53,20 +54,36 @@ import {
|
|
|
53
54
|
generateCallExpressionExport,
|
|
54
55
|
generateCallExpression,
|
|
55
56
|
createPropertyIdentifier,
|
|
56
|
-
XMLJsonBuilder,
|
|
57
|
-
XMLJsonElement,
|
|
58
57
|
ObjectData,
|
|
58
|
+
isSNScope,
|
|
59
|
+
GUID,
|
|
59
60
|
} from '@servicenow/sdk-build-core'
|
|
60
|
-
import {
|
|
61
|
-
import { isArray, isEmpty, isObject,
|
|
61
|
+
import { create } from 'xmlbuilder2'
|
|
62
|
+
import { isArray, isEmpty, isObject, parseInt } from 'lodash'
|
|
62
63
|
import { z } from 'zod'
|
|
63
64
|
import { RecordPlugin, TextBooleanSchema, TextNumberSchema, TextStringSchema } from './RecordPlugin'
|
|
64
|
-
import {
|
|
65
|
-
|
|
65
|
+
import {
|
|
66
|
+
SyntaxKind,
|
|
67
|
+
Node,
|
|
68
|
+
ts,
|
|
69
|
+
ObjectLiteralExpression,
|
|
70
|
+
CallExpression,
|
|
71
|
+
Structure,
|
|
72
|
+
SourceFile,
|
|
73
|
+
ImportDeclarationStructure,
|
|
74
|
+
PropertySignatureStructure,
|
|
75
|
+
InterfaceDeclarationStructure,
|
|
76
|
+
ImportSpecifierStructure,
|
|
77
|
+
OptionalKind,
|
|
78
|
+
ImportDeclaration,
|
|
79
|
+
} from 'ts-morph'
|
|
66
80
|
import { Diagnostic } from '@servicenow/sdk-project'
|
|
67
81
|
import { formatScript } from '../ScriptTemplatePlugin'
|
|
68
82
|
import * as os from 'os'
|
|
69
83
|
import * as path from 'path'
|
|
84
|
+
import { XMLBuilder } from 'xmlbuilder2/lib/interfaces'
|
|
85
|
+
import { getLabelDiagnostics } from './DBUtils'
|
|
86
|
+
import { ModuleFunctionData } from '../ServerModulePlugin'
|
|
70
87
|
|
|
71
88
|
/**
|
|
72
89
|
* The access levels are mapped to the following values on the platform
|
|
@@ -92,7 +109,7 @@ export const TableSchemaFull = z.object({
|
|
|
92
109
|
* if table definition not present locally */
|
|
93
110
|
extends: z.string().optional(),
|
|
94
111
|
/** Will match name if not provided, cannot exceed 80 characters */
|
|
95
|
-
label: z.string().max(80, 'Cannot exceed 80 characters').optional(),
|
|
112
|
+
label: z.string().max(80, 'Cannot exceed 80 characters').or(z.array(z.any())).optional(),
|
|
96
113
|
/** Must match column name */
|
|
97
114
|
display: z.string().optional(),
|
|
98
115
|
|
|
@@ -158,17 +175,18 @@ type IndexType = {
|
|
|
158
175
|
|
|
159
176
|
// Everything which goes into a element with type='string/int/etc' in bootstrap.xml
|
|
160
177
|
type ColumnSchema = z.infer<typeof ColumnSchema>
|
|
161
|
-
|
|
178
|
+
const ColumnSchema = z.object({
|
|
162
179
|
entityKind: z.string().optional(),
|
|
163
180
|
column_type: z.string().optional(),
|
|
164
181
|
audit: z.boolean().optional().default(false),
|
|
165
|
-
label: z.string().optional(),
|
|
182
|
+
label: z.string().or(z.array(z.any())).optional(),
|
|
166
183
|
maxLength: z.number().optional().default(40),
|
|
167
184
|
mandatory: z.boolean().optional().default(false),
|
|
168
185
|
read_only: z.boolean().optional().default(false),
|
|
169
186
|
default: z.any().optional(),
|
|
170
187
|
attributes: z.record(z.string(), z.union([z.string(), z.boolean(), z.number()])).optional(),
|
|
171
188
|
referenceTable: z.string().optional(),
|
|
189
|
+
cascadeRule: z.string().optional(),
|
|
172
190
|
dropdown: z.enum(choiceDropdown).default('none').optional(),
|
|
173
191
|
choices: z.record(z.any()).or(z.array(z.any())).optional(),
|
|
174
192
|
choice_elements: z.record(z.any()).or(z.array(z.any())).optional(),
|
|
@@ -232,7 +250,7 @@ const DBObjectIncomingAttributes = z.object({
|
|
|
232
250
|
export const TableSchemaBootstrap = z.object({
|
|
233
251
|
name: z.string(),
|
|
234
252
|
extends: z.string().optional(),
|
|
235
|
-
label: z.string().max(80, 'Cannot exceed 80 characters').optional(),
|
|
253
|
+
label: z.string().max(80, 'Cannot exceed 80 characters').or(z.array(z.any())).optional(),
|
|
236
254
|
|
|
237
255
|
// Controls
|
|
238
256
|
extensible: z.boolean().optional().default(false),
|
|
@@ -306,7 +324,7 @@ const DBObjectIncoming = z.object({
|
|
|
306
324
|
sys_db_object: z.record(z.any()),
|
|
307
325
|
})
|
|
308
326
|
|
|
309
|
-
export const
|
|
327
|
+
export const SysDocumentation = z.object({
|
|
310
328
|
sys_documentation: z.object({
|
|
311
329
|
element: TextStringSchema.optional(),
|
|
312
330
|
label: TextStringSchema.optional(),
|
|
@@ -315,6 +333,7 @@ export const Documentation = z.object({
|
|
|
315
333
|
help: TextStringSchema.optional(),
|
|
316
334
|
name: TextStringSchema.optional(),
|
|
317
335
|
plural: TextStringSchema.optional(),
|
|
336
|
+
sys_id: TextStringSchema.optional(),
|
|
318
337
|
url: TextStringSchema.optional(),
|
|
319
338
|
url_target: TextStringSchema.optional(),
|
|
320
339
|
}),
|
|
@@ -326,7 +345,7 @@ export const TableSchemaBootstrapAttributes = z
|
|
|
326
345
|
'@_name': z.string(),
|
|
327
346
|
'@_extends': z.string().optional(),
|
|
328
347
|
'@_label': z.string().optional(),
|
|
329
|
-
'@_max_length': z.
|
|
348
|
+
'@_max_length': z.any().optional(),
|
|
330
349
|
|
|
331
350
|
// From sys_dictionary
|
|
332
351
|
'@_audit': BooleanFromString,
|
|
@@ -415,10 +434,6 @@ type ComposedTableData = {
|
|
|
415
434
|
}
|
|
416
435
|
}
|
|
417
436
|
|
|
418
|
-
function isSNScope(scopeName: string) {
|
|
419
|
-
return scopeName.startsWith('sn') || scopeName.startsWith('now')
|
|
420
|
-
}
|
|
421
|
-
|
|
422
437
|
export default Plugin({
|
|
423
438
|
name: 'Table',
|
|
424
439
|
ownedTables: {
|
|
@@ -426,92 +441,94 @@ export default Plugin({
|
|
|
426
441
|
},
|
|
427
442
|
extractors: {
|
|
428
443
|
entity: {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
444
|
+
fluent: {
|
|
445
|
+
// TODO: Cast to `any` is temporary workaround for the circular nature of Table entities
|
|
446
|
+
CallExpression: (node, context) => {
|
|
447
|
+
const result = extractCallExpression(Table, 'table', node, context, (table) => table.name)
|
|
448
|
+
if (!result.handled || !(0 in result.data)) {
|
|
449
|
+
return result
|
|
450
|
+
}
|
|
435
451
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
if (
|
|
452
|
+
const data = result.data[0]
|
|
453
|
+
const diagnostics = result.diagnostics
|
|
454
|
+
const scopeName = context.app.config.scope
|
|
455
|
+
const scopeRegex = new RegExp(`^${scopeName}_`)
|
|
456
|
+
const columnNameRegex = new RegExp(`^[a-z_][a-z0-9_]*$`)
|
|
457
|
+
const tableNameRegex = new RegExp(`^[a-z_][a-z0-9_]*[a-z0-9]$`)
|
|
458
|
+
const tableName = data.getProperty('name')
|
|
459
|
+
const scopeMatches = tableName.getValue().match(scopeRegex)
|
|
460
|
+
let ignoreColumnNameCheck = false
|
|
461
|
+
|
|
462
|
+
if (!tableName.getValue().match(tableNameRegex)) {
|
|
447
463
|
diagnostics.push(
|
|
448
464
|
new FluentDiagnostic(
|
|
449
465
|
tableName.getNode(),
|
|
450
|
-
`
|
|
466
|
+
`Table name must only contain lowercase letters, numbers, and underscores and end with a letter or number`
|
|
451
467
|
)
|
|
452
468
|
)
|
|
453
469
|
}
|
|
454
|
-
}
|
|
455
470
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
`Column name must be prefixed with scope '${scopeName}' if table name does not contain prefix`
|
|
471
|
+
if (!scopeMatches) {
|
|
472
|
+
const nameNode = tableName.getNode().asKindOrThrow(SyntaxKind.StringLiteral)
|
|
473
|
+
if (nameNode.getParent().getKindName() !== 'AsExpression') {
|
|
474
|
+
diagnostics.push(
|
|
475
|
+
new FluentDiagnostic(
|
|
476
|
+
tableName.getNode(),
|
|
477
|
+
`'name' property must start with scope prefix '${scopeName}'`
|
|
478
|
+
)
|
|
465
479
|
)
|
|
466
|
-
|
|
480
|
+
} else {
|
|
481
|
+
ignoreColumnNameCheck = true
|
|
482
|
+
}
|
|
467
483
|
}
|
|
468
484
|
|
|
469
|
-
|
|
485
|
+
const schema = data.getProperty('schema')
|
|
486
|
+
const schemaNode = schema.getNode().asKindOrThrow(SyntaxKind.ObjectLiteralExpression)
|
|
487
|
+
for (const columnName in schema.getValue()) {
|
|
488
|
+
const columnNode = schemaNode.getPropertyOrThrow(columnName)
|
|
489
|
+
if (
|
|
490
|
+
!ignoreColumnNameCheck &&
|
|
491
|
+
!scopeMatches &&
|
|
492
|
+
!isSNScope(scopeName) &&
|
|
493
|
+
!columnName.match(scopeRegex)
|
|
494
|
+
) {
|
|
495
|
+
diagnostics.push(
|
|
496
|
+
new FluentDiagnostic(
|
|
497
|
+
columnNode,
|
|
498
|
+
`Column name must be prefixed with scope '${scopeName}' if table name does not contain prefix`
|
|
499
|
+
)
|
|
500
|
+
)
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (!columnName.match(columnNameRegex)) {
|
|
504
|
+
diagnostics.push(
|
|
505
|
+
new FluentDiagnostic(
|
|
506
|
+
columnNode,
|
|
507
|
+
`Column name must only contain lowercase letters, numbers, and underscores`
|
|
508
|
+
)
|
|
509
|
+
)
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const declaration = node.getParentIfKind(SyntaxKind.VariableDeclaration)
|
|
514
|
+
if (
|
|
515
|
+
!declaration ||
|
|
516
|
+
!declaration.isExported() ||
|
|
517
|
+
!declaration.isNamedExport() ||
|
|
518
|
+
declaration.getName() !== tableName.getValue()
|
|
519
|
+
) {
|
|
470
520
|
diagnostics.push(
|
|
471
521
|
new FluentDiagnostic(
|
|
472
|
-
|
|
473
|
-
`
|
|
522
|
+
node,
|
|
523
|
+
`Table definition should be exported as a named export with the name '${tableName.getValue()}'`,
|
|
524
|
+
{ level: Diagnostic.Level.Warn }
|
|
474
525
|
)
|
|
475
526
|
)
|
|
476
527
|
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
const declaration = node.getParentIfKind(SyntaxKind.VariableDeclaration)
|
|
480
|
-
if (
|
|
481
|
-
!declaration ||
|
|
482
|
-
!declaration.isExported() ||
|
|
483
|
-
!declaration.isNamedExport() ||
|
|
484
|
-
declaration.getName() !== tableName.getValue()
|
|
485
|
-
) {
|
|
486
|
-
diagnostics.push(
|
|
487
|
-
new FluentDiagnostic(
|
|
488
|
-
node,
|
|
489
|
-
`Table definition should be exported as a named export with the name '${tableName.getValue()}'`,
|
|
490
|
-
{ level: Diagnostic.Level.Warn }
|
|
491
|
-
)
|
|
492
|
-
)
|
|
493
|
-
}
|
|
494
528
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
raw: {
|
|
499
|
-
FunctionDeclaration(node, context) {
|
|
500
|
-
const info = scriptInfo(node, context, Table.name)
|
|
501
|
-
if (!info) {
|
|
502
|
-
return { handled: false }
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
return { handled: true, diagnostics: [], data: [info] }
|
|
506
|
-
},
|
|
507
|
-
|
|
508
|
-
FunctionExpression(node, context) {
|
|
509
|
-
const info = scriptInfo(node, context, Table.name)
|
|
510
|
-
if (!info) {
|
|
511
|
-
return { handled: false }
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
return { handled: true, diagnostics: [], data: [info] }
|
|
529
|
+
diagnostics.push(...getLabelDiagnostics(data))
|
|
530
|
+
return { handled: true, data: [data], diagnostics }
|
|
531
|
+
},
|
|
515
532
|
},
|
|
516
533
|
},
|
|
517
534
|
xml(xml, context) {
|
|
@@ -551,16 +568,16 @@ export default Plugin({
|
|
|
551
568
|
}
|
|
552
569
|
|
|
553
570
|
if (table === 'sys_documentation') {
|
|
554
|
-
const data =
|
|
571
|
+
const data = SysDocumentation.parse(record_update[table]).sys_documentation
|
|
555
572
|
return new XmlData(
|
|
556
573
|
{
|
|
557
574
|
table: table,
|
|
558
|
-
id: record_update[table][table]['sys_id']['#text'],
|
|
559
|
-
data
|
|
575
|
+
id: record_update[table][table]['sys_id']?.['#text'] ?? GUID(),
|
|
576
|
+
data,
|
|
560
577
|
},
|
|
561
578
|
xml.filePath,
|
|
562
|
-
'
|
|
563
|
-
record_update[table][
|
|
579
|
+
'sys_documentation',
|
|
580
|
+
record_update[table]['@_action']
|
|
564
581
|
)
|
|
565
582
|
}
|
|
566
583
|
|
|
@@ -623,12 +640,15 @@ export default Plugin({
|
|
|
623
640
|
const table = TableSchemaFull.parse(data)
|
|
624
641
|
|
|
625
642
|
const composeData = compose(table)
|
|
626
|
-
|
|
643
|
+
const { columnData, documentation } = composeColumns(
|
|
627
644
|
table.schema as Record<string, ColumnSchema>,
|
|
628
645
|
context,
|
|
646
|
+
table.name,
|
|
629
647
|
table.display
|
|
630
648
|
)
|
|
631
649
|
|
|
650
|
+
composeData['columns'] = columnData
|
|
651
|
+
|
|
632
652
|
const entities: LinkedDocument[] = []
|
|
633
653
|
if (table.auto_number) {
|
|
634
654
|
const data = table.auto_number
|
|
@@ -650,6 +670,25 @@ export default Plugin({
|
|
|
650
670
|
composeData['number_ref'] = composedNumber.guid
|
|
651
671
|
}
|
|
652
672
|
|
|
673
|
+
documentation.forEach((doc) => {
|
|
674
|
+
entities.push({
|
|
675
|
+
kind: 'sys_documentation',
|
|
676
|
+
node: node,
|
|
677
|
+
data: doc,
|
|
678
|
+
guid: doc.$id as string,
|
|
679
|
+
})
|
|
680
|
+
})
|
|
681
|
+
|
|
682
|
+
const docs = composeDocumentation('', table.name, table.label)
|
|
683
|
+
docs.forEach((doc) => {
|
|
684
|
+
entities.push({
|
|
685
|
+
kind: 'sys_documentation',
|
|
686
|
+
node: node,
|
|
687
|
+
data: doc,
|
|
688
|
+
guid: doc.$id as string,
|
|
689
|
+
})
|
|
690
|
+
})
|
|
691
|
+
|
|
653
692
|
composeData['index'] = table.index
|
|
654
693
|
|
|
655
694
|
entities.push({
|
|
@@ -734,42 +773,23 @@ export default Plugin({
|
|
|
734
773
|
attributes['live_feed'] = true
|
|
735
774
|
}
|
|
736
775
|
|
|
737
|
-
const
|
|
738
|
-
const databaseObj = builder.createRoot('database', undefined, undefined)
|
|
776
|
+
const root = create({ version: '1.0' }).ele('database')
|
|
739
777
|
const composedAttributes = composeAttributes(attributes)
|
|
740
|
-
const
|
|
741
|
-
'element',
|
|
742
|
-
undefined,
|
|
743
|
-
{ ...bootstrapData, attributes: composedAttributes }!
|
|
744
|
-
)
|
|
778
|
+
const collection = root.ele('element', { ...bootstrapData, attributes: composedAttributes }!)
|
|
745
779
|
for (const column of Object.values(columns)) {
|
|
746
780
|
const { choice_elements, attributes, ...parsedColumn } = column as ColumnSchema
|
|
747
781
|
const columnAttributes = composeAttributes(attributes)
|
|
748
|
-
const
|
|
749
|
-
'element',
|
|
750
|
-
undefined,
|
|
751
|
-
{
|
|
752
|
-
...(parsedColumn as any),
|
|
753
|
-
attributes: columnAttributes,
|
|
754
|
-
}!
|
|
755
|
-
)
|
|
782
|
+
const element = collection.ele('element', { ...parsedColumn, attributes: columnAttributes }!)
|
|
756
783
|
|
|
757
|
-
buildChoices(column as ColumnSchema,
|
|
784
|
+
buildChoices(column as ColumnSchema, element)
|
|
758
785
|
}
|
|
759
786
|
|
|
760
|
-
buildIndexes(index,
|
|
761
|
-
|
|
762
|
-
const bootstrapXmlJson = new XMLBuilder({
|
|
763
|
-
ignoreAttributes: false,
|
|
764
|
-
format: true,
|
|
765
|
-
suppressBooleanAttributes: false,
|
|
766
|
-
suppressEmptyNode: true,
|
|
767
|
-
}).build(builder.buildJsonObj())
|
|
787
|
+
buildIndexes(index, collection)
|
|
768
788
|
|
|
769
789
|
return {
|
|
770
790
|
name: `${bootstrapData.name}.xml`,
|
|
771
791
|
directory: 'dictionary',
|
|
772
|
-
content:
|
|
792
|
+
content: root.end({ prettyPrint: true }),
|
|
773
793
|
usedIds: [],
|
|
774
794
|
}
|
|
775
795
|
},
|
|
@@ -863,13 +883,72 @@ export default Plugin({
|
|
|
863
883
|
},
|
|
864
884
|
postProcessors: {
|
|
865
885
|
entities(entities, context) {
|
|
886
|
+
const { interfaces, properties, imports, namedImports } = {
|
|
887
|
+
interfaces: [] as OptionalKind<InterfaceDeclarationStructure>[],
|
|
888
|
+
properties: [] as OptionalKind<PropertySignatureStructure>[],
|
|
889
|
+
imports: {} as Record<string, OptionalKind<ImportDeclarationStructure>>,
|
|
890
|
+
namedImports: [] as (OptionalKind<ImportSpecifierStructure> & {
|
|
891
|
+
existingImport: ImportDeclaration
|
|
892
|
+
})[],
|
|
893
|
+
}
|
|
894
|
+
const generatedTableFile: SourceFile | undefined = context.compiler.getGeneratedTableFile()
|
|
866
895
|
entities
|
|
867
896
|
.filter((entity) => entity.getKind() === 'table')
|
|
868
897
|
.forEach((table) => {
|
|
869
898
|
const tableName = table.getProperty('name')!.getValue() as string
|
|
870
|
-
const
|
|
871
|
-
|
|
899
|
+
const extendsTable = table.getProperty('extends')?.getValue() as string
|
|
900
|
+
if (tableName && tableName.trim().length > 0) {
|
|
901
|
+
if (!context.compiler.interfaceExistsInGlobalDeclaration(tableName)) {
|
|
902
|
+
interfaces.push({
|
|
903
|
+
name: tableName,
|
|
904
|
+
extends: [`Helper<typeof ${tableName}>`],
|
|
905
|
+
})
|
|
906
|
+
}
|
|
907
|
+
if (!context.compiler.propertyExistsInGlobalDeclaration(tableName)) {
|
|
908
|
+
let interfaceType = `Table<TableSchemas.${tableName}>`
|
|
909
|
+
if (extendsTable) {
|
|
910
|
+
interfaceType = `Table<TableSchemas.${tableName}, "${extendsTable}">`
|
|
911
|
+
}
|
|
912
|
+
properties.push({
|
|
913
|
+
name: tableName,
|
|
914
|
+
type: interfaceType,
|
|
915
|
+
})
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
const filePath = path.normalize(table.getNode().getSourceFile().getFilePath())
|
|
919
|
+
const resolvedModuleSpecifier = generatedTableFile?.getRelativePathAsModuleSpecifierTo(
|
|
920
|
+
filePath
|
|
921
|
+
) as string
|
|
922
|
+
const importExists = context.compiler.importExistsInGlobalDeclaration(tableName)
|
|
923
|
+
|
|
924
|
+
if (!importExists) {
|
|
925
|
+
if (imports[filePath]) {
|
|
926
|
+
;(imports[filePath].namedImports! as string[]).push(tableName)
|
|
927
|
+
} else {
|
|
928
|
+
imports[filePath] = {
|
|
929
|
+
namedImports: [tableName],
|
|
930
|
+
moduleSpecifier: resolvedModuleSpecifier,
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
} else {
|
|
934
|
+
const namedImportExists = importExists
|
|
935
|
+
?.getNamedImports()
|
|
936
|
+
.find((value) => value.getName() === tableName)
|
|
937
|
+
if (!namedImportExists) {
|
|
938
|
+
namedImports.push({
|
|
939
|
+
existingImport: importExists,
|
|
940
|
+
name: tableName,
|
|
941
|
+
})
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
}
|
|
872
945
|
})
|
|
946
|
+
context.compiler.addTableInterfacesToGlobalDeclaration({
|
|
947
|
+
interfaces,
|
|
948
|
+
properties,
|
|
949
|
+
imports,
|
|
950
|
+
namedImports,
|
|
951
|
+
})
|
|
873
952
|
},
|
|
874
953
|
},
|
|
875
954
|
})
|
|
@@ -908,19 +987,52 @@ function updateDisplayValue(node: CallExpression, columns: ColumnSchema, incomin
|
|
|
908
987
|
}
|
|
909
988
|
}
|
|
910
989
|
|
|
911
|
-
function
|
|
990
|
+
function composeDocumentation(columnName: string, tableName: string, labels?: string | Documentation[]): NowRecord[] {
|
|
991
|
+
const labelData: NowRecord[] = []
|
|
992
|
+
|
|
993
|
+
if (!labels || !isArray(labels)) {
|
|
994
|
+
return labelData
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
labels.forEach((label) => {
|
|
998
|
+
const labelValue = label.label ?? columnName !== '' ? columnName : tableName
|
|
999
|
+
labelData.push(
|
|
1000
|
+
NowRecord({
|
|
1001
|
+
table: 'sys_documentation',
|
|
1002
|
+
$id: `${tableName}_${columnName ? columnName + '_' : ''}${label.language}`,
|
|
1003
|
+
data: { label: labelValue, element: columnName, name: tableName, ...label },
|
|
1004
|
+
})
|
|
1005
|
+
)
|
|
1006
|
+
})
|
|
1007
|
+
return labelData
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
function getLabel(label?: string | Documentation[]): string | undefined {
|
|
1011
|
+
if (!label || isArray(label)) {
|
|
1012
|
+
// use column name if no label specified
|
|
1013
|
+
return
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// label is an explicit string value
|
|
1017
|
+
return label
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
function composeColumns(columns: Record<string, ColumnSchema>, _context: Context, tableName: string, display?: string) {
|
|
912
1021
|
const composedColumns = {}
|
|
1022
|
+
const documentation: NowRecord[] = []
|
|
913
1023
|
for (const [columnName, columnData] of Object.entries(columns)) {
|
|
914
|
-
const
|
|
1024
|
+
const { maxLength, ...rest } = columnData
|
|
1025
|
+
const column = ColumnSchema.parse(rest)
|
|
915
1026
|
const requiresDropdown = column.dynamic_value_definitions || column.choices
|
|
916
1027
|
const element = {
|
|
917
1028
|
name: columnName,
|
|
918
1029
|
type: column.entityKind === 'generic' ? column.column_type : column.entityKind,
|
|
919
1030
|
attributes: column.attributes ?? undefined,
|
|
920
1031
|
reference: column.referenceTable,
|
|
1032
|
+
reference_cascade_rule: column.referenceTable ? column.cascadeRule : undefined,
|
|
921
1033
|
audit: column.audit,
|
|
922
|
-
label: column.label ?? columnName,
|
|
923
|
-
max_length: column.maxLength,
|
|
1034
|
+
label: getLabel(column.label) ?? columnName,
|
|
1035
|
+
max_length: maxLength ?? column.maxLength,
|
|
924
1036
|
mandatory: column.mandatory,
|
|
925
1037
|
read_only: column.read_only,
|
|
926
1038
|
choice: choiceDropdown.indexOf(column.dropdown ?? requiresDropdown ? 'dropdown_with_none' : 'none'),
|
|
@@ -930,7 +1042,10 @@ function composeColumns(columns: Record<string, ColumnSchema>, context: Context,
|
|
|
930
1042
|
virtual: column.dynamic_value_definitions?.type === 'calculated_value' ? true : undefined,
|
|
931
1043
|
calculation:
|
|
932
1044
|
column.dynamic_value_definitions?.type === 'calculated_value'
|
|
933
|
-
?
|
|
1045
|
+
? ModuleFunctionData.getGlueCode(
|
|
1046
|
+
column.dynamic_value_definitions!['calculated_value'],
|
|
1047
|
+
(n) => `${n}(current)`
|
|
1048
|
+
)
|
|
934
1049
|
: undefined,
|
|
935
1050
|
choice_table:
|
|
936
1051
|
column.dynamic_value_definitions?.type === 'choices_from_other_table'
|
|
@@ -961,6 +1076,8 @@ function composeColumns(columns: Record<string, ColumnSchema>, context: Context,
|
|
|
961
1076
|
function_field: column.function_definition ? true : false,
|
|
962
1077
|
}
|
|
963
1078
|
|
|
1079
|
+
documentation.push(...composeDocumentation(columnName, tableName, column.label))
|
|
1080
|
+
|
|
964
1081
|
if (column.choices) {
|
|
965
1082
|
Object.keys(column.choices).forEach((key) => {
|
|
966
1083
|
if (isObject(column.choices![key])) {
|
|
@@ -974,7 +1091,7 @@ function composeColumns(columns: Record<string, ColumnSchema>, context: Context,
|
|
|
974
1091
|
|
|
975
1092
|
composedColumns[columnName] = element
|
|
976
1093
|
}
|
|
977
|
-
return composedColumns
|
|
1094
|
+
return { columnData: composedColumns, documentation }
|
|
978
1095
|
}
|
|
979
1096
|
|
|
980
1097
|
function compose(bootstrapData: z.infer<typeof TableSchemaBootstrap>) {
|
|
@@ -991,7 +1108,7 @@ function compose(bootstrapData: z.infer<typeof TableSchemaBootstrap>) {
|
|
|
991
1108
|
name: bootstrapData.name,
|
|
992
1109
|
type: 'collection',
|
|
993
1110
|
extends: bootstrapData.extends,
|
|
994
|
-
label: bootstrapData.label ?? bootstrapData.name,
|
|
1111
|
+
label: getLabel(bootstrapData.label) ?? bootstrapData.name,
|
|
995
1112
|
|
|
996
1113
|
// Controls
|
|
997
1114
|
is_extendable: bootstrapData.extensible,
|
|
@@ -1095,27 +1212,30 @@ function getActionsArray(data) {
|
|
|
1095
1212
|
return actions
|
|
1096
1213
|
}
|
|
1097
1214
|
|
|
1098
|
-
function getDynamicDefault(val: string) {
|
|
1215
|
+
function getDynamicDefault(val: string | undefined) {
|
|
1216
|
+
if (!val) {
|
|
1217
|
+
return
|
|
1218
|
+
}
|
|
1099
1219
|
return val.startsWith('javascript:') ? val : `javascript:${dynamic_value_mapping[val]}`
|
|
1100
1220
|
}
|
|
1101
1221
|
|
|
1102
|
-
function buildIndexes(indexes: IndexType[] = [], collection:
|
|
1222
|
+
function buildIndexes(indexes: IndexType[] = [], collection: XMLBuilder) {
|
|
1103
1223
|
indexes.forEach((index) => {
|
|
1104
|
-
const indexElement = collection.
|
|
1105
|
-
indexElement.
|
|
1224
|
+
const indexElement = collection.ele('index', { name: index.name, unique: index.unique })
|
|
1225
|
+
indexElement.ele('element', { name: index.element })
|
|
1106
1226
|
})
|
|
1107
1227
|
}
|
|
1108
1228
|
|
|
1109
|
-
function buildChoices(column: ColumnSchema, element:
|
|
1229
|
+
function buildChoices(column: ColumnSchema, element: XMLBuilder) {
|
|
1110
1230
|
if (!column.choice_elements) {
|
|
1111
1231
|
return
|
|
1112
1232
|
}
|
|
1113
1233
|
|
|
1114
|
-
const choices = element.
|
|
1234
|
+
const choices = element.ele('choice')
|
|
1115
1235
|
if (Array.isArray(column.choices) && column.choices.length > 0) {
|
|
1116
1236
|
// Formatted - choices: [1, 2, 3, 4]
|
|
1117
1237
|
for (const choice in column.choices) {
|
|
1118
|
-
choices.
|
|
1238
|
+
choices.ele('element', {
|
|
1119
1239
|
value: column.choices[choice],
|
|
1120
1240
|
label: column.choices[choice],
|
|
1121
1241
|
})
|
|
@@ -1124,12 +1244,12 @@ function buildChoices(column: ColumnSchema, element: XMLJsonElement) {
|
|
|
1124
1244
|
// Formatted - choices: { 1: { label: 'label' }, 2: { lable: label 2}, ...}
|
|
1125
1245
|
Object.entries(column.choice_elements).forEach((value) => {
|
|
1126
1246
|
if (isObject(value[1])) {
|
|
1127
|
-
choices.
|
|
1247
|
+
choices.ele('element', {
|
|
1128
1248
|
value: value[0],
|
|
1129
1249
|
...(value[1] as ChoiceConfig),
|
|
1130
1250
|
})
|
|
1131
1251
|
} else {
|
|
1132
|
-
choices.
|
|
1252
|
+
choices.ele('element', {
|
|
1133
1253
|
value: value[1],
|
|
1134
1254
|
label: value[0],
|
|
1135
1255
|
})
|
|
@@ -1157,11 +1277,12 @@ export function transformColumnData(col: any, callExpFromType: any) {
|
|
|
1157
1277
|
default: def,
|
|
1158
1278
|
max_length,
|
|
1159
1279
|
reference,
|
|
1280
|
+
reference_cascade_rule,
|
|
1160
1281
|
function_field,
|
|
1161
1282
|
...column
|
|
1162
1283
|
} = col
|
|
1163
1284
|
|
|
1164
|
-
if (max_length) {
|
|
1285
|
+
if (max_length !== undefined) {
|
|
1165
1286
|
column.maxLength = max_length
|
|
1166
1287
|
}
|
|
1167
1288
|
|
|
@@ -1175,6 +1296,10 @@ export function transformColumnData(col: any, callExpFromType: any) {
|
|
|
1175
1296
|
|
|
1176
1297
|
if (reference) {
|
|
1177
1298
|
column.referenceTable = reference
|
|
1299
|
+
|
|
1300
|
+
if (reference_cascade_rule) {
|
|
1301
|
+
column.cascadeRule = reference_cascade_rule
|
|
1302
|
+
}
|
|
1178
1303
|
}
|
|
1179
1304
|
|
|
1180
1305
|
// Handle dynamic values
|
|
@@ -1226,21 +1351,6 @@ function isDefaultEdgeEncryptionAttribute(attributes: any) {
|
|
|
1226
1351
|
)
|
|
1227
1352
|
}
|
|
1228
1353
|
|
|
1229
|
-
function getScriptInfo(scriptInfo: ScriptInfo | string, context: Context) {
|
|
1230
|
-
if (isString(scriptInfo)) {
|
|
1231
|
-
return scriptInfo
|
|
1232
|
-
}
|
|
1233
|
-
const { filePath, functionName, isDefault } = scriptInfo
|
|
1234
|
-
const script = buildScriptImport(
|
|
1235
|
-
filePath,
|
|
1236
|
-
functionName,
|
|
1237
|
-
isDefault,
|
|
1238
|
-
context,
|
|
1239
|
-
(funcName: string) => `;${funcName}(current)`
|
|
1240
|
-
)
|
|
1241
|
-
return script.replaceAll('\n', '')
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
1354
|
function removeAttributeTag(obj: any) {
|
|
1245
1355
|
Object.entries(obj).forEach(([key, value]) => {
|
|
1246
1356
|
if (key.startsWith('@_')) {
|
|
@@ -1330,6 +1440,21 @@ export function createObjectExpression(firstArg: object): ts.Expression {
|
|
|
1330
1440
|
ts.factory.createNoSubstitutionTemplateLiteral(calVal, calVal)
|
|
1331
1441
|
)
|
|
1332
1442
|
)
|
|
1443
|
+
} else if (key === 'maxLength') {
|
|
1444
|
+
if (isNaN(+value)) {
|
|
1445
|
+
return ts.factory.createPropertyAssignment(
|
|
1446
|
+
ts.factory.createIdentifier(key),
|
|
1447
|
+
ts.factory.createAsExpression(
|
|
1448
|
+
ts.factory.createStringLiteral(value),
|
|
1449
|
+
ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)
|
|
1450
|
+
)
|
|
1451
|
+
)
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
return ts.factory.createPropertyAssignment(
|
|
1455
|
+
ts.factory.createIdentifier(key),
|
|
1456
|
+
ts.factory.createNumericLiteral(value)
|
|
1457
|
+
)
|
|
1333
1458
|
}
|
|
1334
1459
|
return ts.factory.createPropertyAssignment(
|
|
1335
1460
|
createPropertyIdentifier(key),
|
|
@@ -1388,6 +1513,9 @@ type Undefined<T> = {
|
|
|
1388
1513
|
}
|
|
1389
1514
|
export function getCallExpressionFromType(internal_type: string) {
|
|
1390
1515
|
switch (internal_type) {
|
|
1516
|
+
case undefined:
|
|
1517
|
+
case 'string':
|
|
1518
|
+
return StringColumn
|
|
1391
1519
|
case 'boolean':
|
|
1392
1520
|
return BooleanColumn
|
|
1393
1521
|
case 'choice':
|
|
@@ -1440,8 +1568,6 @@ export function getCallExpressionFromType(internal_type: string) {
|
|
|
1440
1568
|
return UserRolesColumn
|
|
1441
1569
|
case 'version':
|
|
1442
1570
|
return VersionColumn
|
|
1443
|
-
case 'string':
|
|
1444
|
-
return StringColumn
|
|
1445
1571
|
case 'int':
|
|
1446
1572
|
case 'string_types':
|
|
1447
1573
|
case 'approval_rules':
|