@servicenow/sdk-build-plugins 2.0.1 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/dist/AttachmentPlugin.d.ts +8 -6
  2. package/dist/AttachmentPlugin.js +4 -2
  3. package/dist/AttachmentPlugin.js.map +1 -1
  4. package/dist/BusinessRulePlugin.d.ts +5 -31
  5. package/dist/BusinessRulePlugin.js +45 -72
  6. package/dist/BusinessRulePlugin.js.map +1 -1
  7. package/dist/CrossScopePrivilegePlugin.d.ts +14 -1
  8. package/dist/CrossScopePrivilegePlugin.js +36 -1
  9. package/dist/CrossScopePrivilegePlugin.js.map +1 -1
  10. package/dist/DefaultPlugin.d.ts +60 -57
  11. package/dist/DefaultPlugin.js +151 -146
  12. package/dist/DefaultPlugin.js.map +1 -1
  13. package/dist/HtmlTemplatePlugin.d.ts +21 -0
  14. package/dist/HtmlTemplatePlugin.js +52 -0
  15. package/dist/HtmlTemplatePlugin.js.map +1 -0
  16. package/dist/IdPlugin.d.ts +18 -8
  17. package/dist/IdPlugin.js +55 -28
  18. package/dist/IdPlugin.js.map +1 -1
  19. package/dist/JsonPlugin.d.ts +23 -0
  20. package/dist/JsonPlugin.js +74 -0
  21. package/dist/JsonPlugin.js.map +1 -0
  22. package/dist/ListPlugin.d.ts +7 -13
  23. package/dist/ListPlugin.js +32 -15
  24. package/dist/ListPlugin.js.map +1 -1
  25. package/dist/NowConfigPlugin.d.ts +45 -0
  26. package/dist/NowConfigPlugin.js +90 -0
  27. package/dist/NowConfigPlugin.js.map +1 -0
  28. package/dist/PackageJsonPlugin.d.ts +20 -0
  29. package/dist/PackageJsonPlugin.js +48 -0
  30. package/dist/PackageJsonPlugin.js.map +1 -0
  31. package/dist/PropertyPlugin.d.ts +9 -35
  32. package/dist/PropertyPlugin.js +53 -32
  33. package/dist/PropertyPlugin.js.map +1 -1
  34. package/dist/ScriptTemplatePlugin.d.ts +3 -20
  35. package/dist/ScriptTemplatePlugin.js +20 -138
  36. package/dist/ScriptTemplatePlugin.js.map +1 -1
  37. package/dist/ServerModulePlugin.d.ts +62 -0
  38. package/dist/ServerModulePlugin.js +231 -0
  39. package/dist/ServerModulePlugin.js.map +1 -0
  40. package/dist/UserPreferencePlugin.d.ts +4 -1
  41. package/dist/UserPreferencePlugin.js +12 -10
  42. package/dist/UserPreferencePlugin.js.map +1 -1
  43. package/dist/aclAndRole/AclPlugin.d.ts +41 -79
  44. package/dist/aclAndRole/AclPlugin.js +50 -72
  45. package/dist/aclAndRole/AclPlugin.js.map +1 -1
  46. package/dist/aclAndRole/RolePlugin.d.ts +27 -18
  47. package/dist/aclAndRole/RolePlugin.js +66 -41
  48. package/dist/aclAndRole/RolePlugin.js.map +1 -1
  49. package/dist/app/ApplicationMenuPlugin.d.ts +4 -2
  50. package/dist/app/ApplicationMenuPlugin.js +12 -7
  51. package/dist/app/ApplicationMenuPlugin.js.map +1 -1
  52. package/dist/atf/ATFComposer.js +324 -324
  53. package/dist/atf/TestPlugin.d.ts +12 -10
  54. package/dist/atf/TestPlugin.js +14 -12
  55. package/dist/atf/TestPlugin.js.map +1 -1
  56. package/dist/db/ColumnPlugins.d.ts +559 -97
  57. package/dist/db/ColumnPlugins.js +28 -24
  58. package/dist/db/ColumnPlugins.js.map +1 -1
  59. package/dist/db/DBUtils.d.ts +2 -0
  60. package/dist/db/DBUtils.js +27 -0
  61. package/dist/db/DBUtils.js.map +1 -0
  62. package/dist/db/DocumentationPlugin.d.ts +58 -0
  63. package/dist/db/DocumentationPlugin.js +248 -0
  64. package/dist/db/DocumentationPlugin.js.map +1 -0
  65. package/dist/db/RecordPlugin.d.ts +15 -13
  66. package/dist/db/RecordPlugin.js +74 -79
  67. package/dist/db/RecordPlugin.js.map +1 -1
  68. package/dist/db/TablePlugin.d.ts +38 -105
  69. package/dist/db/TablePlugin.js +205 -111
  70. package/dist/db/TablePlugin.js.map +1 -1
  71. package/dist/db/index.d.ts +2 -0
  72. package/dist/db/index.js +4 -1
  73. package/dist/db/index.js.map +1 -1
  74. package/dist/index.d.ts +7 -2
  75. package/dist/index.js +12 -2
  76. package/dist/index.js.map +1 -1
  77. package/dist/scriptedRESTAPI/RESTDeserializationUtils.js +8 -6
  78. package/dist/scriptedRESTAPI/RESTDeserializationUtils.js.map +1 -1
  79. package/dist/scriptedRESTAPI/RESTSerializationUtils.js +7 -6
  80. package/dist/scriptedRESTAPI/RESTSerializationUtils.js.map +1 -1
  81. package/dist/scriptedRESTAPI/RestApiPlugin.d.ts +74 -85
  82. package/dist/scriptedRESTAPI/RestApiPlugin.js +71 -54
  83. package/dist/scriptedRESTAPI/RestApiPlugin.js.map +1 -1
  84. package/dist/scriptedRESTAPI/RestUtils.d.ts +2 -0
  85. package/dist/scriptedRESTAPI/RestUtils.js +37 -9
  86. package/dist/scriptedRESTAPI/RestUtils.js.map +1 -1
  87. package/dist/scripts/ClientScriptPlugin.d.ts +18 -16
  88. package/dist/scripts/ClientScriptPlugin.js +48 -45
  89. package/dist/scripts/ClientScriptPlugin.js.map +1 -1
  90. package/dist/scripts/scriptUtils.d.ts +0 -14
  91. package/dist/scripts/scriptUtils.js +0 -74
  92. package/dist/scripts/scriptUtils.js.map +1 -1
  93. package/dist/uxf/ExperiencePlugin.d.ts +31 -3
  94. package/dist/uxf/ExperiencePlugin.js +10 -4
  95. package/dist/uxf/ExperiencePlugin.js.map +1 -1
  96. package/dist/uxf/RoutesPlugin.d.ts +10 -3
  97. package/dist/uxf/RoutesPlugin.js +20 -14
  98. package/dist/uxf/RoutesPlugin.js.map +1 -1
  99. package/package.json +6 -6
  100. package/src/AttachmentPlugin.ts +10 -2
  101. package/src/BusinessRulePlugin.ts +79 -104
  102. package/src/CrossScopePrivilegePlugin.ts +65 -5
  103. package/src/DefaultPlugin.ts +181 -173
  104. package/src/HtmlTemplatePlugin.ts +31 -0
  105. package/src/IdPlugin.ts +59 -28
  106. package/src/JsonPlugin.ts +81 -0
  107. package/src/ListPlugin.ts +38 -19
  108. package/src/NowConfigPlugin.ts +72 -0
  109. package/src/PackageJsonPlugin.ts +24 -0
  110. package/src/PropertyPlugin.ts +60 -38
  111. package/src/ScriptTemplatePlugin.ts +22 -179
  112. package/src/ServerModulePlugin.ts +267 -0
  113. package/src/UserPreferencePlugin.ts +28 -19
  114. package/src/aclAndRole/AclPlugin.ts +78 -109
  115. package/src/aclAndRole/RolePlugin.ts +85 -58
  116. package/src/app/ApplicationMenuPlugin.ts +15 -11
  117. package/src/atf/ATFComposer.ts +1 -1
  118. package/src/atf/TestPlugin.ts +14 -12
  119. package/src/db/ColumnPlugins.ts +36 -37
  120. package/src/db/DBUtils.ts +36 -0
  121. package/src/db/DocumentationPlugin.ts +282 -0
  122. package/src/db/RecordPlugin.ts +96 -98
  123. package/src/db/TablePlugin.ts +278 -152
  124. package/src/db/index.ts +2 -0
  125. package/src/index.ts +7 -2
  126. package/src/scriptedRESTAPI/RESTDeserializationUtils.ts +15 -7
  127. package/src/scriptedRESTAPI/RESTSerializationUtils.ts +7 -6
  128. package/src/scriptedRESTAPI/RestApiPlugin.ts +85 -75
  129. package/src/scriptedRESTAPI/RestUtils.ts +44 -11
  130. package/src/scripts/ClientScriptPlugin.ts +64 -68
  131. package/src/scripts/scriptUtils.ts +0 -76
  132. package/src/uxf/ExperiencePlugin.ts +14 -13
  133. package/src/uxf/RoutesPlugin.ts +22 -27
