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

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 (63) 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 +34 -1
  6. package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +29 -7
  7. package/src/emit/crud/crudActionPlan.js +41 -6
  8. package/src/emit/crud/emitCrudApplicationArtifacts.js +56 -8
  9. package/src/emit/crud/emitDynamicCrudFlowSchemas.js +153 -27
  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/CreateConfirmComplete_Main.ejs +54 -0
  15. package/src/emit/crud/templates/CreateEndpoint_Main.ejs +252 -154
  16. package/src/emit/crud/templates/CreateLogicalLib.ejs +1163 -236
  17. package/src/emit/crud/templates/CreateNodeCallback_Main.ejs +221 -0
  18. package/src/emit/crud/templates/DeleteComplete_Main.ejs +1 -1
  19. package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +33 -8
  20. package/src/emit/crud/templates/DeleteLogicalLib.ejs +6 -3
  21. package/src/emit/crud/templates/FlowEndpointRbacCallback_Main.ejs +20 -120
  22. package/src/emit/crud/templates/FlowEndpoint_HdrWbs.ejs +79 -10
  23. package/src/emit/crud/templates/FlowMain_Wbs.ejs +31 -35
  24. package/src/emit/crud/templates/GetEndpoint_Main.ejs +13 -0
  25. package/src/emit/crud/templates/UpdateComplete_Main.ejs +1 -1
  26. package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +52 -3
  27. package/src/emit/crud/templates/UpdateLogicalLib.ejs +4 -3
  28. package/src/emit/custom/customFlows.js +47 -0
  29. package/src/emit/custom/emitCustomFlowApplicationArtifacts.js +168 -49
  30. package/src/emit/custom/generateCustomFlowApplicationArtifacts.js +1 -1
  31. package/src/emit/custom/templates/FlowEndpointRbacCallback_Main.ejs +194 -0
  32. package/src/emit/custom/templates/FlowEndpoint_Main.ejs +14 -22
  33. package/src/emit/custom/templates/FlowStep_Main.ejs +1 -1
  34. package/src/emit/function/templates/WbsFunction_Yaml.ejs +0 -2
  35. package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +33 -8
  36. package/src/emit/rbac/emitRbacApplicationArtifacts.js +204 -7
  37. package/src/emit/rbac/templates/CancelUsage_Main.ejs +163 -0
  38. package/src/emit/rbac/templates/ConfirmUsage_Main.ejs +187 -0
  39. package/src/emit/rbac/templates/CreateTargetRole_Main.ejs +68 -6
  40. package/src/emit/rbac/templates/ListRbac_Main.ejs +9 -3
  41. package/src/emit/rbac/templates/RbacMutation_Main.ejs +1 -1
  42. package/src/emit/rbac/templates/ReservedDynamicUsage_Main.ejs +166 -0
  43. package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +84 -23
  44. package/src/emit/rbac/templates/UserRoleMutation_Main.ejs +11 -8
  45. package/src/emit/relationship/emitDynamicRelationshipFlowSchemas.js +1 -1
  46. package/src/emit/relationship/templates/ChangeRelationship_Main.ejs +4 -4
  47. package/src/emit/resource/emitResourceArtifacts.js +351 -108
  48. package/src/emit/resource/templates/GeneratedLambdaRoleServiceConfig.ejs +3 -1
  49. package/src/emit/resource/templates/SystemDynamoDB_Yaml.ejs +55 -2
  50. package/src/emit/shared/templates/FlowHandler_HdrSqs.ejs +1 -1
  51. package/src/emit/system/emitSystemWebSocketArtifacts.js +6 -5
  52. package/src/emit/system/templates/WebSocketConnect.ejs +71 -12
  53. package/src/emit/system/templates/WebSocketConnect_Yaml.ejs +6 -0
  54. package/src/emit/systemText/emitSystemTextSchemas.js +1 -1
  55. package/src/generate.js +41 -10
  56. package/test/attributeTreeSchemas.test.js +219 -0
  57. package/test/createTargetRoleHandler.test.js +59 -4
  58. package/test/crudApplicationArtifacts.test.js +20 -3
  59. package/test/customFlowApplicationArtifacts.test.js +5 -5
  60. package/test/dynamicCrudFlowSchemas.test.js +107 -6
  61. package/test/dynamicRbacFlowSchemas.test.js +15 -2
  62. package/test/publicApi.test.js +6 -6
  63. package/test/resourceArtifacts.test.js +89 -27
