@izara_project/izara-core-generate-service-code 1.0.83 → 1.0.84

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 (59) hide show
  1. package/package.json +4 -4
  2. package/src/compile/flowValidator.js +13 -2
  3. package/src/compile/loadSchemas.js +116 -7
  4. package/src/emit/attributeTree/emitAttributeTreeSchemas.js +388 -0
  5. package/src/emit/coreFunction/emitCoreFunctionArtifacts.js +29 -1
  6. package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +16 -3
  7. package/src/emit/crud/crudActionPlan.js +38 -5
  8. package/src/emit/crud/emitCrudApplicationArtifacts.js +19 -4
  9. package/src/emit/crud/emitDynamicCrudFlowSchemas.js +27 -6
  10. package/src/emit/crud/generateCrudApplicationArtifacts.js +3 -2
  11. package/src/emit/crud/generateDynamicCrudFlowSchemas.js +3 -2
  12. package/src/emit/crud/loadLocalSchemaArrays.js +3 -3
  13. package/src/emit/crud/templates/CreateComplete_Main.ejs +1 -1
  14. package/src/emit/crud/templates/CreateEndpoint_Main.ejs +50 -19
  15. package/src/emit/crud/templates/CreateLogicalLib.ejs +73 -8
  16. package/src/emit/crud/templates/DeleteComplete_Main.ejs +1 -1
  17. package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +33 -8
  18. package/src/emit/crud/templates/DeleteLogicalLib.ejs +6 -3
  19. package/src/emit/crud/templates/FlowEndpointRbacCallback_Main.ejs +2 -2
  20. package/src/emit/crud/templates/FlowEndpoint_HdrWbs.ejs +7 -0
  21. package/src/emit/crud/templates/FlowMain_Wbs.ejs +1 -1
  22. package/src/emit/crud/templates/GetEndpoint_Main.ejs +13 -0
  23. package/src/emit/crud/templates/UpdateComplete_Main.ejs +1 -1
  24. package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +52 -3
  25. package/src/emit/crud/templates/UpdateLogicalLib.ejs +4 -3
  26. package/src/emit/custom/customFlows.js +47 -0
  27. package/src/emit/custom/emitCustomFlowApplicationArtifacts.js +168 -49
  28. package/src/emit/custom/generateCustomFlowApplicationArtifacts.js +1 -1
  29. package/src/emit/custom/templates/FlowEndpointRbacCallback_Main.ejs +194 -0
  30. package/src/emit/custom/templates/FlowEndpoint_Main.ejs +14 -22
  31. package/src/emit/custom/templates/FlowStep_Main.ejs +1 -1
  32. package/src/emit/function/templates/WbsFunction_Yaml.ejs +0 -2
  33. package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +33 -8
  34. package/src/emit/rbac/emitRbacApplicationArtifacts.js +204 -7
  35. package/src/emit/rbac/templates/CancelUsage_Main.ejs +103 -0
  36. package/src/emit/rbac/templates/ConfirmUsage_Main.ejs +71 -0
  37. package/src/emit/rbac/templates/CreateTargetRole_Main.ejs +49 -6
  38. package/src/emit/rbac/templates/ListRbac_Main.ejs +9 -3
  39. package/src/emit/rbac/templates/RbacMutation_Main.ejs +1 -1
  40. package/src/emit/rbac/templates/ReservedDynamicUsage_Main.ejs +147 -0
  41. package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +59 -23
  42. package/src/emit/rbac/templates/UserRoleMutation_Main.ejs +1 -1
  43. package/src/emit/relationship/emitDynamicRelationshipFlowSchemas.js +1 -1
  44. package/src/emit/relationship/templates/ChangeRelationship_Main.ejs +4 -4
  45. package/src/emit/resource/emitResourceArtifacts.js +342 -108
  46. package/src/emit/resource/templates/SystemDynamoDB_Yaml.ejs +53 -2
  47. package/src/emit/system/emitSystemWebSocketArtifacts.js +6 -5
  48. package/src/emit/system/templates/WebSocketConnect.ejs +71 -12
  49. package/src/emit/system/templates/WebSocketConnect_Yaml.ejs +7 -0
  50. package/src/emit/systemText/emitSystemTextSchemas.js +1 -1
  51. package/src/generate.js +41 -10
  52. package/test/attributeTreeSchemas.test.js +219 -0
  53. package/test/createTargetRoleHandler.test.js +59 -4
  54. package/test/crudApplicationArtifacts.test.js +20 -3
  55. package/test/customFlowApplicationArtifacts.test.js +5 -5
  56. package/test/dynamicCrudFlowSchemas.test.js +91 -4
  57. package/test/dynamicRbacFlowSchemas.test.js +15 -2
  58. package/test/publicApi.test.js +6 -6
  59. package/test/resourceArtifacts.test.js +68 -27
