@izara_project/izara-core-generate-service-code 1.0.87 → 1.0.89

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 (32) hide show
  1. package/package.json +1 -1
  2. package/src/compile/flowValidator.js +25 -0
  3. package/src/compile/loadSchemas.js +199 -10
  4. package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +3 -7
  5. package/src/emit/crud/crudActionPlan.js +105 -6
  6. package/src/emit/crud/emitCrudApplicationArtifacts.js +63 -3
  7. package/src/emit/crud/emitDynamicCrudFlowSchemas.js +15 -32
  8. package/src/emit/crud/templates/CreateAfterHookCallback_Main.ejs +121 -0
  9. package/src/emit/crud/templates/CreateBeforeHookCallback_Main.ejs +108 -0
  10. package/src/emit/crud/templates/CreateEndpoint_Main.ejs +140 -340
  11. package/src/emit/crud/templates/CreateLogicalLib.ejs +338 -82
  12. package/src/emit/crud/templates/CreateNodeCallback_Main.ejs +90 -0
  13. package/src/emit/crud/templates/DeleteAfterHookCallback_Main.ejs +118 -0
  14. package/src/emit/crud/templates/DeleteBeforeHookCallback_Main.ejs +99 -0
  15. package/src/emit/crud/templates/DeleteComplete_Main.ejs +124 -10
  16. package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +151 -179
  17. package/src/emit/crud/templates/DeleteLogicalLib.ejs +244 -27
  18. package/src/emit/crud/templates/UpdateAfterHookCallback_Main.ejs +120 -0
  19. package/src/emit/crud/templates/UpdateBeforeHookCallback_Main.ejs +112 -0
  20. package/src/emit/crud/templates/UpdateComplete_Main.ejs +104 -10
  21. package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +166 -185
  22. package/src/emit/crud/templates/UpdateLogicalLib.ejs +123 -3
  23. package/src/emit/custom/customFlows.js +3 -1
  24. package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +8 -2
  25. package/src/emit/rbac/templates/CancelUsage_Main.ejs +34 -14
  26. package/src/emit/rbac/templates/ConfirmUsage_Main.ejs +34 -14
  27. package/src/emit/resource/emitResourceArtifacts.js +119 -59
  28. package/test/crudApplicationArtifacts.test.js +876 -0
  29. package/test/dynamicCrudFlowSchemas.test.js +10 -5
  30. package/test/dynamicRbacFlowSchemas.test.js +24 -2
  31. package/test/graphDrivenFlowSchema.test.js +36 -0
  32. package/test/resourceArtifacts.test.js +271 -12
@@ -19,7 +19,7 @@ import {
19
19
  objectHash as hash,
20
20
  identifierUuid
21
21
  } from '@izara_project/izara-shared-core';