@@ -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,42 @@ 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
+ requestParams.targetIdentifiers?.submittedByUserId ||
96
+ _izContext.correlationIds.get(consts.SUBMITTED_BY_USER_ID);
97
+
98
+ const targetIdentifiers = requestParams.targetIdentifiers;
99
+ let targetId = '';
100
+ if (targetIdentifiers) {
101
+ if (typeof targetIdentifiers === 'string') {
102
+ targetId = targetIdentifiers;
103
+ } else if (typeof targetIdentifiers === 'object') {
104
+ if (targetIdentifiers.identifierConcat) {
105
+ targetId = targetIdentifiers.identifierConcat;
106
+ } else {
107
+ const sortedKeys = Object.keys(targetIdentifiers).sort();
108
+ const parts = [];
109
+ for (const key of sortedKeys) {
110
+ const value = targetIdentifiers[key];
111
+ if (
112
+ key !== 'submittedByUserId' &&
113
+ key !== 'identifierConcat' &&
114
+ value !== undefined &&
115
+ value !== null
116
+ ) {
117
+ parts.push(`${key}#${value}`);
118
+ }
119
+ }
120
+ if (parts.length > 0) {
121
+ targetId = parts.join('|');
122
+ }
123
+ }
124
+ }
125
+ }
126
+
127
+ if (!submittedByUserId || !targetIdentifiers) {
128
+ throw new Error('Missing required request parameters: submittedByUserId or targetIdentifiers');
97
129
  }
98
130
 
99
131
  const { flowTypeConcat, inputParameterGroups, parentFlowId } = requestParams;
@@ -101,40 +133,69 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
101
133
  throw new Error('Missing flowTypeConcat in requestParams');
102
134
  }
103
135
 
104
- _izContext.logger.debug('[<%- flowTag %>] requestParams:', {
105
- userId,
136
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] requestParams:', {
137
+ submittedByUserId,
106
138
  targetId,
107
139
  flowTypeConcat,
108
140
  inputParameterGroups
109
141
  });
110
142
 
111
143
  const cacheSubKey = `${targetId}#${flowTypeConcat}#${JSON.stringify(inputParameterGroups || {})}`;
112
- const fullCacheKey = `${userId}:${cacheSubKey}`;
144
+ const fullCacheKey = `${submittedByUserId}:${cacheSubKey}`;
145
+
146
+ // ── Fast-path for User Self-Action (submittedByUserId === targetId) ───────────────────
147
+ const isSelfUser = Boolean(
148
+ submittedByUserId &&
149
+ targetId &&
150
+ (submittedByUserId === targetId ||
151
+ targetId === `user#${submittedByUserId}` ||
152
+ targetId === `submittedByUserId#${submittedByUserId}`)
153
+ );
154
+
155
+ if (isSelfUser) {
156
+ _izContext.logger.debug(
157
+ '[<%- flowTag %>] Fast-path triggered: user self-action',
158
+ { submittedByUserId, targetId }
159
+ );
160
+ const result = {
161
+ returnValue: {
162
+ permission: true,
163
+ roleIdKey: 'self_user',
164
+ rbacType: 'self_user'
165
+ },
166
+ errorsFound: [],
167
+ status: 'Completed'
168
+ };
169
+ _setL1Cache(fullCacheKey, result);
170
+ await _setL2Cache(_izContext, submittedByUserId, cacheSubKey, result);
171
+ await _publicMessageOut(_izContext, { ...result, parentFlowId });
172
+ return 'Completed';
173
+ }
113
174
 
114
175
  // 1. Check L1 In-Memory Cache
115
176
  const l1Hit = _getL1Cache(fullCacheKey);
116
177
  if (l1Hit) {
117
- _izContext.logger.debug('[<%- flowTag %>] L1 Cache hit:', l1Hit);
178
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] L1 Cache hit:', l1Hit);
118
179
  await _publicMessageOut(_izContext, { ...l1Hit, parentFlowId });
119
180
  return;
120
181
  }
121
182
 
122
183
  // 2. Check L2 Shared DynamoDB Cache
123
- const l2Hit = await _getL2Cache(_izContext, userId, cacheSubKey);
184
+ const l2Hit = await _getL2Cache(_izContext, submittedByUserId, cacheSubKey);
124
185
  if (l2Hit) {
125
- _izContext.logger.debug('[<%- flowTag %>] L2 Cache hit:', l2Hit);
186
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] L2 Cache hit:', l2Hit);
126
187
  _setL1Cache(fullCacheKey, l2Hit);
127
188
  await _publicMessageOut(_izContext, { ...l2Hit, parentFlowId });
128
189
  return;
129
190
  }
130
191
 
131
- _izContext.logger.debug('[<%- flowTag %>] Cache miss — querying DynamoDB');
192
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] Cache miss — querying DynamoDB');
132
193
 
133
194
  // 3. Fallback to DB Query
134
195
  const userRolesResult = await dynamodbSharedLib.query(
135
196
  _izContext,
136
197
  dynamodbSharedLib.tableName(_izContext, 'UserRoles'),
137
- { userId }
198
+ { submittedByUserId }
138
199
  );