@@ -6,11 +6,12 @@ import {
6
6
  extractCallExpression,
7
7
  FluentDiagnostic,
8
8
  findObjectPropertyValue,
9
- EntityData,
10
- ObjectData,
11
9
  Data,
10
+ isSNScope,
11
+ LinkedDocument,
12
+ ArrayData,
13
+ fixFunctionArgumentTypes,
12
14
  } from '@servicenow/sdk-build-core'
13
- import { Record } from '@servicenow/sdk-core/runtime/db'
14
15
  import {
15
16
  generateCallExpressionExportForDocument,
16
17
  getOrCreateEntitySourceFile,
@@ -21,22 +22,39 @@ import {
21
22
  import { addObjectToArrayById, removeObjectFromArrayById } from './Util'
22
23
  import { SyntaxKind, Node } from 'ts-morph'
23
24
  import { except } from '../scripts/scriptUtils'
24
- import { RecordPlugin } from '../db/RecordPlugin'
25
- import { Diagnostic } from '@servicenow/sdk-project'
25
+ import { Diagnostic, SupportedNode } from '@servicenow/sdk-project'
26
+
27
+ export function getRolesString(roles: ArrayData<unknown[]>): string {
28
+ const uniqueRoles = [
29
+ ...new Set(
30
+ roles.getElements().map((role) => {
31
+ return Data.isEntity(role) ? role.getPropertyOrThrow('name', 'string').getValue() : role.getValue()
32
+ })
33
+ ),
34
+ ]
35
+
36
+ return uniqueRoles.join(',')
37
+ }
26
38
 
27
- function mapRoleToRole(context, roleId, containedRoleId) {
28
- const m2mId = context.keys.registerCompositeId('sys_user_role_contains', {
39
+ function mapRoleToRole(context: Context, roleId: string, containedRoleId: string, node: SupportedNode): LinkedDocument {
40
+ const mappingId = context.keys.registerCompositeId('sys_user_role_contains', {
29
41
  role: roleId,
30
42
  contains: containedRoleId,
31
43
  })
32
- return Record({
33
- table: 'sys_user_role_contains',
34
- $id: m2mId,
44
+
45
+ return {
46
+ kind: 'record',
47
+ guid: mappingId,
48
+ node,
35
49
  data: {
36
- contains: containedRoleId,
37
- role: roleId,
50
+ $id: mappingId,
51
+ table: 'sys_user_role_contains',
52
+ data: {
53
+ contains: containedRoleId,
54
+ role: roleId,
55
+ },
38
56
  },
39
- })
57
+ }
40
58
  }
41
59
 
42
60
  type RoleDoc = {
@@ -61,33 +79,35 @@ export default Plugin({
61
79
  },
62
80
  extractors: {
63
81
  entity: {
64
- CallExpression: (node, context) => {
65
- const result = extractCallExpression(
66
- Role as (role: RoleConfig) => RoleConfig,
67
- 'role',
68
- node,
69
- context,
70
- ({ $id }) => context.registerExplicitId('sys_user_role', $id as string)
71
- )
72
-
73
- if (!result.handled || !(0 in result.data)) {
74
- return result
75
- }
82
+ fluent: {
83
+ CallExpression: (node, context) => {
84
+ const result = extractCallExpression(
85
+ Role as (role: RoleConfig) => RoleConfig,
86
+ 'role',
87
+ node,
88
+ context,
89
+ ({ $id }) => context.registerExplicitId('sys_user_role', $id as string)
90
+ )
91
+
92
+ if (!result.handled || !(0 in result.data)) {
93
+ return result
94
+ }
76
95
 
77
- const role = result.data[0]
78
- const diagnostics = result.diagnostics
79
- const scope = context.app?.config?.scope
80
- const name = role.getProperty('name').getValue()
81
- if (!name.startsWith(`${scope}.`)) {
82
- const nameValue = findObjectPropertyValue(node, 'name')
83
- diagnostics.push(new FluentDiagnostic(nameValue, `Role name must begin with '${scope}.'`))
84
- }
96
+ const role = result.data[0]
97
+ const diagnostics = result.diagnostics
98
+ const scope = context.app?.config?.scope
99
+ const name = role.getProperty('name').getValue()
100
+ if (!isSNScope(scope) && !name.startsWith(`${scope}.`)) {
101
+ const nameValue = findObjectPropertyValue(node, 'name')
102
+ diagnostics.push(new FluentDiagnostic(nameValue, `Role name must begin with '${scope}.'`))
103
+ }
85
104
 
86
- return {
87
- handled: true,
88
- diagnostics,
89
- data: [role],
90
- }
105
+ return {
106
+ handled: true,
107
+ diagnostics,
108
+ data: [role],
109
+ }
110
+ },
91
111
  },
92
112
  },
93
113
  },
@@ -96,7 +116,6 @@ export default Plugin({
96
116
  role(entity, context) {
97
117
  const scope = context.app?.config?.scope
98
118
  const guid = entity.getGuid()
99
- const node = entity.getNode()
100
119
 
101
120
  const role = roleSchema.safeParse(entity.getValue())
102
121
  if (!role.success) {
@@ -105,36 +124,43 @@ export default Plugin({
105
124
 
106
125
  const { $id: _, name, contains_roles, ...rest } = role.data
107
126
 
108
- const roleToRoles: Record<'sys_user_role_contains'>[] = []
127
+ const roleMappingDocuments: LinkedDocument[] = []
109
128
  const containsRoles = entity.getProperty('contains_roles')
110
129
  if (Data.isArray(containsRoles)) {
111
130
  for (const containedRole of containsRoles.getElements()) {
112
131
  const containedRoleSysId = Data.isEntity(containedRole)
113
132
  ? containedRole.getGuid()
114
- : containedRole.getValue() // Should be a string
133
+ : Data.isString(containedRole)
134
+ ? containedRole.getValue()
135
+ : undefined
115
136
 
116
- roleToRoles.push(mapRoleToRole(context, guid, containedRoleSysId))
137
+ if (!containedRoleSysId) {
138
+ throw new Error(`Invalid contained role value: ${containedRole.getValue()}`)
139
+ }
140
+
141
+ roleMappingDocuments.push(
142
+ mapRoleToRole(context, guid, containedRoleSysId, containedRole.getNode())
143
+ )
117
144
  }
118
145
  }
119
146
 
120
147
  const suffix = name.replace(new RegExp(`^${scope}\\.`), '')
121
-
122
- const roleRecord = Record({
123
- table: 'sys_user_role',
124
- $id: guid,
148
+ const roleDocument = {
149
+ kind: 'record',
150
+ guid,
151
+ node: entity.getNode(),
125
152
  data: {
126
- name,
127
- suffix,
128
- ...rest,
153
+ $id: guid,
154
+ table: 'sys_user_role',
155
+ data: {
156
+ name,
157
+ suffix,
158
+ ...rest,
159
+ },
129
160
  },
130
- })
161
+ }
131
162
 
132
- return context.composeEntities(
133
- [roleRecord, ...roleToRoles].map(
134
- (r) => new EntityData('record', r.$id.toString(), ObjectData.fromObjectValue(r, node), node)
135
- ),
136
- [RecordPlugin]
137
- )
163
+ return [roleDocument, ...roleMappingDocuments]
138
164
  },
139
165
  },
140
166
  },
@@ -217,8 +243,9 @@ export default Plugin({
217
243
  key === 'suffix' ||
218
244
  key === 'includes_roles'
219
245
  )
220
-
221
- return transformFunctionArguments(document.node, Role, roleData)
246
+ transformFunctionArguments(document.node, Role, roleData)
247
+ fixFunctionArgumentTypes(context, document.node)
248
+ return true
222
249
  },
223
250
  },
224
251
  },
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { Node, SyntaxKind } from 'ts-morph'
3
- import { ApplicationMenu, ApplicationMenuSchema, getRolesString, getRolesArray } from '@servicenow/sdk-core/runtime/app'
3
+ import { ApplicationMenu, ApplicationMenuSchema, getRolesArray } from '@servicenow/sdk-core/runtime/app'
4
4
  import {
5
5
  Plugin,
6
6
  extractCallExpression,
@@ -19,6 +19,7 @@ import {
19
19
  import { RecordPlugin } from '../db/RecordPlugin'
20
20
  import { Record } from '@servicenow/sdk-core/runtime/db'
21
21
  import { Diagnostic } from '@servicenow/sdk-project'
22
+ import { getRolesString } from '../aclAndRole/RolePlugin'
22
23
 
23
24
  type AppMenuDocumentSchema = z.output<typeof AppMenuDocumentSchema>
24
25
 
@@ -26,9 +27,9 @@ const AppMenuDocumentSchema = z.object({
26
27
  table: z.literal('sys_app_application'),
27
28
  data: ApplicationMenuSchema.omit({ roles: true, category: true, $id: true }).extend({
28
29
  roles: z.string().optional(),
29
- category: z.string().optional(),
30
- device_type: z.string().optional(),
31
- view_name: z.string().optional(),
30
+ category: z.coerce.string().optional(),
31
+ device_type: z.coerce.string().optional(),
32
+ view_name: z.coerce.string().optional(),
32
33
  }),
33
34
  })
34
35
 
@@ -49,10 +50,12 @@ export default Plugin({
49
50
  },
50
51
  extractors: {
51
52
  entity: {
52
- CallExpression: (node, context) =>
53
- extractCallExpression(ApplicationMenu, 'appMenu', node, context, (menu) =>
54
- context.registerExplicitId('sys_app_application', menu.$id as string)
55
- ),
53
+ fluent: {
54
+ CallExpression: (node, context) =>
55
+ extractCallExpression(ApplicationMenu, 'appMenu', node, context, (menu) =>
56
+ context.registerExplicitId('sys_app_application', menu.$id as string)
57
+ ),
58
+ },
56
59
  },
57
60
  },
58
61
  composers: {
@@ -62,7 +65,8 @@ export default Plugin({
62
65
  if (!appMenuData.success) {
63
66
  return undefined
64
67
  }
65
- const { $id, roles, category, ...props } = appMenuData.data
68
+ const { $id, category, ...props } = appMenuData.data
69
+ const roles = entity.getProperty('roles', 'array')
66
70
  const rolesString = roles ? getRolesString(roles) : ''
67
71
 
68
72
  const recordEntity = Record({
@@ -93,10 +97,10 @@ export default Plugin({
93
97
 
94
98
  generators: {
95
99
  record(document, context) {
96
- if (!AppMenuDocumentSchema.safeParse(document.data).success) {
100
+ const tableName = document.data!['table']
101
+ if (tableName !== 'sys_app_application') {
97
102
  return undefined
98
103
  }
99
-
100
104
  return linkDocument(
101
105
  document,
102
106
  generateCallExpressionExportForDocument(
@@ -1,6 +1,6 @@
1
1
  import { Data, Record, TableName } from '@servicenow/sdk-core/runtime/db'
2
2
  import type { Role } from '@servicenow/sdk-core/runtime/app'
3
- import { StepConfig, Variables } from '@servicenow/sdk-core/runtime/tables'
3
+ import { StepConfig, Variables } from '@servicenow/sdk-core/runtime/fluent'
4
4
  import { Context, InputObject } from '@servicenow/sdk-build-core'
5
5
  import * as atf from '@servicenow/sdk-core/runtime/atf'
6
6
 
@@ -18,19 +18,21 @@ export default Plugin({
18
18
  name: 'Test',
19
19
  extractors: {
20
20
  entity: {
21
- CallExpression: (node: CallExpression, context: Context) => {
22
- // This constructs an entity of kind 'test' and returns it. This entity is then parsed by the composer
23
- const configExtractor = new ConfigurationFunctionExtractor(node, context, ATF)
24
- const result = configExtractor.extractConfigFunction('test', context.logger)
25
- if (!result) {
26
- return { handled: false }
27
- }
21
+ fluent: {
22
+ CallExpression: (node: CallExpression, context: Context) => {
23
+ // This constructs an entity of kind 'test' and returns it. This entity is then parsed by the composer
24
+ const configExtractor = new ConfigurationFunctionExtractor(node, context, ATF)
25
+ const result = configExtractor.extractConfigFunction('test', context.logger)
26
+ if (!result) {
27
+ return { handled: false }
28
+ }
28
29
 
29
- return {
30
- handled: true,
31
- diagnostics: [],
32
- data: [result],
33
- }
30
+ return {
31
+ handled: true,
32
+ diagnostics: [],
33
+ data: [result],
34
+ }
35
+ },
34
36
  },
35
37
  },
36
38
  },
@@ -1,5 +1,6 @@
1
1
  import * as db from '@servicenow/sdk-core/runtime/db'
2
- import { EntityData, FluentDiagnostic, ObjectData, Plugin, extractCallExpression } from '@servicenow/sdk-build-core'
2
+ import { FluentDiagnostic, ObjectData, Plugin, extractCallExpression } from '@servicenow/sdk-build-core'
3
+ import { getLabelDiagnostics } from './DBUtils'
3
4
 
4
5
  const validFunctions = [
5
6
  'add',
@@ -28,42 +29,41 @@ function ColumnPlugin<
28
29
  return Plugin({
29
30
  name: `${entityKind}Column`,
30
31
  extractors: {
31
- entity: {
32
- CallExpression(node, context) {
33
- const result = extractCallExpression(
34
- fn,
35
- `${entityKind}Column`,
36
- node,
37
- context,
38
- () => 'NO_GUID_GENERATED' // TODO: We shouldn't need to generate any GUID here but maybe should provide something unique to be on the safe side
39
- )
40
- if (!result.handled || !(0 in result.data)) {
41
- return result
42
- }
32
+ raw: {
33
+ fluent: {
34
+ CallExpression(node, context) {
35
+ const result = extractCallExpression(
36
+ fn,
37
+ `${entityKind}Column`,
38
+ node,
39
+ context,
40
+ () => 'NO_GUID_GENERATED' // TODO: We shouldn't need to generate any GUID here but maybe should provide something unique to be on the safe side
41
+ )
42
+ if (!result.handled || !(0 in result.data)) {
43
+ return result
44
+ }
43
45
 
44
- const entity = result.data[0]
45
- const diagnostics = result.diagnostics
46
- const functionDefinition = entity.getProperty('function_definition')?.getValue()
47
- if (functionDefinition) {
48
- if (typeof functionDefinition !== 'string' || !functionDefinition.match(regExp)) {
49
- diagnostics.push(
50
- new FluentDiagnostic(
51
- node,
52
- `'function_definition' must start with 'glidefunction:' and include a single function call to a predefined function:\n${validFunctions.join(
53
- '\n'
54
- )}`
46
+ const entity = result.data[0]
47
+ const diagnostics = result.diagnostics
48
+ diagnostics.push(...getLabelDiagnostics(entity))
49
+ const functionDefinition = entity.getProperty('function_definition')?.getValue()
50
+ if (functionDefinition) {
51
+ if (typeof functionDefinition !== 'string' || !functionDefinition.match(regExp)) {
52
+ diagnostics.push(
53
+ new FluentDiagnostic(
54
+ node,
55
+ `'function_definition' must start with 'glidefunction:' and include a single function call to a predefined function:\n${validFunctions.join(
56
+ '\n'
57
+ )}`
58
+ )
55
59
  )
56
- )
60
+ }
57
61
  }
58
- }
59
62
 
60
- return {
61
- handled: true,
62
- diagnostics,
63
- data: [
64
- new EntityData(
65
- entity.getKind(),
66
- entity.getGuid(),
63
+ return {
64
+ handled: true,
65
+ diagnostics,
66
+ data: [
67
67
  ObjectData.fromObjectValue(
68
68
  {
69
69
  ...entity.getValue(),
@@ -71,10 +71,9 @@ function ColumnPlugin<
71
71
  },
72
72
  entity.getNode()
73
73
  ),
74
- entity.getNode()
75
- ),
76
- ],
77
- }
74
+ ],
75
+ }
76
+ },
78
77
  },
79
78
  },
80
79
  },
@@ -0,0 +1,36 @@
1
+ import { FluentDiagnostic } from '@servicenow/sdk-build-core'
2
+ import { isArray } from 'lodash'
3
+ import { Diagnostic } from '@servicenow/sdk-project'
4
+
5
+ export function getLabelDiagnostics(entity) {
6
+ const diagnostics: FluentDiagnostic[] = []
7
+ const label = entity.getProperty('label')?.getValue()
8
+ if (label && isArray(label)) {
9
+ const languages = {}
10
+ label.forEach((l, i) => {
11
+ const language = l.language
12
+ if (languages[language] !== undefined) {
13
+ diagnostics.push(
14
+ new FluentDiagnostic(
15
+ entity.getProperty('label')!.getNode().getChildrenOfKind(210)![i]?.getProperty('language') ??
16
+ entity.getProperty('label')!.getNode(),
17
+ `'language' must be unique per table/column`,
18
+ { level: Diagnostic.Level.Error }
19
+ )
20
+ )
21
+ }
22
+ languages[language] = i
23
+ if (language === '' || (language && language.length !== 2)) {
24
+ diagnostics.push(
25
+ new FluentDiagnostic(
26
+ entity.getProperty('label')!.getNode().getChildrenOfKind(210)![i]?.getProperty('language') ??
27
+ entity.getProperty('label')!.getNode(),
28
+ `'language' must be a 2 character identifier (Ex. 'en', 'es')`,
29
+ { level: Diagnostic.Level.Error }
30
+ )
31
+ )
32
+ }
33
+ })
34
+ }
35
+ return diagnostics
36
+ }