@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.
- package/dist/AttachmentPlugin.d.ts +253 -0
- package/dist/AttachmentPlugin.js +216 -0
- package/dist/AttachmentPlugin.js.map +1 -0
- package/dist/BusinessRulePlugin.d.ts +56 -0
- package/dist/BusinessRulePlugin.js +171 -0
- package/dist/BusinessRulePlugin.js.map +1 -0
- package/dist/CrossScopePrivilegePlugin.d.ts +22 -0
- package/dist/CrossScopePrivilegePlugin.js +42 -0
- package/dist/CrossScopePrivilegePlugin.js.map +1 -0
- package/dist/DefaultPlugin.d.ts +71 -0
- package/dist/DefaultPlugin.js +238 -0
- package/dist/DefaultPlugin.js.map +1 -0
- package/dist/IdPlugin.d.ts +17 -0
- package/dist/IdPlugin.js +45 -0
- package/dist/IdPlugin.js.map +1 -0
- package/dist/ListPlugin.d.ts +91 -0
- package/dist/ListPlugin.js +398 -0
- package/dist/ListPlugin.js.map +1 -0
- package/dist/PropertyPlugin.d.ts +122 -0
- package/dist/PropertyPlugin.js +165 -0
- package/dist/PropertyPlugin.js.map +1 -0
- package/dist/ScriptTemplatePlugin.d.ts +31 -0
- package/dist/ScriptTemplatePlugin.js +208 -0
- package/dist/ScriptTemplatePlugin.js.map +1 -0
- package/dist/UserPreferencePlugin.d.ts +16 -0
- package/dist/UserPreferencePlugin.js +30 -0
- package/dist/UserPreferencePlugin.js.map +1 -0
- package/dist/aclAndRole/AclPlugin.d.ts +117 -0
- package/dist/aclAndRole/AclPlugin.js +285 -0
- package/dist/aclAndRole/AclPlugin.js.map +1 -0
- package/dist/aclAndRole/RolePlugin.d.ts +58 -0
- package/dist/aclAndRole/RolePlugin.js +152 -0
- package/dist/aclAndRole/RolePlugin.js.map +1 -0
- package/dist/aclAndRole/Util.d.ts +3 -0
- package/dist/aclAndRole/Util.js +106 -0
- package/dist/aclAndRole/Util.js.map +1 -0
- package/dist/app/ApplicationMenuPlugin.d.ts +32 -0
- package/dist/app/ApplicationMenuPlugin.js +106 -0
- package/dist/app/ApplicationMenuPlugin.js.map +1 -0
- package/dist/atf/ATFComposer.d.ts +492 -0
- package/dist/atf/ATFComposer.js +2717 -0
- package/dist/atf/ATFComposer.js.map +1 -0
- package/dist/atf/TestPlugin.d.ts +31 -0
- package/dist/atf/TestPlugin.js +95 -0
- package/dist/atf/TestPlugin.js.map +1 -0
- package/dist/atf/index.d.ts +1 -0
- package/dist/atf/index.js +9 -0
- package/dist/atf/index.js.map +1 -0
- package/dist/db/ColumnPlugins.d.ts +278 -0
- package/dist/db/ColumnPlugins.js +112 -0
- package/dist/db/ColumnPlugins.js.map +1 -0
- package/dist/db/RecordPlugin.d.ts +208 -0
- package/dist/db/RecordPlugin.js +287 -0
- package/dist/db/RecordPlugin.js.map +1 -0
- package/dist/db/TablePlugin.d.ts +742 -0
- package/dist/db/TablePlugin.js +1249 -0
- package/dist/db/TablePlugin.js.map +1 -0
- package/dist/db/index.d.ts +3 -0
- package/dist/db/index.js +27 -0
- package/dist/db/index.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/scriptedRESTAPI/RESTDeserializationUtils.d.ts +12 -0
- package/dist/scriptedRESTAPI/RESTDeserializationUtils.js +371 -0
- package/dist/scriptedRESTAPI/RESTDeserializationUtils.js.map +1 -0
- package/dist/scriptedRESTAPI/RESTSerializationUtils.d.ts +15 -0
- package/dist/scriptedRESTAPI/RESTSerializationUtils.js +177 -0
- package/dist/scriptedRESTAPI/RESTSerializationUtils.js.map +1 -0
- package/dist/scriptedRESTAPI/RestApiPlugin.d.ts +144 -0
- package/dist/scriptedRESTAPI/RestApiPlugin.js +318 -0
- package/dist/scriptedRESTAPI/RestApiPlugin.js.map +1 -0
- package/dist/scriptedRESTAPI/RestSchemaUtils.d.ts +190 -0
- package/dist/scriptedRESTAPI/RestSchemaUtils.js +53 -0
- package/dist/scriptedRESTAPI/RestSchemaUtils.js.map +1 -0
- package/dist/scriptedRESTAPI/RestUtils.d.ts +75 -0
- package/dist/scriptedRESTAPI/RestUtils.js +469 -0
- package/dist/scriptedRESTAPI/RestUtils.js.map +1 -0
- package/dist/scripts/ClientScriptPlugin.d.ts +43 -0
- package/dist/scripts/ClientScriptPlugin.js +190 -0
- package/dist/scripts/ClientScriptPlugin.js.map +1 -0
- package/dist/scripts/scriptUtils.d.ts +15 -0
- package/dist/scripts/scriptUtils.js +83 -0
- package/dist/scripts/scriptUtils.js.map +1 -0
- package/dist/uxf/ExperiencePlugin.d.ts +22 -0
- package/dist/uxf/ExperiencePlugin.js +55 -0
- package/dist/uxf/ExperiencePlugin.js.map +1 -0
- package/dist/uxf/RoutesPlugin.d.ts +22 -0
- package/dist/uxf/RoutesPlugin.js +176 -0
- package/dist/uxf/RoutesPlugin.js.map +1 -0
- package/dist/uxf/UxfFormulaParser/cleanUxValue.d.ts +4 -0
- package/dist/uxf/UxfFormulaParser/cleanUxValue.js +65 -0
- package/dist/uxf/UxfFormulaParser/cleanUxValue.js.map +1 -0
- package/dist/uxf/UxfFormulaParser/grammerParser/api.d.ts +189 -0
- package/dist/uxf/UxfFormulaParser/grammerParser/api.js +158 -0
- package/dist/uxf/UxfFormulaParser/grammerParser/api.js.map +1 -0
- package/dist/uxf/UxfFormulaParser/grammerParser/clientTransformMap.d.ts +13 -0
- package/dist/uxf/UxfFormulaParser/grammerParser/clientTransformMap.js +604 -0
- package/dist/uxf/UxfFormulaParser/grammerParser/clientTransformMap.js.map +1 -0
- package/dist/uxf/UxfFormulaParser/grammerParser/grammarParser.d.ts +12 -0
- package/dist/uxf/UxfFormulaParser/grammerParser/grammarParser.js +551 -0
- package/dist/uxf/UxfFormulaParser/grammerParser/grammarParser.js.map +1 -0
- package/dist/uxf/UxfFormulaParser/grammerParser/spanHelpers.d.ts +31 -0
- package/dist/uxf/UxfFormulaParser/grammerParser/spanHelpers.js +64 -0
- package/dist/uxf/UxfFormulaParser/grammerParser/spanHelpers.js.map +1 -0
- package/dist/uxf/UxfFormulaParser/index.d.ts +3 -0
- package/dist/uxf/UxfFormulaParser/index.js +11 -0
- package/dist/uxf/UxfFormulaParser/index.js.map +1 -0
- package/dist/uxf/UxfFormulaParser/parser.d.ts +8 -0
- package/dist/uxf/UxfFormulaParser/parser.js +87 -0
- package/dist/uxf/UxfFormulaParser/parser.js.map +1 -0
- package/dist/uxf/UxfFormulaParser/utils/getErrorMsg.d.ts +8 -0
- package/dist/uxf/UxfFormulaParser/utils/getErrorMsg.js +17 -0
- package/dist/uxf/UxfFormulaParser/utils/getErrorMsg.js.map +1 -0
- package/dist/uxf/constants.d.ts +2 -0
- package/dist/uxf/constants.js +8 -0
- package/dist/uxf/constants.js.map +1 -0
- package/dist/uxf/index.d.ts +2 -0
- package/dist/uxf/index.js +11 -0
- package/dist/uxf/index.js.map +1 -0
- package/dist/uxf/tectonicIdGenerator.d.ts +12 -0
- package/dist/uxf/tectonicIdGenerator.js +102 -0
- package/dist/uxf/tectonicIdGenerator.js.map +1 -0
- package/license +9 -0
- package/package.json +42 -0
- package/src/AttachmentPlugin.ts +262 -0
- package/src/BusinessRulePlugin.ts +251 -0
- package/src/CrossScopePrivilegePlugin.ts +54 -0
- package/src/DefaultPlugin.ts +272 -0
- package/src/IdPlugin.ts +47 -0
- package/src/ListPlugin.ts +497 -0
- package/src/PropertyPlugin.ts +218 -0
- package/src/ScriptTemplatePlugin.ts +223 -0
- package/src/UserPreferencePlugin.ts +36 -0
- package/src/aclAndRole/AclPlugin.ts +410 -0
- package/src/aclAndRole/RolePlugin.ts +225 -0
- package/src/aclAndRole/Util.ts +104 -0
- package/src/app/ApplicationMenuPlugin.ts +158 -0
- package/src/atf/ATFComposer.ts +3356 -0
- package/src/atf/TestPlugin.ts +119 -0
- package/src/atf/index.ts +1 -0
- package/src/db/ColumnPlugins.ts +117 -0
- package/src/db/RecordPlugin.ts +391 -0
- package/src/db/TablePlugin.ts +1581 -0
- package/src/db/index.ts +3 -0
- package/src/index.ts +16 -0
- package/src/scriptedRESTAPI/RESTDeserializationUtils.ts +410 -0
- package/src/scriptedRESTAPI/RESTSerializationUtils.ts +227 -0
- package/src/scriptedRESTAPI/RestApiPlugin.ts +438 -0
- package/src/scriptedRESTAPI/RestSchemaUtils.ts +72 -0
- package/src/scriptedRESTAPI/RestUtils.ts +507 -0
- package/src/scripts/ClientScriptPlugin.ts +251 -0
- package/src/scripts/scriptUtils.ts +81 -0
- package/src/uxf/ExperiencePlugin.ts +64 -0
- package/src/uxf/RoutesPlugin.ts +215 -0
- package/src/uxf/UxfFormulaParser/cleanUxValue.ts +73 -0
- package/src/uxf/UxfFormulaParser/grammerParser/api.js +166 -0
- package/src/uxf/UxfFormulaParser/grammerParser/clientTransformMap.js +606 -0
- package/src/uxf/UxfFormulaParser/grammerParser/grammarParser.js +551 -0
- package/src/uxf/UxfFormulaParser/grammerParser/spanHelpers.js +65 -0
- package/src/uxf/UxfFormulaParser/index.ts +4 -0
- package/src/uxf/UxfFormulaParser/parser.ts +64 -0
- package/src/uxf/UxfFormulaParser/utils/getErrorMsg.ts +13 -0
- package/src/uxf/constants.ts +4 -0
- package/src/uxf/index.ts +2 -0
- package/src/uxf/tectonicIdGenerator.ts +81 -0
|
@@ -0,0 +1,1581 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChoiceConfig,
|
|
3
|
+
Table,
|
|
4
|
+
Record as NowRecord,
|
|
5
|
+
TableName,
|
|
6
|
+
choiceDropdown,
|
|
7
|
+
DateColumn,
|
|
8
|
+
BasicDateTimeColumn,
|
|
9
|
+
GenericColumn,
|
|
10
|
+
BooleanColumn,
|
|
11
|
+
ChoiceColumn,
|
|
12
|
+
IntegerDateColumn,
|
|
13
|
+
ScheduleDateTimeColumn,
|
|
14
|
+
ConditionsColumn,
|
|
15
|
+
DateTimeColumn,
|
|
16
|
+
CalendarDateTimeColumn,
|
|
17
|
+
DueDateColumn,
|
|
18
|
+
OtherDateColumn,
|
|
19
|
+
DecimalColumn,
|
|
20
|
+
DocumentIdColumn,
|
|
21
|
+
DomainIdColumn,
|
|
22
|
+
DomainPathColumn,
|
|
23
|
+
FieldNameColumn,
|
|
24
|
+
IntegerColumn,
|
|
25
|
+
RadioColumn,
|
|
26
|
+
ReferenceColumn,
|
|
27
|
+
ScriptColumn,
|
|
28
|
+
SystemClassNameColumn,
|
|
29
|
+
TableNameColumn,
|
|
30
|
+
TranslatedFieldColumn,
|
|
31
|
+
TranslatedTextColumn,
|
|
32
|
+
UserRolesColumn,
|
|
33
|
+
VersionColumn,
|
|
34
|
+
StringColumn,
|
|
35
|
+
} from '@servicenow/sdk-core/runtime/db'
|
|
36
|
+
import {
|
|
37
|
+
Context,
|
|
38
|
+
EntityData,
|
|
39
|
+
LinkedDocument,
|
|
40
|
+
Plugin,
|
|
41
|
+
extractCallExpression,
|
|
42
|
+
getCallExpressionName,
|
|
43
|
+
getOrCreateEntitySourceFile,
|
|
44
|
+
getOrCreatePropertyAssignment,
|
|
45
|
+
linkDocument,
|
|
46
|
+
removeNode,
|
|
47
|
+
stringify,
|
|
48
|
+
transformFunctionArguments,
|
|
49
|
+
writeCustomProperty,
|
|
50
|
+
XmlData,
|
|
51
|
+
FluentDiagnostic,
|
|
52
|
+
PartialElements,
|
|
53
|
+
generateCallExpressionExport,
|
|
54
|
+
generateCallExpression,
|
|
55
|
+
createPropertyIdentifier,
|
|
56
|
+
XMLJsonBuilder,
|
|
57
|
+
XMLJsonElement,
|
|
58
|
+
ObjectData,
|
|
59
|
+
} from '@servicenow/sdk-build-core'
|
|
60
|
+
import { XMLBuilder } from 'fast-xml-parser'
|
|
61
|
+
import { isArray, isEmpty, isObject, isString, parseInt } from 'lodash'
|
|
62
|
+
import { z } from 'zod'
|
|
63
|
+
import { RecordPlugin, TextBooleanSchema, TextNumberSchema, TextStringSchema } from './RecordPlugin'
|
|
64
|
+
import { ScriptInfo, buildScriptImport, scriptInfo } from '../scripts/scriptUtils'
|
|
65
|
+
import { SyntaxKind, Node, ts, ObjectLiteralExpression, CallExpression, Structure, SourceFile } from 'ts-morph'
|
|
66
|
+
import { Diagnostic } from '@servicenow/sdk-project'
|
|
67
|
+
import { formatScript } from '../ScriptTemplatePlugin'
|
|
68
|
+
import * as os from 'os'
|
|
69
|
+
import * as path from 'path'
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The access levels are mapped to the following values on the platform
|
|
73
|
+
*
|
|
74
|
+
* - `none`: 0
|
|
75
|
+
* - `tracking`: 1
|
|
76
|
+
* - `restricted`: 2
|
|
77
|
+
*
|
|
78
|
+
*/
|
|
79
|
+
export const callerAccess = ['none', 'tracking', 'restricted'] as const
|
|
80
|
+
|
|
81
|
+
// Basic info needed for table references
|
|
82
|
+
export const TableSchemaFull = z.object({
|
|
83
|
+
/** lowercase, one word name. Numbers, letters and underscores only */
|
|
84
|
+
name: z.string(),
|
|
85
|
+
/** Object literal of column names with <type>Column() calls as values.
|
|
86
|
+
* Ex. { column_name_1: StringColumn({...args}), column_name_2: IntegerColumn({...args}), }
|
|
87
|
+
* Import column definitions from `@servicenow/sdk-core/db`
|
|
88
|
+
*/
|
|
89
|
+
schema: z.record(z.record(z.any())).default({}),
|
|
90
|
+
/** Table as a string, this table will inherit schema from extends table.
|
|
91
|
+
* Escape with `` (import from `@servicenow/sdk-core/global`)
|
|
92
|
+
* if table definition not present locally */
|
|
93
|
+
extends: z.string().optional(),
|
|
94
|
+
/** Will match name if not provided, cannot exceed 80 characters */
|
|
95
|
+
label: z.string().max(80, 'Cannot exceed 80 characters').optional(),
|
|
96
|
+
/** Must match column name */
|
|
97
|
+
display: z.string().optional(),
|
|
98
|
+
|
|
99
|
+
// Controls
|
|
100
|
+
extensible: z.boolean().optional().default(false),
|
|
101
|
+
live_feed: z.boolean().optional().default(false),
|
|
102
|
+
|
|
103
|
+
/** sys_number to create auto prefixing. Specify as Object literal */
|
|
104
|
+
auto_number: z
|
|
105
|
+
.object({
|
|
106
|
+
prefix: z.string().default('pre').optional(),
|
|
107
|
+
number: z.number().default(1000).optional(),
|
|
108
|
+
number_of_digits: z.number().default(7).optional(),
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
.optional(),
|
|
112
|
+
|
|
113
|
+
// Application Access
|
|
114
|
+
accessible_from: z
|
|
115
|
+
.union([z.literal('package_private'), z.literal('public')])
|
|
116
|
+
.optional()
|
|
117
|
+
.default('public'),
|
|
118
|
+
caller_access: z.enum(callerAccess).optional(),
|
|
119
|
+
/** Array of read, update, create, delete actions (['read', 'update',...])*/
|
|
120
|
+
actions: z
|
|
121
|
+
.array(z.union([z.literal('read'), z.literal('update'), z.literal('delete'), z.literal('create')]))
|
|
122
|
+
.optional(),
|
|
123
|
+
|
|
124
|
+
allow_web_service_access: z.boolean().optional().default(false),
|
|
125
|
+
allow_new_fields: z.boolean().optional().default(false),
|
|
126
|
+
allow_ui_actions: z.boolean().optional().default(false),
|
|
127
|
+
allow_client_scripts: z.boolean().optional().default(false),
|
|
128
|
+
|
|
129
|
+
// From sys_dictionary
|
|
130
|
+
audit: z.boolean().optional().default(false),
|
|
131
|
+
read_only: z.boolean().optional().default(false),
|
|
132
|
+
text_index: z.boolean().optional().default(false),
|
|
133
|
+
|
|
134
|
+
/** Object literal of key value pairs corresponding to attributes */
|
|
135
|
+
attributes: z.record(z.union([z.string(), z.number(), z.boolean()])).optional(),
|
|
136
|
+
|
|
137
|
+
/** Array of objects [{ { name: <columnName>, element: ...,}...}] */
|
|
138
|
+
index: z
|
|
139
|
+
.array(
|
|
140
|
+
z.object({
|
|
141
|
+
name: z.string(),
|
|
142
|
+
unique: z.boolean(),
|
|
143
|
+
element: z.string(),
|
|
144
|
+
})
|
|
145
|
+
)
|
|
146
|
+
.optional(),
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
const componentTables = ['sys_dictionary', 'sys_choice', 'sys_documentation']
|
|
150
|
+
|
|
151
|
+
type IndexType = {
|
|
152
|
+
name: string
|
|
153
|
+
unique: boolean
|
|
154
|
+
element: string
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Everything that goes into a table
|
|
158
|
+
|
|
159
|
+
// Everything which goes into a element with type='string/int/etc' in bootstrap.xml
|
|
160
|
+
type ColumnSchema = z.infer<typeof ColumnSchema>
|
|
161
|
+
export const ColumnSchema = z.object({
|
|
162
|
+
entityKind: z.string().optional(),
|
|
163
|
+
column_type: z.string().optional(),
|
|
164
|
+
audit: z.boolean().optional().default(false),
|
|
165
|
+
label: z.string().optional(),
|
|
166
|
+
maxLength: z.number().optional().default(40),
|
|
167
|
+
mandatory: z.boolean().optional().default(false),
|
|
168
|
+
read_only: z.boolean().optional().default(false),
|
|
169
|
+
default: z.any().optional(),
|
|
170
|
+
attributes: z.record(z.string(), z.union([z.string(), z.boolean(), z.number()])).optional(),
|
|
171
|
+
referenceTable: z.string().optional(),
|
|
172
|
+
dropdown: z.enum(choiceDropdown).default('none').optional(),
|
|
173
|
+
choices: z.record(z.any()).or(z.array(z.any())).optional(),
|
|
174
|
+
choice_elements: z.record(z.any()).or(z.array(z.any())).optional(),
|
|
175
|
+
display: z.boolean().default(false).optional(),
|
|
176
|
+
function_definition: z.string().optional(),
|
|
177
|
+
dynamic_value_definitions: z
|
|
178
|
+
.object({
|
|
179
|
+
type: z.union([
|
|
180
|
+
z.literal('dynamic_default'),
|
|
181
|
+
z.literal('choices_from_other_table'),
|
|
182
|
+
z.literal('calculated_value'),
|
|
183
|
+
z.literal('dependent_field'),
|
|
184
|
+
]),
|
|
185
|
+
})
|
|
186
|
+
.and(z.any())
|
|
187
|
+
.optional(),
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
const BooleanFromString = z
|
|
191
|
+
.string()
|
|
192
|
+
.transform((str) => str === 'true')
|
|
193
|
+
.or(z.boolean())
|
|
194
|
+
.optional()
|
|
195
|
+
|
|
196
|
+
const IntFromString = z
|
|
197
|
+
.string()
|
|
198
|
+
.transform((str) => parseInt(str))
|
|
199
|
+
.or(z.number())
|
|
200
|
+
.optional()
|
|
201
|
+
|
|
202
|
+
// These fields transform from sys_db_object only, not bootstrap. (Except name, which is needed to match)
|
|
203
|
+
const DBObjectIncomingAttributes = z.object({
|
|
204
|
+
name: TextStringSchema.or(z.string()).optional(),
|
|
205
|
+
|
|
206
|
+
read_access: TextBooleanSchema.or(BooleanFromString).optional().default(true),
|
|
207
|
+
create_access: TextBooleanSchema.or(BooleanFromString).optional().default(true),
|
|
208
|
+
update_access: TextBooleanSchema.or(BooleanFromString).optional().default(true),
|
|
209
|
+
delete_access: TextBooleanSchema.or(BooleanFromString).optional().default(false),
|
|
210
|
+
|
|
211
|
+
ws_access: TextBooleanSchema.or(BooleanFromString).optional().default(true),
|
|
212
|
+
caller_access: TextNumberSchema.or(IntFromString)
|
|
213
|
+
.transform((val) => {
|
|
214
|
+
if (val) {
|
|
215
|
+
return callerAccess[val]
|
|
216
|
+
}
|
|
217
|
+
return
|
|
218
|
+
})
|
|
219
|
+
.optional(),
|
|
220
|
+
alter_access: TextBooleanSchema.or(BooleanFromString).optional().default(false),
|
|
221
|
+
client_scripts_access: TextBooleanSchema.or(BooleanFromString).optional().default(false),
|
|
222
|
+
actions_access: TextBooleanSchema.or(BooleanFromString).optional().default(false),
|
|
223
|
+
//configuration_access: TextBooleanSchema.or(BooleanFromString).optional().default(false),
|
|
224
|
+
|
|
225
|
+
is_extendable: TextBooleanSchema.or(BooleanFromString).optional().default(false),
|
|
226
|
+
// live_feed_enabled: TextBooleanSchema.or(BooleanFromString).optional().default(false),
|
|
227
|
+
|
|
228
|
+
access: TextStringSchema.or(z.string()).optional(),
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
// Attributes in the element='collection' tag of bootstrap xml
|
|
232
|
+
export const TableSchemaBootstrap = z.object({
|
|
233
|
+
name: z.string(),
|
|
234
|
+
extends: z.string().optional(),
|
|
235
|
+
label: z.string().max(80, 'Cannot exceed 80 characters').optional(),
|
|
236
|
+
|
|
237
|
+
// Controls
|
|
238
|
+
extensible: z.boolean().optional().default(false),
|
|
239
|
+
live_feed: z.boolean().optional().default(false),
|
|
240
|
+
|
|
241
|
+
auto_number: z
|
|
242
|
+
.object({
|
|
243
|
+
prefix: z.string().default('pre').optional(),
|
|
244
|
+
number: z.number().default(1000).optional(),
|
|
245
|
+
number_of_digits: z.number().default(7).optional(),
|
|
246
|
+
})
|
|
247
|
+
.optional(),
|
|
248
|
+
|
|
249
|
+
number_ref: z.any().optional(),
|
|
250
|
+
|
|
251
|
+
// Application Access
|
|
252
|
+
accessible_from: z
|
|
253
|
+
.union([z.literal('public'), z.literal('package_private')])
|
|
254
|
+
.optional()
|
|
255
|
+
.default('public'),
|
|
256
|
+
caller_access: z.enum(callerAccess).default('none').optional(),
|
|
257
|
+
|
|
258
|
+
actions: z
|
|
259
|
+
.array(z.union([z.literal('read'), z.literal('update'), z.literal('delete'), z.literal('create')]))
|
|
260
|
+
.default(['read'])
|
|
261
|
+
.optional(),
|
|
262
|
+
|
|
263
|
+
allow_web_service_access: z.boolean().optional().default(true),
|
|
264
|
+
allow_new_fields: z.boolean().optional().default(false),
|
|
265
|
+
allow_ui_actions: z.boolean().optional().default(false),
|
|
266
|
+
allow_client_scripts: z.boolean().optional().default(false),
|
|
267
|
+
|
|
268
|
+
// From sys_dictionary
|
|
269
|
+
audit: z.boolean().optional().default(false),
|
|
270
|
+
read_only: z.boolean().optional().default(false),
|
|
271
|
+
text_index: z.boolean().optional().default(false),
|
|
272
|
+
|
|
273
|
+
attributes: z
|
|
274
|
+
.record(z.union([z.string(), z.number(), z.boolean()]))
|
|
275
|
+
.default({})
|
|
276
|
+
.optional(),
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
const TableSchemaBootstrapIncoming = TableSchemaBootstrap.omit({ actions: true }).extend({
|
|
280
|
+
caller_access: z
|
|
281
|
+
.union([z.literal(0), z.literal(1), z.literal(2)])
|
|
282
|
+
.transform((val) => {
|
|
283
|
+
if (val) {
|
|
284
|
+
return callerAccess[val]
|
|
285
|
+
}
|
|
286
|
+
return
|
|
287
|
+
})
|
|
288
|
+
.optional(),
|
|
289
|
+
accessible_from: z.union([z.literal('public'), z.literal('package_private')]).optional(),
|
|
290
|
+
extends: z
|
|
291
|
+
.string()
|
|
292
|
+
.or(z.object({ name: z.string() }).transform((obj) => obj.name))
|
|
293
|
+
.optional(),
|
|
294
|
+
live_feed: z.boolean().optional().default(false),
|
|
295
|
+
index: z.any().optional(),
|
|
296
|
+
columns: z.any().optional(),
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
// Everything in side <database> in boostrap.xml
|
|
300
|
+
const BootstrapDatabaseElement = z.object({
|
|
301
|
+
element: z.record(z.any()),
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
const DBObjectIncoming = z.object({
|
|
305
|
+
'@_table': z.literal('sys_db_object'),
|
|
306
|
+
sys_db_object: z.record(z.any()),
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
export const Documentation = z.object({
|
|
310
|
+
sys_documentation: z.object({
|
|
311
|
+
element: TextStringSchema.optional(),
|
|
312
|
+
label: TextStringSchema.optional(),
|
|
313
|
+
language: TextStringSchema.optional(),
|
|
314
|
+
hint: TextStringSchema.optional(),
|
|
315
|
+
help: TextStringSchema.optional(),
|
|
316
|
+
name: TextStringSchema.optional(),
|
|
317
|
+
plural: TextStringSchema.optional(),
|
|
318
|
+
url: TextStringSchema.optional(),
|
|
319
|
+
url_target: TextStringSchema.optional(),
|
|
320
|
+
}),
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
// Attributes from element=table in bootstrap.xml
|
|
324
|
+
export const TableSchemaBootstrapAttributes = z
|
|
325
|
+
.object({
|
|
326
|
+
'@_name': z.string(),
|
|
327
|
+
'@_extends': z.string().optional(),
|
|
328
|
+
'@_label': z.string().optional(),
|
|
329
|
+
'@_max_length': z.coerce.number().optional(),
|
|
330
|
+
|
|
331
|
+
// From sys_dictionary
|
|
332
|
+
'@_audit': BooleanFromString,
|
|
333
|
+
'@_read_only': BooleanFromString,
|
|
334
|
+
'@_text_index': BooleanFromString,
|
|
335
|
+
'@_client_scripts_access': BooleanFromString,
|
|
336
|
+
'@_ws_access': BooleanFromString,
|
|
337
|
+
'@_alter_access': BooleanFromString,
|
|
338
|
+
'@_create_access': BooleanFromString,
|
|
339
|
+
'@_delete_access': BooleanFromString,
|
|
340
|
+
'@_update_access': BooleanFromString,
|
|
341
|
+
'@_query_access': BooleanFromString,
|
|
342
|
+
'@_actions_access': BooleanFromString,
|
|
343
|
+
'@_read_access': BooleanFromString,
|
|
344
|
+
'@_is_extendable': BooleanFromString,
|
|
345
|
+
'@_caller_access': IntFromString,
|
|
346
|
+
|
|
347
|
+
'@_attributes': z
|
|
348
|
+
.string()
|
|
349
|
+
.transform((values) => {
|
|
350
|
+
const ret = {}
|
|
351
|
+
if (values === '') {
|
|
352
|
+
return undefined
|
|
353
|
+
}
|
|
354
|
+
const entries = values.split(',')
|
|
355
|
+
entries.forEach((attr) => {
|
|
356
|
+
const s = attr.split('=')
|
|
357
|
+
if (s.length === 2 && s[0] && s[1]) {
|
|
358
|
+
try {
|
|
359
|
+
ret[s[0]] = JSON.parse(s[1])
|
|
360
|
+
} catch (err) {
|
|
361
|
+
ret[s[0]] = s[1]
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
})
|
|
365
|
+
return ret
|
|
366
|
+
})
|
|
367
|
+
.optional(),
|
|
368
|
+
})
|
|
369
|
+
.catchall(z.any())
|
|
370
|
+
|
|
371
|
+
const IndexSchemaBootstrapAttributes = z.object({
|
|
372
|
+
'@_name': z.string(),
|
|
373
|
+
'@_unique': BooleanFromString,
|
|
374
|
+
element: z.object({
|
|
375
|
+
'@_name': z.string(),
|
|
376
|
+
}),
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
export const ColumnSchemaBootstrapAttributes = TableSchemaBootstrapAttributes.omit({ '@_extends': true })
|
|
380
|
+
.extend({
|
|
381
|
+
'@_choice': z.coerce.number().optional(),
|
|
382
|
+
'@_active': BooleanFromString,
|
|
383
|
+
'@_mandatory': BooleanFromString,
|
|
384
|
+
'@_display': BooleanFromString,
|
|
385
|
+
'@_type': z.string().optional(),
|
|
386
|
+
'@_use_dependent_field': BooleanFromString,
|
|
387
|
+
'@_use_dynamic_default': BooleanFromString,
|
|
388
|
+
'@_reference': z.string().optional(),
|
|
389
|
+
'@_virtual': BooleanFromString,
|
|
390
|
+
'@_default': z.string().optional(),
|
|
391
|
+
'@_calculation': z.string().optional(),
|
|
392
|
+
'@_choice_field': z.string().optional(),
|
|
393
|
+
'@_function_definition': z.string().optional(),
|
|
394
|
+
choice: z.object({ element: z.array(z.any()).or(z.record(z.any())).optional() }).optional(),
|
|
395
|
+
})
|
|
396
|
+
.catchall(z.any())
|
|
397
|
+
|
|
398
|
+
const ChoiceSchema = z.object({
|
|
399
|
+
label: z.string().optional(),
|
|
400
|
+
value: z.string().optional(),
|
|
401
|
+
sequence: z.coerce.number().optional(),
|
|
402
|
+
dependent_value: z.coerce.number().optional(),
|
|
403
|
+
hint: z.string().optional(),
|
|
404
|
+
inactive: BooleanFromString.default(false).optional(),
|
|
405
|
+
inactive_on_update: BooleanFromString.default(false).optional(),
|
|
406
|
+
language: z.string().optional(),
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
const ChoiceSchemaIncoming = ChoiceSchema.omit({ value: true })
|
|
410
|
+
|
|
411
|
+
type ComposedTableData = {
|
|
412
|
+
data: {
|
|
413
|
+
bootstrapData: any
|
|
414
|
+
dbObjectData: any
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function isSNScope(scopeName: string) {
|
|
419
|
+
return scopeName.startsWith('sn') || scopeName.startsWith('now')
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
export default Plugin({
|
|
423
|
+
name: 'Table',
|
|
424
|
+
ownedTables: {
|
|
425
|
+
sys_db_object: { diagnosticLevel: Diagnostic.Level.Warn },
|
|
426
|
+
},
|
|
427
|
+
extractors: {
|
|
428
|
+
entity: {
|
|
429
|
+
// TODO: Cast to `any` is temporary workaround for the circular nature of Table entities
|
|
430
|
+
CallExpression: (node, context) => {
|
|
431
|
+
const result = extractCallExpression(Table, 'table', node, context, (table) => table.name)
|
|
432
|
+
if (!result.handled || !(0 in result.data)) {
|
|
433
|
+
return result
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const data = result.data[0]
|
|
437
|
+
const diagnostics = result.diagnostics
|
|
438
|
+
const scopeName = context.app.config.scope
|
|
439
|
+
const scopeRegex = new RegExp(`^${scopeName}_`)
|
|
440
|
+
const nameRegex = new RegExp(`^[a-z_][a-z0-9_]`)
|
|
441
|
+
const tableName = data.getProperty('name')
|
|
442
|
+
const scopeMatches = tableName.getValue().match(scopeRegex)
|
|
443
|
+
|
|
444
|
+
if (!scopeMatches) {
|
|
445
|
+
const nameNode = tableName.getNode().asKindOrThrow(SyntaxKind.StringLiteral)
|
|
446
|
+
if (nameNode.getParent().getKindName() !== 'AsExpression') {
|
|
447
|
+
diagnostics.push(
|
|
448
|
+
new FluentDiagnostic(
|
|
449
|
+
tableName.getNode(),
|
|
450
|
+
`'name' property must start with scope prefix '${scopeName}'`
|
|
451
|
+
)
|
|
452
|
+
)
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const schema = data.getProperty('schema')
|
|
457
|
+
const schemaNode = schema.getNode().asKindOrThrow(SyntaxKind.ObjectLiteralExpression)
|
|
458
|
+
for (const columnName in schema.getValue()) {
|
|
459
|
+
const columnNode = schemaNode.getPropertyOrThrow(columnName)
|
|
460
|
+
if (!scopeMatches && !isSNScope(scopeName) && !columnName.match(scopeRegex)) {
|
|
461
|
+
diagnostics.push(
|
|
462
|
+
new FluentDiagnostic(
|
|
463
|
+
columnNode,
|
|
464
|
+
`Column name must be prefixed with scope '${scopeName}' if table name does not contain prefix`
|
|
465
|
+
)
|
|
466
|
+
)
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (!columnName.match(nameRegex)) {
|
|
470
|
+
diagnostics.push(
|
|
471
|
+
new FluentDiagnostic(
|
|
472
|
+
columnNode,
|
|
473
|
+
`Column name must only contain lowercase letters, numbers, and underscores`
|
|
474
|
+
)
|
|
475
|
+
)
|
|
476
|
+
}
|
|
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
|
+
|
|
495
|
+
return { handled: true, data: [data], diagnostics }
|
|
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] }
|
|
515
|
+
},
|
|
516
|
+
},
|
|
517
|
+
xml(xml, context) {
|
|
518
|
+
const bootstrapData = {}
|
|
519
|
+
let dbObjectData = {}
|
|
520
|
+
const bootstrapDatabaseXML = BootstrapDatabaseElement.safeParse(xml.data['database'])
|
|
521
|
+
if (bootstrapDatabaseXML.success) {
|
|
522
|
+
const data = TableSchemaBootstrapAttributes.parse(bootstrapDatabaseXML.data.element)
|
|
523
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
524
|
+
if (key.startsWith('@_')) {
|
|
525
|
+
bootstrapData[key.substring(2)] = value
|
|
526
|
+
} else if (key === 'element') {
|
|
527
|
+
//
|
|
528
|
+
} else {
|
|
529
|
+
bootstrapData[key] = value
|
|
530
|
+
}
|
|
531
|
+
})
|
|
532
|
+
context.handledXmls[xml.filePath] = 'handled'
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const db_object_inc = DBObjectIncoming.safeParse(xml.data['record_update'])
|
|
536
|
+
if (db_object_inc.success) {
|
|
537
|
+
dbObjectData = DBObjectIncomingAttributes.parse(db_object_inc.data.sys_db_object)
|
|
538
|
+
context.handledXmls[xml.filePath] = 'handled'
|
|
539
|
+
return new XmlData(
|
|
540
|
+
dbObjectData,
|
|
541
|
+
xml.filePath,
|
|
542
|
+
'sys_db_object',
|
|
543
|
+
db_object_inc.data.sys_db_object['@_action']
|
|
544
|
+
)
|
|
545
|
+
} else {
|
|
546
|
+
const record_update = xml.data['record_update']
|
|
547
|
+
if (record_update) {
|
|
548
|
+
const table = Object.keys(record_update)[0]
|
|
549
|
+
if (!table || !componentTables.includes(table)) {
|
|
550
|
+
return
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (table === 'sys_documentation') {
|
|
554
|
+
const data = Documentation.parse(record_update[table])
|
|
555
|
+
return new XmlData(
|
|
556
|
+
{
|
|
557
|
+
table: table,
|
|
558
|
+
id: record_update[table][table]['sys_id']['#text'],
|
|
559
|
+
data: data.sys_documentation,
|
|
560
|
+
},
|
|
561
|
+
xml.filePath,
|
|
562
|
+
'record',
|
|
563
|
+
record_update[table][table]['@_action']
|
|
564
|
+
)
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
context.handledXmls[xml.filePath] = 'ignored'
|
|
568
|
+
return
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Columns and Indexes
|
|
573
|
+
const columns: z.infer<typeof ColumnSchemaBootstrapAttributes>[] = []
|
|
574
|
+
const idxElements: IndexType[] = []
|
|
575
|
+
if (bootstrapDatabaseXML.success && bootstrapDatabaseXML.data.element['element']) {
|
|
576
|
+
const element = bootstrapDatabaseXML.data.element['element']
|
|
577
|
+
const elementsArray = isArray(element) ? element : [element]
|
|
578
|
+
columns.push(...z.array(ColumnSchemaBootstrapAttributes).parse(elementsArray))
|
|
579
|
+
|
|
580
|
+
columns.forEach((element) => {
|
|
581
|
+
if (element.choice && !isEmpty(element.choice)) {
|
|
582
|
+
element['choice_elements'] = {}
|
|
583
|
+
const choices = isArray(element.choice.element)
|
|
584
|
+
? element.choice.element
|
|
585
|
+
: [element.choice.element]
|
|
586
|
+
choices.forEach((choice) => {
|
|
587
|
+
const obj = removeAttributeTag(choice)
|
|
588
|
+
const choiceData = ChoiceSchemaIncoming.parse(obj)
|
|
589
|
+
if (!choiceData.label) {
|
|
590
|
+
choiceData.label = obj.value
|
|
591
|
+
}
|
|
592
|
+
element['choice_elements'][obj.value] = choiceData
|
|
593
|
+
})
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
element = removeAttributeTag(element)
|
|
597
|
+
})
|
|
598
|
+
bootstrapData['columns'] = columns.reduce((map, obj) => ((map[obj['name']] = obj), map), {})
|
|
599
|
+
|
|
600
|
+
let elements = bootstrapDatabaseXML.data.element['index']
|
|
601
|
+
elements = isArray(elements) ? elements : [elements]
|
|
602
|
+
elements.forEach((element) => {
|
|
603
|
+
const idxData = IndexSchemaBootstrapAttributes.safeParse(element)
|
|
604
|
+
if (idxData.success) {
|
|
605
|
+
idxElements.push({
|
|
606
|
+
name: idxData.data['@_name'],
|
|
607
|
+
element: idxData.data.element['@_name'],
|
|
608
|
+
unique: idxData.data['@_unique'] ?? false,
|
|
609
|
+
})
|
|
610
|
+
}
|
|
611
|
+
})
|
|
612
|
+
bootstrapData['index'] = idxElements
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
return new XmlData(bootstrapData, xml.filePath, 'bootstrap')
|
|
616
|
+
},
|
|
617
|
+
},
|
|
618
|
+
composers: {
|
|
619
|
+
entity: {
|
|
620
|
+
table(entity, context) {
|
|
621
|
+
const node = entity.getNode()
|
|
622
|
+
const data = entity.getValue()
|
|
623
|
+
const table = TableSchemaFull.parse(data)
|
|
624
|
+
|
|
625
|
+
const composeData = compose(table)
|
|
626
|
+
composeData['columns'] = composeColumns(
|
|
627
|
+
table.schema as Record<string, ColumnSchema>,
|
|
628
|
+
context,
|
|
629
|
+
table.display
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
const entities: LinkedDocument[] = []
|
|
633
|
+
if (table.auto_number) {
|
|
634
|
+
const data = table.auto_number
|
|
635
|
+
|
|
636
|
+
const numberRecord = SysNumber(table.name, data.number_of_digits, data.number, data.prefix)
|
|
637
|
+
|
|
638
|
+
const composedNumber = RecordPlugin.composers.entity.record(
|
|
639
|
+
new EntityData(
|
|
640
|
+
'record',
|
|
641
|
+
context.registerExplicitId(numberRecord.table, numberRecord.$id),
|
|
642
|
+
ObjectData.fromObjectValue(numberRecord, node),
|
|
643
|
+
node
|
|
644
|
+
),
|
|
645
|
+
context
|
|
646
|
+
)
|
|
647
|
+
|
|
648
|
+
entities.push(composedNumber)
|
|
649
|
+
|
|
650
|
+
composeData['number_ref'] = composedNumber.guid
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
composeData['index'] = table.index
|
|
654
|
+
|
|
655
|
+
entities.push({
|
|
656
|
+
kind: 'bootstrap',
|
|
657
|
+
node,
|
|
658
|
+
guid: composeData.name,
|
|
659
|
+
data: { data: { bootstrapData: composeData, dbObjectData: composeData } },
|
|
660
|
+
})
|
|
661
|
+
|
|
662
|
+
return entities
|
|
663
|
+
},
|
|
664
|
+
},
|
|
665
|
+
xml: {
|
|
666
|
+
bootstrap(xml) {
|
|
667
|
+
return {
|
|
668
|
+
kind: xml.kind,
|
|
669
|
+
guid: xml.data['name'] as string,
|
|
670
|
+
data: { data: { bootstrapData: xml.data } },
|
|
671
|
+
}
|
|
672
|
+
},
|
|
673
|
+
sys_db_object(xml) {
|
|
674
|
+
return {
|
|
675
|
+
kind: 'bootstrap',
|
|
676
|
+
guid: xml.data['name'] as string,
|
|
677
|
+
data: { data: { dbObjectData: xml.data } },
|
|
678
|
+
action: xml.action,
|
|
679
|
+
}
|
|
680
|
+
},
|
|
681
|
+
},
|
|
682
|
+
},
|
|
683
|
+
generators: {
|
|
684
|
+
bootstrap(document, context) {
|
|
685
|
+
const parsedBoostrapData = TableSchemaBootstrapIncoming.safeParse(
|
|
686
|
+
(document.data as ComposedTableData).data.bootstrapData
|
|
687
|
+
)
|
|
688
|
+
if (!parsedBoostrapData.success) {
|
|
689
|
+
return undefined
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
return linkDocument(
|
|
693
|
+
document,
|
|
694
|
+
generateCallExpressionExportForDocument(
|
|
695
|
+
context,
|
|
696
|
+
{
|
|
697
|
+
sourceFile: getOrCreateEntitySourceFile(context, parsedBoostrapData.data.name),
|
|
698
|
+
moduleSpecifier: '@servicenow/sdk/core',
|
|
699
|
+
exportName: parsedBoostrapData.data.name,
|
|
700
|
+
},
|
|
701
|
+
Table,
|
|
702
|
+
{}
|
|
703
|
+
).getInitializerIfKindOrThrow(SyntaxKind.CallExpression)
|
|
704
|
+
)
|
|
705
|
+
},
|
|
706
|
+
|
|
707
|
+
record(document, context) {
|
|
708
|
+
if (
|
|
709
|
+
document.data?.['table'] !== 'sys_documentation' &&
|
|
710
|
+
document.data?.['table'] !== 'ua_table_licensing_config'
|
|
711
|
+
) {
|
|
712
|
+
return
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
return linkDocument(
|
|
716
|
+
document,
|
|
717
|
+
generateCallExpression(
|
|
718
|
+
context,
|
|
719
|
+
getOrCreateEntitySourceFile(context, `${document.data?.['data'].name}`),
|
|
720
|
+
'@servicenow/sdk/core',
|
|
721
|
+
NowRecord,
|
|
722
|
+
{ $id: document.guid }
|
|
723
|
+
)
|
|
724
|
+
)
|
|
725
|
+
},
|
|
726
|
+
},
|
|
727
|
+
serializers: {
|
|
728
|
+
bootstrap(document) {
|
|
729
|
+
const composedData = (document.data as ComposedTableData).data
|
|
730
|
+
const { columns, index, attributes, ...bootstrapData } = composedData.bootstrapData
|
|
731
|
+
|
|
732
|
+
if (bootstrapData.live_feed) {
|
|
733
|
+
// live_feed needs to be a table sys_dictionary attribute so it can be synced to sys_db_object by a startup process
|
|
734
|
+
attributes['live_feed'] = true
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
const builder = new XMLJsonBuilder('1.0')
|
|
738
|
+
const databaseObj = builder.createRoot('database', undefined, undefined)
|
|
739
|
+
const composedAttributes = composeAttributes(attributes)
|
|
740
|
+
const collectionObj = databaseObj.addJsonObj(
|
|
741
|
+
'element',
|
|
742
|
+
undefined,
|
|
743
|
+
{ ...bootstrapData, attributes: composedAttributes }!
|
|
744
|
+
)
|
|
745
|
+
for (const column of Object.values(columns)) {
|
|
746
|
+
const { choice_elements, attributes, ...parsedColumn } = column as ColumnSchema
|
|
747
|
+
const columnAttributes = composeAttributes(attributes)
|
|
748
|
+
const elementObj = collectionObj.addJsonObj(
|
|
749
|
+
'element',
|
|
750
|
+
undefined,
|
|
751
|
+
{
|
|
752
|
+
...(parsedColumn as any),
|
|
753
|
+
attributes: columnAttributes,
|
|
754
|
+
}!
|
|
755
|
+
)
|
|
756
|
+
|
|
757
|
+
buildChoices(column as ColumnSchema, elementObj)
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
buildIndexes(index, collectionObj)
|
|
761
|
+
|
|
762
|
+
const bootstrapXmlJson = new XMLBuilder({
|
|
763
|
+
ignoreAttributes: false,
|
|
764
|
+
format: true,
|
|
765
|
+
suppressBooleanAttributes: false,
|
|
766
|
+
suppressEmptyNode: true,
|
|
767
|
+
}).build(builder.buildJsonObj())
|
|
768
|
+
|
|
769
|
+
return {
|
|
770
|
+
name: `${bootstrapData.name}.xml`,
|
|
771
|
+
directory: 'dictionary',
|
|
772
|
+
content: bootstrapXmlJson,
|
|
773
|
+
usedIds: [],
|
|
774
|
+
}
|
|
775
|
+
},
|
|
776
|
+
},
|
|
777
|
+
transformers: {
|
|
778
|
+
bootstrap: {
|
|
779
|
+
CallExpression(document) {
|
|
780
|
+
const expressionName = getCallExpressionName(document.node)
|
|
781
|
+
if (expressionName !== Table.name) {
|
|
782
|
+
return false
|
|
783
|
+
}
|
|
784
|
+
const [args] = document.node.getArguments()
|
|
785
|
+
if (!Node.isObjectLiteralExpression(args)) {
|
|
786
|
+
return false
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
if (document.action === 'DELETE') {
|
|
790
|
+
removeNode(document.node)
|
|
791
|
+
return true
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
if (!document.changedData) {
|
|
795
|
+
return true
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
const composedData = (document.changedData as ComposedTableData).data
|
|
799
|
+
const xmlData = (document.xmlData as ComposedTableData).data
|
|
800
|
+
|
|
801
|
+
let bootstrap_data = {}
|
|
802
|
+
let db_data = {}
|
|
803
|
+
if (!isEmpty(composedData.bootstrapData)) {
|
|
804
|
+
// sys_db_object or node without bootstrap.xml. Mark as handled and ignore
|
|
805
|
+
bootstrap_data = TableSchemaBootstrapIncoming.partial().parse(composedData.bootstrapData)
|
|
806
|
+
}
|
|
807
|
+
if (!isEmpty(composedData.dbObjectData)) {
|
|
808
|
+
db_data = DBObjectIncomingAttributes.partial().parse(composedData.dbObjectData)
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
const {
|
|
812
|
+
auto_number,
|
|
813
|
+
number_ref,
|
|
814
|
+
extends: ext,
|
|
815
|
+
index,
|
|
816
|
+
columns,
|
|
817
|
+
...bootstrap_rest
|
|
818
|
+
} = mapNames(bootstrap_data)
|
|
819
|
+
const { create_access, delete_access, update_access, read_access, caller_access, ...db_rest } =
|
|
820
|
+
mapNames(db_data)
|
|
821
|
+
const rest = { ...bootstrap_rest, ...db_rest }
|
|
822
|
+
|
|
823
|
+
if (ext) {
|
|
824
|
+
rest['extends'] = ext
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
if (caller_access) {
|
|
828
|
+
rest['caller_access'] = callerAccess[caller_access]
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
if (!isEmpty(db_data)) {
|
|
832
|
+
const actions = getActionsArray({ create_access, delete_access, update_access, read_access })
|
|
833
|
+
if (actions.length > 0) {
|
|
834
|
+
writeCustomProperty(args, 'actions', '[]', stringify(actions))
|
|
835
|
+
} else {
|
|
836
|
+
const actionsProp = getOrCreatePropertyAssignment(args, 'actions', '[]')
|
|
837
|
+
actionsProp.remove()
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
if (!rest.attributes) {
|
|
842
|
+
delete rest.attributes
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
if (xmlData.bootstrapData && xmlData.bootstrapData.index) {
|
|
846
|
+
const indexProp = getOrCreatePropertyAssignment(args, 'index', '[]')
|
|
847
|
+
indexProp.setInitializer(stringify(xmlData.bootstrapData.index))
|
|
848
|
+
if (xmlData.bootstrapData.index.length === 0) {
|
|
849
|
+
indexProp.remove()
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
transformFunctionArguments(document.node, Table as any, rest)
|
|
854
|
+
|
|
855
|
+
// Handle columns
|
|
856
|
+
const incomingColumns = xmlData.bootstrapData ? xmlData.bootstrapData.columns : undefined
|
|
857
|
+
|
|
858
|
+
updateDisplayValue(document.node, columns, incomingColumns)
|
|
859
|
+
transformColumns(incomingColumns, args)
|
|
860
|
+
return true
|
|
861
|
+
},
|
|
862
|
+
},
|
|
863
|
+
},
|
|
864
|
+
postProcessors: {
|
|
865
|
+
entities(entities, context) {
|
|
866
|
+
entities
|
|
867
|
+
.filter((entity) => entity.getKind() === 'table')
|
|
868
|
+
.forEach((table) => {
|
|
869
|
+
const tableName = table.getProperty('name')!.getValue() as string
|
|
870
|
+
const originalSourceFilePath = path.normalize(table.getNode().getSourceFile().getFilePath())
|
|
871
|
+
context.compiler.addTableInterfaceToGlobalDeclaration(originalSourceFilePath, tableName)
|
|
872
|
+
})
|
|
873
|
+
},
|
|
874
|
+
},
|
|
875
|
+
})
|
|
876
|
+
|
|
877
|
+
function generateCallExpressionExportForDocument<const A extends unknown[]>(
|
|
878
|
+
context: Context,
|
|
879
|
+
info: {
|
|
880
|
+
sourceFile: SourceFile
|
|
881
|
+
moduleSpecifier: string
|
|
882
|
+
exportName: string
|
|
883
|
+
},
|
|
884
|
+
fn: (...args: A) => unknown,
|
|
885
|
+
...args: PartialElements<A>
|
|
886
|
+
) {
|
|
887
|
+
const { sourceFile, moduleSpecifier } = info
|
|
888
|
+
return generateCallExpressionExport(context, sourceFile, moduleSpecifier, info.exportName, fn, ...args)
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function composeAttributes(attributes: Record<string, any> | undefined) {
|
|
892
|
+
if (!attributes) {
|
|
893
|
+
return ''
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
return Object.entries(attributes)
|
|
897
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
898
|
+
.join(',')
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
function updateDisplayValue(node: CallExpression, columns: ColumnSchema, incomingColumns: ColumnSchema) {
|
|
902
|
+
const display = Object.values(columns ?? {}).filter((col: any) => col.display)
|
|
903
|
+
if (display) {
|
|
904
|
+
const displayName = Object.values(incomingColumns ?? {}).find((c) => c.display)
|
|
905
|
+
if (displayName) {
|
|
906
|
+
transformFunctionArguments(node, Table as any, { display: displayName.name })
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
function composeColumns(columns: Record<string, ColumnSchema>, context: Context, display?: string) {
|
|
912
|
+
const composedColumns = {}
|
|
913
|
+
for (const [columnName, columnData] of Object.entries(columns)) {
|
|
914
|
+
const column = ColumnSchema.parse(columnData)
|
|
915
|
+
const requiresDropdown = column.dynamic_value_definitions || column.choices
|
|
916
|
+
const element = {
|
|
917
|
+
name: columnName,
|
|
918
|
+
type: column.entityKind === 'generic' ? column.column_type : column.entityKind,
|
|
919
|
+
attributes: column.attributes ?? undefined,
|
|
920
|
+
reference: column.referenceTable,
|
|
921
|
+
audit: column.audit,
|
|
922
|
+
label: column.label ?? columnName,
|
|
923
|
+
max_length: column.maxLength,
|
|
924
|
+
mandatory: column.mandatory,
|
|
925
|
+
read_only: column.read_only,
|
|
926
|
+
choice: choiceDropdown.indexOf(column.dropdown ?? requiresDropdown ? 'dropdown_with_none' : 'none'),
|
|
927
|
+
display: display ? display === columnName : false,
|
|
928
|
+
|
|
929
|
+
// dynamic values
|
|
930
|
+
virtual: column.dynamic_value_definitions?.type === 'calculated_value' ? true : undefined,
|
|
931
|
+
calculation:
|
|
932
|
+
column.dynamic_value_definitions?.type === 'calculated_value'
|
|
933
|
+
? getScriptInfo(column.dynamic_value_definitions!['calculated_value'], context)
|
|
934
|
+
: undefined,
|
|
935
|
+
choice_table:
|
|
936
|
+
column.dynamic_value_definitions?.type === 'choices_from_other_table'
|
|
937
|
+
? column.dynamic_value_definitions.table
|
|
938
|
+
: undefined,
|
|
939
|
+
choice_field:
|
|
940
|
+
column.dynamic_value_definitions?.type === 'choices_from_other_table'
|
|
941
|
+
? column.dynamic_value_definitions.field
|
|
942
|
+
: undefined,
|
|
943
|
+
|
|
944
|
+
use_dependent_field: column.dynamic_value_definitions?.type === 'dependent_field' || undefined,
|
|
945
|
+
|
|
946
|
+
dependent:
|
|
947
|
+
column.dynamic_value_definitions?.type === 'dependent_field'
|
|
948
|
+
? column.dynamic_value_definitions!['column_name']
|
|
949
|
+
: undefined,
|
|
950
|
+
|
|
951
|
+
use_dynamic_default: column.dynamic_value_definitions?.type === 'dynamic_default' || undefined,
|
|
952
|
+
|
|
953
|
+
default_value:
|
|
954
|
+
column.dynamic_value_definitions?.type === 'dynamic_default'
|
|
955
|
+
? getDynamicDefault(column.dynamic_value_definitions?.dynamic_default)
|
|
956
|
+
: undefined,
|
|
957
|
+
|
|
958
|
+
default: column.default,
|
|
959
|
+
|
|
960
|
+
function_definition: column.function_definition,
|
|
961
|
+
function_field: column.function_definition ? true : false,
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
if (column.choices) {
|
|
965
|
+
Object.keys(column.choices).forEach((key) => {
|
|
966
|
+
if (isObject(column.choices![key])) {
|
|
967
|
+
column.choices![key] = { value: key, ...column.choices![key] }
|
|
968
|
+
} else {
|
|
969
|
+
column.choices![key] = { value: key, label: column.choices![key] }
|
|
970
|
+
}
|
|
971
|
+
})
|
|
972
|
+
element['choice_elements'] = z.record(ChoiceSchema).parse(column.choices)
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
composedColumns[columnName] = element
|
|
976
|
+
}
|
|
977
|
+
return composedColumns
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
function compose(bootstrapData: z.infer<typeof TableSchemaBootstrap>) {
|
|
981
|
+
if (bootstrapData.live_feed) {
|
|
982
|
+
bootstrapData.attributes = {
|
|
983
|
+
// live_feed needs to be a table sys_dictionary attribute so it can be synced to sys_db_object by a startup process
|
|
984
|
+
...bootstrapData.attributes,
|
|
985
|
+
live_feed: true,
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
const composedData = {
|
|
990
|
+
// Generic data
|
|
991
|
+
name: bootstrapData.name,
|
|
992
|
+
type: 'collection',
|
|
993
|
+
extends: bootstrapData.extends,
|
|
994
|
+
label: bootstrapData.label ?? bootstrapData.name,
|
|
995
|
+
|
|
996
|
+
// Controls
|
|
997
|
+
is_extendable: bootstrapData.extensible,
|
|
998
|
+
text_index: bootstrapData.text_index,
|
|
999
|
+
read_only: bootstrapData.read_only,
|
|
1000
|
+
audit: bootstrapData.audit,
|
|
1001
|
+
|
|
1002
|
+
access: bootstrapData.accessible_from,
|
|
1003
|
+
caller_access: callerAccess.indexOf(bootstrapData.caller_access ?? 'none'),
|
|
1004
|
+
|
|
1005
|
+
read_access: bootstrapData.actions?.includes('read'),
|
|
1006
|
+
update_access: bootstrapData.actions?.includes('update'),
|
|
1007
|
+
create_access: bootstrapData.actions?.includes('create'),
|
|
1008
|
+
delete_access: bootstrapData.actions?.includes('delete'),
|
|
1009
|
+
|
|
1010
|
+
ws_access: bootstrapData.allow_web_service_access,
|
|
1011
|
+
alter_access: bootstrapData.allow_new_fields,
|
|
1012
|
+
actions_access: bootstrapData.allow_ui_actions,
|
|
1013
|
+
client_scripts_access: bootstrapData.allow_client_scripts,
|
|
1014
|
+
|
|
1015
|
+
attributes: bootstrapData.attributes,
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
return composedData
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
// API properties that are different in the bootstrap/sys_db_object file
|
|
1022
|
+
const property_name_mapping = {
|
|
1023
|
+
client_scripts_access: 'allow_client_scripts',
|
|
1024
|
+
alter_access: 'allow_new_fields',
|
|
1025
|
+
actions_access: 'allow_ui_actions',
|
|
1026
|
+
ws_access: 'allow_web_service_access',
|
|
1027
|
+
is_extendable: 'extensible',
|
|
1028
|
+
access: 'accessible_from',
|
|
1029
|
+
live_feed_enabled: 'live_feed',
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
function mapNames(schema: any) {
|
|
1033
|
+
Object.keys(schema).forEach((key) => {
|
|
1034
|
+
if (property_name_mapping[key]) {
|
|
1035
|
+
schema[property_name_mapping[key]] = schema[key]
|
|
1036
|
+
delete schema[key]
|
|
1037
|
+
}
|
|
1038
|
+
})
|
|
1039
|
+
return schema
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
let schemaPosition = 4
|
|
1043
|
+
|
|
1044
|
+
function transformColumns(incoming_schema: ColumnSchema, args: ObjectLiteralExpression) {
|
|
1045
|
+
const schemaProperty = getOrCreatePropertyAssignment(args, 'schema', '{}')
|
|
1046
|
+
const schemaLiteral = schemaProperty.getChildrenOfKind(ts.SyntaxKind.ObjectLiteralExpression)[0]
|
|
1047
|
+
if (!schemaLiteral) {
|
|
1048
|
+
return
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
const keys = Object.keys(incoming_schema ?? {})
|
|
1052
|
+
if (keys.length === 0) {
|
|
1053
|
+
return
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
//TODO:: need to come up with a way to format script more accurately
|
|
1057
|
+
schemaPosition = schemaLiteral.getSourceFile().getLineAndColumnAtPos(schemaLiteral.getStart()).column
|
|
1058
|
+
const assignments: ts.PropertyAssignment[] = []
|
|
1059
|
+
keys.forEach((key) => {
|
|
1060
|
+
const data = incoming_schema[key]
|
|
1061
|
+
const callExpFromType = getCallExpressionFromType(data.type)
|
|
1062
|
+
const transformedColumn = transformColumnData(data, callExpFromType)
|
|
1063
|
+
const callExp = generateColumnExpression(callExpFromType, {
|
|
1064
|
+
...transformedColumn,
|
|
1065
|
+
choices: data.choice_elements,
|
|
1066
|
+
})
|
|
1067
|
+
assignments.push(ts.factory.createPropertyAssignment(createPropertyIdentifier(data.name), callExp))
|
|
1068
|
+
})
|
|
1069
|
+
|
|
1070
|
+
schemaLiteral.transform((traversal) => {
|
|
1071
|
+
if (ts.isObjectLiteralExpression(traversal.currentNode)) {
|
|
1072
|
+
return traversal.factory.updateObjectLiteralExpression(traversal.currentNode, assignments)
|
|
1073
|
+
}
|
|
1074
|
+
return traversal.currentNode
|
|
1075
|
+
})
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
type DB_ACTIONS = 'read' | 'delete' | 'create' | 'update'
|
|
1079
|
+
|
|
1080
|
+
function getActionsArray(data) {
|
|
1081
|
+
const actions: DB_ACTIONS[] = []
|
|
1082
|
+
if (data.read_access) {
|
|
1083
|
+
actions.push('read')
|
|
1084
|
+
}
|
|
1085
|
+
if (data.update_access) {
|
|
1086
|
+
actions.push('update')
|
|
1087
|
+
}
|
|
1088
|
+
if (data.delete_access) {
|
|
1089
|
+
actions.push('delete')
|
|
1090
|
+
}
|
|
1091
|
+
if (data.create_access) {
|
|
1092
|
+
actions.push('create')
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
return actions
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
function getDynamicDefault(val: string) {
|
|
1099
|
+
return val.startsWith('javascript:') ? val : `javascript:${dynamic_value_mapping[val]}`
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
function buildIndexes(indexes: IndexType[] = [], collection: XMLJsonElement) {
|
|
1103
|
+
indexes.forEach((index) => {
|
|
1104
|
+
const indexElement = collection.addJsonObj('index', undefined, { name: index.name, unique: index.unique })
|
|
1105
|
+
indexElement.addJsonObj('element', undefined, { name: index.element })
|
|
1106
|
+
})
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
function buildChoices(column: ColumnSchema, element: XMLJsonElement) {
|
|
1110
|
+
if (!column.choice_elements) {
|
|
1111
|
+
return
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
const choices = element.addJsonObj('choice', undefined, undefined)
|
|
1115
|
+
if (Array.isArray(column.choices) && column.choices.length > 0) {
|
|
1116
|
+
// Formatted - choices: [1, 2, 3, 4]
|
|
1117
|
+
for (const choice in column.choices) {
|
|
1118
|
+
choices.addJsonObj('element', undefined, {
|
|
1119
|
+
value: column.choices[choice],
|
|
1120
|
+
label: column.choices[choice],
|
|
1121
|
+
})
|
|
1122
|
+
}
|
|
1123
|
+
} else {
|
|
1124
|
+
// Formatted - choices: { 1: { label: 'label' }, 2: { lable: label 2}, ...}
|
|
1125
|
+
Object.entries(column.choice_elements).forEach((value) => {
|
|
1126
|
+
if (isObject(value[1])) {
|
|
1127
|
+
choices.addJsonObj('element', undefined, {
|
|
1128
|
+
value: value[0],
|
|
1129
|
+
...(value[1] as ChoiceConfig),
|
|
1130
|
+
})
|
|
1131
|
+
} else {
|
|
1132
|
+
choices.addJsonObj('element', undefined, {
|
|
1133
|
+
value: value[1],
|
|
1134
|
+
label: value[0],
|
|
1135
|
+
})
|
|
1136
|
+
}
|
|
1137
|
+
})
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
export function transformColumnData(col: any, callExpFromType: any) {
|
|
1142
|
+
const {
|
|
1143
|
+
type: internal_type,
|
|
1144
|
+
choice,
|
|
1145
|
+
name,
|
|
1146
|
+
virtual,
|
|
1147
|
+
display,
|
|
1148
|
+
choice_table,
|
|
1149
|
+
choice_field,
|
|
1150
|
+
use_dependent_field,
|
|
1151
|
+
dependent,
|
|
1152
|
+
calculation,
|
|
1153
|
+
use_dynamic_default,
|
|
1154
|
+
default_value,
|
|
1155
|
+
choice_elements,
|
|
1156
|
+
table,
|
|
1157
|
+
default: def,
|
|
1158
|
+
max_length,
|
|
1159
|
+
reference,
|
|
1160
|
+
function_field,
|
|
1161
|
+
...column
|
|
1162
|
+
} = col
|
|
1163
|
+
|
|
1164
|
+
if (max_length) {
|
|
1165
|
+
column.maxLength = max_length
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
if (internal_type && (callExpFromType.entityKind === 'generic' || callExpFromType.name === 'GenericColumn')) {
|
|
1169
|
+
column.column_type = internal_type
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
if (choice !== undefined) {
|
|
1173
|
+
column.dropdown = choiceDropdown[choice]
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
if (reference) {
|
|
1177
|
+
column.referenceTable = reference
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
// Handle dynamic values
|
|
1181
|
+
if (calculation) {
|
|
1182
|
+
column.dynamic_value_definitions = {
|
|
1183
|
+
type: 'calculated_value',
|
|
1184
|
+
calculated_value: calculation,
|
|
1185
|
+
}
|
|
1186
|
+
} else if (choice_table || choice_field) {
|
|
1187
|
+
column.dynamic_value_definitions = {
|
|
1188
|
+
type: 'choices_from_other_table',
|
|
1189
|
+
table: choice_table,
|
|
1190
|
+
field: choice_field,
|
|
1191
|
+
}
|
|
1192
|
+
} else if (dependent) {
|
|
1193
|
+
column.dynamic_value_definitions = {
|
|
1194
|
+
type: 'dependent_field',
|
|
1195
|
+
column_name: dependent,
|
|
1196
|
+
}
|
|
1197
|
+
} else if (def) {
|
|
1198
|
+
column.default = def
|
|
1199
|
+
} else if (default_value) {
|
|
1200
|
+
column.dynamic_value_definitions = {
|
|
1201
|
+
type: 'dynamic_default',
|
|
1202
|
+
dynamic_default: def,
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
if (column.label && column.label === name) {
|
|
1207
|
+
delete column.label
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
if (isEmpty(column.attributes)) {
|
|
1211
|
+
delete column.attributes
|
|
1212
|
+
} else if (isDefaultEdgeEncryptionAttribute(column.attributes)) {
|
|
1213
|
+
delete column.attributes
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
return column
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
// The instance will sometimes add edge_encryption_enabled=true, this is a default so don't write it if it is the only one
|
|
1220
|
+
function isDefaultEdgeEncryptionAttribute(attributes: any) {
|
|
1221
|
+
return (
|
|
1222
|
+
attributes &&
|
|
1223
|
+
Object.entries(attributes).length === 1 &&
|
|
1224
|
+
attributes.edge_encryption_enabled !== undefined &&
|
|
1225
|
+
attributes.edge_encryption_enabled
|
|
1226
|
+
)
|
|
1227
|
+
}
|
|
1228
|
+
|
|
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
|
+
function removeAttributeTag(obj: any) {
|
|
1245
|
+
Object.entries(obj).forEach(([key, value]) => {
|
|
1246
|
+
if (key.startsWith('@_')) {
|
|
1247
|
+
obj[key.substring(2)] = value
|
|
1248
|
+
delete obj[key]
|
|
1249
|
+
}
|
|
1250
|
+
})
|
|
1251
|
+
return obj
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
export function SysNumber(
|
|
1255
|
+
category: TableName,
|
|
1256
|
+
maximum_digits: number | undefined,
|
|
1257
|
+
number?: number | undefined,
|
|
1258
|
+
prefix?: string | undefined
|
|
1259
|
+
) {
|
|
1260
|
+
return NowRecord({
|
|
1261
|
+
table: 'sys_number',
|
|
1262
|
+
$id: `${category as string}_${prefix}`,
|
|
1263
|
+
data: {
|
|
1264
|
+
category,
|
|
1265
|
+
maximum_digits: maximum_digits ?? 1000,
|
|
1266
|
+
number: number ?? 7,
|
|
1267
|
+
prefix: prefix ?? 'PRE',
|
|
1268
|
+
},
|
|
1269
|
+
})
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
// TODO should this reference sys_filter_option_dynamic instead?
|
|
1273
|
+
export const dynamic_value_mapping = {
|
|
1274
|
+
'Current Name': `current.name`,
|
|
1275
|
+
'Next ECC Sequence Number': `GlideCounter.next('ecc:sequence');`,
|
|
1276
|
+
'Get Module View': `var viewDefault='';if(current.device_type=='mobile') viewDefault='Mobile';else if(!current.application.nil() && current.application.device_type=='mobile' && current.device_type.nil()) viewDefault='Mobile';viewDefault`,
|
|
1277
|
+
'Get Label Display Value': `gs.getDisplayValueFor(current.table, current.table_key, '')`,
|
|
1278
|
+
'Get CMDB Item Category': `gs.include("CMDBItem");var item = new CMDBItem(current);item.setCategory();`,
|
|
1279
|
+
'Parent Record Matcher Table': `if (typeof parent != "undefined") parent.matcher_table`,
|
|
1280
|
+
'Next Cache Flush Number': `GlideCounter.next('sys_cache_flush');`,
|
|
1281
|
+
'Get Next Number': `global.getNextObjNumber()`,
|
|
1282
|
+
'Next Workflow Log Order Number': `GlideCounter.next('wf_log:order');`,
|
|
1283
|
+
Subject: 'email.subject',
|
|
1284
|
+
'Get Mobile Module View': `var viewDefault='';if(typeof parent != 'undefined')if(parent.device_type=='mobile') viewDefault='Mobile';viewDefault`,
|
|
1285
|
+
'Parent Record Model Table': `if (typeof parent != "undefined") parent.model_table`,
|
|
1286
|
+
'Get Label Table Display Value': `gs.getDisplayValueFor(current.table, current.table_key, 'sys_class_name')`,
|
|
1287
|
+
'Parent Record Source Table': `if (typeof parent != "undefined") parent.source_table`,
|
|
1288
|
+
'Get Next Padded Number': `global.getNextObjNumberPadded();`,
|
|
1289
|
+
'My First Name': `gs.getUser().firstName`,
|
|
1290
|
+
'My Last Name': `gs.getUser().lastName`,
|
|
1291
|
+
'My Job Title': `gs.getUser().title`,
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
export function generateColumnExpression<Type extends (...args: any) => any>(
|
|
1295
|
+
expression: Type,
|
|
1296
|
+
args: Parameters<Type>[0],
|
|
1297
|
+
argsSetIfDefined?: Undefined<Parameters<Type>[0]>
|
|
1298
|
+
): ts.CallExpression {
|
|
1299
|
+
const setArgs = { ...args }
|
|
1300
|
+
if (argsSetIfDefined) {
|
|
1301
|
+
Object.keys(argsSetIfDefined).forEach((key) => {
|
|
1302
|
+
if (argsSetIfDefined[key] !== undefined) {
|
|
1303
|
+
setArgs[key] = argsSetIfDefined[key]
|
|
1304
|
+
}
|
|
1305
|
+
})
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
return ts.factory.createCallExpression(ts.factory.createIdentifier(expression.name), undefined, [
|
|
1309
|
+
createObjectExpression(setArgs),
|
|
1310
|
+
])
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
export function createObjectExpression(firstArg: object): ts.Expression {
|
|
1314
|
+
return ts.factory.createObjectLiteralExpression(
|
|
1315
|
+
Object.entries(firstArg)
|
|
1316
|
+
.filter(([_key, value]) => value !== undefined)
|
|
1317
|
+
.map(([key, value]) => {
|
|
1318
|
+
if (key === 'referenceTable') {
|
|
1319
|
+
return ts.factory.createPropertyAssignment(
|
|
1320
|
+
ts.factory.createIdentifier('referenceTable'),
|
|
1321
|
+
ts.factory.createStringLiteral(value)
|
|
1322
|
+
)
|
|
1323
|
+
} else if (key === 'calculated_value') {
|
|
1324
|
+
const calVal = `${os.EOL}${formatScript(value, schemaPosition)}`
|
|
1325
|
+
return ts.factory.createPropertyAssignment(
|
|
1326
|
+
ts.factory.createIdentifier(key),
|
|
1327
|
+
ts.factory.createTaggedTemplateExpression(
|
|
1328
|
+
ts.factory.createIdentifier('script'),
|
|
1329
|
+
undefined,
|
|
1330
|
+
ts.factory.createNoSubstitutionTemplateLiteral(calVal, calVal)
|
|
1331
|
+
)
|
|
1332
|
+
)
|
|
1333
|
+
}
|
|
1334
|
+
return ts.factory.createPropertyAssignment(
|
|
1335
|
+
createPropertyIdentifier(key),
|
|
1336
|
+
factoryCreateValue(value, ts.factory)
|
|
1337
|
+
)
|
|
1338
|
+
})
|
|
1339
|
+
)
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
export function factoryCreateValue(value: unknown, factory: ts.NodeFactory) {
|
|
1343
|
+
if (typeof value === 'string') {
|
|
1344
|
+
return factory.createStringLiteral(value)
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
if (typeof value === 'number') {
|
|
1348
|
+
if (value >= 0) {
|
|
1349
|
+
return factory.createNumericLiteral(value)
|
|
1350
|
+
} else {
|
|
1351
|
+
return factory.createPrefixUnaryExpression(
|
|
1352
|
+
ts.SyntaxKind.MinusToken,
|
|
1353
|
+
factory.createNumericLiteral(Math.abs(value))
|
|
1354
|
+
)
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
if (typeof value === 'boolean') {
|
|
1359
|
+
return value ? factory.createTrue() : factory.createFalse()
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
if (typeof value === 'object') {
|
|
1363
|
+
if (value === null) {
|
|
1364
|
+
return factory.createNull()
|
|
1365
|
+
}
|
|
1366
|
+
if (isArray(value)) {
|
|
1367
|
+
const newvals: ts.Expression[] = []
|
|
1368
|
+
|
|
1369
|
+
Object.values(value).forEach((v) => {
|
|
1370
|
+
newvals.push(factoryCreateValue(v, factory))
|
|
1371
|
+
})
|
|
1372
|
+
|
|
1373
|
+
return factory.createArrayLiteralExpression(newvals)
|
|
1374
|
+
} else if (Structure.isImportSpecifier(value)) {
|
|
1375
|
+
return factory.createIdentifier(value['name'])
|
|
1376
|
+
} else {
|
|
1377
|
+
return createObjectExpression(value)
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
return factory.createNull()
|
|
1382
|
+
|
|
1383
|
+
// throw new Error(`Unsupported value type: ${typeof value}`)
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
type Undefined<T> = {
|
|
1387
|
+
[P in keyof T]: T[P] | undefined
|
|
1388
|
+
}
|
|
1389
|
+
export function getCallExpressionFromType(internal_type: string) {
|
|
1390
|
+
switch (internal_type) {
|
|
1391
|
+
case 'boolean':
|
|
1392
|
+
return BooleanColumn
|
|
1393
|
+
case 'choice':
|
|
1394
|
+
return ChoiceColumn
|
|
1395
|
+
case 'conditions':
|
|
1396
|
+
return ConditionsColumn
|
|
1397
|
+
case 'date':
|
|
1398
|
+
return OtherDateColumn
|
|
1399
|
+
case 'calendar_date_time':
|
|
1400
|
+
return CalendarDateTimeColumn
|
|
1401
|
+
case 'datetime':
|
|
1402
|
+
return BasicDateTimeColumn
|
|
1403
|
+
case 'due_date':
|
|
1404
|
+
return DueDateColumn
|
|
1405
|
+
case 'glide_date':
|
|
1406
|
+
return DateColumn
|
|
1407
|
+
case 'glide_date_time':
|
|
1408
|
+
return DateTimeColumn
|
|
1409
|
+
case 'integer_date':
|
|
1410
|
+
return IntegerDateColumn
|
|
1411
|
+
case 'schedule_date_time':
|
|
1412
|
+
return ScheduleDateTimeColumn
|
|
1413
|
+
case 'decimal':
|
|
1414
|
+
return DecimalColumn
|
|
1415
|
+
case 'document_id':
|
|
1416
|
+
return DocumentIdColumn
|
|
1417
|
+
case 'domain_id':
|
|
1418
|
+
return DomainIdColumn
|
|
1419
|
+
case 'domain_path':
|
|
1420
|
+
return DomainPathColumn
|
|
1421
|
+
case 'field_name':
|
|
1422
|
+
return FieldNameColumn
|
|
1423
|
+
case 'integer':
|
|
1424
|
+
return IntegerColumn
|
|
1425
|
+
case 'radio':
|
|
1426
|
+
return RadioColumn
|
|
1427
|
+
case 'reference':
|
|
1428
|
+
return ReferenceColumn
|
|
1429
|
+
case 'script':
|
|
1430
|
+
return ScriptColumn
|
|
1431
|
+
case 'sys_class_name':
|
|
1432
|
+
return SystemClassNameColumn
|
|
1433
|
+
case 'table_name':
|
|
1434
|
+
return TableNameColumn
|
|
1435
|
+
case 'translated_field':
|
|
1436
|
+
return TranslatedFieldColumn
|
|
1437
|
+
case 'translated_text':
|
|
1438
|
+
return TranslatedTextColumn
|
|
1439
|
+
case 'user_roles':
|
|
1440
|
+
return UserRolesColumn
|
|
1441
|
+
case 'version':
|
|
1442
|
+
return VersionColumn
|
|
1443
|
+
case 'string':
|
|
1444
|
+
return StringColumn
|
|
1445
|
+
case 'int':
|
|
1446
|
+
case 'string_types':
|
|
1447
|
+
case 'approval_rules':
|
|
1448
|
+
case 'audio':
|
|
1449
|
+
case 'auto_increment':
|
|
1450
|
+
case 'auto_number':
|
|
1451
|
+
case 'bootstrap_color':
|
|
1452
|
+
case 'breakdown_element':
|
|
1453
|
+
case 'catalog_preview':
|
|
1454
|
+
case 'char':
|
|
1455
|
+
case 'collection':
|
|
1456
|
+
case 'color':
|
|
1457
|
+
case 'color_display':
|
|
1458
|
+
case 'composite_field':
|
|
1459
|
+
case 'composite_name':
|
|
1460
|
+
case 'compressed':
|
|
1461
|
+
case 'condition_string':
|
|
1462
|
+
case 'counter':
|
|
1463
|
+
case 'css':
|
|
1464
|
+
case 'currency':
|
|
1465
|
+
case 'currency2':
|
|
1466
|
+
case 'data_array':
|
|
1467
|
+
case 'data_object':
|
|
1468
|
+
case 'data_structure':
|
|
1469
|
+
case 'days_of_week':
|
|
1470
|
+
case 'day_of_week':
|
|
1471
|
+
case 'documentation_field':
|
|
1472
|
+
case 'dynamic_json':
|
|
1473
|
+
case 'email':
|
|
1474
|
+
case 'email_script':
|
|
1475
|
+
case 'expression':
|
|
1476
|
+
case 'external_names':
|
|
1477
|
+
case 'field_list':
|
|
1478
|
+
case 'file_attachment':
|
|
1479
|
+
case 'float':
|
|
1480
|
+
case 'formula':
|
|
1481
|
+
case 'geo_point':
|
|
1482
|
+
case 'glide_action_list':
|
|
1483
|
+
case 'glide_duration':
|
|
1484
|
+
case 'glide_list':
|
|
1485
|
+
case 'glide_precise_time':
|
|
1486
|
+
case 'glide_time':
|
|
1487
|
+
case 'glide_utc_time':
|
|
1488
|
+
case 'glide_var':
|
|
1489
|
+
case 'glyphicon':
|
|
1490
|
+
case 'graphql_schema':
|
|
1491
|
+
case 'GUID':
|
|
1492
|
+
case 'html':
|
|
1493
|
+
case 'html_script':
|
|
1494
|
+
case 'html_template':
|
|
1495
|
+
case 'icon':
|
|
1496
|
+
case 'image':
|
|
1497
|
+
case 'index_name':
|
|
1498
|
+
case 'insert_timestamp':
|
|
1499
|
+
case 'integer_time':
|
|
1500
|
+
case 'internal_type':
|
|
1501
|
+
case 'ip_addr':
|
|
1502
|
+
case 'ip_address':
|
|
1503
|
+
case 'journal':
|
|
1504
|
+
case 'journal_input':
|
|
1505
|
+
case 'journal_list':
|
|
1506
|
+
case 'json':
|
|
1507
|
+
case 'json_translations':
|
|
1508
|
+
case 'language':
|
|
1509
|
+
case 'long':
|
|
1510
|
+
case 'longint':
|
|
1511
|
+
case 'mask_code':
|
|
1512
|
+
case 'metric_absolute':
|
|
1513
|
+
case 'metric_counter':
|
|
1514
|
+
case 'metric_derive':
|
|
1515
|
+
case 'metric_gauge':
|
|
1516
|
+
case 'mid_config':
|
|
1517
|
+
case 'month_of_year':
|
|
1518
|
+
case 'multi_small':
|
|
1519
|
+
case 'multi_two_lines':
|
|
1520
|
+
case 'name_values':
|
|
1521
|
+
case 'nds_icon':
|
|
1522
|
+
case 'nl_task_int1':
|
|
1523
|
+
case 'order_index':
|
|
1524
|
+
case 'password':
|
|
1525
|
+
case 'password2':
|
|
1526
|
+
case 'percent_complete':
|
|
1527
|
+
case 'phone_number':
|
|
1528
|
+
case 'phone_number_e164':
|
|
1529
|
+
case 'ph_number':
|
|
1530
|
+
case 'price':
|
|
1531
|
+
case 'properties':
|
|
1532
|
+
case 'records':
|
|
1533
|
+
case 'reference_name':
|
|
1534
|
+
case 'related_tags':
|
|
1535
|
+
case 'reminder_field_name':
|
|
1536
|
+
case 'repeat_count':
|
|
1537
|
+
case 'repeat_type':
|
|
1538
|
+
case 'replication_payload':
|
|
1539
|
+
case 'schedule_interval_count':
|
|
1540
|
+
case 'script_client':
|
|
1541
|
+
case 'script_plain':
|
|
1542
|
+
case 'script_server':
|
|
1543
|
+
case 'short_field_name':
|
|
1544
|
+
case 'short_table_name':
|
|
1545
|
+
case 'simple_name_values':
|
|
1546
|
+
case 'slushbucket':
|
|
1547
|
+
case 'snapshot_template_value':
|
|
1548
|
+
case 'source_id':
|
|
1549
|
+
case 'source_name':
|
|
1550
|
+
case 'source_table':
|
|
1551
|
+
case 'string_boolean':
|
|
1552
|
+
case 'string_full_utf8':
|
|
1553
|
+
case 'structure':
|
|
1554
|
+
case 'sysevent_name':
|
|
1555
|
+
case 'sysrule_field_name':
|
|
1556
|
+
case 'sys_class_path':
|
|
1557
|
+
case 'template_value':
|
|
1558
|
+
case 'time':
|
|
1559
|
+
case 'timer':
|
|
1560
|
+
case 'translated':
|
|
1561
|
+
case 'translated_html':
|
|
1562
|
+
case 'tree_code':
|
|
1563
|
+
case 'tree_path':
|
|
1564
|
+
case 'url':
|
|
1565
|
+
case 'user_image':
|
|
1566
|
+
case 'user_input':
|
|
1567
|
+
case 'variables':
|
|
1568
|
+
case 'variable_conditions':
|
|
1569
|
+
case 'variable_template_value':
|
|
1570
|
+
case 'video':
|
|
1571
|
+
case 'week_of_month':
|
|
1572
|
+
case 'wide_text':
|
|
1573
|
+
case 'wiki_text':
|
|
1574
|
+
case 'wms_job':
|
|
1575
|
+
case 'workflow':
|
|
1576
|
+
case 'workflow_conditions':
|
|
1577
|
+
case 'xml':
|
|
1578
|
+
default:
|
|
1579
|
+
return GenericColumn
|
|
1580
|
+
}
|
|
1581
|
+
}
|