@@ -0,0 +1,103 @@
1
+ /*
2
+ Copyright (C) 2020 Sven Mason <http://izara.io>
3
+
4
+ This program is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU Affero General Public License as
6
+ published by the Free Software Foundation, either version 3 of the
7
+ License, or (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU Affero General Public License for more details.
13
+
14
+ You should have received a copy of the GNU Affero General Public License
15
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+ */
17
+
18
+ import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
19
+ import snsSharedLib from '@izara_project/izara-core-library-sns';
20
+ import { sns } from '@izara_project/izara-core-library-external-request';
21
+ import { consts, NoRetryError } from '@izara_project/izara-core-library-core';
22
+
23
+ export default async function CancelUsage(_izContext, requestParams) {
24
+ try {
25
+ const submittedByUserId =
26
+ requestParams.submittedByUserId ||
27
+ _izContext.correlationIds?.get(consts.SUBMITTED_BY_USER_ID);
28
+ const { limitTag, uniqueIdentify } = requestParams;
29
+
30
+ const targetIdentifiers = requestParams.targetIdentifiers;
31
+ let identifiersConcat = requestParams.identifiersConcat;
32
+ if (!identifiersConcat && targetIdentifiers && typeof targetIdentifiers === 'object') {
33
+ identifiersConcat = Object.entries(targetIdentifiers)
34
+ .map(([k, v]) => `${k}#${v}`)
35
+ .join('_');
36
+ }
37
+ if (!identifiersConcat) {
38
+ identifiersConcat = submittedByUserId;
39
+ }
40
+
41
+ if (!identifiersConcat || !limitTag || !uniqueIdentify) {
42
+ throw new NoRetryError(
43
+ 'identifiersConcat, limitTag, and uniqueIdentify are required.'
44
+ );
45
+ }
46
+
47
+ const runningTotalReservedId = `${identifiersConcat}_${limitTag}`;
48
+ const reservedRecord = await dynamodbSharedLib.getItem(
49
+ _izContext,
50
+ dynamodbSharedLib.tableName(_izContext, 'RunningTotalReservedUsage'),
51
+ {
52
+ runningTotalReservedId,
53
+ usedTimestampId: uniqueIdentify
54
+ }
55
+ );
56
+
57
+ const usageCount = reservedRecord?.usageCount || 0;
58
+
59
+ await dynamodbSharedLib.updateItem(
60
+ _izContext,
61
+ dynamodbSharedLib.tableName(_izContext, 'RunningTotalReservedUsage'),
62
+ {
63
+ runningTotalReservedId,
64
+ usedTimestampId: uniqueIdentify
65
+ },
66
+ {
67
+ runningTotalUsageStatus: 'cancelled'
68
+ }
69
+ );
70
+
71
+ if (usageCount > 0) {
72
+ const usageRecord = await dynamodbSharedLib.getItem(
73
+ _izContext,
74
+ dynamodbSharedLib.tableName(_izContext, 'RunningTotalUsage'),
75
+ { identifiersConcat, limitTag }
76
+ );
77
+ if (usageRecord) {
78
+ const newTotal = Math.max(0, (usageRecord.totalCount || 0) - usageCount);
79
+ await dynamodbSharedLib.putItem(
80
+ _izContext,
81
+ dynamodbSharedLib.tableName(_izContext, 'RunningTotalUsage'),
82
+ {
83
+ identifiersConcat,
84
+ limitTag,
85
+ totalCount: newTotal,
86
+ updateUuid: _izContext.uniqueRequestId || `${Date.now()}`
87
+ }
88
+ );
89
+ }
90
+ }
91
+
92
+ await sns.publishMessage(_izContext, {
93
+ message: { returnResult: { status: true, uniqueIdentify } },
94
+ TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
95
+ _izContext,
96
+ 'CancelUsage_Out'
97
+ )
98
+ });
99
+ } catch (error) {
100
+ _izContext.logger.error(`[${process.env.iz_serviceTag}:CancelUsage] error:`, error);
101
+ throw error;
102
+ }
103
+ }
@@ -0,0 +1,71 @@
1
+ /*
2
+ Copyright (C) 2020 Sven Mason <http://izara.io>
3
+
4
+ This program is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU Affero General Public License as
6
+ published by the Free Software Foundation, either version 3 of the
7
+ License, or (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU Affero General Public License for more details.
13
+
14
+ You should have received a copy of the GNU Affero General Public License
15
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+ */
17
+
18
+ import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
19
+ import snsSharedLib from '@izara_project/izara-core-library-sns';
20
+ import { sns } from '@izara_project/izara-core-library-external-request';
21
+ import { consts, NoRetryError } from '@izara_project/izara-core-library-core';
22
+
23
+ export default async function ConfirmUsage(_izContext, requestParams) {
24
+ try {
25
+ const submittedByUserId =
26
+ requestParams.submittedByUserId ||
27
+ _izContext.correlationIds?.get(consts.SUBMITTED_BY_USER_ID);
28
+ const { limitTag, uniqueIdentify } = requestParams;
29
+
30
+ const targetIdentifiers = requestParams.targetIdentifiers;
31
+ let identifiersConcat = requestParams.identifiersConcat;
32
+ if (!identifiersConcat && targetIdentifiers && typeof targetIdentifiers === 'object') {
33
+ identifiersConcat = Object.entries(targetIdentifiers)
34
+ .map(([k, v]) => `${k}#${v}`)
35
+ .join('_');
36
+ }
37
+ if (!identifiersConcat) {
38
+ identifiersConcat = submittedByUserId;
39
+ }
40
+
41
+ if (!identifiersConcat || !limitTag || !uniqueIdentify) {
42
+ throw new NoRetryError(
43
+ 'identifiersConcat, limitTag, and uniqueIdentify are required.'
44
+ );
45
+ }
46
+
47
+ const runningTotalReservedId = `${identifiersConcat}_${limitTag}`;
48
+ await dynamodbSharedLib.updateItem(
49
+ _izContext,
50
+ dynamodbSharedLib.tableName(_izContext, 'RunningTotalReservedUsage'),
51
+ {
52
+ runningTotalReservedId,
53
+ usedTimestampId: uniqueIdentify
54
+ },
55
+ {
56
+ runningTotalUsageStatus: 'confirmed'
57
+ }
58
+ );
59
+
60
+ await sns.publishMessage(_izContext, {
61
+ message: { returnResult: { status: true, uniqueIdentify } },
62
+ TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
63
+ _izContext,
64
+ 'ConfirmUsage_Out'
65
+ )
66
+ });
67
+ } catch (error) {
68
+ _izContext.logger.error(`[${process.env.iz_serviceTag}:ConfirmUsage] error:`, error);
69
+ throw error;
70
+ }
71
+ }
@@ -23,23 +23,66 @@ import { consts } from '@izara_project/izara-core-library-core';
23
23
 
