@izara_project/izara-core-generate-service-code 1.0.99 → 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/compile/compileProject.js +16 -5
- package/src/compile/flowValidator.js +3 -1
- package/src/compile/loadSchemas.js +18 -4
- package/src/compile/schemaValidatorSetup.js +16 -0
- package/src/emit/application/coreFunction/emitCoreFunctionArtifacts.js +108 -108
- package/src/emit/application/coreFunction/templates/InitialSetupRunner.ejs +2 -2
- package/src/emit/application/crud/templates/CreateComplete_Main.ejs +2 -0
- package/src/emit/application/crud/templates/CreateLogicalLib.ejs +20 -7
- package/src/emit/application/crud/templates/DeleteComplete_Main.ejs +2 -0
- package/src/emit/application/crud/templates/FlowEndpoint_HdrWbs.ejs +7 -5
- package/src/emit/application/crud/templates/UpdateComplete_Main.ejs +2 -0
- package/src/emit/application/custom/customFlowPaths.js +13 -16
- package/src/emit/application/custom/customFlowS3.js +456 -63
- package/src/emit/application/custom/customFlows.js +131 -0
- package/src/emit/application/custom/emitCustomFlowApplicationArtifacts.js +7 -4
- package/src/emit/application/custom/templates/s3/ConfirmAfterUpload_HdrSqs.ejs +111 -0
- package/src/emit/application/custom/templates/s3/ConfirmAfterUpload_Main.ejs +89 -0
- package/src/emit/application/custom/templates/s3/ConfirmComplete_HdrSqs.ejs +111 -0
- package/src/emit/application/custom/templates/s3/ConfirmComplete_Main.ejs +100 -0
- package/src/emit/application/custom/templates/s3/CreateObject_HdrWbs.ejs +2 -1
- package/src/emit/application/custom/templates/s3/CreateObject_Main.ejs +4 -0
- package/src/emit/application/custom/templates/s3/CreatePreSignUrl_HdrSqs.ejs +42 -0
- package/src/emit/application/custom/templates/s3/CreatePreSignUrl_Main.ejs +23 -0
- package/src/emit/application/custom/templates/s3/ProcessS3File_Main.ejs +240 -27
- package/src/emit/application/custom/templates/s3/Start_Main.ejs +216 -0
- package/src/emit/application/rbac/emitDynamicRbacFlowSchemas.js +5 -1
- package/src/emit/application/rbac/emitRbacApplicationArtifacts.js +67 -60
- package/src/emit/application/rbac/templates/ReservedDynamicUsage_Main.ejs +27 -9
- package/src/emit/application/rbac/templates/UserRbacFlow_Main.ejs +4 -3
- package/src/emit/application/system/templates/Register_HdrWbs.ejs +2 -1
- package/src/emit/infrastructure/functions/functionYamlArtifact.js +3 -8
- package/src/emit/infrastructure/functions/templates/S3Function_Yaml.ejs +2 -3
- package/src/emit/infrastructure/resources/iamResourceArtifacts.js +22 -0
- package/src/emit/infrastructure/resources/resourceState.js +15 -5
- package/src/emit/infrastructure/resources/s3ResourceArtifacts.js +19 -3
- package/src/emit/infrastructure/resources/templates/S3Bucket_Yaml.ejs +1 -1
- package/src/emit/infrastructure/resources/templates/SystemDynamoDB_Yaml.ejs +2 -2
- package/src/emit/schemas/error/emitErrorSchemas.js +120 -10
- package/src/emit/schemas/error/templates/errorConsts.ejs +12 -0
- package/src/generate.js +32 -30
- package/src/plan/domainRegistry.js +2 -1
- package/test/integration/publicApi.test.js +33 -24
- package/test/unit/application/createTargetRoleHandler.test.js +33 -10
- package/test/unit/application/customFlowApplicationArtifacts.test.js +6 -6
- package/test/unit/application/customFlowS3.test.js +858 -0
- package/test/unit/infrastructure/functionYamlArtifact.test.js +6 -4
- package/test/unit/infrastructure/resourceArtifacts.test.js +51 -0
- package/test/unit/schemas/dynamicRbacFlowSchemas.test.js +8 -6
- package/test/unit/schemas/dynamicRelationshipFlowSchemas.test.js +51 -0
- package/test/unit/schemas/errorSchemas.test.js +84 -5
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright (C)
|
|
2
|
+
Copyright (C) 2026 Sven Mason <http://izara.io>
|
|
3
3
|
|
|
4
4
|
This program is free software: you can redistribute it and/or modify
|
|
5
5
|
it under the terms of the GNU Affero General Public License as
|
|
@@ -8,42 +8,42 @@ License, or (at your option) any later version.
|
|
|
8
8
|
|
|
9
9
|
This program is distributed in the hope that it will be useful,
|
|
10
10
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
12
|
GNU Affero General Public License for more details.
|
|
13
13
|
|
|
14
14
|
You should have received a copy of the GNU Affero General Public License
|
|
15
|
-
along with this program.
|
|
15
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import {
|
|
19
19
|
S3Client,
|
|
20
20
|
GetObjectCommand,
|
|
21
|
-
|
|
22
|
-
PutObjectCommand,
|
|
23
|
-
CopyObjectCommand
|
|
21
|
+
HeadObjectCommand
|
|
24
22
|
} from '@aws-sdk/client-s3';
|
|
25
23
|
const s3 = new S3Client({ region: process.env.AWS_REGION });
|
|
26
24
|
|
|
25
|
+
import snsSharedLib from '@izara_project/izara-core-library-sns';
|
|
26
|
+
import callingFlowSharedLib from '@izara_project/izara-core-library-calling-flow';
|
|
27
|
+
import lambdaSharedLib from '@izara_project/izara-core-library-lambda';
|
|
28
|
+
import { sns } from '@izara_project/izara-core-library-external-request';
|
|
29
|
+
import { consts } from '@izara_project/izara-middleware';
|
|
30
|
+
|
|
27
31
|
//(<optionalRequired>)
|
|
28
32
|
//(</optionalRequired>)
|
|
29
33
|
|
|
30
34
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* @param {Logger} _izContext.logger.debug - property of _izContext
|
|
36
|
-
* @param {Object} bucket - bucket's data
|
|
37
|
-
* @param {Array} field - image's data
|
|
38
|
-
* @param {Array} importBy - 'webSocket' || 'importData'
|
|
39
|
-
* @param {Array} imageImportId - from importData
|
|
40
|
-
* @param {Array} realImageSize - from importData
|
|
41
|
-
* @param {Array} passBackProperties - from importData
|
|
35
|
+
* Step 2: ProcessAfterUpload (Deferred-Node S3 Pipeline - ADR-016)
|
|
36
|
+
* Triggered by S3 ObjectCreated event. Inspects file, prepares entity payload,
|
|
37
|
+
* and publishes to Create_In SNS topic with callingFlow
|
|
38
|
+
* targeting ConfirmAfterUpload.
|
|
42
39
|
*
|
|
43
|
-
* @
|
|
40
|
+
* @param {Object} _izContext
|
|
41
|
+
* @param {Object} bucket - S3 bucket record
|
|
42
|
+
* @param {Object} field - S3 object record ({ key, size, eTag })
|
|
43
|
+
* @param {Object} passBackProperties
|
|
44
|
+
* @returns {Promise<Object>}
|
|
44
45
|
*/
|
|
45
|
-
|
|
46
|
-
export default async function processAfterUpload(
|
|
46
|
+
export default async function <%- functionName %>(
|
|
47
47
|
_izContext,
|
|
48
48
|
bucket,
|
|
49
49
|
field,
|
|
@@ -52,18 +52,231 @@ export default async function processAfterUpload(
|
|
|
52
52
|
passBackProperties = {}
|
|
53
53
|
) {
|
|
54
54
|
try {
|
|
55
|
-
_izContext.logger.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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:', {
|
|
64
|
+
bucket,
|
|
65
|
+
field,
|
|
66
|
+
passBackProperties
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const s3Key = field.key;
|
|
70
|
+
const actualByteSize = field.size;
|
|
71
|
+
const actualSizeMb = actualByteSize / (1024 * 1024);
|
|
72
|
+
|
|
73
|
+
_izContext.logger.debug('S3 file details:', {
|
|
74
|
+
s3Key,
|
|
75
|
+
actualByteSize,
|
|
76
|
+
actualSizeMb
|
|
61
77
|
});
|
|
62
78
|
|
|
79
|
+
let s3Metadata = {};
|
|
80
|
+
try {
|
|
81
|
+
const headResult = await s3.send(
|
|
82
|
+
new HeadObjectCommand({
|
|
83
|
+
Bucket: bucket.name,
|
|
84
|
+
Key: s3Key
|
|
85
|
+
})
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
_izContext.logger.debug('HeadObjectCommand raw result:', headResult);
|
|
89
|
+
s3Metadata = headResult.Metadata || {};
|
|
90
|
+
_izContext.logger.info('Parsed S3 Metadata:', { s3Metadata });
|
|
91
|
+
} catch (headErr) {
|
|
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
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
//(<extractIdentifiersFromKeyOrMetadata>)
|
|
103
|
+
const keyParts = s3Key.split('/');
|
|
104
|
+
let fileNameWithExt = '';
|
|
105
|
+
if (keyParts.length > 0) {
|
|
106
|
+
fileNameWithExt = keyParts[keyParts.length - 1];
|
|
107
|
+
}
|
|
108
|
+
const rawId = fileNameWithExt.split('.')[0];
|
|
109
|
+
let parentId = '';
|
|
110
|
+
if (keyParts.length > 2) {
|
|
111
|
+
parentId = keyParts[keyParts.length - 2];
|
|
112
|
+
}
|
|
113
|
+
<% if (typeof parentIdKeys !== 'undefined' && parentIdKeys && parentIdKeys.length > 1) { -%>
|
|
114
|
+
<% for (let i = 1; i < parentIdKeys.length; i++) { -%>
|
|
115
|
+
let <%- parentIdKeys[i] %> = '';
|
|
116
|
+
if (keyParts.length > <%- 2 + i %>) {
|
|
117
|
+
<%- parentIdKeys[i] %> = keyParts[keyParts.length - <%- 2 + i %>];
|
|
118
|
+
}
|
|
119
|
+
<% } -%>
|
|
120
|
+
<% } -%>
|
|
121
|
+
//(</extractIdentifiersFromKeyOrMetadata>)
|
|
122
|
+
|
|
63
123
|
//(<endpointHook>)
|
|
64
124
|
//(</endpointHook>)
|
|
125
|
+
|
|
126
|
+
<% if (typeof nextStepFunctionName === 'string' && nextStepFunctionName) { -%>
|
|
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
|
+
|
|
191
|
+
let messageObject = {
|
|
192
|
+
objectType: '<%- objectType %>',
|
|
193
|
+
submittedByUserId: metaUserId,
|
|
194
|
+
identifiers: {
|
|
195
|
+
<%- objectType %>Id: rawId
|
|
196
|
+
},
|
|
197
|
+
<% if (typeof parentIdKeys !== 'undefined' && parentIdKeys && parentIdKeys.length > 1) { -%>
|
|
198
|
+
targetIdentifiers: {
|
|
199
|
+
<%- parentIdKeys[0] %>: parentId,
|
|
200
|
+
<% for (let i = 1; i < parentIdKeys.length; i++) { -%>
|
|
201
|
+
<%- parentIdKeys[i] %>: <%- parentIdKeys[i] %><%- i < parentIdKeys.length - 1 ? ',' : '' %>
|
|
202
|
+
<% } -%>
|
|
203
|
+
},
|
|
204
|
+
<% } else if (typeof parentIdKey !== 'undefined' && parentIdKey) { -%>
|
|
205
|
+
targetIdentifiers: {
|
|
206
|
+
<%- parentIdKey %>: parentId
|
|
207
|
+
},
|
|
208
|
+
<% } -%>
|
|
209
|
+
relationships,
|
|
210
|
+
fieldNames: {
|
|
211
|
+
//(<fieldNamesHook>)
|
|
212
|
+
//(</fieldNamesHook>)
|
|
213
|
+
fileName: fileNameWithExt
|
|
214
|
+
}
|
|
215
|
+
};
|
|
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
|
+
|
|
226
|
+
messageObject =
|
|
227
|
+
callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject(
|
|
228
|
+
messageObject,
|
|
229
|
+
callingFlowSharedLib.addParentCallingFlowConfig(
|
|
230
|
+
passBackProperties,
|
|
231
|
+
callingFlowSharedLib.createCallingFlowConfig(
|
|
232
|
+
confirmCallbackFunction,
|
|
233
|
+
{ rawId: rawId, <%- objectType %>Id: rawId }
|
|
234
|
+
)
|
|
235
|
+
)
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
const topicArn =
|
|
239
|
+
snsSharedLib.snsTopicArnByFlowSchema(_izContext, 'Create_In');
|
|
240
|
+
|
|
241
|
+
let sendMessageToCreateEndpoint = {
|
|
242
|
+
Message: JSON.stringify(messageObject),
|
|
243
|
+
TopicArn: topicArn
|
|
244
|
+
};
|
|
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
|
+
});
|
|
251
|
+
_izContext.logger.debug(
|
|
252
|
+
'Full Create_In message payload:',
|
|
253
|
+
sendMessageToCreateEndpoint
|
|
254
|
+
);
|
|
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
|
+
});
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
status: 'dispatched',
|
|
268
|
+
s3Key,
|
|
269
|
+
messageObject
|
|
270
|
+
};
|
|
271
|
+
<% } else { -%>
|
|
272
|
+
return {
|
|
273
|
+
status: 'processed',
|
|
274
|
+
s3Key
|
|
275
|
+
};
|
|
276
|
+
<% } -%>
|
|
65
277
|
} catch (err) {
|
|
66
|
-
_izContext.logger.error('
|
|
278
|
+
_izContext.logger.error('Error in <%- functionName %>: ', err);
|
|
67
279
|
throw err;
|
|
68
280
|
}
|
|
69
281
|
}
|
|
282
|
+
|
|
@@ -0,0 +1,216 @@
|
|
|
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 { v7 as uuidv7 } from 'uuid';
|
|
19
|
+
import { createPresignedPost } from '@aws-sdk/s3-presigned-post';
|
|
20
|
+
import { S3Client } from '@aws-sdk/client-s3';
|
|
21
|
+
import { consts } from '@izara_project/izara-middleware';
|
|
22
|
+
import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
|
|
23
|
+
import { webSocket } from '@izara_project/izara-core-library-external-request';
|
|
24
|
+
const { postToConnection } = webSocket;
|
|
25
|
+
import { utils } from '@izara_project/izara-core-library-service-schemas';
|
|
26
|
+
const { createFlowTypeConcat } = utils;
|
|
27
|
+
|
|
28
|
+
const client = new S3Client({ region: process.env.AWS_REGION });
|
|
29
|
+
|
|
30
|
+
//(<optionalRequired>)
|
|
31
|
+
//(</optionalRequired>)
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Step 1: Start (Deferred-Node S3 Pipeline - ADR-016)
|
|
35
|
+
* Generates UUIDv7, computes S3 Presigned POST in-memory, writes ephemeral WebSocketTask,
|
|
36
|
+
* and returns upload parameters to client without writing entity records to the database.
|
|
37
|
+
*
|
|
38
|
+
* @param {Object} _izContext
|
|
39
|
+
* @param {Object} requestParams
|
|
40
|
+
* @param {Object} callingFlowConfig
|
|
41
|
+
* @returns {Promise<Object>}
|
|
42
|
+
*/
|
|
43
|
+
export default async function <%- functionName %>(
|
|
44
|
+
_izContext,
|
|
45
|
+
requestParams,
|
|
46
|
+
callingFlowConfig = {}
|
|
47
|
+
//(<additionalParams>)
|
|
48
|
+
//(</additionalParams>)
|
|
49
|
+
) {
|
|
50
|
+
try {
|
|
51
|
+
_izContext.logger.debug('<%- functionName %> started', {
|
|
52
|
+
requestParams,
|
|
53
|
+
callingFlowConfig
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const connectionId = _izContext.correlationIds.get(consts.CONNECTION_ID);
|
|
57
|
+
const submittedByUserId = _izContext.correlationIds?.get(
|
|
58
|
+
consts.SUBMITTED_BY_USER_ID
|
|
59
|
+
);
|
|
60
|
+
if (!submittedByUserId) {
|
|
61
|
+
throw new Error('Submitted by user id is required');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
//(<beforeCreateObjectHook>)
|
|
65
|
+
//(</beforeCreateObjectHook>)
|
|
66
|
+
|
|
67
|
+
<% if (isRbac) { -%>
|
|
68
|
+
const { checkRbacPermission } =
|
|
69
|
+
await import('@izara_project/izara-authorization');
|
|
70
|
+
|
|
71
|
+
//(<rbacParameterGroups_<%- flowTag %>>)
|
|
72
|
+
const parameterGroups = {};
|
|
73
|
+
//(</rbacParameterGroups_<%- flowTag %>>)
|
|
74
|
+
|
|
75
|
+
const resultPermissionCheck = await checkRbacPermission(
|
|
76
|
+
_izContext,
|
|
77
|
+
{
|
|
78
|
+
flowTag: requestParams.flowTag || '<%- flowTag %>',
|
|
79
|
+
serviceTag: process.env.iz_serviceTag
|
|
80
|
+
},
|
|
81
|
+
parameterGroups,
|
|
82
|
+
{
|
|
83
|
+
targetIdentifiers: requestParams.targetIdentifiers,
|
|
84
|
+
rbacObjType: {
|
|
85
|
+
serviceTag: '<%- parentServiceTag || rbacServiceTag %>',
|
|
86
|
+
objectType: '<%- parentObjectType || objectType %>'
|
|
87
|
+
},
|
|
88
|
+
sync: true
|
|
89
|
+
}
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
_izContext.logger.debug(
|
|
93
|
+
`[${process.env.iz_serviceTag}:<%- flowTag %>] checkRbacPermission result:`,
|
|
94
|
+
resultPermissionCheck
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
if (resultPermissionCheck && resultPermissionCheck.permission !== true) {
|
|
98
|
+
throw new Error('Permission denied');
|
|
99
|
+
}
|
|
100
|
+
<% } -%>
|
|
101
|
+
|
|
102
|
+
const <%- objectType %>Id = uuidv7();
|
|
103
|
+
|
|
104
|
+
<% if (typeof parentIdKeys !== 'undefined' && parentIdKeys && parentIdKeys.length > 0) { -%>
|
|
105
|
+
const targetParentId = requestParams.targetIdentifiers?.<%- parentIdKeys[0] %>;
|
|
106
|
+
<% for (const key of parentIdKeys) { -%>
|
|
107
|
+
const target<%- key.charAt(0).toUpperCase() + key.slice(1) %> = requestParams.targetIdentifiers?.<%- key %>;
|
|
108
|
+
if (!target<%- key.charAt(0).toUpperCase() + key.slice(1) %>) {
|
|
109
|
+
throw new Error('targetIdentifiers.<%- key %> is required');
|
|
110
|
+
}
|
|
111
|
+
<% } -%>
|
|
112
|
+
<% } -%>
|
|
113
|
+
|
|
114
|
+
let maxFileSizeMb = <%- maxFileSizeMb %>;
|
|
115
|
+
if (requestParams.file?.size) {
|
|
116
|
+
maxFileSizeMb = Math.ceil(requestParams.file.size / 1048576);
|
|
117
|
+
} else if (requestParams.file?.fileSize) {
|
|
118
|
+
maxFileSizeMb = requestParams.file.fileSize;
|
|
119
|
+
} else if (requestParams.fieldNames?.fileSize) {
|
|
120
|
+
maxFileSizeMb = requestParams.fieldNames.fileSize;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
//(<createKeyForUploadFile>)
|
|
124
|
+
let importType = '<%- defaultImportType %>';
|
|
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
|
+
<% } -%>
|
|
133
|
+
//(</createKeyForUploadFile>)
|
|
134
|
+
const Key = `<%- s3KeyPrefix %>${uploadPath}`;
|
|
135
|
+
const maximumSize = maxFileSizeMb * 1048576;
|
|
136
|
+
|
|
137
|
+
let createPreSignUrlParam = {
|
|
138
|
+
Bucket: '<%- bucketName %>',
|
|
139
|
+
Key: Key,
|
|
140
|
+
Conditions: [
|
|
141
|
+
['content-length-range', 0, maximumSize],
|
|
142
|
+
['eq', '$x-amz-meta-<%- objectType %>Id', <%- objectType %>Id],
|
|
143
|
+
<% if (typeof parentIdKeys !== 'undefined' && parentIdKeys && parentIdKeys.length > 0) { -%>
|
|
144
|
+
<% for (const key of parentIdKeys) { -%>
|
|
145
|
+
['eq', '$x-amz-meta-<%- key %>', target<%- key.charAt(0).toUpperCase() + key.slice(1) %>],
|
|
146
|
+
<% } -%>
|
|
147
|
+
<% } -%>
|
|
148
|
+
['starts-with', '$x-amz-meta-submittedByUserId', ''],
|
|
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
|
+
<% } -%>
|
|
156
|
+
//(<conditionCreateUrlLink>)
|
|
157
|
+
//(</conditionCreateUrlLink>)
|
|
158
|
+
],
|
|
159
|
+
Fields: {
|
|
160
|
+
'x-amz-meta-<%- objectType %>Id': <%- objectType %>Id,
|
|
161
|
+
<% if (typeof parentIdKeys !== 'undefined' && parentIdKeys && parentIdKeys.length > 0) { -%>
|
|
162
|
+
<% for (const key of parentIdKeys) { -%>
|
|
163
|
+
'x-amz-meta-<%- key %>': target<%- key.charAt(0).toUpperCase() + key.slice(1) %>,
|
|
164
|
+
<% } -%>
|
|
165
|
+
<% } -%>
|
|
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
|
+
<% } -%>
|
|
173
|
+
//(<fieldCreateUrlLink>)
|
|
174
|
+
//(</fieldCreateUrlLink>)
|
|
175
|
+
},
|
|
176
|
+
Expires: <%- expires %>
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
//(<modifyPreSignUrlParam>)
|
|
180
|
+
//(</modifyPreSignUrlParam>)
|
|
181
|
+
|
|
182
|
+
const uploadUrl = await createPresignedPost(client, createPreSignUrlParam);
|
|
183
|
+
|
|
184
|
+
if (connectionId) {
|
|
185
|
+
await dynamodbSharedLib.putItem(
|
|
186
|
+
_izContext,
|
|
187
|
+
await dynamodbSharedLib.tableName(_izContext, 'WebSocketTask'),
|
|
188
|
+
{
|
|
189
|
+
taskKey: `${createFlowTypeConcat(_izContext, { flowTag: '<%- flowTag %>', serviceTag: process.env.iz_serviceTag })}${<%- objectType %>Id}`,
|
|
190
|
+
connectionId: connectionId,
|
|
191
|
+
expirationTime: Math.floor(Date.now() / 1000) + 7200
|
|
192
|
+
}
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
await postToConnection(
|
|
196
|
+
_izContext,
|
|
197
|
+
{
|
|
198
|
+
status: 'success',
|
|
199
|
+
data: {
|
|
200
|
+
uploadUrl: uploadUrl,
|
|
201
|
+
<%- objectType %>Id: <%- objectType %>Id
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
connectionId
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
uploadUrl: uploadUrl,
|
|
210
|
+
<%- objectType %>Id: <%- objectType %>Id
|
|
211
|
+
};
|
|
212
|
+
} catch (err) {
|
|
213
|
+
_izContext.logger.error('Error in <%- functionName %>:', err);
|
|
214
|
+
throw err;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
@@ -20,7 +20,7 @@ const RBAC_CORE_FLOW_DEFINITIONS = [
|
|
|
20
20
|
const LIMIT_FLOW_DEFINITIONS = [
|
|
21
21
|
{
|
|
22
22
|
flowTag: 'ReservedDynamicUsage',
|
|
23
|
-
event: ['
|
|
23
|
+
event: ['lambdaSyncInv', 'lambdaSyncApi']
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
26
|
flowTag: 'ConfirmUsage',
|
|
@@ -29,6 +29,10 @@ const LIMIT_FLOW_DEFINITIONS = [
|
|
|
29
29
|
{
|
|
30
30
|
flowTag: 'CancelUsage',
|
|
31
31
|
event: ['ownTopic', 'lambdaSyncInv', 'lambdaSyncApi']
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
flowTag: 'ReservedUsageTtlExpired',
|
|
35
|
+
event: ['dss']
|
|
32
36
|
}
|
|
33
37
|
];
|
|
34
38
|
|
|
@@ -174,14 +174,21 @@ const STATIC_RBAC_APPLICATION_PATHS = [
|
|
|
174
174
|
];
|
|
175
175
|
|
|
176
176
|
const STATIC_LIMIT_APPLICATION_PATHS = [
|
|
177
|
-
...ACCOUNT_LIMIT_HANDLERS.flatMap((flowTag) =>
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
177
|
+
...ACCOUNT_LIMIT_HANDLERS.flatMap((flowTag) => {
|
|
178
|
+
const paths = [
|
|
179
|
+
`app/src/generatedCode/FlowLimit/${flowTag}/source/${flowTag}_Main.js`,
|
|
180
|
+
`app/src/generatedCode/FlowLimit/${flowTag}/source/${flowTag}_HdrInv.js`,
|
|
181
|
+
`app/src/generatedCode/FlowLimit/${flowTag}/source/${flowTag}_HdrApi.js`
|
|
182
|
+
];
|
|
183
|
+
if (flowTag !== 'ReservedDynamicUsage') {
|
|
184
|
+
paths.splice(
|
|
185
|
+
1,
|
|
186
|
+
0,
|
|
187
|
+
`app/src/generatedCode/FlowLimit/${flowTag}/source/${flowTag}_HdrSqs.js`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
return paths;
|
|
191
|
+
}),
|
|
185
192
|
'app/src/generatedCode/FlowLimit/ReservedUsageTtlExpired/source/ReservedUsageTtlExpired_HdrDss.js',
|
|
186
193
|
FLOW_LIMITS_YAML_PATH
|
|
187
194
|
];
|
|
@@ -212,65 +219,65 @@ export function rbacApplicationManagedPaths(objects = []) {
|
|
|
212
219
|
async function createAccountLimitHandler(flowTag) {
|
|
213
220
|
const sourcePath = `app/src/generatedCode/FlowLimit/${flowTag}/source`;
|
|
214
221
|
const handlerPath = `src/generatedCode/FlowLimit/${flowTag}/source`;
|
|
215
|
-
const webSocket = await createWebSocketEntry(
|
|
216
|
-
flowTag,
|
|
217
|
-
sourcePath,
|
|
218
|
-
handlerPath
|
|
219
|
-
);
|
|
220
222
|
const shared = {
|
|
221
223
|
flowTag,
|
|
222
224
|
flowStepName: '',
|
|
223
225
|
mainFileName: `${flowTag}_Main`,
|
|
224
226
|
functionName: flowTag
|
|
225
227
|
};
|
|
228
|
+
const artifacts = [
|
|
229
|
+
{
|
|
230
|
+
path: `${sourcePath}/${flowTag}_Main.js`,
|
|
231
|
+
content: await renderRbacTemplate(`${flowTag}_Main.ejs`, { flowTag })
|
|
232
|
+
}
|
|
233
|
+
];
|
|
234
|
+
const functions = [];
|
|
235
|
+
if (flowTag !== 'ReservedDynamicUsage') {
|
|
236
|
+
artifacts.push({
|
|
237
|
+
path: `${sourcePath}/${flowTag}_HdrSqs.js`,
|
|
238
|
+
content: await renderRbacTemplate('ListRbac_HdrSqs.ejs', { flowTag })
|
|
239
|
+
});
|
|
240
|
+
functions.push({
|
|
241
|
+
type: 'sqs',
|
|
242
|
+
functionName: `${flowTag}HdrSqs`,
|
|
243
|
+
handlerPath: `${handlerPath}/${flowTag}_HdrSqs.main`,
|
|
244
|
+
roleName: 'FlowRbacRole',
|
|
245
|
+
queueName: `${flowTag}HdrSqs`,
|
|
246
|
+
flowTag: flowTag,
|
|
247
|
+
flowStepName: ''
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
artifacts.push(
|
|
251
|
+
{
|
|
252
|
+
path: `${sourcePath}/${flowTag}_HdrInv.js`,
|
|
253
|
+
content: await renderSharedTemplate('FlowHandler_HdrInv.ejs', shared)
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
path: `${sourcePath}/${flowTag}_HdrApi.js`,
|
|
257
|
+
content: await renderSharedTemplate('FlowHandler_HdrApi.ejs', shared)
|
|
258
|
+
}
|
|
259
|
+
);
|
|
260
|
+
functions.push(
|
|
261
|
+
{
|
|
262
|
+
type: 'inv',
|
|
263
|
+
functionName: `${flowTag}HdrInv`,
|
|
264
|
+
handlerPath: `${handlerPath}/${flowTag}_HdrInv.main`,
|
|
265
|
+
roleName: 'FlowRbacRole',
|
|
266
|
+
flowTag: flowTag,
|
|
267
|
+
flowStepName: ''
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
type: 'api',
|
|
271
|
+
functionName: `${flowTag}HdrApi`,
|
|
272
|
+
handlerPath: `${handlerPath}/${flowTag}_HdrApi.main`,
|
|
273
|
+
roleName: 'FlowRbacRole',
|
|
274
|
+
flowTag: flowTag,
|
|
275
|
+
flowStepName: ''
|
|
276
|
+
}
|
|
277
|
+
);
|
|
226
278
|
return {
|
|
227
|
-
artifacts
|
|
228
|
-
|
|
229
|
-
path: `${sourcePath}/${flowTag}_Main.js`,
|
|
230
|
-
content: await renderRbacTemplate(`${flowTag}_Main.ejs`, { flowTag })
|
|
231
|
-
},
|
|
232
|
-
{
|
|
233
|
-
path: `${sourcePath}/${flowTag}_HdrSqs.js`,
|
|
234
|
-
content: await renderRbacTemplate('ListRbac_HdrSqs.ejs', { flowTag })
|
|
235
|
-
},
|
|
236
|
-
{
|
|
237
|
-
path: `${sourcePath}/${flowTag}_HdrInv.js`,
|
|
238
|
-
content: await renderSharedTemplate('FlowHandler_HdrInv.ejs', shared)
|
|
239
|
-
},
|
|
240
|
-
{
|
|
241
|
-
path: `${sourcePath}/${flowTag}_HdrApi.js`,
|
|
242
|
-
content: await renderSharedTemplate('FlowHandler_HdrApi.ejs', shared)
|
|
243
|
-
},
|
|
244
|
-
...webSocket.artifacts
|
|
245
|
-
],
|
|
246
|
-
functions: [
|
|
247
|
-
{
|
|
248
|
-
type: 'sqs',
|
|
249
|
-
functionName: `${flowTag}HdrSqs`,
|
|
250
|
-
handlerPath: `${handlerPath}/${flowTag}_HdrSqs.main`,
|
|
251
|
-
roleName: 'FlowRbacRole',
|
|
252
|
-
queueName: `${flowTag}HdrSqs`,
|
|
253
|
-
flowTag: flowTag,
|
|
254
|
-
flowStepName: ''
|
|
255
|
-
},
|
|
256
|
-
{
|
|
257
|
-
type: 'inv',
|
|
258
|
-
functionName: `${flowTag}HdrInv`,
|
|
259
|
-
handlerPath: `${handlerPath}/${flowTag}_HdrInv.main`,
|
|
260
|
-
roleName: 'FlowRbacRole',
|
|
261
|
-
flowTag: flowTag,
|
|
262
|
-
flowStepName: ''
|
|
263
|
-
},
|
|
264
|
-
{
|
|
265
|
-
type: 'api',
|
|
266
|
-
functionName: `${flowTag}HdrApi`,
|
|
267
|
-
handlerPath: `${handlerPath}/${flowTag}_HdrApi.main`,
|
|
268
|
-
roleName: 'FlowRbacRole',
|
|
269
|
-
flowTag: flowTag,
|
|
270
|
-
flowStepName: ''
|
|
271
|
-
},
|
|
272
|
-
...webSocket.functions
|
|
273
|
-
]
|
|
279
|
+
artifacts,
|
|
280
|
+
functions
|
|
274
281
|
};
|
|
275
282
|
}
|
|
276
283
|
|