@izara_project/izara-core-generate-service-code 1.0.100 → 1.0.101
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/emit/application/crud/templates/CreateLogicalLib.ejs +6 -0
- package/src/emit/application/custom/customFlowS3.js +188 -6
- package/src/emit/application/custom/emitCustomFlowApplicationArtifacts.js +5 -4
- package/src/emit/application/custom/templates/s3/ConfirmAfterUpload_HdrSqs.ejs +1 -0
- package/src/emit/application/custom/templates/s3/ConfirmComplete_HdrSqs.ejs +1 -0
- package/src/emit/application/custom/templates/s3/ConfirmComplete_Main.ejs +28 -17
- package/src/emit/application/custom/templates/s3/ProcessS3File_Main.ejs +127 -22
- package/src/emit/application/custom/templates/s3/Start_Main.ejs +20 -1
- package/src/plan/domainRegistry.js +2 -1
- package/test/unit/application/customFlowS3.test.js +128 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@izara_project/izara-core-generate-service-code",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.101",
|
|
4
4
|
"description": "Code for locally generating per service files",
|
|
5
5
|
"homepage": "https://bitbucket.org/izara-core-support-services/izara-core-support-services-generate-service-code#readme",
|
|
6
6
|
"repository": {
|
|
@@ -140,6 +140,12 @@ export async function resolveSchemaAndIdentity(_izContext, requestParams, foundE
|
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
requestParams.submittedByUserId = submittedByUserId;
|
|
143
|
+
if (submittedByUserId) {
|
|
144
|
+
_izContext.correlationIds?.set(
|
|
145
|
+
coreConsts.SUBMITTED_BY_USER_ID,
|
|
146
|
+
submittedByUserId
|
|
147
|
+
);
|
|
148
|
+
}
|
|
143
149
|
return { objType, objectSchema, targetIdentifiers };
|
|
144
150
|
}
|
|
145
151
|
|
|
@@ -27,7 +27,7 @@ export async function emitS3Flow(
|
|
|
27
27
|
flow,
|
|
28
28
|
source,
|
|
29
29
|
handlerRoot,
|
|
30
|
-
{ objects = [] } = {}
|
|
30
|
+
{ objects = [], relationships = [] } = {}
|
|
31
31
|
) {
|
|
32
32
|
const tag = upperFirst(flow.flowTag);
|
|
33
33
|
const roleName = customFlowRoleName(flow.flowTag);
|
|
@@ -71,7 +71,8 @@ export async function emitS3Flow(
|
|
|
71
71
|
functionName,
|
|
72
72
|
nextStepFunctionName,
|
|
73
73
|
isSimplifiedS3,
|
|
74
|
-
objects
|
|
74
|
+
objects,
|
|
75
|
+
relationships
|
|
75
76
|
})
|
|
76
77
|
);
|
|
77
78
|
const handlerContent = await renderCustom(
|
|
@@ -260,13 +261,182 @@ export function resolveParentIdentifierInfo(flow, objects = []) {
|
|
|
260
261
|
};
|
|
261
262
|
}
|
|
262
263
|
|
|
264
|
+
export function resolveFlowRelationships(
|
|
265
|
+
flow,
|
|
266
|
+
{ objects = [], relationships = [] } = {}
|
|
267
|
+
) {
|
|
268
|
+
if (Array.isArray(flow.relationships) && flow.relationships.length > 0) {
|
|
269
|
+
const list = [];
|
|
270
|
+
for (const rel of flow.relationships) {
|
|
271
|
+
let targetObj = {
|
|
272
|
+
objectType: '',
|
|
273
|
+
serviceTag: ''
|
|
274
|
+
};
|
|
275
|
+
if (rel.targetObjType) {
|
|
276
|
+
targetObj = {
|
|
277
|
+
objectType: rel.targetObjType.objectType || '',
|
|
278
|
+
serviceTag: rel.targetObjType.serviceTag || ''
|
|
279
|
+
};
|
|
280
|
+
} else if (rel.objectType) {
|
|
281
|
+
targetObj = {
|
|
282
|
+
objectType: rel.objectType,
|
|
283
|
+
serviceTag: rel.serviceTag || ''
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
let relType = {
|
|
288
|
+
relationshipTag: '',
|
|
289
|
+
serviceTag: targetObj.serviceTag
|
|
290
|
+
};
|
|
291
|
+
if (rel.relType) {
|
|
292
|
+
relType = {
|
|
293
|
+
relationshipTag: rel.relType.relationshipTag || '',
|
|
294
|
+
serviceTag: rel.relType.serviceTag || targetObj.serviceTag
|
|
295
|
+
};
|
|
296
|
+
} else if (rel.relationshipTag) {
|
|
297
|
+
relType = {
|
|
298
|
+
relationshipTag: rel.relationshipTag,
|
|
299
|
+
serviceTag: rel.serviceTag || targetObj.serviceTag
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
let targetIdentifierKey = '';
|
|
304
|
+
if (rel.targetIdentifierKey) {
|
|
305
|
+
targetIdentifierKey = rel.targetIdentifierKey;
|
|
306
|
+
} else if (rel.targetIdentifiers) {
|
|
307
|
+
const idKeys = Object.keys(rel.targetIdentifiers);
|
|
308
|
+
if (idKeys.length > 0) {
|
|
309
|
+
targetIdentifierKey = idKeys[0];
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (!targetIdentifierKey && targetObj.objectType) {
|
|
313
|
+
targetIdentifierKey = `${targetObj.objectType}Id`;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
let direction = 'to';
|
|
317
|
+
if (rel.relationshipDirection) {
|
|
318
|
+
direction = rel.relationshipDirection;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
list.push({
|
|
322
|
+
targetObjType: targetObj,
|
|
323
|
+
relType,
|
|
324
|
+
targetIdentifierKey,
|
|
325
|
+
relationshipProperties: rel.relationshipProperties || {},
|
|
326
|
+
relationshipDirection: direction
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
return list;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
let targetObjType = '';
|
|
333
|
+
if (typeof flow.objType === 'string') {
|
|
334
|
+
targetObjType = flow.objType;
|
|
335
|
+
} else if (flow.objType?.objectType) {
|
|
336
|
+
targetObjType = flow.objType.objectType;
|
|
337
|
+
} else if (flow.objectType) {
|
|
338
|
+
targetObjType = flow.objectType;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (
|
|
342
|
+
!targetObjType ||
|
|
343
|
+
!Array.isArray(relationships) ||
|
|
344
|
+
relationships.length === 0
|
|
345
|
+
) {
|
|
346
|
+
return [];
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const resolved = [];
|
|
350
|
+
for (const relItem of relationships) {
|
|
351
|
+
if (!relItem || typeof relItem !== 'object') {
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (
|
|
356
|
+
relItem.objectType &&
|
|
357
|
+
relItem.objectType.toLowerCase() === targetObjType.toLowerCase() &&
|
|
358
|
+
relItem.relationshipTag &&
|
|
359
|
+
relItem.relationshipServiceTag
|
|
360
|
+
) {
|
|
361
|
+
const parentService = relItem.relationshipServiceTag;
|
|
362
|
+
let parentObjType = '';
|
|
363
|
+
if (relItem.targetObjectType) {
|
|
364
|
+
parentObjType = relItem.targetObjectType;
|
|
365
|
+
} else if (parentService.length > 0) {
|
|
366
|
+
parentObjType =
|
|
367
|
+
parentService[0].toLowerCase() + parentService.slice(1);
|
|
368
|
+
}
|
|
369
|
+
let targetIdentifierKey = `${parentObjType}Id`;
|
|
370
|
+
if (relItem.targetIdentifierKey) {
|
|
371
|
+
targetIdentifierKey = relItem.targetIdentifierKey;
|
|
372
|
+
}
|
|
373
|
+
let direction = 'to';
|
|
374
|
+
if (relItem.relationshipDirection) {
|
|
375
|
+
direction = relItem.relationshipDirection;
|
|
376
|
+
}
|
|
377
|
+
resolved.push({
|
|
378
|
+
targetObjType: {
|
|
379
|
+
objectType: parentObjType,
|
|
380
|
+
serviceTag: parentService
|
|
381
|
+
},
|
|
382
|
+
relType: {
|
|
383
|
+
relationshipTag: relItem.relationshipTag,
|
|
384
|
+
serviceTag: parentService
|
|
385
|
+
},
|
|
386
|
+
targetIdentifierKey,
|
|
387
|
+
relationshipProperties: {},
|
|
388
|
+
relationshipDirection: direction
|
|
389
|
+
});
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const relKeys = Object.keys(relItem);
|
|
394
|
+
for (const relTag of relKeys) {
|
|
395
|
+
const relDef = relItem[relTag];
|
|
396
|
+
if (!relDef || !Array.isArray(relDef.links)) {
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
for (const link of relDef.links) {
|
|
400
|
+
if (!link || !link.from || !link.to) {
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const isToTarget =
|
|
405
|
+
link.to.objType?.objectType?.toLowerCase() ===
|
|
406
|
+
targetObjType.toLowerCase();
|
|
407
|
+
if (isToTarget && link.to.requiredOnCreate) {
|
|
408
|
+
const fromObj = link.from.objType || {};
|
|
409
|
+
const fromObjType = fromObj.objectType || '';
|
|
410
|
+
const targetIdentifierKey = `${fromObjType}Id`;
|
|
411
|
+
resolved.push({
|
|
412
|
+
targetObjType: {
|
|
413
|
+
objectType: fromObjType,
|
|
414
|
+
serviceTag: fromObj.serviceTag || ''
|
|
415
|
+
},
|
|
416
|
+
relType: {
|
|
417
|
+
relationshipTag: relTag,
|
|
418
|
+
serviceTag: fromObj.serviceTag || ''
|
|
419
|
+
},
|
|
420
|
+
targetIdentifierKey,
|
|
421
|
+
relationshipProperties: {},
|
|
422
|
+
relationshipDirection: 'to'
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return resolved;
|
|
430
|
+
}
|
|
431
|
+
|
|
263
432
|
function _s3TemplateData({
|
|
264
433
|
action,
|
|
265
434
|
flow,
|
|
266
435
|
functionName,
|
|
267
436
|
nextStepFunctionName,
|
|
268
437
|
isSimplifiedS3,
|
|
269
|
-
objects = []
|
|
438
|
+
objects = [],
|
|
439
|
+
relationships = []
|
|
270
440
|
}) {
|
|
271
441
|
let maxFileSizeMb = 50;
|
|
272
442
|
if (flow.maxFileSizeMb) {
|
|
@@ -293,9 +463,15 @@ function _s3TemplateData({
|
|
|
293
463
|
defaultImportType = flow.defaultImportType;
|
|
294
464
|
} else if (flow.importType) {
|
|
295
465
|
defaultImportType = flow.importType;
|
|
296
|
-
} else if (
|
|
466
|
+
} else if (
|
|
467
|
+
typeof contentTypePrefix === 'string' &&
|
|
468
|
+
contentTypePrefix.startsWith('video/')
|
|
469
|
+
) {
|
|
297
470
|
defaultImportType = 'mp4';
|
|
298
|
-
} else if (
|
|
471
|
+
} else if (
|
|
472
|
+
typeof contentTypePrefix === 'string' &&
|
|
473
|
+
contentTypePrefix.startsWith('image/')
|
|
474
|
+
) {
|
|
299
475
|
defaultImportType = 'jpg';
|
|
300
476
|
}
|
|
301
477
|
|
|
@@ -313,6 +489,11 @@ function _s3TemplateData({
|
|
|
313
489
|
rbacServiceTag = flow.objType?.serviceTag || flow.serviceTag || 'Rbac';
|
|
314
490
|
}
|
|
315
491
|
|
|
492
|
+
const flowRelationships = resolveFlowRelationships(flow, {
|
|
493
|
+
objects,
|
|
494
|
+
relationships
|
|
495
|
+
});
|
|
496
|
+
|
|
316
497
|
return {
|
|
317
498
|
action,
|
|
318
499
|
flowTag: flow.flowTag,
|
|
@@ -331,7 +512,8 @@ function _s3TemplateData({
|
|
|
331
512
|
parentIdKeys,
|
|
332
513
|
defaultImportType,
|
|
333
514
|
isRbac,
|
|
334
|
-
rbacServiceTag
|
|
515
|
+
rbacServiceTag,
|
|
516
|
+
flowRelationships
|
|
335
517
|
};
|
|
336
518
|
}
|
|
337
519
|
|
|
@@ -35,7 +35,7 @@ export { customFlowApplicationManagedPaths };
|
|
|
35
35
|
|
|
36
36
|
export async function emitCustomFlowApplicationArtifacts(
|
|
37
37
|
flows,
|
|
38
|
-
{ objects = [] } = {}
|
|
38
|
+
{ objects = [], relationships = [] } = {}
|
|
39
39
|
) {
|
|
40
40
|
const customFlows = supportedCustomFlows(flows);
|
|
41
41
|
const generatedRoots = generatedOwnTopicFlows(flows);
|
|
@@ -51,7 +51,7 @@ export async function emitCustomFlowApplicationArtifacts(
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
const emitted = await Promise.all(
|
|
54
|
-
customFlows.map((flow) => emitFlow(flow, objects))
|
|
54
|
+
customFlows.map((flow) => emitFlow(flow, objects, relationships))
|
|
55
55
|
);
|
|
56
56
|
const roots = await Promise.all(
|
|
57
57
|
generatedRoots.map((flow) => emitOwnTopicRoot(flow, objects))
|
|
@@ -77,7 +77,7 @@ export async function emitCustomFlowApplicationArtifacts(
|
|
|
77
77
|
];
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
async function emitFlow(flow, objects) {
|
|
80
|
+
async function emitFlow(flow, objects, relationships = []) {
|
|
81
81
|
const tag = upperFirst(flow.flowTag);
|
|
82
82
|
const process = tag;
|
|
83
83
|
const source = `app/src/generatedCode/FlowSchemas/${tag}/source`;
|
|
@@ -202,7 +202,8 @@ async function emitFlow(flow, objects) {
|
|
|
202
202
|
}
|
|
203
203
|
if (flow.event.includes('s3')) {
|
|
204
204
|
const s3Artifacts = await emitS3Flow(flow, source, handlerRoot, {
|
|
205
|
-
objects
|
|
205
|
+
objects,
|
|
206
|
+
relationships
|
|
206
207
|
});
|
|
207
208
|
artifacts.push(...s3Artifacts.artifacts);
|
|
208
209
|
functions.push(...s3Artifacts.functions);
|
|
@@ -50,32 +50,43 @@ export default async function <%- functionName %>(
|
|
|
50
50
|
passBackProperties
|
|
51
51
|
});
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
let targetId =
|
|
54
|
+
passBackProperties.rawId ||
|
|
55
|
+
passBackProperties.<%- objectType %>Id ||
|
|
56
|
+
passBackProperties.parentPassBackProperties?.rawId ||
|
|
57
|
+
passBackProperties.parentPassBackProperties?.<%- objectType %>Id ||
|
|
58
|
+
objInstanceFull?.identifiers?.<%- objectType %>Id;
|
|
59
|
+
|
|
60
|
+
if (!targetId && objInstanceFull?.identifiers) {
|
|
61
|
+
targetId = Object.values(objInstanceFull.identifiers)[0];
|
|
62
|
+
}
|
|
56
63
|
|
|
57
64
|
//(<confirmUsageHook>)
|
|
58
65
|
//(</confirmUsageHook>)
|
|
59
66
|
|
|
60
67
|
const taskKey = `${createFlowTypeConcat(_izContext, { flowTag: '<%- flowTag %>', serviceTag: process.env.iz_serviceTag })}${targetId}`;
|
|
61
|
-
const
|
|
68
|
+
const tasks = await dynamodbSharedLib.query(
|
|
62
69
|
_izContext,
|
|
63
70
|
await dynamodbSharedLib.tableName(_izContext, 'WebSocketTask'),
|
|
64
|
-
{ taskKey
|
|
71
|
+
{ taskKey }
|
|
65
72
|
);
|
|
66
73
|
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
if (tasks && tasks.Items && tasks.Items.length > 0) {
|
|
75
|
+
for (const taskRecord of tasks.Items) {
|
|
76
|
+
if (taskRecord.connectionId) {
|
|
77
|
+
await postToConnection(
|
|
78
|
+
_izContext,
|
|
79
|
+
{
|
|
80
|
+
status: 'completed',
|
|
81
|
+
data: {
|
|
82
|
+
<%- objectType %>Id: targetId,
|
|
83
|
+
objInstanceFull: objInstanceFull
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
taskRecord.connectionId
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
79
90
|
}
|
|
80
91
|
|
|
81
92
|
return {
|
|
@@ -26,6 +26,7 @@ import snsSharedLib from '@izara_project/izara-core-library-sns';
|
|
|
26
26
|
import callingFlowSharedLib from '@izara_project/izara-core-library-calling-flow';
|
|
27
27
|
import lambdaSharedLib from '@izara_project/izara-core-library-lambda';
|
|
28
28
|
import { sns } from '@izara_project/izara-core-library-external-request';
|
|
29
|
+
import { consts } from '@izara_project/izara-middleware';
|
|
29
30
|
|
|
30
31
|
//(<optionalRequired>)
|
|
31
32
|
//(</optionalRequired>)
|
|
@@ -33,7 +34,8 @@ import { sns } from '@izara_project/izara-core-library-external-request';
|
|
|
33
34
|
/**
|
|
34
35
|
* Step 2: ProcessAfterUpload (Deferred-Node S3 Pipeline - ADR-016)
|
|
35
36
|
* Triggered by S3 ObjectCreated event. Inspects file, prepares entity payload,
|
|
36
|
-
* and publishes to Create_In SNS topic with callingFlow
|
|
37
|
+
* and publishes to Create_In SNS topic with callingFlow
|
|
38
|
+
* targeting ConfirmAfterUpload.
|
|
37
39
|
*
|
|
38
40
|
* @param {Object} _izContext
|
|
39
41
|
* @param {Object} bucket - S3 bucket record
|
|
@@ -50,7 +52,15 @@ export default async function <%- functionName %>(
|
|
|
50
52
|
passBackProperties = {}
|
|
51
53
|
) {
|
|
52
54
|
try {
|
|
53
|
-
_izContext.logger.
|
|
55
|
+
_izContext.logger.info(
|
|
56
|
+
'----- <%- functionName %> started -----',
|
|
57
|
+
{
|
|
58
|
+
bucketName: bucket.name,
|
|
59
|
+
key: field.key,
|
|
60
|
+
size: field.size
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
_izContext.logger.debug('<%- functionName %> incoming parameters:', {
|
|
54
64
|
bucket,
|
|
55
65
|
field,
|
|
56
66
|
passBackProperties
|
|
@@ -60,6 +70,12 @@ export default async function <%- functionName %>(
|
|
|
60
70
|
const actualByteSize = field.size;
|
|
61
71
|
const actualSizeMb = actualByteSize / (1024 * 1024);
|
|
62
72
|
|
|
73
|
+
_izContext.logger.debug('S3 file details:', {
|
|
74
|
+
s3Key,
|
|
75
|
+
actualByteSize,
|
|
76
|
+
actualSizeMb
|
|
77
|
+
});
|
|
78
|
+
|
|
63
79
|
let s3Metadata = {};
|
|
64
80
|
try {
|
|
65
81
|
const headResult = await s3.send(
|
|
@@ -68,13 +84,19 @@ export default async function <%- functionName %>(
|
|
|
68
84
|
Key: s3Key
|
|
69
85
|
})
|
|
70
86
|
);
|
|
87
|
+
|
|
88
|
+
_izContext.logger.debug('HeadObjectCommand raw result:', headResult);
|
|
71
89
|
s3Metadata = headResult.Metadata || {};
|
|
90
|
+
_izContext.logger.info('Parsed S3 Metadata:', { s3Metadata });
|
|
72
91
|
} catch (headErr) {
|
|
73
|
-
_izContext.logger.warn(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
92
|
+
_izContext.logger.warn(
|
|
93
|
+
'Could not retrieve S3 object metadata via HeadObjectCommand:',
|
|
94
|
+
{
|
|
95
|
+
bucket: bucket.name,
|
|
96
|
+
key: s3Key,
|
|
97
|
+
error: headErr.message
|
|
98
|
+
}
|
|
99
|
+
);
|
|
78
100
|
}
|
|
79
101
|
|
|
80
102
|
//(<extractIdentifiersFromKeyOrMetadata>)
|
|
@@ -102,13 +124,70 @@ export default async function <%- functionName %>(
|
|
|
102
124
|
//(</endpointHook>)
|
|
103
125
|
|
|
104
126
|
<% if (typeof nextStepFunctionName === 'string' && nextStepFunctionName) { -%>
|
|
105
|
-
const metaUserId =
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
127
|
+
const metaUserId = s3Metadata.submittedbyuserid || '';
|
|
128
|
+
<% if (typeof flowRelationships !== 'undefined' && Array.isArray(flowRelationships) && flowRelationships.length > 0) { -%>
|
|
129
|
+
<% for (const rel of flowRelationships) { -%>
|
|
130
|
+
<% const targetIdKey = rel.targetIdentifierKey || `${rel.targetObjType.objectType}Id`; -%>
|
|
131
|
+
<% const metaKey = targetIdKey.toLowerCase(); -%>
|
|
132
|
+
const meta<%- targetIdKey.charAt(0).toUpperCase() + targetIdKey.slice(1) %> = s3Metadata.<%- metaKey %> || '';
|
|
133
|
+
<% } -%>
|
|
134
|
+
<% } -%>
|
|
135
|
+
|
|
136
|
+
_izContext.logger.info('Extracted identifiers and metadata:', {
|
|
137
|
+
<%- objectType %>Id: rawId,
|
|
138
|
+
parentId,
|
|
139
|
+
submittedByUserId: metaUserId,
|
|
140
|
+
<% if (typeof flowRelationships !== 'undefined' && Array.isArray(flowRelationships) && flowRelationships.length > 0) { -%>
|
|
141
|
+
<% for (const rel of flowRelationships) { -%>
|
|
142
|
+
<% const targetIdKey = rel.targetIdentifierKey || `${rel.targetObjType.objectType}Id`; -%>
|
|
143
|
+
<%- targetIdKey %>: meta<%- targetIdKey.charAt(0).toUpperCase() + targetIdKey.slice(1) %>,
|
|
144
|
+
<% } -%>
|
|
145
|
+
<% } -%>
|
|
146
|
+
fileName: fileNameWithExt
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
if (metaUserId) {
|
|
150
|
+
_izContext.correlationIds?.set(consts.SUBMITTED_BY_USER_ID, metaUserId);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
//(<relationshipsHook>)
|
|
154
|
+
let relationships = [];
|
|
155
|
+
<% if (typeof flowRelationships !== 'undefined' && Array.isArray(flowRelationships) && flowRelationships.length > 0) { -%>
|
|
156
|
+
<% for (const rel of flowRelationships) { -%>
|
|
157
|
+
<% const targetIdKey = rel.targetIdentifierKey || `${rel.targetObjType.objectType}Id`; -%>
|
|
158
|
+
<% const targetObj = rel.targetObjType || {}; -%>
|
|
159
|
+
<% const relTypeObj = rel.relType || {}; -%>
|
|
160
|
+
<% const relDir = rel.relationshipDirection || 'to'; -%>
|
|
161
|
+
<% const varName = `meta${targetIdKey.charAt(0).toUpperCase() + targetIdKey.slice(1)}`; -%>
|
|
162
|
+
if (<%- varName %>) {
|
|
163
|
+
relationships.push({
|
|
164
|
+
targetObjType: {
|
|
165
|
+
objectType: '<%- targetObj.objectType %>',
|
|
166
|
+
serviceTag: '<%- targetObj.serviceTag %>'
|
|
167
|
+
},
|
|
168
|
+
targetIdentifiers: {
|
|
169
|
+
<%- targetIdKey %>: <%- varName %>
|
|
170
|
+
},
|
|
171
|
+
relType: {
|
|
172
|
+
relationshipTag: '<%- relTypeObj.relationshipTag %>',
|
|
173
|
+
serviceTag: '<%- relTypeObj.serviceTag %>'
|
|
174
|
+
},
|
|
175
|
+
relationshipProperties: <%- JSON.stringify(rel.relationshipProperties || {}) %>,
|
|
176
|
+
relationshipDirection: '<%- relDir %>'
|
|
177
|
+
});
|
|
178
|
+
_izContext.logger.info('Built relationship for <%- targetObj.objectType %>:', {
|
|
179
|
+
relationships
|
|
180
|
+
});
|
|
181
|
+
} else {
|
|
182
|
+
_izContext.logger.warn(
|
|
183
|
+
'<%- varName %> is empty! ' +
|
|
184
|
+
'No <%- relTypeObj.relationshipTag %> relationship attached.'
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
<% } -%>
|
|
188
|
+
<% } -%>
|
|
189
|
+
//(</relationshipsHook>)
|
|
190
|
+
|
|
112
191
|
let messageObject = {
|
|
113
192
|
objectType: '<%- objectType %>',
|
|
114
193
|
submittedByUserId: metaUserId,
|
|
@@ -127,37 +206,62 @@ export default async function <%- functionName %>(
|
|
|
127
206
|
<%- parentIdKey %>: parentId
|
|
128
207
|
},
|
|
129
208
|
<% } -%>
|
|
209
|
+
relationships,
|
|
130
210
|
fieldNames: {
|
|
131
211
|
//(<fieldNamesHook>)
|
|
132
212
|
//(</fieldNamesHook>)
|
|
213
|
+
fileName: fileNameWithExt
|
|
133
214
|
}
|
|
134
215
|
};
|
|
135
216
|
|
|
217
|
+
const confirmCallbackFunction =
|
|
218
|
+
await lambdaSharedLib.lambdaFunctionName(
|
|
219
|
+
_izContext,
|
|
220
|
+
'<%- nextStepFunctionName %>'
|
|
221
|
+
);
|
|
222
|
+
_izContext.logger.debug('Resolved confirm callback function:', {
|
|
223
|
+
confirmCallbackFunction
|
|
224
|
+
});
|
|
225
|
+
|
|
136
226
|
messageObject =
|
|
137
227
|
callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject(
|
|
138
228
|
messageObject,
|
|
139
229
|
callingFlowSharedLib.addParentCallingFlowConfig(
|
|
140
230
|
passBackProperties,
|
|
141
231
|
callingFlowSharedLib.createCallingFlowConfig(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
'<%- nextStepFunctionName %>'
|
|
145
|
-
),
|
|
146
|
-
{}
|
|
232
|
+
confirmCallbackFunction,
|
|
233
|
+
{ rawId: rawId, <%- objectType %>Id: rawId }
|
|
147
234
|
)
|
|
148
235
|
)
|
|
149
236
|
);
|
|
150
237
|
|
|
238
|
+
const topicArn =
|
|
239
|
+
snsSharedLib.snsTopicArnByFlowSchema(_izContext, 'Create_In');
|
|
240
|
+
|
|
151
241
|
let sendMessageToCreateEndpoint = {
|
|
152
242
|
Message: JSON.stringify(messageObject),
|
|
153
|
-
TopicArn:
|
|
243
|
+
TopicArn: topicArn
|
|
154
244
|
};
|
|
155
245
|
|
|
246
|
+
_izContext.logger.info('Publishing to Create_In SNS topic:', {
|
|
247
|
+
topicArn,
|
|
248
|
+
<%- objectType %>Id: rawId,
|
|
249
|
+
parentId<%- (typeof flowRelationships !== 'undefined' && Array.isArray(flowRelationships) && flowRelationships.length > 0) ? flowRelationships.map(r => { const k = r.targetIdentifierKey || `${r.targetObjType.objectType}Id`; return `,\n ${k}: meta${k.charAt(0).toUpperCase() + k.slice(1)}`; }).join('') : '' %>
|
|
250
|
+
});
|
|
156
251
|
_izContext.logger.debug(
|
|
157
|
-
'
|
|
252
|
+
'Full Create_In message payload:',
|
|
158
253
|
sendMessageToCreateEndpoint
|
|
159
254
|
);
|
|
160
|
-
|
|
255
|
+
|
|
256
|
+
const publishResult = await sns.publishAsync(
|
|
257
|
+
_izContext,
|
|
258
|
+
sendMessageToCreateEndpoint
|
|
259
|
+
);
|
|
260
|
+
_izContext.logger.info('Successfully published to Create_In SNS:', {
|
|
261
|
+
publishResult,
|
|
262
|
+
s3Key,
|
|
263
|
+
<%- objectType %>Id: rawId
|
|
264
|
+
});
|
|
161
265
|
|
|
162
266
|
return {
|
|
163
267
|
status: 'dispatched',
|
|
@@ -175,3 +279,4 @@ export default async function <%- functionName %>(
|
|
|
175
279
|
throw err;
|
|
176
280
|
}
|
|
177
281
|
}
|
|
282
|
+
|
|
@@ -123,6 +123,13 @@ export default async function <%- functionName %>(
|
|
|
123
123
|
//(<createKeyForUploadFile>)
|
|
124
124
|
let importType = '<%- defaultImportType %>';
|
|
125
125
|
let uploadPath = '';
|
|
126
|
+
<% if (typeof flowRelationships !== 'undefined' && Array.isArray(flowRelationships) && flowRelationships.length > 0) { -%>
|
|
127
|
+
<% for (const rel of flowRelationships) { -%>
|
|
128
|
+
<% const relIdKey = rel.targetIdentifierKey || `${rel.targetObjType.objectType}Id`; -%>
|
|
129
|
+
const <%- relIdKey %> =
|
|
130
|
+
requestParams.fieldNames?.<%- relIdKey %> || requestParams.file?.<%- relIdKey %>;
|
|
131
|
+
<% } -%>
|
|
132
|
+
<% } -%>
|
|
126
133
|
//(</createKeyForUploadFile>)
|
|
127
134
|
const Key = `<%- s3KeyPrefix %>${uploadPath}`;
|
|
128
135
|
const maximumSize = maxFileSizeMb * 1048576;
|
|
@@ -139,7 +146,13 @@ export default async function <%- functionName %>(
|
|
|
139
146
|
<% } -%>
|
|
140
147
|
<% } -%>
|
|
141
148
|
['starts-with', '$x-amz-meta-submittedByUserId', ''],
|
|
142
|
-
['starts-with', '$Content-Type', '<%- contentTypePrefix %>']
|
|
149
|
+
['starts-with', '$Content-Type', '<%- contentTypePrefix %>'],
|
|
150
|
+
<% if (typeof flowRelationships !== 'undefined' && Array.isArray(flowRelationships) && flowRelationships.length > 0) { -%>
|
|
151
|
+
<% for (const rel of flowRelationships) { -%>
|
|
152
|
+
<% const relIdKey = rel.targetIdentifierKey || `${rel.targetObjType.objectType}Id`; -%>
|
|
153
|
+
['starts-with', '$x-amz-meta-<%- relIdKey %>', ''],
|
|
154
|
+
<% } -%>
|
|
155
|
+
<% } -%>
|
|
143
156
|
//(<conditionCreateUrlLink>)
|
|
144
157
|
//(</conditionCreateUrlLink>)
|
|
145
158
|
],
|
|
@@ -151,6 +164,12 @@ export default async function <%- functionName %>(
|
|
|
151
164
|
<% } -%>
|
|
152
165
|
<% } -%>
|
|
153
166
|
'x-amz-meta-submittedByUserId': submittedByUserId,
|
|
167
|
+
<% if (typeof flowRelationships !== 'undefined' && Array.isArray(flowRelationships) && flowRelationships.length > 0) { -%>
|
|
168
|
+
<% for (const rel of flowRelationships) { -%>
|
|
169
|
+
<% const relIdKey = rel.targetIdentifierKey || `${rel.targetObjType.objectType}Id`; -%>
|
|
170
|
+
'x-amz-meta-<%- relIdKey %>': <%- relIdKey %> || '',
|
|
171
|
+
<% } -%>
|
|
172
|
+
<% } -%>
|
|
154
173
|
//(<fieldCreateUrlLink>)
|
|
155
174
|
//(</fieldCreateUrlLink>)
|
|
156
175
|
},
|
|
@@ -725,6 +725,134 @@ test('emitS3Flow with ADR-016 pipeline without step-level event enum resolves co
|
|
|
725
725
|
]);
|
|
726
726
|
});
|
|
727
727
|
|
|
728
|
+
test('emitS3Flow generates relationships with direction to in ProcessAfterUpload_Main and fields in Start_Main', async () => {
|
|
729
|
+
const flowWithRel = {
|
|
730
|
+
flowTag: 'FeedChunkRecordUrl',
|
|
731
|
+
initiateBy: 'user',
|
|
732
|
+
handleObj: 'one',
|
|
733
|
+
statusType: 'statusField',
|
|
734
|
+
event: ['s3'],
|
|
735
|
+
objType: {
|
|
736
|
+
objectType: 'feedChunk',
|
|
737
|
+
serviceTag: 'FeedChunk'
|
|
738
|
+
},
|
|
739
|
+
bucketName: 'video-record-temp',
|
|
740
|
+
flowSteps: [
|
|
741
|
+
{ stepName: 'Start', event: ['wbs'] },
|
|
742
|
+
{ stepName: 'ProcessAfterUpload', event: ['s3'] },
|
|
743
|
+
{ stepName: 'ConfirmComplete', event: ['extTopic'] }
|
|
744
|
+
],
|
|
745
|
+
relationships: [
|
|
746
|
+
{
|
|
747
|
+
targetObjType: { objectType: 'videoFeed', serviceTag: 'VideoFeed' },
|
|
748
|
+
targetIdentifierKey: 'videoFeedId',
|
|
749
|
+
relType: { relationshipTag: 'hasFeedChunk', serviceTag: 'VideoFeed' },
|
|
750
|
+
relationshipDirection: 'to'
|
|
751
|
+
}
|
|
752
|
+
]
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
const source = 'app/src/generatedCode/FlowSchemas/FeedChunkRecordUrl/source';
|
|
756
|
+
const handlerRoot = 'src/generatedCode/FlowSchemas/FeedChunkRecordUrl/source';
|
|
757
|
+
|
|
758
|
+
const result = await emitS3Flow(flowWithRel, source, handlerRoot);
|
|
759
|
+
|
|
760
|
+
const startMain = result.artifacts.find(
|
|
761
|
+
(a) => a.path === `${source}/FeedChunkRecordUrl_Main.js`
|
|
762
|
+
);
|
|
763
|
+
assert.ok(
|
|
764
|
+
startMain.content.includes("requestParams.fieldNames?.videoFeedId"),
|
|
765
|
+
'Start_Main should extract videoFeedId from requestParams'
|
|
766
|
+
);
|
|
767
|
+
assert.ok(
|
|
768
|
+
startMain.content.includes("['starts-with', '$x-amz-meta-videoFeedId', '']"),
|
|
769
|
+
'Start_Main should include videoFeedId condition in Presigned POST'
|
|
770
|
+
);
|
|
771
|
+
assert.ok(
|
|
772
|
+
startMain.content.includes("'x-amz-meta-videoFeedId': videoFeedId || ''"),
|
|
773
|
+
'Start_Main should include videoFeedId field in Presigned POST'
|
|
774
|
+
);
|
|
775
|
+
|
|
776
|
+
const procMain = result.artifacts.find(
|
|
777
|
+
(a) => a.path === `${source}/FeedChunkRecordUrlProcessAfterUpload_Main.js`
|
|
778
|
+
);
|
|
779
|
+
assert.ok(
|
|
780
|
+
procMain.content.includes("const metaVideoFeedId = s3Metadata.videofeedid || '';"),
|
|
781
|
+
'ProcessAfterUpload_Main should extract metaVideoFeedId from s3Metadata'
|
|
782
|
+
);
|
|
783
|
+
assert.ok(
|
|
784
|
+
procMain.content.includes("videoFeedId: metaVideoFeedId"),
|
|
785
|
+
'ProcessAfterUpload_Main should include videoFeedId in extracted metadata log'
|
|
786
|
+
);
|
|
787
|
+
assert.ok(
|
|
788
|
+
procMain.content.includes("objectType: 'videoFeed'"),
|
|
789
|
+
'ProcessAfterUpload_Main should include targetObjType objectType'
|
|
790
|
+
);
|
|
791
|
+
assert.ok(
|
|
792
|
+
procMain.content.includes("relationshipTag: 'hasFeedChunk'"),
|
|
793
|
+
'ProcessAfterUpload_Main should include relType relationshipTag'
|
|
794
|
+
);
|
|
795
|
+
assert.ok(
|
|
796
|
+
procMain.content.includes("relationshipDirection: 'to'"),
|
|
797
|
+
'ProcessAfterUpload_Main must generate relationshipDirection to'
|
|
798
|
+
);
|
|
799
|
+
assert.ok(
|
|
800
|
+
procMain.content.includes('Built relationship for videoFeed'),
|
|
801
|
+
'ProcessAfterUpload_Main should log relationship construction'
|
|
802
|
+
);
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
test('emitS3Flow resolves relationships from schemas with relationshipDirection to', async () => {
|
|
806
|
+
const flow = {
|
|
807
|
+
flowTag: 'FeedChunkRecordUrl',
|
|
808
|
+
initiateBy: 'user',
|
|
809
|
+
handleObj: 'one',
|
|
810
|
+
statusType: 'statusField',
|
|
811
|
+
event: ['s3'],
|
|
812
|
+
objType: {
|
|
813
|
+
objectType: 'feedChunk',
|
|
814
|
+
serviceTag: 'FeedChunk'
|
|
815
|
+
},
|
|
816
|
+
bucketName: 'video-record-temp',
|
|
817
|
+
flowSteps: [
|
|
818
|
+
{ stepName: 'Start', event: ['wbs'] },
|
|
819
|
+
{ stepName: 'ProcessAfterUpload', event: ['s3'] },
|
|
820
|
+
{ stepName: 'ConfirmComplete', event: ['extTopic'] }
|
|
821
|
+
]
|
|
822
|
+
};
|
|
823
|
+
|
|
824
|
+
const relationshipsSchema = [
|
|
825
|
+
{
|
|
826
|
+
objectType: 'feedChunk',
|
|
827
|
+
relationshipTag: 'hasFeedChunk',
|
|
828
|
+
relationshipServiceTag: 'VideoFeed'
|
|
829
|
+
}
|
|
830
|
+
];
|
|
831
|
+
|
|
832
|
+
const source = 'app/src/generatedCode/FlowSchemas/FeedChunkRecordUrl/source';
|
|
833
|
+
const handlerRoot = 'src/generatedCode/FlowSchemas/FeedChunkRecordUrl/source';
|
|
834
|
+
|
|
835
|
+
const result = await emitS3Flow(flow, source, handlerRoot, {
|
|
836
|
+
relationships: relationshipsSchema
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
const procMain = result.artifacts.find(
|
|
840
|
+
(a) => a.path === `${source}/FeedChunkRecordUrlProcessAfterUpload_Main.js`
|
|
841
|
+
);
|
|
842
|
+
assert.ok(
|
|
843
|
+
procMain.content.includes("relationshipDirection: 'to'"),
|
|
844
|
+
'ProcessAfterUpload_Main should infer relationshipDirection to from RefObjectRelationships'
|
|
845
|
+
);
|
|
846
|
+
assert.ok(
|
|
847
|
+
procMain.content.includes("relationshipTag: 'hasFeedChunk'"),
|
|
848
|
+
'ProcessAfterUpload_Main should infer relationshipTag hasFeedChunk'
|
|
849
|
+
);
|
|
850
|
+
assert.ok(
|
|
851
|
+
procMain.content.includes("serviceTag: 'VideoFeed'"),
|
|
852
|
+
'ProcessAfterUpload_Main should infer serviceTag VideoFeed'
|
|
853
|
+
);
|
|
854
|
+
});
|
|
855
|
+
|
|
728
856
|
|
|
729
857
|
|
|
730
858
|
|