24
24
  export default async function CreateTargetRole(_izContext, requestParams) {
25
25
  try {
26
- const userId = _izContext.correlationIds.get(consts.BASE_USER_ID);
27
- const { roleName } = requestParams;
28
- const roleId = identifierUuid();
26
+ const submittedByUserId =
27
+ requestParams.submittedByUserId ||
28
+ _izContext.correlationIds?.get(consts.SUBMITTED_BY_USER_ID);
29
+
30
+ const targetIdentifiers = requestParams.targetIdentifiers;
31
+ let identifiersConcat = requestParams.identifiersConcat;
32
+ if (!identifiersConcat && targetIdentifiers && typeof targetIdentifiers === 'object') {
33
+ identifiersConcat = Object.entries(targetIdentifiers)
34
+ .map(([k, v]) => `${k}#${v}`)
35
+ .join('_');
36
+ }
37
+ if (!identifiersConcat) {
38
+ identifiersConcat = submittedByUserId;
39
+ }
40
+
41
+ const { roleName = 'owner', roleId: inputRoleId } = requestParams;
42
+ const roleId = inputRoleId || roleName || identifierUuid();
43
+ const roleIdKey = `${identifiersConcat}_${roleId}`;
44
+
29
45
  await dynamodbSharedLib.putItem(
30
46
  _izContext,
31
47
  dynamodbSharedLib.tableName(_izContext, 'TargetRoles'),
32
- { targetId: userId, roleIdKey: `${userId}_${roleId}`, roleName }
48
+ {
49
+ identifiersConcat,
50
+ roleIdKey,
51
+ roleName,
52
+ submittedByUserId
53
+ }
33
54
  );
55
+
56
+ if (submittedByUserId) {
57
+ try {
58
+ await dynamodbSharedLib.transactWriteItems(_izContext, [
59
+ {
60
+ Put: {
61
+ TableName: dynamodbSharedLib.tableName(_izContext, 'RoleUsers'),
62
+ Item: { roleIdKey, submittedByUserId }
63
+ }
64
+ },
65
+ {
66
+ Put: {
67
+ TableName: dynamodbSharedLib.tableName(_izContext, 'UserRoles'),
68
+ Item: { submittedByUserId, roleIdKey }
69
+ }
70
+ }
71
+ ]);
72
+ } catch (assignError) {
73
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:CreateTargetRole] UserRoles transactWrite ignored:', assignError);
74
+ }
75
+ }
76
+
34
77
  await sns.publishMessage(_izContext, {
35
- message: { returnResult: { roleId } },
78
+ message: { returnResult: { roleId, roleIdKey } },
36
79
  TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
37
80
  _izContext,
38
81
  'CreateTargetRole_Out'
39
82
  )
40
83
  });
