@izara_project/izara-core-generate-service-code 1.0.98 → 1.0.100

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 (70) hide show
  1. package/package.json +1 -1
  2. package/src/compile/compileProject.js +16 -5
  3. package/src/compile/flowValidator.js +47 -8
  4. package/src/compile/loadSchemas.js +18 -7
  5. package/src/compile/schemaValidatorSetup.js +16 -0
  6. package/src/emit/application/coreFunction/emitCoreFunctionArtifacts.js +123 -109
  7. package/src/emit/application/coreFunction/templates/InitialSetupConfig.ejs +8 -4
  8. package/src/emit/application/coreFunction/templates/InitialSetupRunner.ejs +2 -2
  9. package/src/emit/application/crud/templates/CreateComplete_Main.ejs +2 -0
  10. package/src/emit/application/crud/templates/CreateEndpoint_Main.ejs +4 -4
  11. package/src/emit/application/crud/templates/CreateLogicalLib.ejs +74 -15
  12. package/src/emit/application/crud/templates/DeleteComplete_Main.ejs +2 -0
  13. package/src/emit/application/crud/templates/DeleteEndpoint_Main.ejs +4 -4
  14. package/src/emit/application/crud/templates/FlowEndpoint_HdrWbs.ejs +7 -5
  15. package/src/emit/application/crud/templates/UpdateComplete_Main.ejs +2 -0
  16. package/src/emit/application/crud/templates/UpdateEndpoint_Main.ejs +4 -4
  17. package/src/emit/application/custom/customFlowPaths.js +13 -16
  18. package/src/emit/application/custom/customFlowS3.js +277 -66
  19. package/src/emit/application/custom/customFlows.js +131 -0
  20. package/src/emit/application/custom/emitCustomFlowApplicationArtifacts.js +3 -1
  21. package/src/emit/application/custom/templates/FlowEndpoint_Main.ejs +2 -2
  22. package/src/emit/application/custom/templates/s3/ConfirmAfterUpload_HdrSqs.ejs +110 -0
  23. package/src/emit/application/custom/templates/s3/ConfirmAfterUpload_Main.ejs +89 -0
  24. package/src/emit/application/custom/templates/s3/ConfirmComplete_HdrSqs.ejs +110 -0
  25. package/src/emit/application/custom/templates/s3/ConfirmComplete_Main.ejs +89 -0
  26. package/src/emit/application/custom/templates/s3/CreateObject_HdrWbs.ejs +2 -1
  27. package/src/emit/application/custom/templates/s3/CreateObject_Main.ejs +4 -0
  28. package/src/emit/application/custom/templates/s3/CreatePreSignUrl_HdrSqs.ejs +42 -0
  29. package/src/emit/application/custom/templates/s3/CreatePreSignUrl_Main.ejs +23 -0
  30. package/src/emit/application/custom/templates/s3/ProcessS3File_Main.ejs +135 -27
  31. package/src/emit/application/custom/templates/s3/Start_Main.ejs +197 -0
  32. package/src/emit/application/rbac/emitDynamicRbacFlowSchemas.js +8 -1
  33. package/src/emit/application/rbac/emitRbacApplicationArtifacts.js +107 -25
  34. package/src/emit/application/rbac/templates/ReservedDynamicUsage_Main.ejs +29 -9
  35. package/src/emit/application/rbac/templates/ReservedUsageTtlExpired_HdrDss.ejs +176 -0
  36. package/src/emit/application/rbac/templates/UserRbacFlow_Main.ejs +4 -3
  37. package/src/emit/application/system/templates/Register_HdrWbs.ejs +2 -1
  38. package/src/emit/infrastructure/functions/functionYamlArtifact.js +31 -8
  39. package/src/emit/infrastructure/functions/templates/DssFunction_Yaml.ejs +21 -0
  40. package/src/emit/infrastructure/functions/templates/S3Function_Yaml.ejs +2 -3
  41. package/src/emit/infrastructure/resources/iamResourceArtifacts.js +67 -5
  42. package/src/emit/infrastructure/resources/resourceState.js +15 -5
  43. package/src/emit/infrastructure/resources/s3ResourceArtifacts.js +19 -3
  44. package/src/emit/infrastructure/resources/templates/GeneratedLambdaRoleServiceConfig.ejs +5 -3
  45. package/src/emit/infrastructure/resources/templates/S3Bucket_Yaml.ejs +1 -1
  46. package/src/emit/infrastructure/resources/templates/SystemDynamoDB_Yaml.ejs +2 -2
  47. package/src/emit/schemas/attributeTree/emitAttributeTreeSchemas.js +62 -1
  48. package/src/emit/schemas/error/emitErrorSchemas.js +120 -10
  49. package/src/emit/schemas/error/templates/errorConsts.ejs +12 -0
  50. package/src/generate.js +32 -34
  51. package/src/output/mergeHooks.js +105 -5
  52. package/src/plan/domainRegistry.js +0 -10
  53. package/task.md +40 -96
  54. package/test/integration/graphDrivenFlowSchema.test.js +58 -0
  55. package/test/integration/publicApi.test.js +33 -24
  56. package/test/unit/application/createTargetRoleHandler.test.js +101 -9
  57. package/test/unit/application/crudApplicationArtifacts.test.js +4 -0
  58. package/test/unit/application/customFlowApplicationArtifacts.test.js +6 -6
  59. package/test/unit/application/customFlowS3.test.js +730 -0
  60. package/test/unit/infrastructure/functionYamlArtifact.test.js +6 -4
  61. package/test/unit/infrastructure/resourceArtifacts.test.js +103 -0
  62. package/test/unit/output/mergeHooks.test.js +137 -0
  63. package/test/unit/schemas/attributeTreeSchemas.test.js +32 -3
  64. package/test/unit/schemas/attributeTreeSingleService.test.js +175 -0
  65. package/test/unit/schemas/dynamicRbacFlowSchemas.test.js +8 -6
  66. package/test/unit/schemas/dynamicRelationshipFlowSchemas.test.js +51 -0
  67. package/test/unit/schemas/errorSchemas.test.js +84 -5
  68. package/test/unit/templates/hookTagsValidation.test.js +83 -0
  69. package/src/emit/schemas/attributeTree/emitAttributeTreeTargetValueStandard.js +0 -132
  70. package/test/unit/schemas/attributeTreeDirectives.test.js +0 -177