22
- import { consts as coreConsts } from '@izara_project/izara-core-library-core';
22
+
23
23
  import {
24
24
  sns,
25
25
  lambda
@@ -29,17 +29,18 @@ import callingFlowSharedLib from '@izara_project/izara-core-library-calling-flow
29
29
  import lambdaSharedLib from '@izara_project/izara-core-library-lambda';
30
30
  import {
31
31
  consts,
32
- getObjectSchema,
33
32
  utils
34
33
  } from '@izara_project/izara-core-library-service-schemas';
35
34
  import snsSharedLib from '@izara_project/izara-core-library-sns';
36
35
 
37
- import { <%- flowTag %>LogicalLib } from './<%- upperCaseFlowTag %>LogicalLib.js';
36
+ import {
37
+ <%- flowTag %>LogicalLib,
38
+ resolveSchemaAndIdentity
39
+ } from './<%- upperCaseFlowTag %>LogicalLib.js';
38
40
 
39
41
  //(<optionalRequire>)
40
42
  //(</optionalRequire>)
41
43
 
42
- const { createObjType } = utils;
43
44
 
44
45
  /**
45
46
  * Deletes an object instance with optional RBAC routing
@@ -62,53 +63,13 @@ export default async function <%- flowTag %>(
62
63
  });
63
64
 
64
65
  const errorsFound = [];
65
- const objType = createObjType(requestParams.objectType);
66
66
 
67
67
  //(<beforeDelete>)
68
68
  //(</beforeDelete>)
69
69
 
70
- // ── Load schema ───────────────────────────────────────────────────────────
71
- const objectSchema = await getObjectSchema.getObjSchemaS3WithHierarchy(
72
- _izContext,
73
- objType
74
- );
75
- _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] objectSchema:`, objectSchema);
76
-
77
- // ── Resolve submittedByUserId / targetIdentifiers ─────────────────────────
78
- let submittedByUserId, targetIdentifiers;
79
- if (
80
- objectSchema.generatedBy === 'user' ||
81
- objectSchema.generatedBy === 'systemWithUser'
82
- ) {
83
- submittedByUserId =
84
- requestParams.submittedByUserId ||
85
- _izContext.correlationIds.get(coreConsts.SUBMITTED_BY_USER_ID);
86
-
87
- targetIdentifiers = requestParams.targetIdentifiers;
88
- delete requestParams.targetIdentifiers;
89
-
90
- if (!targetIdentifiers && objectSchema.belongTo?.objectType === 'user' && submittedByUserId) {
91
- targetIdentifiers = { submittedByUserId };
92
- }
93
-
94
- if (!submittedByUserId) errorsFound.push('Not have submittedByUserId');
95
- if (objectSchema.hasOwnProperty('belongTo') && !targetIdentifiers)
96
- errorsFound.push('not have targetIdentifiers');
97
-
98
- requestParams.submittedByUserId = submittedByUserId;
99
- if (targetIdentifiers) {
100
- if (!targetIdentifiers.submittedByUserId && submittedByUserId) {
101
- targetIdentifiers.submittedByUserId = submittedByUserId;
102
- }
103
- }
104
-
105
- _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] Identity & Target Context Resolved:`, {
106
- submittedByUserId: requestParams.submittedByUserId,
107
- targetIdentifiers,
108
- belongTo: objectSchema.belongTo,
109
- generatedBy: objectSchema.generatedBy
110
- });
111
- }
70
+ // ── Resolve Schema & Identity ─────────────────────────────────────────────
71
+ const { objType, objectSchema, targetIdentifiers } =
72
+ await resolveSchemaAndIdentity(_izContext, requestParams, errorsFound);
112
73
 
113
74
  //(<beforeQuery>)
114
75
  //(</beforeQuery>)
@@ -116,36 +77,36 @@ export default async function <%- flowTag %>(
116
77
  // ── Build identifiers used by asynchronous processing ────────────────────
117
78
  const identifiers = requestParams.identifiers;
118
79
 
119
- if (!objectSchema.hasOwnProperty('belongTo')) {
120
- if (!targetIdentifiers && identifiers) {
121
- targetIdentifiers = { ...identifiers };
122
- } else if (targetIdentifiers && identifiers) {
123
- for (const [key, value] of Object.entries(identifiers)) {
124
- if (targetIdentifiers[key] === undefined && value !== undefined) {
125
- targetIdentifiers[key] = value;
126
- }
127
- }
128
- }
129
- if (targetIdentifiers) {
130
- if (!targetIdentifiers.submittedByUserId && submittedByUserId) {
131
- targetIdentifiers.submittedByUserId = submittedByUserId;
132
- }
133
- }
134
- }
135
-
136
- if (targetIdentifiers && identifiers) {
137
- for (const [key, value] of Object.entries(identifiers)) {
138
- if (
139
- targetIdentifiers[key] !== undefined &&
140
- value !== undefined &&
141
- targetIdentifiers[key] !== value
142
- ) {
143
- errorsFound.push(
144
- `targetIdentifiers.${key} must match identifiers.${key}`
145
- );
146
- }
147
- }
148
- }
80
+ // if (!objectSchema.hasOwnProperty('belongTo')) {
81
+ // if (!targetIdentifiers && identifiers) {
82
+ // targetIdentifiers = { ...identifiers };
83
+ // } else if (targetIdentifiers && identifiers) {
84
+ // for (const [key, value] of Object.entries(identifiers)) {
85
+ // if (targetIdentifiers[key] === undefined && value !== undefined) {
86
+ // targetIdentifiers[key] = value;
87
+ // }
88
+ // }
89
+ // }
90
+ // if (targetIdentifiers) {
91
+ // if (!targetIdentifiers.submittedByUserId && submittedByUserId) {
92
+ // targetIdentifiers.submittedByUserId = submittedByUserId;
93
+ // }
94
+ // }
95
+ // }
96
+
97
+ // if (targetIdentifiers && identifiers) {
98
+ // for (const [key, value] of Object.entries(identifiers)) {
99
+ // if (
100
+ // targetIdentifiers[key] !== undefined &&
101
+ // value !== undefined &&
102
+ // targetIdentifiers[key] !== value
103
+ // ) {
104
+ // errorsFound.push(
105
+ // `targetIdentifiers.${key} must match identifiers.${key}`
106
+ // );
107
+ // }
108
+ // }
109
+ // }
149
110
 
150
111
  if (errorsFound.length > 0)
151
112
  return await <%- flowTag %>LogicalLib(
@@ -227,110 +188,117 @@ export default async function <%- flowTag %>(
227
188
 
228
189
  // ── Optional phase extension point ────────────────────────────────────────
229
190
  <%_
230
- const groupedByObjType = [];
191
+ const allCrudObjectTypes = (objects || []).filter(isCrudObject).map(o => o.objectType);
192
+ const beforeHooksByObjType = (plan?.beforeHooks || []).reduce((acc, hook) => {
193
+ const targets = (hook?.targetObjectTypes && hook.targetObjectTypes.length > 0)
194
+ ? hook.targetObjectTypes
195
+ : allCrudObjectTypes;
196
+ for (const key of targets) {
197
+ if (!acc[key]) acc[key] = [];
198
+ acc[key].push(hook);
199
+ }
200
+ return acc;
201
+ }, {});
231
202
  _%>
232
- <%_ Object.entries(groupedByObjType).forEach(([objectType, configs]) => { _%>
203
+ <%_ Object.entries(beforeHooksByObjType).forEach(([objectType, configs]) => { _%>
233
204
  <%_
234
- const hasOwnTopic = configs.some(c => c.invocationType === 'ownTopic');
235
- const hasLambdaSyncInv = configs.some(c => c.invocationType === 'lambdaSyncInv');
236
- _%>
205
+ const hasOwnTopic = configs.some(c => c?.generatedCodeConfig?.invocationType === 'ownTopic' || c?.event?.includes('ownTopic'));
206
+ const hasLambdaSyncInv = configs.some(c => c?.generatedCodeConfig?.invocationType === 'lambdaSyncInv' || c?.event?.includes('lambdaSyncInv'));
207
+ _%>
237
208
  if (requestParams.objectType === '<%= objectType %>') {
238
209
  <%_ if (hasLambdaSyncInv) { _%>
239
- const lambdaSyncInvResults = await Promise.all([
240
- <%_ configs.filter(c => c.invocationType === 'lambdaSyncInv').forEach((config, index, arr) => { _%>
241
- // lambdaSyncInv <%= config.flowTag %>
242
- lambda
243
- .invokeSync(
210
+ const lambdaSyncInvResults = [];
211
+ <%_ for (const config of configs.filter(c => c?.generatedCodeConfig?.invocationType === 'lambdaSyncInv' || c?.event?.includes('lambdaSyncInv'))) { _%>
212
+ try {
213
+ let hookPayload_<%= config.flowTag %> = {
214
+ objType,
215
+ identifiers: requestParams.identifiers,
216
+ targetIdentifiers,
217
+ submittedByUserId: requestParams.submittedByUserId
218
+ };
219
+ //(<beforeSendHook_<%= config.flowTag %>>)
220
+ //(</beforeSendHook_<%= config.flowTag %>>)
221
+ const returnValue = await lambda.invokeSync(
222
+ _izContext,
223
+ await lambdaSharedLib.lambdaFunctionName(
244
224
  _izContext,
245
- await lambdaSharedLib.lambdaFunctionName(
246
- _izContext,
247
- 'Process<%= config.flowTag %>HdrInv'
248
- ),
249
- { requestParams, callingFlowConfig }
250
- )
251
- .then((returnValue) => ({
252
- flowTag: '<%= config.flowTag %>',
253
- returnValue
254
- })) /*__EJS_COMMA_14__*/
255
- // lambdaSyncInv <%= config.flowTag %>
256
- <%_ }) _%>
257
- ]);
225
+ '<%= config.flowTag %>'
226
+ ),
227
+ {
228
+ requestParams: hookPayload_<%= config.flowTag %>,
229
+ callingFlowConfig
230
+ }
231
+ );
232
+ lambdaSyncInvResults.push({
233
+ flowTag: '<%= config.flowTag %>',
234
+ returnValue
235
+ });
236
+ if (returnValue?.errorsFound?.length > 0) {
237
+ errorsFound.push(...returnValue.errorsFound);
238
+ } else if (returnValue?.status === 'error') {
239
+ errorsFound.push('Error from <%= config.flowTag %>');
240
+ } else {
241
+ if (returnValue?.identifiers && requestParams?.identifiers) {
242
+ Object.assign(requestParams.identifiers, returnValue.identifiers);
243
+ }
244
+ }
245
+ } catch (hookErr) {
246
+ _izContext.logger.error(
247
+ '[<%- flowTag %>] Sync hook <%= config.flowTag %> error:',
248
+ hookErr
249
+ );
250
+ errorsFound.push(hookErr.message || 'Hook <%= config.flowTag %> failed');
251
+ }
252
+ <%_ } _%>
253
+
254
+ if (errorsFound.length > 0) {
255
+ return await <%- flowTag %>LogicalLib(
256
+ _izContext,
257
+ requestParams,
258
+ callingFlowConfig,
259
+ errorsFound
260
+ );
261
+ }
258
262
  <%_ } _%>