41
84
  } catch (error) {
42
- _izContext.logger.error('[Rbac:CreateTargetRole] error:', error);
85
+ _izContext.logger.error(`[${process.env.iz_serviceTag}:CreateTargetRole] error:`, error);
43
86
  throw error;
44
87
  }
45
88
  }
@@ -22,8 +22,14 @@ import { consts, NoRetryError } from '@izara_project/izara-core-library-core';
22
22
 
23
23
  export default async function <%- flowTag %>(_izContext, requestParams) {
24
24
  try {
25
- <% if (useContextUserId) { -%>
26
- const userId = _izContext.correlationIds.get(consts.BASE_USER_ID);
25
+ <% if (flowTag === 'ListTargetRole') { -%>
26
+ const userId = requestParams.submittedByUserId || _izContext.correlationIds.get(consts.SUBMITTED_BY_USER_ID);
27
+ const identifiersConcat =
28
+ requestParams.identifiersConcat || requestParams.targetId || userId;
29
+ if (!identifiersConcat) throw new NoRetryError('Identifier not found.');
30
+ const queryParams = { identifiersConcat };
31
+ <% } else if (useContextUserId) { -%>
32
+ const userId = requestParams.submittedByUserId || _izContext.correlationIds.get(consts.SUBMITTED_BY_USER_ID);
27
33
  if (!userId) throw new NoRetryError('User Id not found.');
28
34
  const queryParams = { userId };
29
35
  <% } else { -%>
@@ -43,7 +49,7 @@ export default async function <%- flowTag %>(_izContext, requestParams) {
43
49
  )
44
50
  });
45
51
  } catch (error) {
46
- _izContext.logger.error('[Rbac:<%- flowTag %>] error:', error);
52
+ _izContext.logger.error(`[${process.env.iz_serviceTag}:<%- flowTag %>] error:`, error);
47
53
  throw error;
48
54
  }
49
55
  }
@@ -36,7 +36,7 @@ export default async function <%- flowTag %>(_izContext, requestParams) {
36
36
  )
37
37
  });
38
38
  } catch (error) {
39
- _izContext.logger.error('[Rbac:<%- flowTag %>] error:', error);
39
+ _izContext.logger.error(`[${process.env.iz_serviceTag}:<%- flowTag %>] error:`, error);
40
40
  throw error;
41
41
  }
42
42
  }
