@izara_project/izara-core-generate-service-code 1.0.100 → 1.0.102
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 +226 -6
- package/src/emit/application/custom/customFlows.js +22 -14
- package/src/emit/application/custom/emitCustomFlowApplicationArtifacts.js +5 -4
- 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 +234 -23
- package/src/emit/application/custom/templates/s3/Start_HdrWbs.ejs +65 -0
- package/src/emit/application/custom/templates/s3/Start_Main.ejs +40 -17
- package/src/emit/application/custom/templates/s3/{ConfirmAfterUpload_HdrSqs.ejs → legacy/ConfirmAfterUpload_HdrSqs.ejs} +1 -0
- package/src/emit/application/custom/templates/s3/{ConfirmAfterUpload_Main.ejs → legacy/ConfirmAfterUpload_Main.ejs} +1 -1
- package/src/emit/infrastructure/functions/templates/DssFunction_Yaml.ejs +1 -1
- package/src/emit/infrastructure/resources/dynamoResourceArtifacts.js +6 -2
- package/src/plan/domainRegistry.js +2 -1
- package/test/unit/application/createTargetRoleHandler.test.js +1 -1
- package/test/unit/application/customFlowS3.test.js +284 -2
- package/test/unit/infrastructure/resourceArtifacts.test.js +4 -0
- /package/src/emit/application/custom/templates/s3/{ConfirmReserved_HdrSqs.ejs → legacy/ConfirmReserved_HdrSqs.ejs} +0 -0
- /package/src/emit/application/custom/templates/s3/{ConfirmReserved_Main.ejs → legacy/ConfirmReserved_Main.ejs} +0 -0
- /package/src/emit/application/custom/templates/s3/{CreateObject_HdrWbs.ejs → legacy/CreateObject_HdrWbs.ejs} +0 -0
- /package/src/emit/application/custom/templates/s3/{CreateObject_Main.ejs → legacy/CreateObject_Main.ejs} +0 -0
- /package/src/emit/application/custom/templates/s3/{CreatePreSignUrl_HdrSqs.ejs → legacy/CreatePreSignUrl_HdrSqs.ejs} +0 -0
- /package/src/emit/application/custom/templates/s3/{CreatePreSignUrl_Main.ejs → legacy/CreatePreSignUrl_Main.ejs} +0 -0
- /package/src/emit/application/custom/templates/s3/{ReservedLimit_HdrSqs.ejs → legacy/ReservedLimit_HdrSqs.ejs} +0 -0
- /package/src/emit/application/custom/templates/s3/{ReservedLimit_Main.ejs → legacy/ReservedLimit_Main.ejs} +0 -0
|
@@ -26,6 +26,12 @@ 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';
|
|
30
|
+
<% if (isSimplifiedS3) { -%>
|
|
31
|
+
import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
|
|
32
|
+
import { utils } from '@izara_project/izara-core-library-service-schemas';
|
|
33
|
+
const { createFlowTypeConcat } = utils;
|
|
34
|
+
<% } -%>
|
|
29
35
|
|
|
30
36
|
//(<optionalRequired>)
|
|
31
37
|
//(</optionalRequired>)
|
|
@@ -33,7 +39,8 @@ import { sns } from '@izara_project/izara-core-library-external-request';
|
|
|
33
39
|
/**
|
|
34
40
|
* Step 2: ProcessAfterUpload (Deferred-Node S3 Pipeline - ADR-016)
|
|
35
41
|
* Triggered by S3 ObjectCreated event. Inspects file, prepares entity payload,
|
|
36
|
-
* and publishes to Create_In SNS topic with callingFlow
|
|
42
|
+
* and publishes to Create_In SNS topic with callingFlow
|
|
43
|
+
* targeting ConfirmAfterUpload.
|
|
37
44
|
*
|
|
38
45
|
* @param {Object} _izContext
|
|
39
46
|
* @param {Object} bucket - S3 bucket record
|
|
@@ -50,7 +57,15 @@ export default async function <%- functionName %>(
|
|
|
50
57
|
passBackProperties = {}
|
|
51
58
|
) {
|
|
52
59
|
try {
|
|
53
|
-
_izContext.logger.
|
|
60
|
+
_izContext.logger.info(
|
|
61
|
+
'----- <%- functionName %> started -----',
|
|
62
|
+
{
|
|
63
|
+
bucketName: bucket.name,
|
|
64
|
+
key: field.key,
|
|
65
|
+
size: field.size
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
_izContext.logger.debug('<%- functionName %> incoming parameters:', {
|
|
54
69
|
bucket,
|
|
55
70
|
field,
|
|
56
71
|
passBackProperties
|
|
@@ -58,8 +73,24 @@ export default async function <%- functionName %>(
|
|
|
58
73
|
|
|
59
74
|
const s3Key = field.key;
|
|
60
75
|
const actualByteSize = field.size;
|
|
76
|
+
|
|
77
|
+
if (!s3Key || s3Key.endsWith('/') || actualByteSize === 0) {
|
|
78
|
+
_izContext.logger.warn('Skipping directory marker or 0-byte object:', {
|
|
79
|
+
bucketName: bucket.name,
|
|
80
|
+
key: s3Key,
|
|
81
|
+
size: actualByteSize
|
|
82
|
+
});
|
|
83
|
+
return { status: 'skipped_empty_or_dir', s3Key };
|
|
84
|
+
}
|
|
85
|
+
|
|
61
86
|
const actualSizeMb = actualByteSize / (1024 * 1024);
|
|
62
87
|
|
|
88
|
+
_izContext.logger.debug('S3 file details:', {
|
|
89
|
+
s3Key,
|
|
90
|
+
actualByteSize,
|
|
91
|
+
actualSizeMb
|
|
92
|
+
});
|
|
93
|
+
|
|
63
94
|
let s3Metadata = {};
|
|
64
95
|
try {
|
|
65
96
|
const headResult = await s3.send(
|
|
@@ -68,13 +99,19 @@ export default async function <%- functionName %>(
|
|
|
68
99
|
Key: s3Key
|
|
69
100
|
})
|
|
70
101
|
);
|
|
102
|
+
|
|
103
|
+
_izContext.logger.debug('HeadObjectCommand raw result:', headResult);
|
|
71
104
|
s3Metadata = headResult.Metadata || {};
|
|
105
|
+
_izContext.logger.info('Parsed S3 Metadata:', { s3Metadata });
|
|
72
106
|
} catch (headErr) {
|
|
73
|
-
_izContext.logger.warn(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
107
|
+
_izContext.logger.warn(
|
|
108
|
+
'Could not retrieve S3 object metadata via HeadObjectCommand:',
|
|
109
|
+
{
|
|
110
|
+
bucket: bucket.name,
|
|
111
|
+
key: s3Key,
|
|
112
|
+
error: headErr.message
|
|
113
|
+
}
|
|
114
|
+
);
|
|
78
115
|
}
|
|
79
116
|
|
|
80
117
|
//(<extractIdentifiersFromKeyOrMetadata>)
|
|
@@ -98,23 +135,164 @@ export default async function <%- functionName %>(
|
|
|
98
135
|
<% } -%>
|
|
99
136
|
//(</extractIdentifiersFromKeyOrMetadata>)
|
|
100
137
|
|
|
138
|
+
<% if (isSimplifiedS3) { -%>
|
|
139
|
+
const flowTypeTag = createFlowTypeConcat(_izContext, {
|
|
140
|
+
flowTag: '<%- flowTag %>',
|
|
141
|
+
serviceTag: process.env.iz_serviceTag
|
|
142
|
+
});
|
|
143
|
+
const taskKey = `${flowTypeTag}${rawId}`;
|
|
144
|
+
const taskTableName = await dynamodbSharedLib.tableName(
|
|
145
|
+
_izContext,
|
|
146
|
+
'WebSocketTask'
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
let resolvedConnectionId =
|
|
150
|
+
s3Metadata.connectionid || s3Metadata.connectionId || '';
|
|
151
|
+
if (!resolvedConnectionId) {
|
|
152
|
+
const queryTasks = await dynamodbSharedLib.query(
|
|
153
|
+
_izContext,
|
|
154
|
+
taskTableName,
|
|
155
|
+
{ taskKey }
|
|
156
|
+
);
|
|
157
|
+
if (queryTasks && queryTasks.Items && queryTasks.Items.length > 0) {
|
|
158
|
+
resolvedConnectionId = queryTasks.Items[0].connectionId;
|
|
159
|
+
} else {
|
|
160
|
+
_izContext.logger.warn(
|
|
161
|
+
`[Idempotency] No task record found for <%- objectType %>Id: ${rawId}.` +
|
|
162
|
+
' Aborting dispatch to Create_In.'
|
|
163
|
+
);
|
|
164
|
+
return {
|
|
165
|
+
status: 'unauthorized_or_unknown_upload',
|
|
166
|
+
s3Key,
|
|
167
|
+
<%- objectType %>Id: rawId
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
await dynamodbSharedLib.updateItem(
|
|
174
|
+
_izContext,
|
|
175
|
+
taskTableName,
|
|
176
|
+
{
|
|
177
|
+
taskKey,
|
|
178
|
+
connectionId: resolvedConnectionId
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
UpdateExpression: 'SET uploadClaimed = :claimed, claimedAt = :now',
|
|
182
|
+
ConditionExpression:
|
|
183
|
+
'attribute_exists(taskKey) AND attribute_not_exists(uploadClaimed)',
|
|
184
|
+
ExpressionAttributeValues: {
|
|
185
|
+
':claimed': true,
|
|
186
|
+
':now': Date.now()
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
);
|
|
190
|
+
_izContext.logger.info(
|
|
191
|
+
`[Idempotency] Successfully claimed upload for <%- (typeof identifierKey !== 'undefined' && identifierKey) ? identifierKey : `${objectType}Id` %>: ${rawId}`
|
|
192
|
+
);
|
|
193
|
+
} catch (claimErr) {
|
|
194
|
+
if (claimErr.name === 'ConditionalCheckFailedException') {
|
|
195
|
+
_izContext.logger.warn(
|
|
196
|
+
`[Idempotency] Duplicate or unauthorized S3 upload detected for` +
|
|
197
|
+
` <%- (typeof identifierKey !== 'undefined' && identifierKey) ? identifierKey : `${objectType}Id` %>: ${rawId}. Aborting dispatch to Create_In.`
|
|
198
|
+
);
|
|
199
|
+
return {
|
|
200
|
+
status: 'duplicate_upload_ignored',
|
|
201
|
+
s3Key,
|
|
202
|
+
<%- (typeof identifierKey !== 'undefined' && identifierKey) ? identifierKey : `${objectType}Id` %>: rawId
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
_izContext.logger.error(
|
|
206
|
+
'[Idempotency] Failed to execute atomic claim on WebSocketTask:',
|
|
207
|
+
claimErr
|
|
208
|
+
);
|
|
209
|
+
throw claimErr;
|
|
210
|
+
}
|
|
211
|
+
<% } -%>
|
|
212
|
+
|
|
101
213
|
//(<endpointHook>)
|
|
102
214
|
//(</endpointHook>)
|
|
103
215
|
|
|
104
216
|
<% if (typeof nextStepFunctionName === 'string' && nextStepFunctionName) { -%>
|
|
105
|
-
const metaUserId =
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
217
|
+
const metaUserId = s3Metadata.submittedbyuserid || '';
|
|
218
|
+
<% if (typeof flowRelationships !== 'undefined' && Array.isArray(flowRelationships) && flowRelationships.length > 0) { -%>
|
|
219
|
+
<% for (const rel of flowRelationships) { -%>
|
|
220
|
+
<% const targetIdKey = rel.targetIdentifierKey || `${rel.targetObjType.objectType}Id`; -%>
|
|
221
|
+
<% const metaKey = targetIdKey.toLowerCase(); -%>
|
|
222
|
+
const meta<%- targetIdKey.charAt(0).toUpperCase() + targetIdKey.slice(1) %> = s3Metadata.<%- metaKey %> || '';
|
|
223
|
+
<% } -%>
|
|
224
|
+
<% } -%>
|
|
225
|
+
|
|
226
|
+
_izContext.logger.info('Extracted identifiers and metadata:', {
|
|
227
|
+
<%- (typeof identifierKey !== 'undefined' && identifierKey) ? identifierKey : `${objectType}Id` %>: rawId,
|
|
228
|
+
parentId,
|
|
229
|
+
submittedByUserId: metaUserId,
|
|
230
|
+
<% if (typeof flowRelationships !== 'undefined' && Array.isArray(flowRelationships) && flowRelationships.length > 0) { -%>
|
|
231
|
+
<% for (const rel of flowRelationships) { -%>
|
|
232
|
+
<% const targetIdKey = rel.targetIdentifierKey || `${rel.targetObjType.objectType}Id`; -%>
|
|
233
|
+
<%- targetIdKey %>: meta<%- targetIdKey.charAt(0).toUpperCase() + targetIdKey.slice(1) %>,
|
|
234
|
+
<% } -%>
|
|
235
|
+
<% } -%>
|
|
236
|
+
fileName: fileNameWithExt
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
if (metaUserId) {
|
|
240
|
+
_izContext.correlationIds?.set(consts.SUBMITTED_BY_USER_ID, metaUserId);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
//(<relationshipsHook>)
|
|
244
|
+
let relationships = [];
|
|
245
|
+
<% if (typeof flowRelationships !== 'undefined' && Array.isArray(flowRelationships) && flowRelationships.length > 0) { -%>
|
|
246
|
+
<% for (const rel of flowRelationships) { -%>
|
|
247
|
+
<% const targetIdKey = rel.targetIdentifierKey || `${rel.targetObjType.objectType}Id`; -%>
|
|
248
|
+
<% const targetObj = rel.targetObjType || {}; -%>
|
|
249
|
+
<% const relTypeObj = rel.relType || {}; -%>
|
|
250
|
+
<% const relDir = rel.relationshipDirection || 'to'; -%>
|
|
251
|
+
<% const varName = `meta${targetIdKey.charAt(0).toUpperCase() + targetIdKey.slice(1)}`; -%>
|
|
252
|
+
if (<%- varName %>) {
|
|
253
|
+
relationships.push({
|
|
254
|
+
targetObjType: {
|
|
255
|
+
objectType: '<%- targetObj.objectType %>',
|
|
256
|
+
serviceTag: '<%- targetObj.serviceTag %>'
|
|
257
|
+
},
|
|
258
|
+
targetIdentifiers: {
|
|
259
|
+
<%- targetIdKey %>: <%- varName %>
|
|
260
|
+
},
|
|
261
|
+
relType: {
|
|
262
|
+
relationshipTag: '<%- relTypeObj.relationshipTag %>',
|
|
263
|
+
serviceTag: '<%- relTypeObj.serviceTag %>'
|
|
264
|
+
},
|
|
265
|
+
relationshipProperties: <%- JSON.stringify(rel.relationshipProperties || {}) %>,
|
|
266
|
+
relationshipDirection: '<%- relDir %>'
|
|
267
|
+
});
|
|
268
|
+
_izContext.logger.info('Built relationship for <%- targetObj.objectType %>:', {
|
|
269
|
+
relationships
|
|
270
|
+
});
|
|
271
|
+
} else {
|
|
272
|
+
_izContext.logger.warn(
|
|
273
|
+
'<%- varName %> is empty! ' +
|
|
274
|
+
'No <%- relTypeObj.relationshipTag %> relationship attached.'
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
<% } -%>
|
|
278
|
+
<% } -%>
|
|
279
|
+
//(</relationshipsHook>)
|
|
280
|
+
|
|
112
281
|
let messageObject = {
|
|
113
282
|
objectType: '<%- objectType %>',
|
|
114
283
|
submittedByUserId: metaUserId,
|
|
284
|
+
<% if (typeof identifierKeys !== 'undefined' && identifierKeys && identifierKeys.length > 1) { -%>
|
|
115
285
|
identifiers: {
|
|
116
|
-
<%-
|
|
286
|
+
<%- identifierKeys[0] %>: rawId,
|
|
287
|
+
<% for (let i = 1; i < identifierKeys.length; i++) { -%>
|
|
288
|
+
<%- identifierKeys[i] %>: <%- identifierKeys[i] %><%- i < identifierKeys.length - 1 ? ',' : '' %>
|
|
289
|
+
<% } -%>
|
|
117
290
|
},
|
|
291
|
+
<% } else { -%>
|
|
292
|
+
identifiers: {
|
|
293
|
+
<%- (typeof identifierKey !== 'undefined' && identifierKey) ? identifierKey : `${objectType}Id` %>: rawId
|
|
294
|
+
},
|
|
295
|
+
<% } -%>
|
|
118
296
|
<% if (typeof parentIdKeys !== 'undefined' && parentIdKeys && parentIdKeys.length > 1) { -%>
|
|
119
297
|
targetIdentifiers: {
|
|
120
298
|
<%- parentIdKeys[0] %>: parentId,
|
|
@@ -127,37 +305,69 @@ export default async function <%- functionName %>(
|
|
|
127
305
|
<%- parentIdKey %>: parentId
|
|
128
306
|
},
|
|
129
307
|
<% } -%>
|
|
308
|
+
relationships,
|
|
130
309
|
fieldNames: {
|
|
310
|
+
<% if (typeof identifierKeys !== 'undefined' && identifierKeys && identifierKeys.length > 1) { -%>
|
|
311
|
+
<%- identifierKeys[0] %>: rawId,
|
|
312
|
+
<% for (let i = 1; i < identifierKeys.length; i++) { -%>
|
|
313
|
+
<%- identifierKeys[i] %>: <%- identifierKeys[i] %>,
|
|
314
|
+
<% } -%>
|
|
315
|
+
<% } else { -%>
|
|
316
|
+
<%- (typeof identifierKey !== 'undefined' && identifierKey) ? identifierKey : `${objectType}Id` %>: rawId,
|
|
317
|
+
<% } -%>
|
|
131
318
|
//(<fieldNamesHook>)
|
|
132
319
|
//(</fieldNamesHook>)
|
|
133
320
|
}
|
|
134
321
|
};
|
|
135
322
|
|
|
323
|
+
const confirmCallbackFunction =
|
|
324
|
+
await lambdaSharedLib.lambdaFunctionName(
|
|
325
|
+
_izContext,
|
|
326
|
+
'<%- nextStepFunctionName %>'
|
|
327
|
+
);
|
|
328
|
+
_izContext.logger.debug('Resolved confirm callback function:', {
|
|
329
|
+
confirmCallbackFunction
|
|
330
|
+
});
|
|
331
|
+
|
|
136
332
|
messageObject =
|
|
137
333
|
callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject(
|
|
138
334
|
messageObject,
|
|
139
335
|
callingFlowSharedLib.addParentCallingFlowConfig(
|
|
140
336
|
passBackProperties,
|
|
141
337
|
callingFlowSharedLib.createCallingFlowConfig(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
'<%- nextStepFunctionName %>'
|
|
145
|
-
),
|
|
146
|
-
{}
|
|
338
|
+
confirmCallbackFunction,
|
|
339
|
+
{ rawId: rawId, <%- (typeof identifierKey !== 'undefined' && identifierKey) ? identifierKey : `${objectType}Id` %>: rawId }
|
|
147
340
|
)
|
|
148
341
|
)
|
|
149
342
|
);
|
|
150
343
|
|
|
344
|
+
const topicArn =
|
|
345
|
+
snsSharedLib.snsTopicArnByFlowSchema(_izContext, 'Create_In');
|
|
346
|
+
|
|
151
347
|
let sendMessageToCreateEndpoint = {
|
|
152
348
|
Message: JSON.stringify(messageObject),
|
|
153
|
-
TopicArn:
|
|
349
|
+
TopicArn: topicArn
|
|
154
350
|
};
|
|
155
351
|
|
|
352
|
+
_izContext.logger.info('Publishing to Create_In SNS topic:', {
|
|
353
|
+
topicArn,
|
|
354
|
+
<%- (typeof identifierKey !== 'undefined' && identifierKey) ? identifierKey : `${objectType}Id` %>: rawId,
|
|
355
|
+
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('') : '' %>
|
|
356
|
+
});
|
|
156
357
|
_izContext.logger.debug(
|
|
157
|
-
'
|
|
358
|
+
'Full Create_In message payload:',
|
|
359
|
+
sendMessageToCreateEndpoint
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
const publishResult = await sns.publishAsync(
|
|
363
|
+
_izContext,
|
|
158
364
|
sendMessageToCreateEndpoint
|
|
159
365
|
);
|
|
160
|
-
|
|
366
|
+
_izContext.logger.info('Successfully published to Create_In SNS:', {
|
|
367
|
+
publishResult,
|
|
368
|
+
s3Key,
|
|
369
|
+
<%- (typeof identifierKey !== 'undefined' && identifierKey) ? identifierKey : `${objectType}Id` %>: rawId
|
|
370
|
+
});
|
|
161
371
|
|
|
162
372
|
return {
|
|
163
373
|
status: 'dispatched',
|
|
@@ -175,3 +385,4 @@ export default async function <%- functionName %>(
|
|
|
175
385
|
throw err;
|
|
176
386
|
}
|
|
177
387
|
}
|
|
388
|
+
|
|
@@ -0,0 +1,65 @@
|
|
|
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 {
|
|
19
|
+
middlewareHandler as middleware,
|
|
20
|
+
response
|
|
21
|
+
} from '@izara_project/izara-middleware';
|
|
22
|
+
import callingFlowSharedLib from '@izara_project/izara-core-library-calling-flow';
|
|
23
|
+
import { webSocket } from '@izara_project/izara-core-library-external-request';
|
|
24
|
+
const { postToConnection } = webSocket;
|
|
25
|
+
|
|
26
|
+
import <%= functionName %> from './<%= functionName %>_Main.js';
|
|
27
|
+
|
|
28
|
+
export const main = middleware.wrap(async (event, context) => {
|
|
29
|
+
event._izContext.logger.debug('Event:', event);
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
if (event.requestContext) {
|
|
33
|
+
const route = event.requestContext.routeKey;
|
|
34
|
+
|
|
35
|
+
switch (route) {
|
|
36
|
+
case '<%- route %>':
|
|
37
|
+
console.log('event in route webSocket', event);
|
|
38
|
+
let eventParams = JSON.parse(event.body);
|
|
39
|
+
|
|
40
|
+
let callingFlow = eventParams?.callingFlow
|
|
41
|
+
? eventParams.callingFlow
|
|
42
|
+
: {};
|
|
43
|
+
await <%= functionName %>(
|
|
44
|
+
event._izContext,
|
|
45
|
+
eventParams,
|
|
46
|
+
callingFlowSharedLib.addCallingFlowToPassOnProperties(callingFlow)
|
|
47
|
+
//(<additionalParams>)
|
|
48
|
+
//(</additionalParams>)
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
const connId = event.connId;
|
|
55
|
+
await postToConnection(event._izContext, { message: event.message }, connId);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return response.webSocketSuccess();
|
|
59
|
+
} catch (err) {
|
|
60
|
+
const connId = event.requestContext.connectionId;
|
|
61
|
+
event._izContext.logger.error('Error, WebSocket: ', err);
|
|
62
|
+
await postToConnection(event._izContext, { message: err.message }, connId);
|
|
63
|
+
return response.failure(err);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
@@ -15,7 +15,7 @@ You should have received a copy of the GNU Affero General Public License
|
|
|
15
15
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import {
|
|
18
|
+
import { identifierUuid } from '@izara_project/izara-shared-core';
|
|
19
19
|
import { createPresignedPost } from '@aws-sdk/s3-presigned-post';
|
|
20
20
|
import { S3Client } from '@aws-sdk/client-s3';
|
|
21
21
|
import { consts } from '@izara_project/izara-middleware';
|
|
@@ -99,7 +99,8 @@ export default async function <%- functionName %>(
|
|
|
99
99
|
}
|
|
100
100
|
<% } -%>
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
<% const targetIdName = (typeof identifierKey !== 'undefined' && identifierKey) ? identifierKey : `${objectType}Id`; -%>
|
|
103
|
+
const <%- targetIdName %> = identifierUuid();
|
|
103
104
|
|
|
104
105
|
<% if (typeof parentIdKeys !== 'undefined' && parentIdKeys && parentIdKeys.length > 0) { -%>
|
|
105
106
|
const targetParentId = requestParams.targetIdentifiers?.<%- parentIdKeys[0] %>;
|
|
@@ -123,6 +124,13 @@ export default async function <%- functionName %>(
|
|
|
123
124
|
//(<createKeyForUploadFile>)
|
|
124
125
|
let importType = '<%- defaultImportType %>';
|
|
125
126
|
let uploadPath = '';
|
|
127
|
+
<% if (typeof flowRelationships !== 'undefined' && Array.isArray(flowRelationships) && flowRelationships.length > 0) { -%>
|
|
128
|
+
<% for (const rel of flowRelationships) { -%>
|
|
129
|
+
<% const relIdKey = rel.targetIdentifierKey || `${rel.targetObjType.objectType}Id`; -%>
|
|
130
|
+
const <%- relIdKey %> =
|
|
131
|
+
requestParams.fieldNames?.<%- relIdKey %> || requestParams.file?.<%- relIdKey %>;
|
|
132
|
+
<% } -%>
|
|
133
|
+
<% } -%>
|
|
126
134
|
//(</createKeyForUploadFile>)
|
|
127
135
|
const Key = `<%- s3KeyPrefix %>${uploadPath}`;
|
|
128
136
|
const maximumSize = maxFileSizeMb * 1048576;
|
|
@@ -132,25 +140,39 @@ export default async function <%- functionName %>(
|
|
|
132
140
|
Key: Key,
|
|
133
141
|
Conditions: [
|
|
134
142
|
['content-length-range', 0, maximumSize],
|
|
135
|
-
['eq', '$x-amz-meta-<%-
|
|
143
|
+
['eq', '$x-amz-meta-<%- targetIdName %>', <%- targetIdName %>],
|
|
136
144
|
<% if (typeof parentIdKeys !== 'undefined' && parentIdKeys && parentIdKeys.length > 0) { -%>
|
|
137
145
|
<% for (const key of parentIdKeys) { -%>
|
|
138
146
|
['eq', '$x-amz-meta-<%- key %>', target<%- key.charAt(0).toUpperCase() + key.slice(1) %>],
|
|
139
147
|
<% } -%>
|
|
140
148
|
<% } -%>
|
|
141
149
|
['starts-with', '$x-amz-meta-submittedByUserId', ''],
|
|
142
|
-
['starts-with', '$Content-Type', '<%- contentTypePrefix %>']
|
|
150
|
+
['starts-with', '$Content-Type', '<%- contentTypePrefix %>'],
|
|
151
|
+
['starts-with', '$x-amz-meta-connectionId', ''],
|
|
152
|
+
<% if (typeof flowRelationships !== 'undefined' && Array.isArray(flowRelationships) && flowRelationships.length > 0) { -%>
|
|
153
|
+
<% for (const rel of flowRelationships) { -%>
|
|
154
|
+
<% const relIdKey = rel.targetIdentifierKey || `${rel.targetObjType.objectType}Id`; -%>
|
|
155
|
+
['starts-with', '$x-amz-meta-<%- relIdKey %>', ''],
|
|
156
|
+
<% } -%>
|
|
157
|
+
<% } -%>
|
|
143
158
|
//(<conditionCreateUrlLink>)
|
|
144
159
|
//(</conditionCreateUrlLink>)
|
|
145
160
|
],
|
|
146
161
|
Fields: {
|
|
147
|
-
'x-amz-meta-<%-
|
|
162
|
+
'x-amz-meta-<%- targetIdName %>': <%- targetIdName %>,
|
|
148
163
|
<% if (typeof parentIdKeys !== 'undefined' && parentIdKeys && parentIdKeys.length > 0) { -%>
|
|
149
164
|
<% for (const key of parentIdKeys) { -%>
|
|
150
165
|
'x-amz-meta-<%- key %>': target<%- key.charAt(0).toUpperCase() + key.slice(1) %>,
|
|
151
166
|
<% } -%>
|
|
152
167
|
<% } -%>
|
|
153
168
|
'x-amz-meta-submittedByUserId': submittedByUserId,
|
|
169
|
+
'x-amz-meta-connectionId': connectionId || 'NONE',
|
|
170
|
+
<% if (typeof flowRelationships !== 'undefined' && Array.isArray(flowRelationships) && flowRelationships.length > 0) { -%>
|
|
171
|
+
<% for (const rel of flowRelationships) { -%>
|
|
172
|
+
<% const relIdKey = rel.targetIdentifierKey || `${rel.targetObjType.objectType}Id`; -%>
|
|
173
|
+
'x-amz-meta-<%- relIdKey %>': <%- relIdKey %> || '',
|
|
174
|
+
<% } -%>
|
|
175
|
+
<% } -%>
|
|
154
176
|
//(<fieldCreateUrlLink>)
|
|
155
177
|
//(</fieldCreateUrlLink>)
|
|
156
178
|
},
|
|
@@ -162,24 +184,25 @@ export default async function <%- functionName %>(
|
|
|
162
184
|
|
|
163
185
|
const uploadUrl = await createPresignedPost(client, createPreSignUrlParam);
|
|
164
186
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
187
|
+
const resolvedConnectionId = connectionId || 'NONE';
|
|
188
|
+
await dynamodbSharedLib.putItem(
|
|
189
|
+
_izContext,
|
|
190
|
+
await dynamodbSharedLib.tableName(_izContext, 'WebSocketTask'),
|
|
191
|
+
{
|
|
192
|
+
taskKey: `${createFlowTypeConcat(_izContext, { flowTag: '<%- flowTag %>', serviceTag: process.env.iz_serviceTag })}${<%- targetIdName %>}`,
|
|
193
|
+
connectionId: resolvedConnectionId,
|
|
194
|
+
expirationTime: Math.floor(Date.now() / 1000) + 7200
|
|
195
|
+
}
|
|
196
|
+
);
|
|
175
197
|
|
|
198
|
+
if (connectionId) {
|
|
176
199
|
await postToConnection(
|
|
177
200
|
_izContext,
|
|
178
201
|
{
|
|
179
202
|
status: 'success',
|
|
180
203
|
data: {
|
|
181
204
|
uploadUrl: uploadUrl,
|
|
182
|
-
<%-
|
|
205
|
+
<%- targetIdName %>: <%- targetIdName %>
|
|
183
206
|
}
|
|
184
207
|
},
|
|
185
208
|
connectionId
|
|
@@ -188,7 +211,7 @@ export default async function <%- functionName %>(
|
|
|
188
211
|
|
|
189
212
|
return {
|
|
190
213
|
uploadUrl: uploadUrl,
|
|
191
|
-
<%-
|
|
214
|
+
<%- targetIdName %>: <%- targetIdName %>
|
|
192
215
|
};
|
|
193
216
|
} catch (err) {
|
|
194
217
|
_izContext.logger.error('Error in <%- functionName %>:', err);
|
|
@@ -64,7 +64,7 @@ export default async function <%- functionName %>(
|
|
|
64
64
|
{ taskKey: taskKey }
|
|
65
65
|
);
|
|
66
66
|
|
|
67
|
-
if (taskRecord && taskRecord.connectionId) {
|
|
67
|
+
if (taskRecord && taskRecord.connectionId && taskRecord.connectionId !== 'NONE') {
|
|
68
68
|
await postToConnection(
|
|
69
69
|
_izContext,
|
|
70
70
|
{
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
- stream:
|
|
8
8
|
type: dynamodb
|
|
9
9
|
arn:
|
|
10
|
-
Fn::
|
|
10
|
+
Fn::ImportValue: ${self:custom.iz_resourcePrefix}<%= tableName %>StreamArn
|
|
11
11
|
batchSize: <%= batchSize %>
|
|
12
12
|
startingPosition: <%= startingPosition %>
|
|
13
13
|
<% if (Array.isArray(filterPatterns) && filterPatterns.length > 0) { -%>
|
|
@@ -11,8 +11,12 @@ export async function renderDynamo(state) {
|
|
|
11
11
|
)
|
|
12
12
|
]);
|
|
13
13
|
const indentedTables = tables.map((t) => indentYaml(t, 2)).join('\n');
|
|
14
|
+
let content = system;
|
|
14
15
|
if (indentedTables) {
|
|
15
|
-
|
|
16
|
+
content = `${content}\n${indentedTables}`;
|
|
16
17
|
}
|
|
17
|
-
|
|
18
|
+
if (state.selfRbac) {
|
|
19
|
+
content = `${content}\n\nOutputs:\n RunningTotalReservedUsageStreamArn:\n Value:\n Fn::GetAtt: [RunningTotalReservedUsageTable, StreamArn]\n Export:\n Name: \${self:custom.iz_resourcePrefix}RunningTotalReservedUsageStreamArn\n`;
|
|
20
|
+
}
|
|
21
|
+
return content;
|
|
18
22
|
}
|
|
@@ -332,7 +332,7 @@ test('generates synchronous HdrInv and HdrApi handlers for FlowLimit flows', asy
|
|
|
332
332
|
/ReservedUsageTtlExpiredHdrDss:\n\s+handler: src\/generatedCode\/FlowLimit\/ReservedUsageTtlExpired\/source\/ReservedUsageTtlExpired_HdrDss\.main/
|
|
333
333
|
);
|
|
334
334
|
assert.match(limitYaml, /type: dynamodb/);
|
|
335
|
-
assert.match(limitYaml, /
|
|
335
|
+
assert.match(limitYaml, /RunningTotalReservedUsageStreamArn/);
|
|
336
336
|
assert.match(limitYaml, /eventName: \["REMOVE"\]/);
|
|
337
337
|
});
|
|
338
338
|
|