@izara_project/izara-core-generate-service-code 1.0.78 → 1.0.80
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/package.json +2 -2
- package/src/compile/flowValidator.js +163 -0
- package/src/compile/loadSchemas.js +15 -5
- package/src/emit/coreFunction/emitCoreFunctionArtifacts.js +137 -7
- package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +137 -17
- package/src/emit/crud/crudActionPlan.js +82 -0
- package/src/emit/crud/emitCrudApplicationArtifacts.js +169 -112
- package/src/emit/crud/emitDynamicCrudFlowSchemas.js +43 -65
- package/src/emit/crud/generateDynamicCrudFlowSchemas.js +4 -1
- package/src/emit/crud/templates/CreateComplete_Main.ejs +8 -3
- package/src/emit/crud/templates/CreateEndpoint_Main.ejs +90 -513
- package/src/emit/crud/templates/CreateLogicalLib.ejs +541 -0
- package/src/emit/crud/templates/DeleteComplete_Main.ejs +8 -3
- package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +93 -203
- package/src/emit/crud/templates/DeleteLogicalLib.ejs +213 -0
- package/src/emit/crud/templates/FlowEndpointRbacCallback_Main.ejs +217 -0
- package/src/emit/crud/templates/GetEndpoint_Main.ejs +1 -1
- package/src/emit/crud/templates/GetRangeEndpoint_Main.ejs +118 -37
- package/src/emit/crud/templates/UpdateComplete_Main.ejs +8 -3
- package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +92 -444
- package/src/emit/crud/templates/UpdateLogicalLib.ejs +477 -0
- package/src/emit/custom/customFlows.js +27 -2
- package/src/emit/custom/emitCustomFlowApplicationArtifacts.js +1 -2
- package/src/emit/custom/templates/FlowEndpoint_Main.ejs +10 -3
- package/src/emit/custom/templates/FlowStep_Main.ejs +1 -1
- package/src/emit/custom/templates/s3/CreateObject_Main.ejs +1 -1
- package/src/emit/custom/templates/triggerCache/CheckTriggerCache_HdrSqs.ejs +2 -2
- package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +3 -9
- package/src/emit/rbac/emitRbacApplicationArtifacts.js +21 -16
- package/src/emit/rbac/rbacFlowTag.js +2 -7
- package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +148 -21
- package/src/emit/relationship/emitDynamicRelationshipFlowSchemas.js +4 -2
- package/src/emit/relationship/templates/ChangeRelationshipComplete_Main.ejs +7 -2
- package/src/emit/relationship/templates/ChangeRelationship_Main.ejs +6 -2
- package/src/emit/relationship/templates/CreateRelationshipComplete_Main.ejs +7 -2
- package/src/emit/relationship/templates/CreateRelationship_Main.ejs +6 -2
- package/src/emit/relationship/templates/DeleteRelationshipComplete_Main.ejs +7 -2
- package/src/emit/relationship/templates/DeleteRelationship_Main.ejs +6 -2
- package/src/emit/relationship/templates/GetRelationship_Main.ejs +1 -1
- package/src/emit/relationship/templates/MoveRelationshipComplete_Main.ejs +7 -2
- package/src/emit/relationship/templates/MoveRelationship_Main.ejs +6 -2
- package/src/emit/relationship/templates/UpdateRelationshipComplete_Main.ejs +7 -2
- package/src/emit/relationship/templates/UpdateRelationship_Main.ejs +6 -2
- package/src/emit/resource/emitResourceArtifacts.js +149 -79
- package/src/emit/resource/templates/DynamoDBTable_Yaml.ejs +4 -4
- package/src/emit/resource/templates/GeneratedLambdaRoleServiceConfig.ejs +31 -5
- package/src/emit/resource/templates/SystemDynamoDB_Yaml.ejs +21 -0
- package/src/emit/resource/templates/WebSocketSubscription_Yaml.ejs +7 -0
- package/src/emit/shared/templates/FlowHandler_HdrApi.ejs +1 -1
- package/src/emit/shared/templates/FlowHandler_HdrDsq.ejs +2 -2
- package/src/emit/shared/templates/FlowHandler_HdrInv.ejs +1 -1
- package/src/emit/shared/templates/FlowHandler_HdrSqs.ejs +2 -2
- package/src/emit/systemText/emitSystemTextSchemas.js +246 -0
- package/src/emit/systemText/templates/SystemTextSchemas.ejs +10 -0
- package/src/generate.js +55 -8
- package/test/crudApplicationArtifacts.test.js +158 -19
- package/test/customFlowApplicationArtifacts.test.js +307 -16
- package/test/dynamicCrudFlowSchemas.test.js +150 -14
- package/test/dynamicRbacFlowSchemas.test.js +2 -2
- package/test/dynamicRelationshipFlowSchemas.test.js +1 -1
- package/test/graphDrivenFlowSchema.test.js +257 -0
- package/test/publicApi.test.js +14 -5
- package/test/resourceArtifacts.test.js +66 -19
- package/test/systemTextSchemas.test.js +154 -0
- package/src/emit/crud/templates/CreateComplete_HdrSqs.ejs +0 -99
- package/src/emit/crud/templates/DeleteComplete_HdrSqs.ejs +0 -99
- package/src/emit/crud/templates/FlowEndpointBeforeLogical_Main.ejs +0 -174
- package/src/emit/crud/templates/FlowEndpointComplete_Main.ejs +0 -187
- package/src/emit/crud/templates/UpdateComplete_HdrSqs.ejs +0 -99
- package/src/emit/resource/templates/ResourceBeforeLogicalYaml.ejs +0 -47
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import ejsLib from 'ejs';
|
|
3
|
+
import { rbacFlowTag } from '../rbac/rbacFlowTag.js';
|
|
4
|
+
|
|
5
|
+
const TEMPLATE_URL = new URL('./templates/', import.meta.url);
|
|
6
|
+
const SYSTEM_TEXT_SCHEMA_PATH =
|
|
7
|
+
'app/src/schemas/SystemTextSchemas/generatedCode/SystemTextSchemas.js';
|
|
8
|
+
|
|
9
|
+
const CRUD_ACTIONS = ['Create', 'Update', 'Delete', 'Get'];
|
|
10
|
+
const RELATIONSHIP_ACTIONS = [
|
|
11
|
+
'CreateRelationship',
|
|
12
|
+
'UpdateRelationship',
|
|
13
|
+
'DeleteRelationship',
|
|
14
|
+
'GetRelationship',
|
|
15
|
+
'ChangeRelationship',
|
|
16
|
+
'MoveRelationship'
|
|
17
|
+
];
|
|
18
|
+
const RBAC_SHARED_FLOW_TAGS = [
|
|
19
|
+
'CreateTargetRole',
|
|
20
|
+
'ListTargetRole',
|
|
21
|
+
'DeleteTargetRole',
|
|
22
|
+
'CreateRolePermissions',
|
|
23
|
+
'ListRolePermissions',
|
|
24
|
+
'DeleteRolePermissions',
|
|
25
|
+
'CreateUserRole',
|
|
26
|
+
'ListUserInRoles',
|
|
27
|
+
'DeleteUserFromRole'
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
export function systemTextSchemaManagedPaths() {
|
|
31
|
+
return [SYSTEM_TEXT_SCHEMA_PATH];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Emits SystemTextSchemas.js artifact based on objects, relationships, flows, and notifications
|
|
36
|
+
*
|
|
37
|
+
* @param {Object} params
|
|
38
|
+
* @param {Object} params.project
|
|
39
|
+
* @param {Array} [params.objects=[]]
|
|
40
|
+
* @param {Array} [params.relationships=[]]
|
|
41
|
+
* @param {Array} [params.flows=[]]
|
|
42
|
+
* @param {Array} [params.notifications=[]]
|
|
43
|
+
* @returns {Promise<Array<{path: string, content: string}>>}
|
|
44
|
+
*/
|
|
45
|
+
export async function emitSystemTextSchemas({
|
|
46
|
+
project = {},
|
|
47
|
+
objects = [],
|
|
48
|
+
relationships = [],
|
|
49
|
+
flows = [],
|
|
50
|
+
notifications = []
|
|
51
|
+
} = {}) {
|
|
52
|
+
const safeObjects = Array.isArray(objects) ? objects : [];
|
|
53
|
+
const safeRelationships = Array.isArray(relationships) ? relationships : [];
|
|
54
|
+
const safeFlows = Array.isArray(flows) ? flows : [];
|
|
55
|
+
const safeNotifications = Array.isArray(notifications) ? notifications : [];
|
|
56
|
+
|
|
57
|
+
if (
|
|
58
|
+
safeObjects.length === 0 &&
|
|
59
|
+
safeRelationships.length === 0 &&
|
|
60
|
+
safeFlows.length === 0 &&
|
|
61
|
+
safeNotifications.length === 0
|
|
62
|
+
) {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const serviceTag = project?.serviceTag || '';
|
|
67
|
+
const datas = [];
|
|
68
|
+
|
|
69
|
+
// 1. Process Objects
|
|
70
|
+
for (const object of safeObjects) {
|
|
71
|
+
const objectType = object?.objectType;
|
|
72
|
+
if (!objectType) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const objServiceTag = object?.serviceTag || serviceTag;
|
|
76
|
+
const fieldNames = Object.keys(object?.fieldNames || {});
|
|
77
|
+
|
|
78
|
+
if (Array.isArray(object?.addOnDataStructure)) {
|
|
79
|
+
for (const addOn of object.addOnDataStructure) {
|
|
80
|
+
if (addOn?.type === 'versionedData' && addOn?.fieldNames) {
|
|
81
|
+
fieldNames.push(...Object.keys(addOn.fieldNames));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
for (const fieldName of fieldNames) {
|
|
87
|
+
const nameSpace = `${objectType}_${objServiceTag}_description`;
|
|
88
|
+
const systemTextTag = `${fieldName}_${objectType}_${objServiceTag}`;
|
|
89
|
+
const openHook = `//(<${objectType}_${objServiceTag}${fieldName}Hook>)`;
|
|
90
|
+
const closeHook = `//(</${objectType}_${objServiceTag}${fieldName}Hook>)`;
|
|
91
|
+
datas.push({
|
|
92
|
+
nameSpace,
|
|
93
|
+
systemTextTag,
|
|
94
|
+
openHook,
|
|
95
|
+
closeHook
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 2. Process Notifications
|
|
101
|
+
for (const notification of safeNotifications) {
|
|
102
|
+
if (notification?.notificationGrouping && notification?.notificationTag) {
|
|
103
|
+
const nameSpace = `sysNotification_${notification.notificationGrouping}`;
|
|
104
|
+
const systemTextTag = notification.notificationTag;
|
|
105
|
+
const openHook = `//(<${notification.notificationTag}Hook>)`;
|
|
106
|
+
const closeHook = `//(</${notification.notificationTag}Hook>)`;
|
|
107
|
+
datas.push({
|
|
108
|
+
nameSpace,
|
|
109
|
+
systemTextTag,
|
|
110
|
+
openHook,
|
|
111
|
+
closeHook
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 3. Process Relationships
|
|
117
|
+
for (const rel of safeRelationships) {
|
|
118
|
+
let relTag = rel?.relationshipTag;
|
|
119
|
+
if (!relTag && typeof rel === 'object' && rel !== null) {
|
|
120
|
+
relTag = Object.keys(rel)[0];
|
|
121
|
+
}
|
|
122
|
+
if (!relTag) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
const relData = rel[relTag] || rel;
|
|
126
|
+
const relServiceTag = relData?.serviceTag || serviceTag;
|
|
127
|
+
|
|
128
|
+
if (relData?.fieldNames && Object.keys(relData.fieldNames).length > 0) {
|
|
129
|
+
for (const fieldName of Object.keys(relData.fieldNames)) {
|
|
130
|
+
const nameSpace = `${relTag}_${relServiceTag}_description`;
|
|
131
|
+
const systemTextTag = `${fieldName}_${relTag}_${relServiceTag}`;
|
|
132
|
+
const openHook = `//(<${relTag}_${relServiceTag}${fieldName}Hook>)`;
|
|
133
|
+
const closeHook = `//(</${relTag}_${relServiceTag}${fieldName}Hook>)`;
|
|
134
|
+
datas.push({
|
|
135
|
+
nameSpace,
|
|
136
|
+
systemTextTag,
|
|
137
|
+
openHook,
|
|
138
|
+
closeHook
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// 4. Process Flows (Custom flows + Dynamic flows)
|
|
145
|
+
const flowTags = [];
|
|
146
|
+
const seenFlowTags = new Set();
|
|
147
|
+
|
|
148
|
+
for (const flow of safeFlows) {
|
|
149
|
+
const tag = flow?.flowTag || flow;
|
|
150
|
+
if (typeof tag === 'string' && tag.trim() !== '' && !seenFlowTags.has(tag)) {
|
|
151
|
+
seenFlowTags.add(tag);
|
|
152
|
+
flowTags.push(tag);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const dynamicFlowTags = [];
|
|
157
|
+
|
|
158
|
+
// Dynamic RBAC flows
|
|
159
|
+
const rbacObjects = safeObjects.filter(_hasRbacAddOn);
|
|
160
|
+
if (rbacObjects.length > 0) {
|
|
161
|
+
for (const rbacObj of rbacObjects) {
|
|
162
|
+
const tag = rbacFlowTag(rbacObj);
|
|
163
|
+
if (!seenFlowTags.has(tag) && !dynamicFlowTags.includes(tag)) {
|
|
164
|
+
dynamicFlowTags.push(tag);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const hasSelfRbac = safeObjects.some(_hasSelfRbacAddOn);
|
|
168
|
+
if (hasSelfRbac) {
|
|
169
|
+
for (const tag of RBAC_SHARED_FLOW_TAGS) {
|
|
170
|
+
if (!seenFlowTags.has(tag) && !dynamicFlowTags.includes(tag)) {
|
|
171
|
+
dynamicFlowTags.push(tag);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Dynamic CRUD flows
|
|
178
|
+
const hasCrudObjects = safeObjects.some(
|
|
179
|
+
(object) =>
|
|
180
|
+
object?.generatedBy === 'userGenerated' ||
|
|
181
|
+
object?.generatedBy === 'systemGenerated'
|
|
182
|
+
);
|
|
183
|
+
if (hasCrudObjects) {
|
|
184
|
+
for (const action of CRUD_ACTIONS) {
|
|
185
|
+
if (!seenFlowTags.has(action) && !dynamicFlowTags.includes(action)) {
|
|
186
|
+
dynamicFlowTags.push(action);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Dynamic Relationship flows
|
|
192
|
+
if (safeRelationships.length > 0) {
|
|
193
|
+
for (const action of RELATIONSHIP_ACTIONS) {
|
|
194
|
+
if (!seenFlowTags.has(action) && !dynamicFlowTags.includes(action)) {
|
|
195
|
+
dynamicFlowTags.push(action);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Sort dynamic flows in descending alphabetical order to match legacy contract
|
|
201
|
+
dynamicFlowTags.sort((a, b) => b.localeCompare(a));
|
|
202
|
+
|
|
203
|
+
for (const tag of dynamicFlowTags) {
|
|
204
|
+
if (!seenFlowTags.has(tag)) {
|
|
205
|
+
seenFlowTags.add(tag);
|
|
206
|
+
flowTags.push(tag);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
for (const flowTag of flowTags) {
|
|
211
|
+
const nameSpace = `${flowTag}_${serviceTag}`;
|
|
212
|
+
const systemTextTag = `errors_${flowTag}_${serviceTag}`;
|
|
213
|
+
const openHook = `//(<${flowTag}_${serviceTag}Hook>)`;
|
|
214
|
+
const closeHook = `//(</${flowTag}_${serviceTag}Hook>)`;
|
|
215
|
+
datas.push({
|
|
216
|
+
nameSpace,
|
|
217
|
+
systemTextTag,
|
|
218
|
+
openHook,
|
|
219
|
+
closeHook
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const templateContent = await readFile(
|
|
224
|
+
new URL('SystemTextSchemas.ejs', TEMPLATE_URL),
|
|
225
|
+
'utf8'
|
|
226
|
+
);
|
|
227
|
+
const content = ejsLib.render(templateContent, { datas });
|
|
228
|
+
|
|
229
|
+
return [
|
|
230
|
+
{
|
|
231
|
+
path: SYSTEM_TEXT_SCHEMA_PATH,
|
|
232
|
+
content
|
|
233
|
+
}
|
|
234
|
+
];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function _hasSelfRbacAddOn(object) {
|
|
238
|
+
if (Array.isArray(object?.addOnDataStructure)) {
|
|
239
|
+
return object.addOnDataStructure.some((addOn) => addOn?.type === 'rbac');
|
|
240
|
+
}
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function _hasRbacAddOn(object) {
|
|
245
|
+
return _hasSelfRbacAddOn(object);
|
|
246
|
+
}
|
package/src/generate.js
CHANGED
|
@@ -51,6 +51,10 @@ import {
|
|
|
51
51
|
emitCoreFunctionArtifacts,
|
|
52
52
|
coreFunctionManagedPaths
|
|
53
53
|
} from './emit/coreFunction/emitCoreFunctionArtifacts.js';
|
|
54
|
+
import {
|
|
55
|
+
emitSystemTextSchemas,
|
|
56
|
+
systemTextSchemaManagedPaths
|
|
57
|
+
} from './emit/systemText/emitSystemTextSchemas.js';
|
|
54
58
|
|
|
55
59
|
export async function generate(rootPath, options = {}) {
|
|
56
60
|
const startedAt = Date.now();
|
|
@@ -58,10 +62,11 @@ export async function generate(rootPath, options = {}) {
|
|
|
58
62
|
logger?.info?.(`[INFO] [Generate] STATUS=STARTED | rootPath=${rootPath}`);
|
|
59
63
|
try {
|
|
60
64
|
const project = compileProject({ ...options, rootPath });
|
|
61
|
-
const [rawObjects, relationships, flows] = await Promise.all([
|
|
65
|
+
const [rawObjects, relationships, flows, notifications] = await Promise.all([
|
|
62
66
|
loadLocalSchemaDirectory(project, 'ObjectSchemas'),
|
|
63
67
|
loadLocalSchemaDirectory(project, 'RelationshipSchemas'),
|
|
64
|
-
loadLocalSchemaDirectory(project, 'FlowSchemas')
|
|
68
|
+
loadLocalSchemaDirectory(project, 'FlowSchemas'),
|
|
69
|
+
loadLocalSchemaDirectory(project, 'NotificationSchemas')
|
|
65
70
|
]);
|
|
66
71
|
const schemaBucketName =
|
|
67
72
|
options.schemaBucketName || process.env.iz_serviceSchemaBucketName;
|
|
@@ -85,6 +90,7 @@ export async function generate(rootPath, options = {}) {
|
|
|
85
90
|
objects,
|
|
86
91
|
relationships,
|
|
87
92
|
flows,
|
|
93
|
+
notifications,
|
|
88
94
|
objectGraphServiceTags,
|
|
89
95
|
relationshipGraphServiceTags
|
|
90
96
|
});
|
|
@@ -120,8 +126,25 @@ export async function generate(rootPath, options = {}) {
|
|
|
120
126
|
}
|
|
121
127
|
}
|
|
122
128
|
|
|
123
|
-
async function _emitGroups({
|
|
124
|
-
|
|
129
|
+
async function _emitGroups({
|
|
130
|
+
project,
|
|
131
|
+
objects,
|
|
132
|
+
relationships,
|
|
133
|
+
flows,
|
|
134
|
+
notifications,
|
|
135
|
+
objectGraphServiceTags,
|
|
136
|
+
relationshipGraphServiceTags
|
|
137
|
+
}) {
|
|
138
|
+
const [
|
|
139
|
+
crud,
|
|
140
|
+
relationship,
|
|
141
|
+
rbac,
|
|
142
|
+
system,
|
|
143
|
+
findData,
|
|
144
|
+
resource,
|
|
145
|
+
coreFunction,
|
|
146
|
+
systemText
|
|
147
|
+
] = await Promise.all([
|
|
125
148
|
emitCrudApplicationArtifacts({
|
|
126
149
|
objects,
|
|
127
150
|
flows,
|
|
@@ -133,10 +156,26 @@ async function _emitGroups({ project, objects, relationships, flows, objectGraph
|
|
|
133
156
|
serviceTag: project.serviceTag
|
|
134
157
|
}),
|
|
135
158
|
emitRbacApplicationArtifacts(objects),
|
|
136
|
-
emitSystemWebSocketArtifacts(flows, {
|
|
159
|
+
emitSystemWebSocketArtifacts(flows, {
|
|
160
|
+
includeRelationshipOwnTopic: true
|
|
161
|
+
}),
|
|
137
162
|
emitSystemFindDataArtifacts(objects),
|
|
138
163
|
emitResourceArtifacts({ objects, relationships, flows }),
|
|
139
|
-
emitCoreFunctionArtifacts({
|
|
164
|
+
emitCoreFunctionArtifacts({
|
|
165
|
+
project,
|
|
166
|
+
objects,
|
|
167
|
+
relationships,
|
|
168
|
+
flows,
|
|
169
|
+
objectGraphServiceTags,
|
|
170
|
+
relationshipGraphServiceTags
|
|
171
|
+
}),
|
|
172
|
+
emitSystemTextSchemas({
|
|
173
|
+
project,
|
|
174
|
+
objects,
|
|
175
|
+
relationships,
|
|
176
|
+
flows,
|
|
177
|
+
notifications
|
|
178
|
+
})
|
|
140
179
|
]);
|
|
141
180
|
const custom = await emitCustomFlowApplicationArtifacts(flows, {
|
|
142
181
|
objects
|
|
@@ -158,11 +197,14 @@ async function _emitGroups({ project, objects, relationships, flows, objectGraph
|
|
|
158
197
|
|
|
159
198
|
let resolvedCoreFunctionManagedPaths = coreFunctionManagedPaths;
|
|
160
199
|
if (typeof coreFunctionManagedPaths === 'function') {
|
|
161
|
-
resolvedCoreFunctionManagedPaths = coreFunctionManagedPaths(
|
|
200
|
+
resolvedCoreFunctionManagedPaths = coreFunctionManagedPaths({
|
|
201
|
+
project,
|
|
162
202
|
objects,
|
|
203
|
+
relationships,
|
|
204
|
+
flows,
|
|
163
205
|
objectGraphServiceTags,
|
|
164
206
|
relationshipGraphServiceTags
|
|
165
|
-
);
|
|
207
|
+
});
|
|
166
208
|
}
|
|
167
209
|
|
|
168
210
|
return [
|
|
@@ -223,6 +265,11 @@ async function _emitGroups({ project, objects, relationships, flows, objectGraph
|
|
|
223
265
|
scope: 'core-function',
|
|
224
266
|
managedPaths: resolvedCoreFunctionManagedPaths,
|
|
225
267
|
artifacts: coreFunction
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
scope: 'system-text-schemas',
|
|
271
|
+
managedPaths: systemTextSchemaManagedPaths(),
|
|
272
|
+
artifacts: systemText
|
|
226
273
|
}
|
|
227
274
|
];
|
|
228
275
|
}
|
|
@@ -22,7 +22,7 @@ test('emits CRUD handlers and function YAML from schema data without reading gen
|
|
|
22
22
|
serviceTag: 'orders'
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
assert.equal(result.files.length,
|
|
25
|
+
assert.equal(result.files.length, 25);
|
|
26
26
|
assert.ok(result.files.every((file) => !file.path.includes('/Process')));
|
|
27
27
|
assert.match(
|
|
28
28
|
await readFile(
|
|
@@ -34,16 +34,15 @@ test('emits CRUD handlers and function YAML from schema data without reading gen
|
|
|
34
34
|
),
|
|
35
35
|
/dataFromGraphV2/
|
|
36
36
|
);
|
|
37
|
-
|
|
38
|
-
await readFile(
|
|
37
|
+
const createMain = await readFile(
|
|
39
38
|
join(
|
|
40
39
|
rootPath,
|
|
41
40
|
'app/src/generatedCode/FlowObjects/Create/source/Create_Main.js'
|
|
42
41
|
),
|
|
43
42
|
'utf8'
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
);
|
|
43
|
+
);
|
|
44
|
+
assert.match(createMain, /CreateLogicalLib/);
|
|
45
|
+
assert.doesNotMatch(createMain, /FlowHook|beforeLogical|codeHookTag/);
|
|
47
46
|
const yaml = await readFile(
|
|
48
47
|
join(rootPath, 'app/sls_yaml/generatedCode/source/flow-objects.yml'),
|
|
49
48
|
'utf8'
|
|
@@ -53,14 +52,16 @@ test('emits CRUD handlers and function YAML from schema data without reading gen
|
|
|
53
52
|
assert.doesNotMatch(yaml, /handler: .*\/Process/);
|
|
54
53
|
assert.match(yaml, /path: \/orders\/order\/get/);
|
|
55
54
|
assert.match(yaml, /GetHdrInv:/);
|
|
55
|
+
assert.doesNotMatch(yaml, /GetRangeHdrApi:/);
|
|
56
|
+
assert.match(yaml, /UpdateCompleteHdrSqs:/);
|
|
57
|
+
assert.match(yaml, /#<#UpdateCompleteHdrSqsFunctionSetting#>/);
|
|
56
58
|
assert.match(yaml, /CreateCompleteHdrSqs:/);
|
|
57
|
-
assert.
|
|
58
|
-
assert.match(yaml, /filterPatterns:[\s\S]*CreateCompleteHdrSqs/);
|
|
59
|
+
assert.doesNotMatch(yaml, /FlowHook|RbacCallbackAndSendFlowHook/);
|
|
59
60
|
assert.match(
|
|
60
61
|
await readFile(
|
|
61
62
|
join(
|
|
62
63
|
rootPath,
|
|
63
|
-
'app/src/generatedCode/FlowObjects/
|
|
64
|
+
'app/src/generatedCode/FlowObjects/Update/source/UpdateComplete_Main.js'
|
|
64
65
|
),
|
|
65
66
|
'utf8'
|
|
66
67
|
),
|
|
@@ -84,7 +85,7 @@ test('emits CRUD handlers and function YAML from schema data without reading gen
|
|
|
84
85
|
)
|
|
85
86
|
)
|
|
86
87
|
);
|
|
87
|
-
for (const action of ['
|
|
88
|
+
for (const action of ['Update', 'Delete']) {
|
|
88
89
|
execFileSync(
|
|
89
90
|
process.execPath,
|
|
90
91
|
[
|
|
@@ -120,10 +121,10 @@ test('preserves function-setting hooks across CRUD regeneration', async () => {
|
|
|
120
121
|
await generateCrudApplicationArtifacts(rootPath, { serviceTag: 'orders' });
|
|
121
122
|
|
|
122
123
|
const hookCode =
|
|
123
|
-
' #<#
|
|
124
|
+
' #<#UpdateCompleteHdrSqsFunctionSetting#>\n memorySize: 512\n #<#/UpdateCompleteHdrSqsFunctionSetting#>\n';
|
|
124
125
|
let yaml = await readFile(yamlPath, 'utf8');
|
|
125
126
|
const withHook = yaml.replace(
|
|
126
|
-
/ #<#
|
|
127
|
+
/ #<#UpdateCompleteHdrSqsFunctionSetting#>[\s\S]*?#<#\/UpdateCompleteHdrSqsFunctionSetting#>/,
|
|
127
128
|
hookCode.trim()
|
|
128
129
|
);
|
|
129
130
|
assert.notEqual(yaml, withHook, 'hook placeholder must exist in generated YAML');
|
|
@@ -134,13 +135,13 @@ test('preserves function-setting hooks across CRUD regeneration', async () => {
|
|
|
134
135
|
yaml = await readFile(yamlPath, 'utf8');
|
|
135
136
|
assert.match(
|
|
136
137
|
yaml,
|
|
137
|
-
/#<#
|
|
138
|
+
/#<#UpdateCompleteHdrSqsFunctionSetting#>\n memorySize: 512\n #<#\/UpdateCompleteHdrSqsFunctionSetting#>/
|
|
138
139
|
);
|
|
139
140
|
|
|
140
141
|
const customFilterHook =
|
|
141
|
-
' #<#
|
|
142
|
+
' #<#UpdateCompleteHdrSqsFilterPatterns#>\n filterPatterns:\n - body: custom\n #<#/UpdateCompleteHdrSqsFilterPatterns#>';
|
|
142
143
|
const withFilterHook = yaml.replace(
|
|
143
|
-
/ #<#
|
|
144
|
+
/ #<#UpdateCompleteHdrSqsFilterPatterns#>[\s\S]*?#<#\/UpdateCompleteHdrSqsFilterPatterns#>/,
|
|
144
145
|
customFilterHook
|
|
145
146
|
);
|
|
146
147
|
assert.notEqual(
|
|
@@ -155,7 +156,7 @@ test('preserves function-setting hooks across CRUD regeneration', async () => {
|
|
|
155
156
|
yaml = await readFile(yamlPath, 'utf8');
|
|
156
157
|
assert.match(
|
|
157
158
|
yaml,
|
|
158
|
-
/#<#
|
|
159
|
+
/#<#UpdateCompleteHdrSqsFilterPatterns#>\n filterPatterns:\n - body: custom\n #<#\/UpdateCompleteHdrSqsFilterPatterns#>/
|
|
159
160
|
);
|
|
160
161
|
|
|
161
162
|
const conflictPath = join(
|
|
@@ -172,6 +173,127 @@ test('preserves function-setting hooks across CRUD regeneration', async () => {
|
|
|
172
173
|
assert.equal(await readFile(yamlPath, 'utf8'), yaml);
|
|
173
174
|
});
|
|
174
175
|
|
|
176
|
+
test('does not add RBAC handlers to Create', async () => {
|
|
177
|
+
const rootPath = await createRbacProject();
|
|
178
|
+
const result = await generateCrudApplicationArtifacts(rootPath, {
|
|
179
|
+
serviceTag: 'orders'
|
|
180
|
+
});
|
|
181
|
+
const createPaths = result.files
|
|
182
|
+
.map((file) => file.path)
|
|
183
|
+
.filter((path) => path.includes('/FlowObjects/Create/source/'));
|
|
184
|
+
|
|
185
|
+
assert.ok(createPaths.includes(
|
|
186
|
+
'app/src/generatedCode/FlowObjects/Create/source/Create_Main.js'
|
|
187
|
+
));
|
|
188
|
+
assert.ok(!createPaths.includes(
|
|
189
|
+
'app/src/generatedCode/FlowObjects/Create/source/CreateComplete_Main.js'
|
|
190
|
+
));
|
|
191
|
+
assert.ok(
|
|
192
|
+
!createPaths.some((path) => path.includes('CreateRbacCallback'))
|
|
193
|
+
);
|
|
194
|
+
assert.doesNotMatch(
|
|
195
|
+
await readFile(
|
|
196
|
+
join(
|
|
197
|
+
rootPath,
|
|
198
|
+
'app/src/generatedCode/FlowObjects/Create/source/Create_Main.js'
|
|
199
|
+
),
|
|
200
|
+
'utf8'
|
|
201
|
+
),
|
|
202
|
+
/checkRbacPermission/
|
|
203
|
+
);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test('emits a single Complete RBAC handler for Update without Graph', async () => {
|
|
207
|
+
const rootPath = await createRbacProject();
|
|
208
|
+
const result = await generateCrudApplicationArtifacts(rootPath, {
|
|
209
|
+
serviceTag: 'orders'
|
|
210
|
+
});
|
|
211
|
+
const updatePaths = result.files
|
|
212
|
+
.map((file) => file.path)
|
|
213
|
+
.filter((path) => path.includes('/FlowObjects/Update/source/'));
|
|
214
|
+
|
|
215
|
+
assert.ok(updatePaths.includes(
|
|
216
|
+
'app/src/generatedCode/FlowObjects/Update/source/UpdateComplete_Main.js'
|
|
217
|
+
));
|
|
218
|
+
assert.ok(!updatePaths.some((path) => path.includes('UpdateRbacCallback')));
|
|
219
|
+
assert.match(
|
|
220
|
+
await readFile(
|
|
221
|
+
join(
|
|
222
|
+
rootPath,
|
|
223
|
+
'app/src/generatedCode/FlowObjects/Update/source/UpdateComplete_Main.js'
|
|
224
|
+
),
|
|
225
|
+
'utf8'
|
|
226
|
+
),
|
|
227
|
+
/UpdateLogicalLib/
|
|
228
|
+
);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test('emits RbacCallback and Complete handlers for Update with Graph', async () => {
|
|
232
|
+
const rootPath = await createRbacProject(false, true);
|
|
233
|
+
const result = await generateCrudApplicationArtifacts(rootPath, {
|
|
234
|
+
serviceTag: 'orders'
|
|
235
|
+
});
|
|
236
|
+
const updatePaths = result.files
|
|
237
|
+
.map((file) => file.path)
|
|
238
|
+
.filter((path) => path.includes('/FlowObjects/Update/source/'));
|
|
239
|
+
|
|
240
|
+
assert.ok(updatePaths.includes(
|
|
241
|
+
'app/src/generatedCode/FlowObjects/Update/source/UpdateRbacCallback_Main.js'
|
|
242
|
+
));
|
|
243
|
+
assert.ok(updatePaths.includes(
|
|
244
|
+
'app/src/generatedCode/FlowObjects/Update/source/UpdateComplete_Main.js'
|
|
245
|
+
));
|
|
246
|
+
execFileSync(
|
|
247
|
+
process.execPath,
|
|
248
|
+
[
|
|
249
|
+
'--check',
|
|
250
|
+
join(
|
|
251
|
+
rootPath,
|
|
252
|
+
'app/src/generatedCode/FlowObjects/Update/source/UpdateRbacCallback_Main.js'
|
|
253
|
+
)
|
|
254
|
+
],
|
|
255
|
+
{ stdio: 'inherit' }
|
|
256
|
+
);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test('rejects deprecated FlowHook schemas', async () => {
|
|
260
|
+
const rootPath = await createRbacProject(true);
|
|
261
|
+
|
|
262
|
+
await assert.rejects(
|
|
263
|
+
generateCrudApplicationArtifacts(rootPath, { serviceTag: 'orders' }),
|
|
264
|
+
/generatedCodeConfig\.codeHookTag is no longer supported/
|
|
265
|
+
);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test('emits GetRangeHdrApi when an object defines a sortKey identifier', async () => {
|
|
269
|
+
const rootPath = await createProject(true);
|
|
270
|
+
await writeFile(
|
|
271
|
+
join(rootPath, 'app/src/schemas/ObjectSchemas/Order.js'),
|
|
272
|
+
"export default { objectType: 'order', generatedBy: 'userGenerated', identifiers: [{ type: 'identifier', fieldName: 'orderId' }, { type: 'sortKey', fieldName: 'createdAt' }], storageResources: { graph: { storageType: 'graph' } } };\n"
|
|
273
|
+
);
|
|
274
|
+
const result = await generateCrudApplicationArtifacts(rootPath, {
|
|
275
|
+
serviceTag: 'orders'
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
assert.equal(result.files.length, 27);
|
|
279
|
+
const yaml = await readFile(
|
|
280
|
+
join(rootPath, 'app/sls_yaml/generatedCode/source/flow-objects.yml'),
|
|
281
|
+
'utf8'
|
|
282
|
+
);
|
|
283
|
+
assert.match(yaml, /GetRangeHdrApi:/);
|
|
284
|
+
assert.match(yaml, /path: \/orders\/order\/getRange/);
|
|
285
|
+
const getRangeMain = await readFile(
|
|
286
|
+
join(
|
|
287
|
+
rootPath,
|
|
288
|
+
'app/src/generatedCode/FlowObjects/Get/source/GetRange_Main.js'
|
|
289
|
+
),
|
|
290
|
+
'utf8'
|
|
291
|
+
);
|
|
292
|
+
assert.match(getRangeMain, /identifierField/);
|
|
293
|
+
assert.match(getRangeMain, /startKey/);
|
|
294
|
+
assert.match(getRangeMain, /itemNumber/);
|
|
295
|
+
assert.match(getRangeMain, /queryResults/);
|
|
296
|
+
});
|
|
175
297
|
|
|
176
298
|
test('removes managed CRUD artifacts when no applicable object remains', async () => {
|
|
177
299
|
const rootPath = await createProject(true);
|
|
@@ -219,12 +341,29 @@ async function createProject(withCrudObject) {
|
|
|
219
341
|
await writeFile(
|
|
220
342
|
join(schemaRoot, 'ObjectSchemas/Order.js'),
|
|
221
343
|
withCrudObject
|
|
222
|
-
? "export default { objectType: 'order', generatedBy: 'userGenerated' };\n"
|
|
344
|
+
? "export default { objectType: 'order', generatedBy: 'userGenerated', storageResources: { graph: { storageType: 'graph' } } };\n"
|
|
223
345
|
: "export default { objectType: 'order' };\n"
|
|
224
346
|
);
|
|
347
|
+
return rootPath;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
async function createRbacProject(withHook = false, withGraph = false) {
|
|
351
|
+
const rootPath = await mkdtemp(
|
|
352
|
+
join(tmpdir(), 'generate-service-code-v2-crud-rbac-artifacts-')
|
|
353
|
+
);
|
|
354
|
+
roots.add(rootPath);
|
|
355
|
+
const schemaRoot = join(rootPath, 'app/src/schemas');
|
|
356
|
+
await mkdir(join(schemaRoot, 'ObjectSchemas'), { recursive: true });
|
|
357
|
+
await mkdir(join(schemaRoot, 'FlowSchemas'), { recursive: true });
|
|
225
358
|
await writeFile(
|
|
226
|
-
join(schemaRoot, '
|
|
227
|
-
|
|
359
|
+
join(schemaRoot, 'ObjectSchemas/Order.js'),
|
|
360
|
+
`export default { objectType: 'order', generatedBy: 'userGenerated', belongTo: { serviceTag: 'UserAccount', objectType: 'user' }${withGraph ? ", storageResources: { graph: { storageType: 'graph' } }" : ''} };\n`
|
|
228
361
|
);
|
|
362
|
+
if (withHook) {
|
|
363
|
+
await writeFile(
|
|
364
|
+
join(schemaRoot, 'FlowSchemas/OrderBeforeLogical.js'),
|
|
365
|
+
"export default { flowTag: 'orderHook', event: ['ownTopic'], generatedCodeConfig: { generatedCodeTag: 'create', codeHookTag: 'beforeLogical', invocationType: 'ownTopic', objType: { objectType: 'order' } } };\n"
|
|
366
|
+
);
|
|
367
|
+
}
|
|
229
368
|
return rootPath;
|
|
230
369
|
}
|