@@ -0,0 +1,147 @@
1
+ /*
2
+ Copyright (C) 2020 Sven Mason <http://izara.io>
3
+
4
+ This program is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU Affero General Public License as
6
+ published by the Free Software Foundation, either version 3 of the
7
+ License, or (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU Affero General Public License for more details.
13
+
14
+ You should have received a copy of the GNU Affero General Public License
15
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+ */
17
+
18
+ import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
19
+ import snsSharedLib from '@izara_project/izara-core-library-sns';
20
+ import { sns } from '@izara_project/izara-core-library-external-request';
21
+ import { consts, NoRetryError } from '@izara_project/izara-core-library-core';
22
+
23
+ export default async function ReservedDynamicUsage(_izContext, requestParams) {
24
+ try {
25
+ const submittedByUserId =
26
+ requestParams.submittedByUserId ||
27
+ _izContext.correlationIds?.get(consts.SUBMITTED_BY_USER_ID);
28
+ const {
29
+ limitTag,
30
+ value = 1,
31
+ usedTimestamp = new Date().toISOString(),
32
+ roleLimitId
33
+ } = requestParams;
34
+
35
+ const targetIdentifiers = requestParams.targetIdentifiers;
36
+ let identifiersConcat = requestParams.identifiersConcat;
37
+ if (!identifiersConcat && targetIdentifiers && typeof targetIdentifiers === 'object') {
38
+ identifiersConcat = Object.entries(targetIdentifiers)
39
+ .map(([k, v]) => `${k}#${v}`)
40
+ .join('_');
41
+ }
42
+ if (!identifiersConcat) {
43
+ identifiersConcat = submittedByUserId;
44
+ }
45
+
46
+ if (!identifiersConcat) {
47
+ throw new NoRetryError('Identifier not found.');
48
+ }
49
+ if (!limitTag) {
50
+ throw new NoRetryError('limitTag is required.');
51
+ }
52
+
53
+ const uniqueIdentify = `${usedTimestamp}_${_izContext.uniqueRequestId || Date.now()}`;
54
+ const roleLimitKeys = Array.isArray(requestParams.roleLimitIds)
55
+ ? requestParams.roleLimitIds
56
+ : [roleLimitId || `${identifiersConcat}_${limitTag}`];
57
+
58
+ let maxLimit = Infinity;
59
+ const limitRecords = [];
60
+
61
+ for (const key of roleLimitKeys) {
62
+ const record = await dynamodbSharedLib.getItem(
63
+ _izContext,
64
+ dynamodbSharedLib.tableName(_izContext, 'RoleLimit'),
65
+ { roleLimitId: key, limitType: 'runningTotal' }
66
+ );
67
+ if (record && typeof record.limitValue === 'number') {
68
+ limitRecords.push(record);
69
+ }
70
+ }
71
+
72
+ if (limitRecords.length > 0) {
73
+ const isDecrease = (rec) =>
74
+ rec.effect === 'decrease' ||
75
+ rec.actionType === 'decrease' ||
76
+ rec.action === 'decrease';
77
+
78
+ const increaseLimits = limitRecords.filter((rec) => !isDecrease(rec));
79
+ const decreaseLimits = limitRecords.filter((rec) => isDecrease(rec));
80
+
81
+ const maxIncrease =
82
+ increaseLimits.length > 0
83
+ ? Math.max(...increaseLimits.map((r) => r.limitValue))
84
+ : Infinity;
85
+ const minDecrease =
86
+ decreaseLimits.length > 0
87
+ ? Math.min(...decreaseLimits.map((r) => r.limitValue))
88
+ : Infinity;
89
+
90
+ maxLimit = Math.min(maxIncrease, minDecrease);
91
+ }
92
+
93
+ const usageRecord = await dynamodbSharedLib.getItem(
94
+ _izContext,
95
+ dynamodbSharedLib.tableName(_izContext, 'RunningTotalUsage'),
96
+ { identifiersConcat, limitTag }
97
+ );
98
+ const currentTotal = usageRecord?.totalCount || 0;
99
+
100
+ let passStatus = false;
101
+ if (currentTotal + value <= maxLimit) {
102
+ passStatus = true;
103
+ const ttlTimestamp = Math.floor(Date.now() / 1000) + 3600;
104
+
105
+ await dynamodbSharedLib.putItem(
106
+ _izContext,
107
+ dynamodbSharedLib.tableName(_izContext, 'RunningTotalReservedUsage'),
108
+ {
109
+ runningTotalReservedId: `${identifiersConcat}_${limitTag}`,
110
+ usedTimestampId: uniqueIdentify,
111
+ usageCount: value,
112
+ runningTotalUsageStatus: 'reserved',
113
+ ttlTimestamp
114
+ }
115
+ );
116
+
117
+ await dynamodbSharedLib.putItem(
118
+ _izContext,
119
+ dynamodbSharedLib.tableName(_izContext, 'RunningTotalUsage'),
120
+ {
121
+ identifiersConcat,
122
+ limitTag,
123
+ totalCount: currentTotal + value,
124
+ updateUuid: _izContext.uniqueRequestId || `${Date.now()}`
125
+ }
126
+ );
127
+ }
128
+
129
+ const returnResult = {
130
+ passStatus,
131
+ uniqueIdentify,
132
+ limitTag,
133
+ identifiersConcat
134
+ };
135
+
136
+ await sns.publishMessage(_izContext, {
137
+ message: { returnResult },
138
+ TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
139
+ _izContext,
140
+ 'ReservedDynamicUsage_Out'
141
+ )
142
+ });
143
+ } catch (error) {
144
+ _izContext.logger.error(`[${process.env.iz_serviceTag}:ReservedDynamicUsage] error:`, error);
145
+ throw error;
146
+ }
147
+ }
@@ -48,11 +48,11 @@ function _setL1Cache(key, value) {
48
48
  // ── L2 Cache (Shared DynamoDB TTL Cache) ──────────────────────────────────────
49
49
  const L2_TTL_SECONDS = 5 * 60; // 5 minutes
50
50
 
51
- async function _getL2Cache(_izContext, userId, cacheSubKey) {
51
+ async function _getL2Cache(_izContext, submittedByUserId, cacheSubKey) {
52
52
  try {
53
53
  const tableName = dynamodbSharedLib.tableName(_izContext, 'RbacPermissionCaches');
54
54
  const result = await dynamodbSharedLib.getItem(_izContext, tableName, {
55
- userId,
55
+ submittedByUserId,
56
56
  cacheKey: cacheSubKey
57
57
  });
58
58
  if (result && result.ttlTimestamp && result.ttlTimestamp > Math.floor(Date.now() / 1000)) {
@@ -72,12 +72,12 @@ async function _getL2Cache(_izContext, userId, cacheSubKey) {
72
72
  return null;
73
73
  }
74
74
 
75
- async function _setL2Cache(_izContext, userId, cacheSubKey, value) {
75
+ async function _setL2Cache(_izContext, submittedByUserId, cacheSubKey, value) {
76
76
  try {
77
77
  const tableName = dynamodbSharedLib.tableName(_izContext, 'RbacPermissionCaches');
78
78
  const ttlTimestamp = Math.floor(Date.now() / 1000) + L2_TTL_SECONDS;
79
79
  await dynamodbSharedLib.putItem(_izContext, tableName, {
80
- userId,
80
+ submittedByUserId,
81
81
  cacheKey: cacheSubKey,
82
82
  permission: value.returnValue ? value.returnValue.permission : value.permission,
83
83
  roleIdKey: value.returnValue ? value.returnValue.roleIdKey : value.roleIdKey,
@@ -90,10 +90,17 @@ async function _setL2Cache(_izContext, userId, cacheSubKey, value) {
90
90
  }
91
91
 
92
92
  export default async function <%- flowTag %>(_izContext, requestParams = {}) {
93
- const userId = _izContext.correlationIds.get(consts.BASE_USER_ID);
94
- const targetId = _izContext.correlationIds.get(consts.TARGET_ID);
95
- if (!userId || !targetId) {
96
- throw new Error('Missing required request parameters: userId or targetId');
93
+ const submittedByUserId =
94
+ requestParams.submittedByUserId ||
95
+ _izContext.correlationIds.get(consts.SUBMITTED_BY_USER_ID);
96
+
97
+ const targetIdentifiers = requestParams.targetIdentifiers;
98
+ let targetId = targetIdentifiers && typeof targetIdentifiers === 'object'
99
+ ? Object.entries(targetIdentifiers).map(([k, v]) => `${k}#${v}`).join('_')
100
+ : undefined;
101
+
102
+ if (!submittedByUserId || !targetIdentifiers) {
103
+ throw new Error('Missing required request parameters: submittedByUserId or targetIdentifiers');
97
104
  }
98
105
 
99
106
  const { flowTypeConcat, inputParameterGroups, parentFlowId } = requestParams;
@@ -101,40 +108,69 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
101
108
  throw new Error('Missing flowTypeConcat in requestParams');
102
109
  }
103
110
 
104
- _izContext.logger.debug('[<%- flowTag %>] requestParams:', {
105
- userId,
111
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] requestParams:', {
112
+ submittedByUserId,
106
113
  targetId,
107
114
  flowTypeConcat,
108
115
  inputParameterGroups
109
116
  });
110
117
 
111
118
  const cacheSubKey = `${targetId}#${flowTypeConcat}#${JSON.stringify(inputParameterGroups || {})}`;
112
- const fullCacheKey = `${userId}:${cacheSubKey}`;
119
+ const fullCacheKey = `${submittedByUserId}:${cacheSubKey}`;
120
+
121
+ // ── Fast-path for User Self-Action (submittedByUserId === targetId) ───────────────────
122
+ const isSelfUser = Boolean(
123
+ submittedByUserId &&
124
+ targetId &&
125
+ (submittedByUserId === targetId ||
126
+ targetId === `user#${submittedByUserId}` ||
127
+ targetId === `submittedByUserId#${submittedByUserId}`)
128
+ );
129
+
130
+ if (isSelfUser) {
131
+ _izContext.logger.debug(
132
+ '[<%- flowTag %>] Fast-path triggered: user self-action',
133
+ { submittedByUserId, targetId }
134
+ );
135
+ const result = {
136
+ returnValue: {
137
+ permission: true,
138
+ roleIdKey: 'self_user',
139
+ rbacType: 'self_user'
140
+ },
141
+ errorsFound: [],
142
+ status: 'Completed'
143
+ };
144
+ _setL1Cache(fullCacheKey, result);
145
+ await _setL2Cache(_izContext, submittedByUserId, cacheSubKey, result);
146
+ await _publicMessageOut(_izContext, { ...result, parentFlowId });
147
+ return 'Completed';
148
+ }
113
149
 
114
150
  // 1. Check L1 In-Memory Cache
115
151
  const l1Hit = _getL1Cache(fullCacheKey);
116
152
  if (l1Hit) {
117
- _izContext.logger.debug('[<%- flowTag %>] L1 Cache hit:', l1Hit);
153
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] L1 Cache hit:', l1Hit);
118
154
  await _publicMessageOut(_izContext, { ...l1Hit, parentFlowId });
119
155
  return;
120
156
  }
121
157
 
122
158
  // 2. Check L2 Shared DynamoDB Cache
123
- const l2Hit = await _getL2Cache(_izContext, userId, cacheSubKey);
159
+ const l2Hit = await _getL2Cache(_izContext, submittedByUserId, cacheSubKey);
124
160
  if (l2Hit) {
125
- _izContext.logger.debug('[<%- flowTag %>] L2 Cache hit:', l2Hit);
161
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] L2 Cache hit:', l2Hit);
126
162
  _setL1Cache(fullCacheKey, l2Hit);
127
163
  await _publicMessageOut(_izContext, { ...l2Hit, parentFlowId });
128
164
  return;
129
165
  }
130
166
 
131
- _izContext.logger.debug('[<%- flowTag %>] Cache miss — querying DynamoDB');
167
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] Cache miss — querying DynamoDB');
132
168
 
133
169
  // 3. Fallback to DB Query
134
170
  const userRolesResult = await dynamodbSharedLib.query(
135
171
  _izContext,
136
172
  dynamodbSharedLib.tableName(_izContext, 'UserRoles'),
137
- { userId }
173
+ { submittedByUserId }
138
174
  );
139
175
 
140
176
  let userRoles = [];
@@ -144,7 +180,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
144
180
  userRoles = userRolesResult.Items;
145
181
  }
146
182
 
147
- _izContext.logger.debug('[<%- flowTag %>] userRoles fetched:', { userRoles });
183
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] userRoles fetched:', { userRoles });
148
184
 
149
185
  const targetIdPrefix = `${targetId}_`;
150
186
  const targetRoles = [];
@@ -154,13 +190,13 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
154
190
  }
155
191
  }
156
192
 
157
- _izContext.logger.debug('[<%- flowTag %>] targetRoles filtered:', {
193
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] targetRoles filtered:', {
158
194
  targetIdPrefix,
159
195
  targetRoles
160
196
  });
161
197
 
162
198
  if (targetRoles.length === 0) {
163
- _izContext.logger.debug('[<%- flowTag %>] no targetRoles found, permission denied');
199
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] no targetRoles found, permission denied');
164
200
  const result = {
165
201
  returnValue: {
166
202
  permission: false,
@@ -171,7 +207,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
171
207
  status: 'Completed'
172
208
  };
173
209
  _setL1Cache(fullCacheKey, result);
174
- await _setL2Cache(_izContext, userId, cacheSubKey, result);
210
+ await _setL2Cache(_izContext, submittedByUserId, cacheSubKey, result);
175
211
  await _publicMessageOut(_izContext, { ...result, parentFlowId });
176
212
  return;
177
213
  }
@@ -189,7 +225,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
189
225
  })