259
263
 
260
264
  //(<afterBeforeLogical_<%= objectType %>>)
261
265
  //(</afterBeforeLogical_<%= objectType %>>)
262
266
 
263
267
  <%_ if (hasOwnTopic) { _%>
264
- // ── ownTopic (async — create awaitingSteps and then publish) ──────────
265
- const awaitingMultipleStepIds = [];
266
- const recordMsgs = [];
267
-
268
- <%_ configs.filter(c => c.invocationType === 'ownTopic').forEach(config => { _%>
269
- // ownTopic <%= config.flowTag %>
270
- awaitingMultipleStepIds.push({
271
- awaitingStepId: await asyncFlowSharedLib.createAwaitingStepId(
272
- hash(identifiers),
273
- 'logical_<%= objectType %>_<%= config.flowTag.charAt(0).toUpperCase() + config.flowTag.slice(1) %>'
274
- ),
275
- additionalAttributes: {}
276
- });
277
-
278
- recordMsgs.push({
279
- msg: callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject(
280
- requestParams,
268
+ // ── ownTopic (async — dispatch to before-hook and await callback) ──────────
269
+ <%_ configs.filter(c => c?.generatedCodeConfig?.invocationType === 'ownTopic' || c?.event?.includes('ownTopic')).forEach(config => { _%>
270
+ let hookPayload_<%= config.flowTag %> = {
271
+ objType,
272
+ identifiers: requestParams.identifiers,
273
+ targetIdentifiers,
274
+ submittedByUserId: requestParams.submittedByUserId
275
+ };
276
+ //(<beforeSendHook_<%= config.flowTag %>>)
277
+ //(</beforeSendHook_<%= config.flowTag %>>)
278
+ const hookMsg_<%= config.flowTag %> =
279
+ callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject(
280
+ hookPayload_<%= config.flowTag %>,
281
281
  callingFlowSharedLib.addParentCallingFlowConfig(
282
282
  callingFlowConfig,
283
283
  callingFlowSharedLib.createCallingFlowConfig(
284
- //(<callingFlowProperties_<%= config.flowTag %>>)
285
284
  await lambdaSharedLib.lambdaFunctionName(
286
285
  _izContext,
287
- '<%= flowTag.toLowerCase() %>Complete'
286
+ '<%- flowTag %>BeforeHookCallbackHdrSqs'
288
287
  ),
289
- {}
290
- //(</callingFlowProperties_<%= config.flowTag %>>)
288
+ {
289
+ originalRequestParams: {
290
+ ...requestParams,
291
+ objType,
292
+ targetIdentifiers
293
+ },
294
+ callingFlowConfig
295
+ }
291
296
  )
292
297
  )
293
- ),
294
- flowTag: '<%= config.flowTag.charAt(0).toUpperCase() + config.flowTag.slice(1) %>'
295
- });
296
- <%_ }) _%>
297
-
298
- <%_ if (hasLambdaSyncInv) { _%>
299
- // ── Add lambdaSyncInv results to awaitingSteps ────────────────────────
300
- for (const syncResult of lambdaSyncInvResults) {
301
- awaitingMultipleStepIds.push({
302
- awaitingStepId: await asyncFlowSharedLib.createAwaitingStepId(
303
- hash(identifiers),
304
- 'logical_<%= objectType %>_' + syncResult.flowTag
305
- ),
306
- additionalAttributes: {
307
- complete: true,
308
- errorsFound: [],
309
- returnValue: syncResult.returnValue
310
- }
311
- });
312
- }
313
- <%_ } _%>
314
-
315
- await asyncFlowSharedLib.createAwaitingMultipleSteps(
316
- _izContext,
317
- asyncFlowSharedLib.createPendingStepId(
318
- _izContext.uniqueRequestId,
319
- 'logical_<%= objectType %>'
320
- ),
321
- awaitingMultipleStepIds,
322
- {
323
- additionalAttributes: {
324
- callingFlowConfig
325
- }
326
- }
327
- );
328
-
329
- //(<beforePublish_<%= objectType %>>)
330
- //(</beforePublish_<%= objectType %>>)
298
+ );
331
299
 
332
- for (const recordMsg of recordMsgs) {
333
- const messageAttributes = sns.createStringMessageAttributes(
300
+ const messageAttributes_<%= config.flowTag %> =
301
+ sns.createStringMessageAttributes(
334
302
  _izContext,
335
303
  callingFlowSharedLib.addCallingFlowToSnsResponseMessageAttributes(
336
304
  callingFlowConfig,
@@ -338,32 +306,36 @@ export default async function <%- flowTag %>(
338
306
  )
339
307
  );
340
308
 
341
- const topicArn = snsSharedLib.snsTopicArnByFlowSchema(
342
- _izContext,
343
- `${recordMsg.flowTag}_In`
344
- );
309
+ const topicArn_<%= config.flowTag %> = snsSharedLib.snsTopicArnByFlowSchema(
310
+ _izContext,
311
+ '<%= config.flowTag %>_In'
312
+ );
345
313
 
346
- await sns.publishAsync(_izContext, {
347
- Message: JSON.stringify(recordMsg.msg),
348
- MessageAttributes: messageAttributes,
349
- TopicArn: topicArn
350
- });
351
- }
314
+ _izContext.logger.debug(
315
+ `[${process.env.iz_serviceTag}:<%- flowTag %>] Dispatching to BeforeHook:`,
316
+ {
317
+ topicArn: topicArn_<%= config.flowTag %>,
318
+ flowTag: '<%= config.flowTag %>'
319
+ }
320
+ );
352
321
 
353
- return 'complete logical <%= objectType %>';
322
+ await sns.publishAsync(_izContext, {
323
+ Message: JSON.stringify(hookMsg_<%= config.flowTag %>),
324
+ MessageAttributes: messageAttributes_<%= config.flowTag %>,
325
+ TopicArn: topicArn_<%= config.flowTag %>
326
+ });
327
+ <%_ }) _%>
354
328
 
329
+ return {
330
+ status: 'pending',
331
+ message: 'Dispatched to BeforeHook'
332
+ };
355
333
  <%_ } else { _%>
356
- // ── lambdaSyncInv only — collect results and proceed to deleteLogicalLib
357
- const phaseResults = Object.fromEntries(
358
- lambdaSyncInvResults.map(({ flowTag: tag, returnValue }) => [tag, returnValue])
359
- );
360
-
361
334
  return await <%- flowTag %>LogicalLib(
362
335
  _izContext,
363
336
  requestParams,
364
337
  callingFlowConfig,
365
- errorsFound,
366
- phaseResults
338
+ errorsFound
367
339
  );
368
340
  <%_ } _%>
369
341
  }