@izara_project/izara-core-generate-service-code 1.0.88 → 1.0.90
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 +1 -1
- package/src/compile/flowValidator.js +25 -0
- package/src/compile/loadSchemas.js +154 -0
- package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +3 -7
- package/src/emit/crud/crudActionPlan.js +67 -9
- package/src/emit/crud/emitDynamicCrudFlowSchemas.js +15 -32
- package/src/emit/crud/templates/CreateComplete_Main.ejs +135 -10
- package/src/emit/crud/templates/CreateEndpoint_Main.ejs +33 -27
- package/src/emit/crud/templates/CreateLogicalLib.ejs +282 -95
- package/src/emit/crud/templates/CreateNodeCallback_Main.ejs +38 -21
- package/src/emit/crud/templates/DeleteAfterHookCallback_Main.ejs +118 -0
- package/src/emit/crud/templates/DeleteComplete_Main.ejs +124 -10
- package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +26 -16
- package/src/emit/crud/templates/DeleteLogicalLib.ejs +113 -4
- package/src/emit/crud/templates/UpdateAfterHookCallback_Main.ejs +120 -0
- package/src/emit/crud/templates/UpdateComplete_Main.ejs +104 -10
- package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +34 -22
- package/src/emit/crud/templates/UpdateLogicalLib.ejs +121 -4
- package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +8 -2
- package/src/emit/rbac/templates/CancelUsage_Main.ejs +34 -14
- package/src/emit/rbac/templates/ConfirmUsage_Main.ejs +34 -14
- package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +20 -13
- package/src/emit/resource/emitResourceArtifacts.js +55 -14
- package/test/createTargetRoleHandler.test.js +11 -8
- package/test/crudApplicationArtifacts.test.js +440 -0
- package/test/dynamicCrudFlowSchemas.test.js +10 -5
- package/test/dynamicRbacFlowSchemas.test.js +24 -2
- package/test/graphDrivenFlowSchema.test.js +36 -0
- package/test/resourceArtifacts.test.js +113 -0
|
@@ -172,7 +172,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
|
|
|
172
172
|
_setL1Cache(fullCacheKey, result);
|
|
173
173
|
await _setL2Cache(_izContext, submittedByUserId, cacheSubKey, result);
|
|
174
174
|
await _publicMessageOut(_izContext, { ...result, parentFlowId });
|
|
175
|
-
return
|
|
175
|
+
return result;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
// 1. Check L1 In-Memory Cache
|
|
@@ -180,7 +180,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
|
|
|
180
180
|
if (l1Hit) {
|
|
181
181
|
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] L1 Cache hit:`, l1Hit);
|
|
182
182
|
await _publicMessageOut(_izContext, { ...l1Hit, parentFlowId });
|
|
183
|
-
return;
|
|
183
|
+
return l1Hit;
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
// 2. Check L2 Shared DynamoDB Cache
|
|
@@ -189,7 +189,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
|
|
|
189
189
|
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] L2 Cache hit:`, l2Hit);
|
|
190
190
|
_setL1Cache(fullCacheKey, l2Hit);
|
|
191
191
|
await _publicMessageOut(_izContext, { ...l2Hit, parentFlowId });
|
|
192
|
-
return;
|
|
192
|
+
return l2Hit;
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] Cache miss — querying DynamoDB`);
|
|
@@ -237,7 +237,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
|
|
|
237
237
|
_setL1Cache(fullCacheKey, result);
|
|
238
238
|
await _setL2Cache(_izContext, submittedByUserId, cacheSubKey, result);
|
|
239
239
|
await _publicMessageOut(_izContext, { ...result, parentFlowId });
|
|
240
|
-
return;
|
|
240
|
+
return result;
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
const rolePermissionsTable = dynamodbSharedLib.tableName(
|
|
@@ -289,19 +289,26 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
|
|
|
289
289
|
|
|
290
290
|
await _publicMessageOut(_izContext, { ...cacheResult, parentFlowId });
|
|
291
291
|
|
|
292
|
-
return
|
|
292
|
+
return cacheResult;
|
|
293
293
|
}
|
|
294
294
|
|
|
295
295
|
async function _publicMessageOut(_izContext, message) {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
296
|
+
try {
|
|
297
|
+
const topicOut = snsSharedLib.snsTopicArnByFlowSchema(
|
|
298
|
+
_izContext,
|
|
299
|
+
process.env.iz_flowTag + '_Out'
|
|
300
|
+
);
|
|
300
301
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
302
|
+
await sns.publishAsync(_izContext, {
|
|
303
|
+
TopicArn: topicOut,
|
|
304
|
+
Message: JSON.stringify(message)
|
|
305
|
+
});
|
|
306
|
+
} catch (error) {
|
|
307
|
+
_izContext.logger.warn(
|
|
308
|
+
`[${process.env.iz_serviceTag}:_publicMessageOut] Failed to publish message out:`,
|
|
309
|
+
error
|
|
310
|
+
);
|
|
311
|
+
}
|
|
305
312
|
}
|
|
306
313
|
|
|
307
314
|
function _matchesParameterGroups(groups, input) {
|
|
@@ -204,7 +204,7 @@ export const resourceArtifactPaths = [
|
|
|
204
204
|
function createResourceState({ objects, relationships, flows }) {
|
|
205
205
|
const crud = objects.some(isCrudObject);
|
|
206
206
|
const selfRbac = objects.some(hasSelfRbacAddOn);
|
|
207
|
-
const rbac = objects.some(
|
|
207
|
+
const rbac = objects.some((obj) => hasRbacAddOn(obj, objects));
|
|
208
208
|
const crudActionPlans = createCrudActionPlans(objects, flows);
|
|
209
209
|
|
|
210
210
|
const crudOwnTopics = crud ? [...CRUD_ACTIONS] : [];
|
|
@@ -244,9 +244,9 @@ function createResourceState({ objects, relationships, flows }) {
|
|
|
244
244
|
rbacSqsQueues.push(`Process${action}HdrSqs`);
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
|
-
if (
|
|
247
|
+
if (selfRbac) {
|
|
248
248
|
const flowTags = [
|
|
249
|
-
...new Set(objects.filter(
|
|
249
|
+
...new Set(objects.filter(hasSelfRbacAddOn).map(rbacFlowTag))
|
|
250
250
|
];
|
|
251
251
|
for (const flowTag of flowTags) {
|
|
252
252
|
rbacOwnTopics.push(flowTag);
|
|
@@ -433,6 +433,7 @@ function createResourceState({ objects, relationships, flows }) {
|
|
|
433
433
|
return {
|
|
434
434
|
crud,
|
|
435
435
|
rbac,
|
|
436
|
+
selfRbac,
|
|
436
437
|
crudActionPlans,
|
|
437
438
|
crudOwnTopics: [...new Set(crudOwnTopics)].filter(Boolean).sort(),
|
|
438
439
|
crudSqsQueues: [...new Set(crudSqsQueues)].filter(Boolean).sort(),
|
|
@@ -638,7 +639,7 @@ async function renderDynamo(state) {
|
|
|
638
639
|
const [system, ...tables] = await Promise.all([
|
|
639
640
|
render('SystemDynamoDB_Yaml.ejs', {
|
|
640
641
|
hasS3: state.s3FlowTags.length > 0,
|
|
641
|
-
hasRbac: state.
|
|
642
|
+
hasRbac: state.selfRbac
|
|
642
643
|
}),
|
|
643
644
|
...state.tables.map((table) => render('DynamoDBTable_Yaml.ejs', table))
|
|
644
645
|
]);
|
|
@@ -650,7 +651,7 @@ async function renderIam(state) {
|
|
|
650
651
|
const roles = ['ProcFindDataRole', 'RegisterRole'];
|
|
651
652
|
if (state.crud) roles.push('FlowObjectsRole');
|
|
652
653
|
if (state.relationships) roles.push('FlowRelationshipRole');
|
|
653
|
-
if (state.
|
|
654
|
+
if (state.selfRbac) roles.push('FlowRbacRole');
|
|
654
655
|
if (state.websocket) roles.push('WebSocketMainRole');
|
|
655
656
|
roles.push(...state.customRoles);
|
|
656
657
|
const sortedRoles = roles.sort();
|
|
@@ -668,8 +669,21 @@ async function renderIam(state) {
|
|
|
668
669
|
function getRbacValidatorServiceTags(state) {
|
|
669
670
|
const tags = new Set();
|
|
670
671
|
for (const obj of state.objects || []) {
|
|
671
|
-
|
|
672
|
-
|
|
672
|
+
const targets = obj?.belongTo
|
|
673
|
+
? Array.isArray(obj.belongTo)
|
|
674
|
+
? obj.belongTo
|
|
675
|
+
: [obj.belongTo]
|
|
676
|
+
: [];
|
|
677
|
+
for (const target of targets) {
|
|
678
|
+
if (target?.serviceTag && target.serviceTag !== 'Rbac') {
|
|
679
|
+
tags.add(target.serviceTag);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
if (
|
|
683
|
+
obj.belongToSchema?.serviceTag &&
|
|
684
|
+
obj.belongToSchema.serviceTag !== 'Rbac'
|
|
685
|
+
) {
|
|
686
|
+
tags.add(obj.belongToSchema.serviceTag);
|
|
673
687
|
}
|
|
674
688
|
}
|
|
675
689
|
return Array.from(tags).sort();
|
|
@@ -751,7 +765,7 @@ function addStatement(statements, action, resource) {
|
|
|
751
765
|
|
|
752
766
|
function roleNeedsSchemaRead(state, roleName) {
|
|
753
767
|
if (!roleName.endsWith('Role') || !roleName) return false;
|
|
754
|
-
return roleName !== 'FlowRbacRole' || state.
|
|
768
|
+
return roleName !== 'FlowRbacRole' || state.selfRbac;
|
|
755
769
|
}
|
|
756
770
|
|
|
757
771
|
function roleTableNames(state, roleName) {
|
|
@@ -767,7 +781,7 @@ function roleTableNames(state, roleName) {
|
|
|
767
781
|
'AwaitingMultipleSteps',
|
|
768
782
|
'AwaitingMultipleStepByPending',
|
|
769
783
|
'WebSocketTask',
|
|
770
|
-
...(state.
|
|
784
|
+
...(state.selfRbac
|
|
771
785
|
? [
|
|
772
786
|
'Roles',
|
|
773
787
|
'TargetRoles',
|
|
@@ -958,7 +972,7 @@ function roleTopicNames(state, roleName) {
|
|
|
958
972
|
}
|
|
959
973
|
}
|
|
960
974
|
}
|
|
961
|
-
return topics;
|
|
975
|
+
return [...new Set(topics)];
|
|
962
976
|
}
|
|
963
977
|
if (roleName === 'FlowRelationshipRole') {
|
|
964
978
|
const actions = state.outTopics.filter((topic) =>
|
|
@@ -1102,8 +1116,15 @@ function roleExternalLambdaArns(state, roleName) {
|
|
|
1102
1116
|
(tag) =>
|
|
1103
1117
|
`arn:aws:lambda:\${self:custom.iz_region}:\${self:custom.iz_accountId}:function:${tag}\${self:custom.iz_stage}ValidatorRbacHdrInv`
|
|
1104
1118
|
);
|
|
1119
|
+
if (roleName === 'FlowObjectsRole') {
|
|
1120
|
+
return [
|
|
1121
|
+
`arn:aws:lambda:\${self:custom.iz_region}:\${self:custom.iz_accountId}:function:Rbac\${self:custom.iz_stage}ValidatorRbacHdrInv`,
|
|
1122
|
+
`arn:aws:lambda:\${self:custom.iz_region}:\${self:custom.iz_accountId}:function:Rbac\${self:custom.iz_stage}ConfirmUsageHdrInv`,
|
|
1123
|
+
`arn:aws:lambda:\${self:custom.iz_region}:\${self:custom.iz_accountId}:function:Rbac\${self:custom.iz_stage}CancelUsageHdrInv`,
|
|
1124
|
+
...extraLambdas
|
|
1125
|
+
];
|
|
1126
|
+
}
|
|
1105
1127
|
if (
|
|
1106
|
-
roleName === 'FlowObjectsRole' ||
|
|
1107
1128
|
roleName === 'FlowRbacRole' ||
|
|
1108
1129
|
roleName === 'FlowRelationshipRole'
|
|
1109
1130
|
) {
|
|
@@ -1281,8 +1302,28 @@ function hasSelfRbacAddOn(object) {
|
|
|
1281
1302
|
);
|
|
1282
1303
|
}
|
|
1283
1304
|
|
|
1284
|
-
function hasRbacAddOn(object) {
|
|
1285
|
-
|
|
1305
|
+
function hasRbacAddOn(object, objects = []) {
|
|
1306
|
+
const selfHasRbac = hasSelfRbacAddOn(object);
|
|
1307
|
+
const belongToTargets = object?.belongTo
|
|
1308
|
+
? Array.isArray(object.belongTo)
|
|
1309
|
+
? object.belongTo
|
|
1310
|
+
: [object.belongTo]
|
|
1311
|
+
: [];
|
|
1312
|
+
const belongToHasRbac =
|
|
1313
|
+
(Array.isArray(object?.belongToSchema?.addOnDataStructure) &&
|
|
1314
|
+
object.belongToSchema.addOnDataStructure.some(
|
|
1315
|
+
(addOn) => addOn?.type === 'rbac'
|
|
1316
|
+
)) ||
|
|
1317
|
+
belongToTargets.some((target) => {
|
|
1318
|
+
if (target?.serviceTag === 'UserAccount') return true;
|
|
1319
|
+
const matched = objects.find(
|
|
1320
|
+
(candidate) =>
|
|
1321
|
+
candidate?.serviceTag === target?.serviceTag &&
|
|
1322
|
+
candidate?.objectType === target?.objectType
|
|
1323
|
+
);
|
|
1324
|
+
return hasSelfRbacAddOn(matched);
|
|
1325
|
+
});
|
|
1326
|
+
return selfHasRbac || belongToHasRbac;
|
|
1286
1327
|
}
|
|
1287
1328
|
|
|
1288
1329
|
/**
|
|
@@ -1292,7 +1333,7 @@ function hasRbacAddOn(object) {
|
|
|
1292
1333
|
*/
|
|
1293
1334
|
function rbacFlowQueueNames(state) {
|
|
1294
1335
|
const flowTags = [
|
|
1295
|
-
...new Set((state.objects ?? []).filter(
|
|
1336
|
+
...new Set((state.objects ?? []).filter(hasSelfRbacAddOn).map(rbacFlowTag))
|
|
1296
1337
|
];
|
|
1297
1338
|
return flowTags.map((flowTag) => `${flowTag}HdrSqs`);
|
|
1298
1339
|
}
|
|
@@ -106,16 +106,19 @@ test('generates RBAC own-topic WebSocket entries alongside their SQS handlers',
|
|
|
106
106
|
),
|
|
107
107
|
/ListTargetRole/
|
|
108
108
|
);
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
'app/src/generatedCode/FlowRbac/ValidatorRbac/source/ValidatorRbac_Main.js'
|
|
114
|
-
),
|
|
115
|
-
'utf8'
|
|
109
|
+
const validatorRbac = await readFile(
|
|
110
|
+
join(
|
|
111
|
+
rootPath,
|
|
112
|
+
'app/src/generatedCode/FlowRbac/ValidatorRbac/source/ValidatorRbac_Main.js'
|
|
116
113
|
),
|
|
117
|
-
|
|
114
|
+
'utf8'
|
|
118
115
|
);
|
|
116
|
+
assert.match(validatorRbac, /Fast-path triggered: user self-action/);
|
|
117
|
+
assert.match(validatorRbac, /return l1Hit;/);
|
|
118
|
+
assert.match(validatorRbac, /return l2Hit;/);
|
|
119
|
+
assert.match(validatorRbac, /return cacheResult;/);
|
|
120
|
+
assert.doesNotMatch(validatorRbac, /L1 Cache hit:[^;]+;\s*return;/);
|
|
121
|
+
assert.doesNotMatch(validatorRbac, /L2 Cache hit:[^;]+;\s*return;/);
|
|
119
122
|
assert.match(
|
|
120
123
|
await readFile(
|
|
121
124
|
join(
|