190
226
  );
191
227
 
192
- _izContext.logger.debug('[<%- flowTag %>] rolePermissions fetched:', {
228
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] rolePermissions fetched:', {
193
229
  rolePermissions
194
230
  });
195
231
 
@@ -217,11 +253,11 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
217
253
  status: 'Completed'
218
254
  };
219
255
 
220
- _izContext.logger.debug('[<%- flowTag %>] validation result:', cacheResult);
256
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] validation result:', cacheResult);
221
257
 
222
258
  // Store in L1 & L2 Cache
223
259
  _setL1Cache(fullCacheKey, cacheResult);
224
- await _setL2Cache(_izContext, userId, cacheSubKey, cacheResult);
260
+ await _setL2Cache(_izContext, submittedByUserId, cacheSubKey, cacheResult);
225
261
 
226
262
  await _publicMessageOut(_izContext, { ...cacheResult, parentFlowId });
227
263
 
@@ -47,7 +47,7 @@ export default async function <%- flowTag %>(_izContext, requestParams) {
47
47
  )
48
48
  });
49
49
  } catch (error) {
50
- _izContext.logger.error('[Rbac:<%- flowTag %>] error:', error);
50
+ _izContext.logger.error(`[${process.env.iz_serviceTag}:<%- flowTag %>] error:`, error);
51
51
  throw error;
52
52
  }