@@ -1,5 +1,10 @@
1
1
  import { renderCustom } from './customFlowTemplates.js';
2
- import { customFlowRoleName, upperFirst } from './customFlows.js';
2
+ import {
3
+ customFlowRoleName,
4
+ upperFirst,
5
+ resolveS3Parts,
6
+ flowHasRbac
7
+ } from './customFlows.js';
3
8
  import {
4
9
  renderInvFunctionYaml,
5
10
  renderS3FunctionYaml,
@@ -7,63 +12,81 @@ import {
7
12
  renderWbsFunctionYaml
8
13
  } from '../../infrastructure/functions/functionYamlArtifact.js';
9
14
 
10
- export async function emitS3Flow(flow, source, handlerRoot) {
15
+ export function isSimplifiedS3Flow(flow) {
16
+ if (!Array.isArray(flow?.flowSteps) || flow.flowSteps.length === 0) {
17
+ return false;
18
+ }
19
+ const stepNames = flow.flowSteps.map((s) => s.stepName);
20
+ if (stepNames.includes('ReservedLimit')) {
21
+ return false;
22
+ }
23
+ return true;
24
+ }
25
+
26
+ export async function emitS3Flow(
27
+ flow,
28
+ source,
29
+ handlerRoot,
30
+ { objects = [] } = {}
31
+ ) {
11
32
  const tag = upperFirst(flow.flowTag);
12
33
  const roleName = customFlowRoleName(flow.flowTag);
13
- const parts = [
14
- {
15
- action: 'CreateObject',
16
- handler: 'HdrWbs',
17
- type: 'wbs',
18
- mainTemplate: 'CreateObject_Main.ejs',
19
- handlerTemplate: 'CreateObject_HdrWbs.ejs'
20
- },
21
- {
22
- action: 'ReservedLimit',
23
- handler: 'HdrSqs',
24
- type: 'sqs',
25
- mainTemplate: 'ReservedLimit_Main.ejs',
26
- handlerTemplate: 'ReservedLimit_HdrSqs.ejs'
27
- },
28
- {
29
- action: 'CreatePreSignUrl',
30
- handler: 'HdrSqs',
31
- type: 'sqs',
32
- mainTemplate: 'CreatePreSignUrl_Main.ejs',
33
- handlerTemplate: 'CreatePreSignUrl_HdrSqs.ejs'
34
- },
35
- {
36
- action: 'ProcessAfterUpload',
37
- handler: 'HdrS3',
38
- type: 's3',
39
- mainTemplate: 'ProcessS3File_Main.ejs',
40
- handlerTemplate: 'FlowHandler_HdrS3.ejs'
41
- },
42
- {
43
- action: 'ConfirmAfterUpload',
44
- handler: 'HdrSqs',
45
- type: 'sqs',
46
- mainTemplate: 'ConfirmReserved_Main.ejs',
47
- handlerTemplate: 'ConfirmReserved_HdrSqs.ejs'
48
- }
49
- ];
34
+ const parts = resolveS3Parts(flow);
35
+ const isSimplifiedS3 = isSimplifiedS3Flow(flow);
50
36
 
51
37
  const emitted = await Promise.all(
52
38
  parts.map(
53
- async ({ action, handler, type, mainTemplate, handlerTemplate }) => {
54
- const functionName = `${tag}${action}`;
55
- const mainFileName = `${functionName}_Main`;
56
- const handlerFileName = `${functionName}_${handler}`;
39
+ async (
40
+ { action, handler, type, mainTemplate, handlerTemplate },
41
+ index
42
+ ) => {
43
+ let isStart = false;
44
+ if (typeof action === 'string' && action.toLowerCase() === 'start') {
45
+ isStart = true;
46
+ }
47
+ let functionName = `${tag}${action}`;
48
+ let mainFileName = `${functionName}_Main`;
49
+ let handlerFileName = `${functionName}_${handler}`;
50
+ let slsFunctionName = `${functionName}${handler}`;
51
+ let flowStepName = action;
52
+ if (isStart) {
53
+ functionName = tag;
54
+ mainFileName = `${tag}_Main`;
55
+ handlerFileName = `${tag}_${handler}`;
56
+ slsFunctionName = `${tag}${handler}`;
57
+ flowStepName = '';
58
+ }
59
+
60
+ const nextPart = parts.find((p, i) => i > index && p.type === 'sqs');
61
+ let nextStepFunctionName = null;
62
+ if (nextPart) {
63
+ nextStepFunctionName = `${tag}${nextPart.action}${nextPart.handler}`;
64
+ }
65
+
57
66
  const mainContent = await renderCustom(
58
67
  `s3/${mainTemplate}`,
59
- _s3TemplateData({ action, flow, functionName })
68
+ _s3TemplateData({
69
+ action,
70
+ flow,
71
+ functionName,
72
+ nextStepFunctionName,
73
+ isSimplifiedS3,
74
+ objects
75
+ })
60
76
  );
61
77
  const handlerContent = await renderCustom(
62
78
  `s3/${handlerTemplate}`,
63
- _s3HandlerData({ action, flow, functionName, mainFileName, handler })
79
+ _s3HandlerData({
80
+ action,
81
+ flow,
82
+ functionName,
83
+ mainFileName,
84
+ handler,
85
+ isSimplifiedS3
86
+ })
64
87
  );
65
88
  const descriptor = {
66
- functionName: `${functionName}${handler}`,
89
+ functionName: slsFunctionName,
67
90
  handlerPath: `${handlerRoot}/${handlerFileName}.main`,
68
91
  roleName
69
92
  };
@@ -73,18 +96,18 @@ export async function emitS3Flow(flow, source, handlerRoot) {
73
96
  ...descriptor,
74
97
  routeName: tag,
75
98
  flowTag: flow.flowTag,
76
- flowStepName: ''
99
+ flowStepName
77
100
  });
78
101
  } else if (type === 'sqs') {
79
102
  let filterPatternFunctionName = descriptor.functionName;
80
- if (action === 'CreatePreSignUrl') {
103
+ if (!isSimplifiedS3 && action === 'CreatePreSignUrl') {
81
104
  filterPatternFunctionName = 'CreatePresignUrlHdrSqs';
82
105
  }
83
106
  functionYaml = renderSqsFunctionYaml({
84
107
  ...descriptor,
85
108
  queueName: descriptor.functionName,
86
109
  flowTag: flow.flowTag,
87
- flowStepName: '',
110
+ flowStepName: action,
88
111
  filterPatternFunctionName
89
112
  });
90
113
  } else if (type === 's3') {
@@ -100,14 +123,15 @@ export async function emitS3Flow(flow, source, handlerRoot) {
100
123
  functionYaml = renderS3FunctionYaml({
101
124
  ...descriptor,
102
125
  bucketName: flow.bucketName,
126
+ s3KeyPrefix: flow.s3KeyPrefix,
103
127
  flowTag: flow.flowTag,
104
- flowStepName: ''
128
+ flowStepName: action
105
129
  });
106
130
  } else {
107
131
  functionYaml = renderInvFunctionYaml({
108
132
  ...descriptor,
109
133
  flowTag: flow.flowTag,
110
- flowStepName: ''
134
+ flowStepName: action
111
135
  });
112
136
  }
113
137
  return {
@@ -127,23 +151,209 @@ export async function emitS3Flow(flow, source, handlerRoot) {
127
151
  };
128
152
  }
129
153
 
130
- function _s3TemplateData({ action, flow, functionName }) {
131
- if (action === 'CreateObject') {
132
- return {
133
- flowTag: flow.flowTag,
134
- objectType: flow.objType?.objectType,
135
- functionName
136
- };
154
+ export function extractIdentifierFieldNames(identifiers) {
155
+ if (!Array.isArray(identifiers) || identifiers.length === 0) {
156
+ return [];
137
157
  }
138
- if (action === 'CreatePreSignUrl') {
139
- return { flowTag: flow.flowTag };
158
+ const keys = [];
159
+ for (const item of identifiers) {
160
+ if (typeof item === 'string') {
161
+ if (item && !keys.includes(item)) {
162
+ keys.push(item);
163
+ }
164
+ } else if (item && typeof item === 'object') {
165
+ if (Array.isArray(item.fieldNames) && item.fieldNames.length > 0) {
166
+ for (const fn of item.fieldNames) {
167
+ if (typeof fn === 'string' && fn && !keys.includes(fn)) {
168
+ keys.push(fn);
169
+ }
170
+ }
171
+ } else if (typeof item.fieldName === 'string' && item.fieldName) {
172
+ if (!keys.includes(item.fieldName)) {
173
+ keys.push(item.fieldName);
174
+ }
175
+ } else if (typeof item.name === 'string' && item.name) {
176
+ if (!keys.includes(item.name)) {
177
+ keys.push(item.name);
178
+ }
179
+ }
180
+ }
140
181
  }
141
- return { flowTag: flow.flowTag, functionName };
182
+ return keys;
142
183
  }
143
184
 
144
- function _s3HandlerData({ action, flow, functionName, mainFileName, handler }) {
145
- if (action === 'CreateObject') {
146
- return { functionName, handlerType: handler, route: flow.flowTag };
185
+ export function resolveParentIdentifierInfo(flow, objects = []) {
186
+ let parentObjectType = null;
187
+ let parentServiceTag = null;
188
+ const targetObjType =
189
+ typeof flow.objType === 'string'
190
+ ? flow.objType
191
+ : flow.objType?.objectType || flow.objectType;
192
+ const matchedObject = (objects || []).find(
193
+ (o) => o.objectType === targetObjType
194
+ );
195
+
196
+ if (flow.parentObjectType) {
197
+ parentObjectType = flow.parentObjectType;
198
+ } else if (flow.belongTo?.objectType) {
199
+ parentObjectType = flow.belongTo.objectType;
200
+ parentServiceTag = flow.belongTo.serviceTag;
201
+ } else if (Array.isArray(flow.belongTo) && flow.belongTo[0]?.objectType) {
202
+ parentObjectType = flow.belongTo[0].objectType;
203
+ parentServiceTag = flow.belongTo[0].serviceTag;
204
+ }
205
+
206
+ if (!parentObjectType && matchedObject?.belongTo) {
207
+ const belongTo = matchedObject.belongTo;
208
+ if (Array.isArray(belongTo) && belongTo[0]?.objectType) {
209
+ parentObjectType = belongTo[0].objectType;
210
+ parentServiceTag = belongTo[0].serviceTag;
211
+ } else if (belongTo.objectType) {
212
+ parentObjectType = belongTo.objectType;
213
+ parentServiceTag = belongTo.serviceTag;
214
+ }
215
+ }
216
+
217
+ const belongToDef = flow.belongTo || matchedObject?.belongTo;
218
+ const singleBelongTo = Array.isArray(belongToDef) ? belongToDef[0] : belongToDef;
219
+
220
+ let parentIdKeys = [];
221
+ if (Array.isArray(flow.parentIdentifierKeys) && flow.parentIdentifierKeys.length > 0) {
222
+ parentIdKeys = [...flow.parentIdentifierKeys];
223
+ } else if (flow.parentIdentifierKey) {
224
+ parentIdKeys = [flow.parentIdentifierKey];
225
+ } else if (
226
+ Array.isArray(singleBelongTo?.parentIdentifierKeys) &&
227
+ singleBelongTo.parentIdentifierKeys.length > 0
228
+ ) {
229
+ parentIdKeys = [...singleBelongTo.parentIdentifierKeys];
230
+ } else if (
231
+ Array.isArray(singleBelongTo?.identifiers) &&
232
+ singleBelongTo.identifiers.length > 0
233
+ ) {
234
+ parentIdKeys = extractIdentifierFieldNames(singleBelongTo.identifiers);
235
+ } else {
236
+ const parentObj =
237
+ Array.isArray(objects) && parentObjectType
238
+ ? objects.find((o) => o.objectType === parentObjectType)
239
+ : null;
240
+ const parentIdentifiers =
241
+ parentObj?.identifiers || matchedObject?.belongToSchema?.identifiers;
242
+ if (Array.isArray(parentIdentifiers) && parentIdentifiers.length > 0) {
243
+ parentIdKeys = extractIdentifierFieldNames(parentIdentifiers);
244
+ }
245
+ if (parentIdKeys.length === 0 && parentObjectType) {
246
+ parentIdKeys = [`${parentObjectType}Id`];
247
+ }
248
+ }
249
+
250
+ let parentIdKey = null;
251
+ if (parentIdKeys.length > 0) {
252
+ parentIdKey = parentIdKeys[0];
253
+ }
254
+
255
+ return {
256
+ parentObjectType,
257
+ parentServiceTag,
258
+ parentIdKey,
259
+ parentIdKeys
260
+ };
261
+ }
262
+
263
+ function _s3TemplateData({
264
+ action,
265
+ flow,
266
+ functionName,
267
+ nextStepFunctionName,
268
+ isSimplifiedS3,
269
+ objects = []
270
+ }) {
271
+ let maxFileSizeMb = 50;
272
+ if (flow.maxFileSizeMb) {
273
+ maxFileSizeMb = flow.maxFileSizeMb;
274
+ }
275
+ let expires = 86400;
276
+ if (flow.expires) {
277
+ expires = flow.expires;
278
+ }
279
+ let contentTypePrefix = 'video/';
280
+ if (flow.contentTypePrefix) {
281
+ contentTypePrefix = flow.contentTypePrefix;
282
+ } else if (flow.contentType) {
283
+ contentTypePrefix = flow.contentType;
284
+ }
285
+ const { parentObjectType, parentServiceTag, parentIdKey, parentIdKeys } =
286
+ resolveParentIdentifierInfo(flow, objects);
287
+ let s3KeyPrefix = 'uploads/';
288
+ if (flow.s3KeyPrefix) {
289
+ s3KeyPrefix = flow.s3KeyPrefix;
290
+ }
291
+ let defaultImportType = 'bin';
292
+ if (flow.defaultImportType) {
293
+ defaultImportType = flow.defaultImportType;
294
+ } else if (flow.importType) {
295
+ defaultImportType = flow.importType;
296
+ } else if (typeof contentTypePrefix === 'string' && contentTypePrefix.startsWith('video/')) {
297
+ defaultImportType = 'mp4';
298
+ } else if (typeof contentTypePrefix === 'string' && contentTypePrefix.startsWith('image/')) {
299
+ defaultImportType = 'jpg';
300
+ }
301
+
302
+ let isRbac = false;
303
+ if (typeof flow.isRbac === 'boolean') {
304
+ isRbac = flow.isRbac;
305
+ } else if (flowHasRbac(flow, objects)) {
306
+ isRbac = true;
307
+ } else if (parentObjectType && parentServiceTag) {
308
+ isRbac = true;
309
+ }
310
+
311
+ let rbacServiceTag = parentServiceTag;
312
+ if (!rbacServiceTag) {
313
+ rbacServiceTag = flow.objType?.serviceTag || flow.serviceTag || 'Rbac';
314
+ }
315
+
316
+ return {
317
+ action,
318
+ flowTag: flow.flowTag,
319
+ objectType: flow.objType?.objectType,
320
+ functionName,
321
+ nextStepFunctionName,
322
+ isSimplifiedS3,
323
+ bucketName: flow.bucketName,
324
+ s3KeyPrefix,
325
+ maxFileSizeMb,
326
+ expires,
327
+ contentTypePrefix,
328
+ parentObjectType,
329
+ parentServiceTag,
330
+ parentIdKey,
331
+ parentIdKeys,
332
+ defaultImportType,
333
+ isRbac,
334
+ rbacServiceTag
335
+ };
336
+ }
337
+
338
+ function _s3HandlerData({
339
+ action,
340
+ flow,
341
+ functionName,
342
+ mainFileName,
343
+ handler,
344
+ isSimplifiedS3
345
+ }) {
346
+ const isStartOrCreate =
347
+ action === 'CreateObject' ||
348
+ (typeof action === 'string' && action.toLowerCase() === 'start');
349
+ if (isStartOrCreate) {
350
+ return {
351
+ functionName,
352
+ mainFileName,
353
+ handlerType: handler,
354
+ route: flow.flowTag,
355
+ isSimplifiedS3
356
+ };
147
357
  }
148
358
  if (action === 'ProcessAfterUpload') {
149
359
  let bucketName = '';
@@ -154,8 +364,9 @@ function _s3HandlerData({ action, flow, functionName, mainFileName, handler }) {
154
364
  flowTag: flow.flowTag,
155
365
  functionName,
156
366
  mainFileName,
157
- s3Bucket: bucketName
367
+ s3Bucket: bucketName,
368
+ isSimplifiedS3
158
369
  };
159
370
  }
160
- return { functionName, handlerType: handler };
371
+ return { functionName, mainFileName, handlerType: handler, isSimplifiedS3 };
161
372
  }
@@ -79,6 +79,9 @@ export function flowExtTopicSteps(flows) {
79
79
  if (typeof flow?.flowTag !== 'string' || flow.flowTag === '') {
80
80
  return [];
81
81
  }
82
+ if (Array.isArray(flow.event) && flow.event.includes('s3')) {
83
+ return [];
84
+ }
82
85
  const hasTriggerFlowTypes =
83
86
  Array.isArray(flow.triggerFlowTypes) && flow.triggerFlowTypes.length > 0;
84
87
  if (flow.statusType === 'triggerCache' && !hasTriggerFlowTypes) {
@@ -341,3 +344,131 @@ function normalizeBelongTo(object) {
341
344
  if (!belongTo) return [];
342
345
  return Array.isArray(belongTo) ? belongTo : [belongTo];
343
346
  }
347
+
348
+ export const LEGACY_S3_PARTS = [
349
+ {
350
+ action: 'CreateObject',
351
+ handler: 'HdrWbs',
352
+ type: 'wbs',
353
+ mainTemplate: 'CreateObject_Main.ejs',
354
+ handlerTemplate: 'CreateObject_HdrWbs.ejs'
355
+ },
356
+ {
357
+ action: 'ReservedLimit',
358
+ handler: 'HdrSqs',
359
+ type: 'sqs',
360
+ mainTemplate: 'ReservedLimit_Main.ejs',
361
+ handlerTemplate: 'ReservedLimit_HdrSqs.ejs'
362
+ },
363
+ {
364
+ action: 'CreatePreSignUrl',
365
+ handler: 'HdrSqs',
366
+ type: 'sqs',
367
+ mainTemplate: 'CreatePreSignUrl_Main.ejs',
368
+ handlerTemplate: 'CreatePreSignUrl_HdrSqs.ejs'
369
+ },
370
+ {
371
+ action: 'ProcessAfterUpload',
372
+ handler: 'HdrS3',
373
+ type: 's3',
374
+ mainTemplate: 'ProcessS3File_Main.ejs',
375
+ handlerTemplate: 'FlowHandler_HdrS3.ejs'
376
+ },
377
+ {
378
+ action: 'ConfirmAfterUpload',
379
+ handler: 'HdrSqs',
380
+ type: 'sqs',
381
+ mainTemplate: 'ConfirmReserved_Main.ejs',
382
+ handlerTemplate: 'ConfirmReserved_HdrSqs.ejs'
383
+ }
384
+ ];
385
+
386
+ export function resolveS3Parts(flow) {
387
+ if (!Array.isArray(flow?.flowSteps) || flow.flowSteps.length === 0) {
388
+ return LEGACY_S3_PARTS;
389
+ }
390
+
391
+ const stepNames = flow.flowSteps.map((s) => s.stepName);
392
+ if (
393
+ stepNames.includes('CreateObject') &&
394
+ stepNames.includes('ReservedLimit') &&
395
+ stepNames.includes('ConfirmAfterUpload')
396
+ ) {
397
+ return LEGACY_S3_PARTS;
398
+ }
399
+
400
+ const hasS3Step = flow.flowSteps.some(
401
+ (step) =>
402
+ (Array.isArray(step.event) && step.event.includes('s3')) ||
403
+ (typeof step.stepName === 'string' &&
404
+ step.stepName.toLowerCase().includes('afterupload'))
405
+ );
406
+
407
+ if (!hasS3Step) {
408
+ return LEGACY_S3_PARTS;
409
+ }
410
+
411
+ const isDeferredNodeFlow =
412
+ !stepNames.includes('CreatePreSignUrl') &&
413
+ (stepNames.includes('ConfirmComplete') ||
414
+ stepNames.includes('ConfirmAfterUpload') ||
415
+ stepNames.length <= 3);
416
+
417
+ return flow.flowSteps.map((step) => {
418
+ const action = upperFirst(step.stepName);
419
+ const eventType = Array.isArray(step.event) ? step.event[0] : 'sqs';
420
+
421
+ if (eventType === 'wbs' || action === 'Start' || action === 'CreateObject') {
422
+ let mainTemplate = 'CreateObject_Main.ejs';
423
+ if (isDeferredNodeFlow) {
424
+ mainTemplate = 'Start_Main.ejs';
425
+ }
426
+ return {
427
+ action,
428
+ handler: 'HdrWbs',
429
+ type: 'wbs',
430
+ mainTemplate,
431
+ handlerTemplate: 'CreateObject_HdrWbs.ejs'
432
+ };
433
+ }
434
+
435
+ if (eventType === 's3' || action === 'ProcessAfterUpload') {
436
+ return {
437
+ action,
438
+ handler: 'HdrS3',
439
+ type: 's3',
440
+ mainTemplate: 'ProcessS3File_Main.ejs',
441
+ handlerTemplate: 'FlowHandler_HdrS3.ejs'
442
+ };
443
+ }
444
+
445
+ if (action === 'ConfirmComplete') {
446
+ return {
447
+ action,
448
+ handler: 'HdrSqs',
449
+ type: 'sqs',
450
+ mainTemplate: 'ConfirmComplete_Main.ejs',
451
+ handlerTemplate: 'ConfirmComplete_HdrSqs.ejs'
452
+ };
453
+ }
454
+
455
+ if (action === 'ConfirmAfterUpload') {
456
+ return {
457
+ action,
458
+ handler: 'HdrSqs',
459
+ type: 'sqs',
460
+ mainTemplate: 'ConfirmAfterUpload_Main.ejs',
461
+ handlerTemplate: 'ConfirmAfterUpload_HdrSqs.ejs'
462
+ };
463
+ }
464
+
465
+ return {
466
+ action,
467
+ handler: 'HdrSqs',
468
+ type: 'sqs',
469
+ mainTemplate: 'CreatePreSignUrl_Main.ejs',
470
+ handlerTemplate: 'CreatePreSignUrl_HdrSqs.ejs'
471
+ };
472
+ });
473
+ }
474
+
@@ -201,7 +201,9 @@ async function emitFlow(flow, objects) {
201
201
  functions.push(...root.functions);
202
202
  }
203
203
  if (flow.event.includes('s3')) {
204
- const s3Artifacts = await emitS3Flow(flow, source, handlerRoot);
204
+ const s3Artifacts = await emitS3Flow(flow, source, handlerRoot, {
205
+ objects
206
+ });
205
207
  artifacts.push(...s3Artifacts.artifacts);
206
208
  functions.push(...s3Artifacts.functions);
207
209
  }
@@ -247,8 +247,8 @@ export default async function <%- typeof functionName !== 'undefined' ? function
247
247
  resultPermissionCheck
248
248
  );
249
249
 
250
- //(<rbacPublish_<%- flowTag %>)
251
- //(</rbacPublish_<%- flowTag %>)
250
+ //(<rbacPublish_<%- flowTag %>>)
251
+ //(</rbacPublish_<%- flowTag %>>)
252
252
 
253
253
  return resultPermissionCheck;
254
254
  <% } else { -%>
@@ -0,0 +1,110 @@
1
+ /*
2
+ Copyright (C) 2026 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 { middlewareHandler as middleware } from '@izara_project/izara-middleware';
19
+ import { recordHandlerSharedLib } from '@izara_project/izara-core-library-record-handler';
20
+ import Logger from '@izara_project/izara-core-library-logger';
21
+ import callingFlowSharedLib from '@izara_project/izara-core-library-calling-flow';
22
+
23
+ import <%= functionName %> from './<%= functionName %>_Main.js';
24
+
25
+ middleware.setValidatorSchema(recordHandlerSharedLib.baseValidatorSchema());
26
+
27
+ let perRecordsValidatorSchema = {
28
+ type: 'object',
29
+ required: ['objType', 'objInstanceFull'],
30
+ properties: {
31
+ objType: {
32
+ type: 'object',
33
+ required: ['objectType', 'serviceTag'],
34
+ properties: {
35
+ objectType: {
36
+ type: 'string',
37
+ pattern: '^[a-zA-Z0-9_-]+$'
38
+ },
39
+ serviceTag: {
40
+ type: 'string',
41
+ pattern: '^[a-zA-Z0-9_-]+$'
42
+ }
43
+ }
44
+ },
45
+ objInstanceFull: {
46
+ type: 'object',
47
+ properties: {
48
+ identifiers: {
49
+ type: 'object',
50
+ minProperties: 1
51
+ },
52
+ fields: {
53
+ type: 'object'
54
+ }
55
+ }
56
+ }
57
+ }
58
+ };
59
+
60
+ perRecordsValidatorSchema =
61
+ callingFlowSharedLib.addOptionalPassBackPropertiesToValidatorSchema(
62
+ perRecordsValidatorSchema
63
+ );
64
+
65
+ export const main = middleware.wrap(async (event, context) => {
66
+ try {
67
+ let recordPromises = [];
68
+
69
+ await Promise.all(
70
+ event.Records.map(async (record) => {
71
+ let passOnProperties = [];
72
+ record._izContext.logger.debug('record <%= functionName %><%= handlerType %>', record);
73
+
74
+ await recordHandlerSharedLib.validateRecord(
75
+ record,
76
+ '<%= functionName %><%= handlerType %>',
77
+ perRecordsValidatorSchema
78
+ );
79
+
80
+ passOnProperties.push(record.body.Message.objType);
81
+ passOnProperties.push(record.body.Message.objInstanceFull);
82
+ //(<additionalProperties>)
83
+ //(</additionalProperties>)
84
+ passOnProperties.push(
85
+ callingFlowSharedLib.addPassBackPropertiesToPassOnProperties(
86
+ record.body.Message
87
+ )
88
+ );
89
+
90
+ record._izContext.logger.debug(
91
+ 'passOnProperties in <%= functionName %><%= handlerType %>',
92
+ passOnProperties
93
+ );
94
+
95
+ let returnPromise = await recordHandlerSharedLib.recordHandler(
96
+ record,
97
+ <%= functionName %>,
98
+ passOnProperties
99
+ );
100
+
101
+ recordPromises.push(returnPromise);
102
+ })
103
+ );
104
+
105
+ return await Promise.all(recordPromises);
106
+ } catch (err) {
107
+ Logger.error('Error <%= functionName %><%= handlerType %>: ', err);
108
+ throw err;
109
+ }
110
+ });