139
200
 
140
201
  let userRoles = [];
@@ -144,7 +205,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
144
205
  userRoles = userRolesResult.Items;
145
206
  }
146
207
 
147
- _izContext.logger.debug('[<%- flowTag %>] userRoles fetched:', { userRoles });
208
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] userRoles fetched:', { userRoles });
148
209
 
149
210
  const targetIdPrefix = `${targetId}_`;
150
211
  const targetRoles = [];
@@ -154,13 +215,13 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
154
215
  }
155
216
  }
156
217
 
157
- _izContext.logger.debug('[<%- flowTag %>] targetRoles filtered:', {
218
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] targetRoles filtered:', {
158
219
  targetIdPrefix,
159
220
  targetRoles
160
221
  });
161
222
 
162
223
  if (targetRoles.length === 0) {
163
- _izContext.logger.debug('[<%- flowTag %>] no targetRoles found, permission denied');
224
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] no targetRoles found, permission denied');
164
225
  const result = {
165
226
  returnValue: {
166
227
  permission: false,
@@ -171,7 +232,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
171
232
  status: 'Completed'
172
233
  };
173
234
  _setL1Cache(fullCacheKey, result);
174
- await _setL2Cache(_izContext, userId, cacheSubKey, result);
235
+ await _setL2Cache(_izContext, submittedByUserId, cacheSubKey, result);
175
236
  await _publicMessageOut(_izContext, { ...result, parentFlowId });
176
237
  return;
177
238
  }
@@ -189,7 +250,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
189
250
  })
190
251
  );
191
252
 
192
- _izContext.logger.debug('[<%- flowTag %>] rolePermissions fetched:', {
253
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] rolePermissions fetched:', {
193
254
  rolePermissions
194
255
  });
195
256
 
@@ -217,11 +278,11 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
217
278
  status: 'Completed'
218
279
  };
219
280
 
220
- _izContext.logger.debug('[<%- flowTag %>] validation result:', cacheResult);
281
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] validation result:', cacheResult);
221
282
 
222
283
  // Store in L1 & L2 Cache
223
284
  _setL1Cache(fullCacheKey, cacheResult);
224
- await _setL2Cache(_izContext, userId, cacheSubKey, cacheResult);
285
+ await _setL2Cache(_izContext, submittedByUserId, cacheSubKey, cacheResult);
225
286
 
226
287
  await _publicMessageOut(_izContext, { ...cacheResult, parentFlowId });
227
288
 
@@ -16,7 +16,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
16
16
  */
17
17
 
18
18
  <%
19
- const itemType = operation === 'Put' ? 'Item' : 'Key';
19
+ const action = operation === 'Put' ? 'put' : 'delete';
20
+ const propName = operation === 'Put' ? 'attributes' : 'keyValues';
20
21
  const roleUsersKey = operation === 'Put' ? 'userId, roleIdKey' : 'roleIdKey, userId';
21
22
  -%> import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
22
23
  import snsSharedLib from '@izara_project/izara-core-library-sns';
@@ -27,15 +28,17 @@ export default async function <%- flowTag %>(_izContext, requestParams) {
27
28
  const { roleIdKey, userId } = requestParams;
28
29
  await dynamodbSharedLib.transactWriteItems(_izContext, [
29
30
  {
30
- <%- operation %>: {
31
- TableName: dynamodbSharedLib.tableName(_izContext, 'RoleUsers'),
32
- <%- itemType %>: { <%- roleUsersKey %> }
31
+ action: '<%- action %>',
32
+ params: {
33
+ tableName: dynamodbSharedLib.tableName(_izContext, 'RoleUsers'),
34
+ <%- propName %>: { <%- roleUsersKey %> }
33
35
  }
34
36
  },
35
37
  {
36
- <%- operation %>: {
37
- TableName: dynamodbSharedLib.tableName(_izContext, 'UserRoles'),
38
- <%- itemType %>: { userId, roleIdKey }
38
+ action: '<%- action %>',
39
+ params: {
40
+ tableName: dynamodbSharedLib.tableName(_izContext, 'UserRoles'),
41
+ <%- propName %>: { userId, roleIdKey }
39
42
  }
40
43
  }
41
44
  ]);
@@ -47,7 +50,7 @@ export default async function <%- flowTag %>(_izContext, requestParams) {
47
50
  )
48
51
  });
49
52
  } catch (error) {
50
- _izContext.logger.error('[Rbac:<%- flowTag %>] error:', error);
53
+ _izContext.logger.error(`[${process.env.iz_serviceTag}:<%- flowTag %>] error:`, error);
51
54
  throw error;
52
55
  }
53
56
  }
@@ -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
  }