53
53
  }
@@ -96,7 +96,7 @@ function createDynamicRelationshipFlow(action) {
96
96
 
97
97
  return {
98
98
  flowTag: action,
99
- initiateBy: 'systemWithNoUser',
99
+ initiateBy: 'systemNoUser',
100
100
  handleObj: 'one',
101
101
  statusType: 'none',
102
102
  event: outputTopic
@@ -78,7 +78,7 @@ export default async function <%- functionName %>(
78
78
 
79
79
  let originTimestamp = Date.now();
80
80
  let changedByUserId = _izContext.correlationIds.get(
81
- coreConsts.BASE_USER_ID
81
+ coreConsts.SUBMITTED_BY_USER_ID
82
82
  );
83
83
  const {
84
84
  firstObject,
@@ -191,14 +191,14 @@ export default async function <%- functionName %>(
191
191
  */
192
192
  // ? [Fix]: The legacy logic incorrectly blocked "user" from regular execution and allowed non-user values.
193
193
  // ? It has been corrected to require changeBy === "user" for normal execution.
194
- const isUserProcess = Boolean(_izContext.correlationIds?.get(consts.BASE_USER_ID));
194
+ const isUserProcess = Boolean(_izContext.correlationIds?.get(consts.SUBMITTED_BY_USER_ID));
195
195
  if (isUserProcess) {
196
196
  if (canChangeToRelType.changeBy !== 'user' && canChangeToRelType.changeBy !== 'systemWithUser') {
197
197
  errorsFound.push(`CanChangeToRelType changeBy is not user or systemWithUser`);
198
198
  }
199
199
  } else {
200
- if (canChangeToRelType.changeBy !== 'systemWithNoUser' && canChangeToRelType.changeBy !== 'system') {
201
- errorsFound.push(`CanChangeToRelType changeBy is not systemWithNoUser`);
200
+ if (canChangeToRelType.changeBy !== 'systemNoUser') {
201
+ errorsFound.push(`CanChangeToRelType changeBy is not systemNoUser`);
202
202
  }
203
203
  }
204
204
  }