@izara_project/izara-core-generate-service-code 1.0.84 → 1.0.86
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 +1 -2
- package/src/compile/loadSchemas.js +29 -17
- package/src/emit/coreFunction/emitCoreFunctionArtifacts.js +6 -0
- package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +26 -6
- package/src/emit/crud/crudActionPlan.js +3 -1
- package/src/emit/crud/emitCrudApplicationArtifacts.js +47 -8
- package/src/emit/crud/emitDynamicCrudFlowSchemas.js +136 -25
- package/src/emit/crud/templates/CreateConfirmComplete_Main.ejs +54 -0
- package/src/emit/crud/templates/CreateEndpoint_Main.ejs +248 -181
- package/src/emit/crud/templates/CreateLogicalLib.ejs +1155 -293
- package/src/emit/crud/templates/CreateNodeCallback_Main.ejs +221 -0
- package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +36 -4
- package/src/emit/crud/templates/FlowEndpointRbacCallback_Main.ejs +20 -120
- package/src/emit/crud/templates/FlowEndpoint_HdrWbs.ejs +76 -14
- package/src/emit/crud/templates/FlowMain_Wbs.ejs +31 -35
- package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +35 -3
- package/src/emit/crud/templates/UpdateLogicalLib.ejs +754 -353
- package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +5 -2
- package/src/emit/rbac/emitRbacApplicationArtifacts.js +102 -6
- package/src/emit/rbac/templates/CancelUsage_Main.ejs +82 -22
- package/src/emit/rbac/templates/ConfirmUsage_Main.ejs +130 -14
- package/src/emit/rbac/templates/CreateTargetRole_Main.ejs +61 -12
- package/src/emit/rbac/templates/DeleteTargetRole_Main.ejs +96 -0
- package/src/emit/rbac/templates/GetMyPermissions_Main.ejs +106 -0
- package/src/emit/rbac/templates/GetTargetRole_Main.ejs +62 -0
- package/src/emit/rbac/templates/ReservedDynamicUsage_Main.ejs +30 -11
- package/src/emit/rbac/templates/UpdateTargetRole_Main.ejs +107 -0
- package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +40 -15
- package/src/emit/rbac/templates/UserRoleMutation_Main.ejs +22 -7
- package/src/emit/resource/emitResourceArtifacts.js +52 -2
- package/src/emit/resource/templates/GeneratedLambdaRoleServiceConfig.ejs +6 -2
- package/src/emit/resource/templates/SystemDynamoDB_Yaml.ejs +2 -0
- package/src/emit/shared/templates/FlowHandler_HdrSqs.ejs +1 -1
- package/src/emit/system/templates/WebSocketConnect_Yaml.ejs +0 -1
- package/src/emit/systemText/emitSystemTextSchemas.js +5 -2
- package/src/generate.js +18 -12
- package/src/generateResources/IntTest/generateResources/generateResources.js +1 -1
- package/src/generateResources/IntTest/generateTests/generateTemplate.js +1 -1
- package/src/generateResources/IntTest/upload/uploadIntTest.js +8 -2
- package/test/createTargetRoleHandler.test.js +42 -1
- package/test/crudApplicationArtifacts.test.js +53 -0
- package/test/dynamicCrudFlowSchemas.test.js +16 -2
- package/test/dynamicRbacFlowSchemas.test.js +5 -2
- package/test/publicApi.test.js +18 -14
- package/test/resourceArtifacts.test.js +24 -0
|
@@ -61,11 +61,1089 @@ const PREFIX = {
|
|
|
61
61
|
CREATE_OBJECT_EXTERNAL_TOPIC: 'CreateExternalTopicEndpoint'
|
|
62
62
|
};
|
|
63
63
|
|
|
64
|
+
// ── 1. Create_Main Helpers ───────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Loads ObjectSchema and resolves submittedByUserId and targetIdentifiers
|
|
68
|
+
* @param {Object} _izContext - Izara context object
|
|
69
|
+
* @param {Object} requestParams - Incoming request parameters
|
|
70
|
+
* @param {Array<string>} errorsFound - Array to collect validation errors
|
|
71
|
+
* @returns {Promise<{ objType: string, objectSchema: Object }>}
|
|
72
|
+
*/
|
|
73
|
+
export async function resolveSchemaAndIdentity(_izContext, requestParams, errorsFound) {
|
|
74
|
+
const objType = createObjType(requestParams.objectType);
|
|
75
|
+
const objectSchema = await getObjectSchema.getObjSchemaS3WithHierarchy(
|
|
76
|
+
_izContext,
|
|
77
|
+
objType
|
|
78
|
+
);
|
|
79
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] objectSchema:`, objectSchema);
|
|
80
|
+
|
|
81
|
+
let submittedByUserId, targetIdentifiers;
|
|
82
|
+
if (
|
|
83
|
+
objectSchema.generatedBy === 'user' ||
|
|
84
|
+
objectSchema.generatedBy === 'systemWithUser'
|
|
85
|
+
) {
|
|
86
|
+
submittedByUserId =
|
|
87
|
+
requestParams.submittedByUserId ||
|
|
88
|
+
_izContext.correlationIds.get(coreConsts.SUBMITTED_BY_USER_ID);
|
|
89
|
+
|
|
90
|
+
targetIdentifiers = requestParams.targetIdentifiers;
|
|
91
|
+
|
|
92
|
+
if (!targetIdentifiers && submittedByUserId) {
|
|
93
|
+
targetIdentifiers = { submittedByUserId };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (!submittedByUserId) errorsFound.push('Not have submittedByUserId');
|
|
97
|
+
if (objectSchema.hasOwnProperty('belongTo') && !targetIdentifiers)
|
|
98
|
+
errorsFound.push('not have targetIdentifiers');
|
|
99
|
+
|
|
100
|
+
if (targetIdentifiers) {
|
|
101
|
+
if (!targetIdentifiers.submittedByUserId && submittedByUserId) {
|
|
102
|
+
targetIdentifiers.submittedByUserId = submittedByUserId;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (Array.isArray(objectSchema.identifiers)) {
|
|
106
|
+
for (const identifier of objectSchema.identifiers) {
|
|
107
|
+
if (identifier.fieldName) {
|
|
108
|
+
if (targetIdentifiers[identifier.fieldName] === undefined) {
|
|
109
|
+
targetIdentifiers[identifier.fieldName] = null;
|
|
110
|
+
}
|
|
111
|
+
} else if (Array.isArray(identifier.fieldNames)) {
|
|
112
|
+
for (const fn of identifier.fieldNames) {
|
|
113
|
+
if (targetIdentifiers[fn] === undefined) {
|
|
114
|
+
targetIdentifiers[fn] = null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
} else if (identifier.name) {
|
|
118
|
+
if (targetIdentifiers[identifier.name] === undefined) {
|
|
119
|
+
targetIdentifiers[identifier.name] = null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
requestParams.targetIdentifiers = targetIdentifiers;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] Identity & Target Context Resolved:`, {
|
|
129
|
+
submittedByUserId: requestParams.submittedByUserId,
|
|
130
|
+
targetIdentifiers: requestParams.targetIdentifiers,
|
|
131
|
+
belongTo: objectSchema.belongTo,
|
|
132
|
+
generatedBy: objectSchema.generatedBy
|
|
133
|
+
});
|
|
134
|
+
} else if (objectSchema.generatedBy === 'searchResultGenerated') {
|
|
135
|
+
submittedByUserId = requestParams.submittedByUserId;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
requestParams.submittedByUserId = submittedByUserId;
|
|
139
|
+
return { objType, objectSchema };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Resolves and generates primary identifiers for the object
|
|
144
|
+
* @param {Object} _izContext - Izara context object
|
|
145
|
+
* @param {Object} objectSchemas - Combined object schema
|
|
146
|
+
* @param {Object} requestParams - Incoming request parameters
|
|
147
|
+
* @returns {Promise<void>}
|
|
148
|
+
*/
|
|
149
|
+
export async function resolveAndGenerateIdentifiers(_izContext, objectSchemas, requestParams) {
|
|
150
|
+
const identifiers = await createIdentifiersByUseCase(
|
|
151
|
+
_izContext,
|
|
152
|
+
objectSchemas,
|
|
153
|
+
requestParams.fieldNames
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
for (const [key, value] of Object.entries(identifiers)) {
|
|
157
|
+
if (!requestParams.fieldNames.hasOwnProperty(key)) {
|
|
158
|
+
requestParams.fieldNames[key] = value;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
_izContext.logger.debug(
|
|
163
|
+
`[${process.env.iz_serviceTag}:<%- flowTag %>] Identifiers generated:`,
|
|
164
|
+
identifiers
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Validates request field names against schema definition
|
|
170
|
+
* @param {Object} _izContext - Izara context object
|
|
171
|
+
* @param {Object} objectSchemas - Combined object schema
|
|
172
|
+
* @param {Object} requestParams - Incoming request parameters
|
|
173
|
+
* @param {Array<string>} errorsFound - Array to collect validation errors
|
|
174
|
+
*/
|
|
175
|
+
export function validateFieldNames(_izContext, objectSchemas, requestParams, errorsFound) {
|
|
176
|
+
const invalidFieldNames = [];
|
|
177
|
+
for (const fieldName of Object.keys(requestParams.fieldNames || {})) {
|
|
178
|
+
if (!objectSchemas.fieldNames.includes(fieldName)) {
|
|
179
|
+
invalidFieldNames.push(fieldName);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (invalidFieldNames.length > 0) {
|
|
184
|
+
errorsFound.push(
|
|
185
|
+
`Invalid fieldName not match from Schema: ${invalidFieldNames.join(',')}`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ── 2. CreateRbacCallback_Main Helpers ────────────────────────────────────────
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Checks awaiting steps status and collects returned results from App and Target RBAC
|
|
194
|
+
* @param {Object} _izContext - Izara context object
|
|
195
|
+
* @param {Object} requestParams - Request parameters with parentFlowId
|
|
196
|
+
* @returns {Promise<{ isComplete: boolean, rbacResults?: Object, originalRequestParams?: Object, collectedErrors?: Array<string> }>}
|
|
197
|
+
*/
|
|
198
|
+
export async function collectRbacAwaitingResults(_izContext, requestParams) {
|
|
199
|
+
const parts = requestParams.parentFlowId.split('_');
|
|
200
|
+
const rbacType = parts.pop();
|
|
201
|
+
const rbacPendingStepId = '<%- flowTag %>RbacCallback_' + parts.join('_');
|
|
202
|
+
const currentAwaitingStepId = requestParams.parentFlowId;
|
|
203
|
+
|
|
204
|
+
_izContext.logger.debug(
|
|
205
|
+
'[<%- flowTag %>RbacCallbackMain] Parsed RBAC pending details:',
|
|
206
|
+
{
|
|
207
|
+
rbacType,
|
|
208
|
+
rbacPendingStepId,
|
|
209
|
+
currentAwaitingStepId,
|
|
210
|
+
parentFlowId: requestParams.parentFlowId
|
|
211
|
+
}
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
const rbacAwaitingStepsResult =
|
|
215
|
+
await asyncFlowSharedLib.checkAllAwaitingStepsFinishedWithReturnParams(
|
|
216
|
+
_izContext,
|
|
217
|
+
rbacPendingStepId,
|
|
218
|
+
currentAwaitingStepId,
|
|
219
|
+
requestParams.errorsFound,
|
|
220
|
+
{
|
|
221
|
+
returnValue: requestParams.returnValue,
|
|
222
|
+
rbacType
|
|
223
|
+
}
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
_izContext.logger.debug(
|
|
227
|
+
'[<%- flowTag %>RbacCallbackMain] checkAllAwaitingStepsFinishedWithReturnParams result:',
|
|
228
|
+
{
|
|
229
|
+
rbacAwaitingStepsResult
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
const {
|
|
234
|
+
isComplete,
|
|
235
|
+
collectedReturnValueByAwaitingStepId,
|
|
236
|
+
collectedErrors: errorsFound = [],
|
|
237
|
+
additionalAttributes
|
|
238
|
+
} = rbacAwaitingStepsResult;
|
|
239
|
+
|
|
240
|
+
if (isComplete === false) {
|
|
241
|
+
_izContext.logger.debug(
|
|
242
|
+
'[<%- flowTag %>RbacCallbackMain] Awaiting steps still pending completion:',
|
|
243
|
+
{
|
|
244
|
+
rbacPendingStepId,
|
|
245
|
+
isComplete
|
|
246
|
+
}
|
|
247
|
+
);
|
|
248
|
+
return { isComplete: false };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
_izContext.logger.debug(
|
|
252
|
+
'[<%- flowTag %>RbacCallbackMain] All awaiting steps completed — clearing awaiting steps:',
|
|
253
|
+
{
|
|
254
|
+
rbacPendingStepId
|
|
255
|
+
}
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
await asyncFlowSharedLib.clearAllAwaitingSteps(
|
|
259
|
+
_izContext,
|
|
260
|
+
rbacPendingStepId
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
const originalRequestParams = additionalAttributes?.requestParams || {};
|
|
264
|
+
const rbacResults = Object.fromEntries(
|
|
265
|
+
Object.entries(collectedReturnValueByAwaitingStepId || {}).map(
|
|
266
|
+
([stepId, attrs]) => {
|
|
267
|
+
const type = stepId.split('_').pop();
|
|
268
|
+
return [type, attrs];
|
|
269
|
+
}
|
|
270
|
+
)
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
isComplete: true,
|
|
275
|
+
rbacResults,
|
|
276
|
+
originalRequestParams,
|
|
277
|
+
collectedErrors: errorsFound
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Asserts RBAC permission and publishes rejected response if unauthorized
|
|
283
|
+
* @param {Object} _izContext - Izara context object
|
|
284
|
+
* @param {Object} options - Validation options
|
|
285
|
+
* @returns {Promise<{ hasError: boolean, errorResult?: Object, rbacCallbackResults: Object }>}
|
|
286
|
+
*/
|
|
287
|
+
export async function assertRbacPermission(_izContext, { rbacResults, originalRequestParams, errorsFound }) {
|
|
288
|
+
const rbacCallbackResults = {};
|
|
289
|
+
const hasError = Object.entries(rbacResults).some(([type, result]) => {
|
|
290
|
+
if (result && result.error) {
|
|
291
|
+
errorsFound.push(result.error);
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
rbacCallbackResults[type] = result;
|
|
295
|
+
return false;
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
const isAllowed = Object.values(rbacCallbackResults).some(
|
|
299
|
+
item => item?.returnValue?.permission === true
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
if (!isAllowed) {
|
|
303
|
+
errorsFound.push('Permission denied');
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
_izContext.logger.debug(
|
|
307
|
+
'[<%- flowTag %>RbacCallbackMain] Final rbacCallbackResults before calling <%- flowTag %>MainLogical:',
|
|
308
|
+
{
|
|
309
|
+
rbacCallbackResults,
|
|
310
|
+
isAllowed,
|
|
311
|
+
errorsFound
|
|
312
|
+
}
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
if (hasError || errorsFound.length > 0) {
|
|
316
|
+
const errorResult = {
|
|
317
|
+
errorsFound,
|
|
318
|
+
status: 'rejected',
|
|
319
|
+
requestParams: originalRequestParams
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
_izContext.logger.debug(
|
|
323
|
+
'[<%- flowTag %>RbacCallbackMain] RBAC check failed, publishing rejected status:',
|
|
324
|
+
errorResult
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
await sns.publishAsync(_izContext, {
|
|
328
|
+
TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
|
|
329
|
+
_izContext,
|
|
330
|
+
'<%- upperCaseFlowTag %>_Out',
|
|
331
|
+
process.env.iz_serviceTag
|
|
332
|
+
),
|
|
333
|
+
Message: JSON.stringify(errorResult)
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
return { hasError: true, errorResult, rbacCallbackResults };
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return { hasError: false, rbacCallbackResults };
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ── 3. CreateNodeCallback_Main Helpers ────────────────────────────────────────
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Saves DynamoDB records from listOfRecords
|
|
346
|
+
* @param {Object} _izContext - Izara context object
|
|
347
|
+
* @param {Array<Object>} listOfRecords - Records to persist
|
|
348
|
+
* @returns {Promise<void>}
|
|
349
|
+
*/
|
|
350
|
+
export async function saveDynamoDbRecords(_izContext, listOfRecords) {
|
|
351
|
+
if (!Array.isArray(listOfRecords) || listOfRecords.length === 0) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
await Promise.all(
|
|
356
|
+
listOfRecords.flatMap((records) =>
|
|
357
|
+
Object.values(records).map(async (record) =>
|
|
358
|
+
dynamodbSharedLib.putItem(
|
|
359
|
+
_izContext,
|
|
360
|
+
await dynamodbSharedLib.tableName(
|
|
361
|
+
_izContext,
|
|
362
|
+
record.tableName,
|
|
363
|
+
record.serviceTag
|
|
364
|
+
),
|
|
365
|
+
{
|
|
366
|
+
...record.objInstanceFull.identifiers,
|
|
367
|
+
...record.objInstanceFull.fields
|
|
368
|
+
}
|
|
369
|
+
)
|
|
370
|
+
)
|
|
371
|
+
)
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Auto-provisions owner TargetRole for Root RBAC object after Graph and DynamoDB write
|
|
377
|
+
* @param {Object} _izContext - Izara context object
|
|
378
|
+
* @param {Object} options - Target node and submitter details
|
|
379
|
+
* @returns {Promise<void>}
|
|
380
|
+
*/
|
|
381
|
+
export async function autoProvisionOwnerTargetRole(_izContext, { node, submittedByUserId }) {
|
|
382
|
+
try {
|
|
383
|
+
if (!submittedByUserId || !node?.objInstanceFull?.identifiers) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const identifiers = node.objInstanceFull.identifiers;
|
|
388
|
+
const targetIdentKeys = Object.entries(identifiers)
|
|
389
|
+
.filter(([k]) => k !== 'submittedByUserId')
|
|
390
|
+
.map(([k, v]) => `${k}#${v}`)
|
|
391
|
+
.join('|');
|
|
392
|
+
|
|
393
|
+
const targetIdentifiersConcat =
|
|
394
|
+
targetIdentKeys ||
|
|
395
|
+
(identifiers[Object.keys(identifiers)[0]]
|
|
396
|
+
? `${Object.keys(identifiers)[0]}#${identifiers[Object.keys(identifiers)[0]]}`
|
|
397
|
+
: '');
|
|
398
|
+
|
|
399
|
+
if (!targetIdentifiersConcat) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const roleIdKey = `${targetIdentifiersConcat}_owner`;
|
|
404
|
+
_izContext.logger.debug(
|
|
405
|
+
'[CreateNodeCallback] Auto-provisioning owner TargetRole:',
|
|
406
|
+
{
|
|
407
|
+
identifiersConcat: targetIdentifiersConcat,
|
|
408
|
+
roleIdKey,
|
|
409
|
+
submittedByUserId,
|
|
410
|
+
roleName: 'owner'
|
|
411
|
+
}
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
await dynamodbSharedLib.putItem(
|
|
415
|
+
_izContext,
|
|
416
|
+
dynamodbSharedLib.tableName(_izContext, 'TargetRoles'),
|
|
417
|
+
{
|
|
418
|
+
identifiersConcat: targetIdentifiersConcat,
|
|
419
|
+
roleIdKey,
|
|
420
|
+
submittedByUserId,
|
|
421
|
+
roleName: 'owner',
|
|
422
|
+
assignedAt: new Date().toISOString()
|
|
423
|
+
}
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
await dynamodbSharedLib.transactWriteItems(_izContext, [
|
|
427
|
+
{
|
|
428
|
+
action: 'put',
|
|
429
|
+
params: {
|
|
430
|
+
tableName: dynamodbSharedLib.tableName(
|
|
431
|
+
_izContext,
|
|
432
|
+
'RoleUsers'
|
|
433
|
+
),
|
|
434
|
+
attributes: {
|
|
435
|
+
roleIdKey,
|
|
436
|
+
userId: submittedByUserId,
|
|
437
|
+
submittedByUserId
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
action: 'put',
|
|
443
|
+
params: {
|
|
444
|
+
tableName: dynamodbSharedLib.tableName(
|
|
445
|
+
_izContext,
|
|
446
|
+
'UserRoles'
|
|
447
|
+
),
|
|
448
|
+
attributes: {
|
|
449
|
+
userId: submittedByUserId,
|
|
450
|
+
roleIdKey,
|
|
451
|
+
submittedByUserId
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
action: 'put',
|
|
457
|
+
params: {
|
|
458
|
+
tableName: dynamodbSharedLib.tableName(
|
|
459
|
+
_izContext,
|
|
460
|
+
'RolePermissions'
|
|
461
|
+
),
|
|
462
|
+
attributes: {
|
|
463
|
+
roleIdKey,
|
|
464
|
+
flowTypeConcat: `${process.env.iz_serviceTag}_Update`,
|
|
465
|
+
permission: true,
|
|
466
|
+
allow: 'accept',
|
|
467
|
+
parameterGroups: {},
|
|
468
|
+
submittedByUserId
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
action: 'put',
|
|
474
|
+
params: {
|
|
475
|
+
tableName: dynamodbSharedLib.tableName(
|
|
476
|
+
_izContext,
|
|
477
|
+
'RolePermissions'
|
|
478
|
+
),
|
|
479
|
+
attributes: {
|
|
480
|
+
roleIdKey,
|
|
481
|
+
flowTypeConcat: `${process.env.iz_serviceTag}_Delete`,
|
|
482
|
+
permission: true,
|
|
483
|
+
allow: 'accept',
|
|
484
|
+
parameterGroups: {},
|
|
485
|
+
submittedByUserId
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
]);
|
|
490
|
+
} catch (roleError) {
|
|
491
|
+
_izContext.logger.error(
|
|
492
|
+
'[CreateNodeCallback] Failed to auto-provision owner TargetRole:',
|
|
493
|
+
{ roleError }
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Resolves keys and identifiers for Quota Deduction / Confirmation
|
|
500
|
+
* @param {Object} requestParams - Request parameters
|
|
501
|
+
* @param {Object} passBackProperties - Pass back properties
|
|
502
|
+
* @param {Object} returnValue - Return value from graph service
|
|
503
|
+
* @param {Object} callingFlowConfig - Calling flow configuration
|
|
504
|
+
* @param {Object} _izContext - Izara context object
|
|
505
|
+
* @returns {Object} Quota deduction keys
|
|
506
|
+
*/
|
|
507
|
+
export function resolveQuotaDeductionKeys(
|
|
508
|
+
requestParams,
|
|
509
|
+
passBackProperties,
|
|
510
|
+
returnValue,
|
|
511
|
+
callingFlowConfig,
|
|
512
|
+
_izContext
|
|
513
|
+
) {
|
|
514
|
+
const targetIdentifiers =
|
|
515
|
+
requestParams?.passBackProperties?.targetIdentifiers ||
|
|
516
|
+
passBackProperties?.passBackProperties?.targetIdentifiers ||
|
|
517
|
+
passBackProperties?.targetIdentifiers ||
|
|
518
|
+
returnValue?.requestParams?.targetIdentifiers;
|
|
519
|
+
|
|
520
|
+
const submittedByUserId =
|
|
521
|
+
returnValue?.requestParams?.submittedByUserId ||
|
|
522
|
+
targetIdentifiers?.submittedByUserId ||
|
|
523
|
+
requestParams?.submittedByUserId ||
|
|
524
|
+
requestParams?.passBackProperties?.submittedByUserId ||
|
|
525
|
+
passBackProperties?.passBackProperties?.submittedByUserId ||
|
|
526
|
+
passBackProperties?.submittedByUserId ||
|
|
527
|
+
_izContext.correlationIds?.get(coreConsts.SUBMITTED_BY_USER_ID) ||
|
|
528
|
+
_izContext.correlationIds?.get('submittedByUserId') ||
|
|
529
|
+
_izContext.correlationIds?.get('userId');
|
|
530
|
+
|
|
531
|
+
let identifiersConcat =
|
|
532
|
+
requestParams?.passBackProperties?.identifiersConcat ||
|
|
533
|
+
passBackProperties?.passBackProperties?.identifiersConcat ||
|
|
534
|
+
passBackProperties?.identifiersConcat ||
|
|
535
|
+
null;
|
|
536
|
+
|
|
537
|
+
if (!identifiersConcat && targetIdentifiers && typeof targetIdentifiers === 'object') {
|
|
538
|
+
if (targetIdentifiers.identifierConcat) {
|
|
539
|
+
identifiersConcat = targetIdentifiers.identifierConcat;
|
|
540
|
+
} else {
|
|
541
|
+
const sortedKeys = Object.keys(targetIdentifiers).sort();
|
|
542
|
+
const parts = [];
|
|
543
|
+
for (const key of sortedKeys) {
|
|
544
|
+
const value = targetIdentifiers[key];
|
|
545
|
+
if (
|
|
546
|
+
key !== 'submittedByUserId' &&
|
|
547
|
+
key !== 'identifierConcat' &&
|
|
548
|
+
value !== undefined &&
|
|
549
|
+
value !== null
|
|
550
|
+
) {
|
|
551
|
+
parts.push(`${key}#${value}`);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
if (parts.length > 0) {
|
|
555
|
+
identifiersConcat = parts.join('|');
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
if (!identifiersConcat) {
|
|
560
|
+
identifiersConcat = submittedByUserId;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const limitTag =
|
|
564
|
+
requestParams?.passBackProperties?.limitTag ||
|
|
565
|
+
passBackProperties?.passBackProperties?.limitTag ||
|
|
566
|
+
passBackProperties?.limitTag ||
|
|
567
|
+
callingFlowConfig?.limitTag ||
|
|
568
|
+
requestParams?.passBackProperties?.flowTypeConcat ||
|
|
569
|
+
passBackProperties?.passBackProperties?.flowTypeConcat ||
|
|
570
|
+
passBackProperties?.flowTypeConcat ||
|
|
571
|
+
callingFlowConfig?.flowTypeConcat ||
|
|
572
|
+
`${process.env.iz_serviceTag}_<%- flowTag %>`;
|
|
573
|
+
|
|
574
|
+
const flowTypeConcat = limitTag;
|
|
575
|
+
|
|
576
|
+
const uniqueIdentify =
|
|
577
|
+
requestParams?.passBackProperties?.uniqueIdentify ||
|
|
578
|
+
passBackProperties?.passBackProperties?.uniqueIdentify ||
|
|
579
|
+
passBackProperties?.uniqueIdentify ||
|
|
580
|
+
callingFlowConfig?.uniqueIdentify ||
|
|
581
|
+
_izContext.uniqueRequestId;
|
|
582
|
+
|
|
583
|
+
const usedTimestampId = uniqueIdentify;
|
|
584
|
+
|
|
585
|
+
const runningTotalReservedId =
|
|
586
|
+
requestParams?.passBackProperties?.runningTotalReservedId ||
|
|
587
|
+
passBackProperties?.passBackProperties?.runningTotalReservedId ||
|
|
588
|
+
passBackProperties?.runningTotalReservedId ||
|
|
589
|
+
callingFlowConfig?.runningTotalReservedId ||
|
|
590
|
+
(identifiersConcat && limitTag
|
|
591
|
+
? `${identifiersConcat}_${limitTag}`
|
|
592
|
+
: null);
|
|
593
|
+
|
|
594
|
+
const rbacType =
|
|
595
|
+
requestParams?.passBackProperties?.rbacType ||
|
|
596
|
+
passBackProperties?.passBackProperties?.rbacType ||
|
|
597
|
+
passBackProperties?.rbacType ||
|
|
598
|
+
callingFlowConfig?.rbacType;
|
|
599
|
+
|
|
600
|
+
return {
|
|
601
|
+
limitTag,
|
|
602
|
+
flowTypeConcat,
|
|
603
|
+
uniqueIdentify,
|
|
604
|
+
usedTimestampId,
|
|
605
|
+
runningTotalReservedId,
|
|
606
|
+
identifiersConcat,
|
|
607
|
+
targetIdentifiers,
|
|
608
|
+
rbacType
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Forwards quota confirmation message to ConfirmUsage_In or CancelUsage_In
|
|
614
|
+
* @param {Object} _izContext - Izara context object
|
|
615
|
+
* @param {Object} options - Message forwarding options
|
|
616
|
+
* @returns {Promise<void>}
|
|
617
|
+
*/
|
|
618
|
+
export async function forwardToQuotaConfirmation(
|
|
619
|
+
_izContext,
|
|
620
|
+
{
|
|
621
|
+
status,
|
|
622
|
+
errorsFound,
|
|
623
|
+
quotaKeys,
|
|
624
|
+
actionData,
|
|
625
|
+
callingFlowConfig
|
|
626
|
+
}
|
|
627
|
+
) {
|
|
628
|
+
const nextTargetTopic =
|
|
629
|
+
status === 'complete' && errorsFound.length === 0
|
|
630
|
+
? 'ConfirmUsage_In'
|
|
631
|
+
: 'CancelUsage_In';
|
|
632
|
+
|
|
633
|
+
const forwardMessage =
|
|
634
|
+
callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject(
|
|
635
|
+
{
|
|
636
|
+
...quotaKeys,
|
|
637
|
+
parentPassBackProperties: actionData
|
|
638
|
+
},
|
|
639
|
+
callingFlowSharedLib.addParentCallingFlowConfig(
|
|
640
|
+
callingFlowConfig,
|
|
641
|
+
callingFlowSharedLib.createCallingFlowConfig(
|
|
642
|
+
await lambdaSharedLib.lambdaFunctionName(
|
|
643
|
+
_izContext,
|
|
644
|
+
'CreateCompleteHdrSqs'
|
|
645
|
+
),
|
|
646
|
+
{}
|
|
647
|
+
)
|
|
648
|
+
)
|
|
649
|
+
);
|
|
650
|
+
|
|
651
|
+
const topicArn = snsSharedLib.snsTopicArnByFlowSchema(
|
|
652
|
+
_izContext,
|
|
653
|
+
nextTargetTopic,
|
|
654
|
+
'Rbac'
|
|
655
|
+
);
|
|
656
|
+
|
|
657
|
+
_izContext.logger.debug('[CreateNodeCallback] forwarding to RBAC', {
|
|
658
|
+
nextTargetTopic,
|
|
659
|
+
TopicArn: topicArn,
|
|
660
|
+
forwardMessage
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
await sns.publishAsync(_izContext, {
|
|
664
|
+
Message: JSON.stringify(forwardMessage),
|
|
665
|
+
TopicArn: topicArn
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// ── 4. CreateComplete_Main Helpers ───────────────────────────────────────────
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* Builds response payload and message attributes for Calling Flow
|
|
673
|
+
* @param {Object} callingFlowConfig - Calling flow configuration
|
|
674
|
+
* @param {Object} parentPassBack - Parent pass-back data
|
|
675
|
+
* @returns {{ message: Object, messageAttributes: Object }}
|
|
676
|
+
*/
|
|
677
|
+
export function buildCompletionPayload(callingFlowConfig, parentPassBack) {
|
|
678
|
+
const message =
|
|
679
|
+
callingFlowSharedLib.addParentPassBackPropertiesToSnsResponseMessageObject(
|
|
680
|
+
callingFlowConfig,
|
|
681
|
+
parentPassBack
|
|
682
|
+
);
|
|
683
|
+
|
|
684
|
+
const messageAttributes =
|
|
685
|
+
callingFlowSharedLib.addParentPassBackCallingFlowToSnsResponseMessageAttributes(
|
|
686
|
+
callingFlowConfig,
|
|
687
|
+
{}
|
|
688
|
+
);
|
|
689
|
+
|
|
690
|
+
return { message, messageAttributes };
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Publishes completion response message to outCreateNodeComplete
|
|
695
|
+
* @param {Object} _izContext - Izara context object
|
|
696
|
+
* @param {Object} message - SNS message payload
|
|
697
|
+
* @param {Object} messageAttributes - SNS message attributes
|
|
698
|
+
* @returns {Promise<void>}
|
|
699
|
+
*/
|
|
700
|
+
export async function publishCompletionMessage(_izContext, message, messageAttributes) {
|
|
701
|
+
const topicArn = snsSharedLib.snsTopicArnByFlowSchema(
|
|
702
|
+
_izContext,
|
|
703
|
+
TOPIC_NAME_GENERATE_CODE.outCreateNodeComplete
|
|
704
|
+
);
|
|
705
|
+
|
|
706
|
+
_izContext.logger.debug('[CreateComplete] publishing to SNS', {
|
|
707
|
+
TopicArn: topicArn,
|
|
708
|
+
message
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
await sns.publishAsync(_izContext, {
|
|
712
|
+
Message: JSON.stringify(message),
|
|
713
|
+
MessageAttributes: sns.createStringMessageAttributes(
|
|
714
|
+
_izContext,
|
|
715
|
+
messageAttributes
|
|
716
|
+
),
|
|
717
|
+
TopicArn: topicArn
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// ── 5. CreateLogicalLib Internal Helpers ──────────────────────────────────────
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Resolves and combines object schemas from S3/hierarchy
|
|
725
|
+
* @param {Object} _izContext - Izara context object
|
|
726
|
+
* @param {string} objectType - Object type identifier
|
|
727
|
+
* @returns {Promise<{ objType: Object, objectSchema: Object, objectSchemas: Object, parentObject?: Object }>}
|
|
728
|
+
*/
|
|
729
|
+
export async function resolveObjectSchemas(_izContext, objectType) {
|
|
730
|
+
const objType = createObjType(objectType);
|
|
731
|
+
const objectSchema = await getObjectSchema.getObjSchemaS3WithHierarchy(
|
|
732
|
+
_izContext,
|
|
733
|
+
objType
|
|
734
|
+
);
|
|
735
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>LogicalLib] objectSchema:`, objectSchema);
|
|
736
|
+
|
|
737
|
+
const objectSchemas =
|
|
738
|
+
uploadUseCase.generateObjectSchemaForCombineFieldNames(
|
|
739
|
+
_izContext,
|
|
740
|
+
objectSchema
|
|
741
|
+
);
|
|
742
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>LogicalLib] objectSchemas:`, objectSchemas);
|
|
743
|
+
|
|
744
|
+
let parentObject;
|
|
745
|
+
if (objectSchemas.hasOwnProperty('extendObjType')) {
|
|
746
|
+
parentObject = await getObjectSchema.getObjSchemaS3WithHierarchy(
|
|
747
|
+
_izContext,
|
|
748
|
+
objectSchema.extendObjType
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
return { objType, objectSchema, objectSchemas, parentObject };
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Extracts field values matching storage resource specifications
|
|
757
|
+
* @param {Object} _izContext - Izara context object
|
|
758
|
+
* @param {string} storageTag - Storage resource tag
|
|
759
|
+
* @param {Object} createDataDetails - Complete data details map
|
|
760
|
+
* @param {Object} createDataDetail - Current storage data detail
|
|
761
|
+
* @param {Object} requestParams - Request parameters
|
|
762
|
+
* @param {Object} objInstanceFull - Full object instance with identifiers
|
|
763
|
+
* @returns {Object} Extracted fields object
|
|
764
|
+
*/
|
|
765
|
+
export function extractFieldsByStorageTag(
|
|
766
|
+
_izContext,
|
|
767
|
+
storageTag,
|
|
768
|
+
createDataDetails,
|
|
769
|
+
createDataDetail,
|
|
770
|
+
requestParams,
|
|
771
|
+
objInstanceFull
|
|
772
|
+
) {
|
|
773
|
+
const fields = {};
|
|
774
|
+
for (const fieldName of Object.keys(requestParams.fieldNames || {})) {
|
|
775
|
+
if (
|
|
776
|
+
createDataDetails[storageTag].fieldNames.includes(fieldName) &&
|
|
777
|
+
createDataDetail.fieldNames.includes(fieldName) &&
|
|
778
|
+
!objInstanceFull.identifiers.hasOwnProperty(fieldName) &&
|
|
779
|
+
!fields.hasOwnProperty(fieldName)
|
|
780
|
+
) {
|
|
781
|
+
Object.assign(fields, {
|
|
782
|
+
[fieldName]: requestParams.fieldNames[fieldName]
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
return fields;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/**
|
|
790
|
+
* Auto-provisions owner TargetRole for DynamoDB-only objects (no Graph)
|
|
791
|
+
* @param {Object} _izContext - Izara context object
|
|
792
|
+
* @param {Object} options - Schema and identity parameters
|
|
793
|
+
* @returns {Promise<void>}
|
|
794
|
+
*/
|
|
795
|
+
export async function autoProvisionOwnerTargetRoleForDynamoOnly(
|
|
796
|
+
_izContext,
|
|
797
|
+
{ objectSchema, identifiersForCreateData, requestParams }
|
|
798
|
+
) {
|
|
799
|
+
const hasRbacAddOn =
|
|
800
|
+
Array.isArray(objectSchema?.addOnDataStructure) &&
|
|
801
|
+
objectSchema.addOnDataStructure.some(addOn => addOn?.type === 'rbac');
|
|
802
|
+
|
|
803
|
+
const isRootRbacObject =
|
|
804
|
+
hasRbacAddOn &&
|
|
805
|
+
!objectSchema?.belongTo &&
|
|
806
|
+
objectSchema?.objectType !== 'user' &&
|
|
807
|
+
objectSchema?.generatedBy !== 'systemNoUser';
|
|
808
|
+
|
|
809
|
+
if (!isRootRbacObject) {
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
try {
|
|
814
|
+
const submittedByUserId =
|
|
815
|
+
requestParams.submittedByUserId ||
|
|
816
|
+
_izContext.correlationIds?.get(coreConsts.SUBMITTED_BY_USER_ID);
|
|
817
|
+
|
|
818
|
+
if (submittedByUserId) {
|
|
819
|
+
const targetIdentKeys = Object.entries(identifiersForCreateData)
|
|
820
|
+
.map(([k, v]) => `${k}#${v}`)
|
|
821
|
+
.join('|');
|
|
822
|
+
const targetIdentifiersConcat =
|
|
823
|
+
targetIdentKeys ||
|
|
824
|
+
(identifiersForCreateData[Object.keys(identifiersForCreateData)[0]]
|
|
825
|
+
? `${Object.keys(identifiersForCreateData)[0]}#${identifiersForCreateData[Object.keys(identifiersForCreateData)[0]]}`
|
|
826
|
+
: '');
|
|
827
|
+
|
|
828
|
+
if (targetIdentifiersConcat) {
|
|
829
|
+
const roleIdKey = `${targetIdentifiersConcat}_owner`;
|
|
830
|
+
_izContext.logger.debug(
|
|
831
|
+
'[<%- flowTag %>LogicalLib] Auto-provisioning owner TargetRole for DynamoDB-only:',
|
|
832
|
+
{
|
|
833
|
+
identifiersConcat: targetIdentifiersConcat,
|
|
834
|
+
roleIdKey,
|
|
835
|
+
submittedByUserId,
|
|
836
|
+
roleName: 'owner'
|
|
837
|
+
}
|
|
838
|
+
);
|
|
839
|
+
|
|
840
|
+
await dynamodbSharedLib.putItem(
|
|
841
|
+
_izContext,
|
|
842
|
+
dynamodbSharedLib.tableName(_izContext, 'TargetRoles'),
|
|
843
|
+
{
|
|
844
|
+
identifiersConcat: targetIdentifiersConcat,
|
|
845
|
+
roleIdKey,
|
|
846
|
+
submittedByUserId,
|
|
847
|
+
roleName: 'owner',
|
|
848
|
+
assignedAt: new Date().toISOString()
|
|
849
|
+
}
|
|
850
|
+
);
|
|
851
|
+
|
|
852
|
+
await dynamodbSharedLib.transactWriteItems(_izContext, [
|
|
853
|
+
{
|
|
854
|
+
action: 'put',
|
|
855
|
+
params: {
|
|
856
|
+
tableName: dynamodbSharedLib.tableName(
|
|
857
|
+
_izContext,
|
|
858
|
+
'RoleUsers'
|
|
859
|
+
),
|
|
860
|
+
attributes: {
|
|
861
|
+
roleIdKey,
|
|
862
|
+
userId: submittedByUserId,
|
|
863
|
+
submittedByUserId
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
},
|
|
867
|
+
{
|
|
868
|
+
action: 'put',
|
|
869
|
+
params: {
|
|
870
|
+
tableName: dynamodbSharedLib.tableName(
|
|
871
|
+
_izContext,
|
|
872
|
+
'UserRoles'
|
|
873
|
+
),
|
|
874
|
+
attributes: {
|
|
875
|
+
userId: submittedByUserId,
|
|
876
|
+
roleIdKey,
|
|
877
|
+
submittedByUserId
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
},
|
|
881
|
+
{
|
|
882
|
+
action: 'put',
|
|
883
|
+
params: {
|
|
884
|
+
tableName: dynamodbSharedLib.tableName(
|
|
885
|
+
_izContext,
|
|
886
|
+
'RolePermissions'
|
|
887
|
+
),
|
|
888
|
+
attributes: {
|
|
889
|
+
roleIdKey,
|
|
890
|
+
flowTypeConcat: `${process.env.iz_serviceTag}_Update`,
|
|
891
|
+
permission: true,
|
|
892
|
+
allow: 'accept',
|
|
893
|
+
parameterGroups: {},
|
|
894
|
+
submittedByUserId
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
},
|
|
898
|
+
{
|
|
899
|
+
action: 'put',
|
|
900
|
+
params: {
|
|
901
|
+
tableName: dynamodbSharedLib.tableName(
|
|
902
|
+
_izContext,
|
|
903
|
+
'RolePermissions'
|
|
904
|
+
),
|
|
905
|
+
attributes: {
|
|
906
|
+
roleIdKey,
|
|
907
|
+
flowTypeConcat: `${process.env.iz_serviceTag}_Delete`,
|
|
908
|
+
permission: true,
|
|
909
|
+
allow: 'accept',
|
|
910
|
+
parameterGroups: {},
|
|
911
|
+
submittedByUserId
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
]);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
} catch (roleError) {
|
|
919
|
+
_izContext.logger.error(
|
|
920
|
+
'[<%- flowTag %>LogicalLib] Failed to auto-provision owner TargetRole for DynamoDB-only:',
|
|
921
|
+
{ roleError }
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/**
|
|
927
|
+
* Resolves callback properties to attach to Graph dispatch message
|
|
928
|
+
* @param {Object} phaseResults - Results from previous phases
|
|
929
|
+
* @param {Object} requestParams - Request parameters
|
|
930
|
+
* @returns {Object} Formatted callback properties
|
|
931
|
+
*/
|
|
932
|
+
export function resolveCallbackRbacProperties(phaseResults, requestParams) {
|
|
933
|
+
const uniqueIdentify =
|
|
934
|
+
phaseResults?.uniqueIdentify ||
|
|
935
|
+
Object.values(phaseResults || {}).find(
|
|
936
|
+
(r) => r?.returnValue?.uniqueIdentify
|
|
937
|
+
)?.returnValue?.uniqueIdentify;
|
|
938
|
+
|
|
939
|
+
const rbacType =
|
|
940
|
+
phaseResults?.rbacType ||
|
|
941
|
+
phaseResults?.target?.rbacType ||
|
|
942
|
+
phaseResults?.target?.returnValue?.rbacType ||
|
|
943
|
+
phaseResults?.app?.rbacType ||
|
|
944
|
+
phaseResults?.app?.returnValue?.rbacType ||
|
|
945
|
+
Object.values(phaseResults || {}).find(
|
|
946
|
+
(r) => r?.rbacType || r?.returnValue?.rbacType
|
|
947
|
+
)?.rbacType;
|
|
948
|
+
|
|
949
|
+
return {
|
|
950
|
+
uniqueIdentify,
|
|
951
|
+
targetIdentifiers: requestParams?.targetIdentifiers,
|
|
952
|
+
rbacType,
|
|
953
|
+
phaseResults
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
/**
|
|
958
|
+
* Dispatches node creation request to Graph Service
|
|
959
|
+
* @param {Object} _izContext - Izara context object
|
|
960
|
+
* @param {Object} options - Graph dispatch configuration
|
|
961
|
+
* @returns {Promise<void>}
|
|
962
|
+
*/
|
|
963
|
+
export async function dispatchToGraphEngine(
|
|
964
|
+
_izContext,
|
|
965
|
+
{
|
|
966
|
+
objType,
|
|
967
|
+
objectForCreate,
|
|
968
|
+
graphServiceName,
|
|
969
|
+
requestParams,
|
|
970
|
+
parentFlowId,
|
|
971
|
+
callingFlowConfig,
|
|
972
|
+
phaseResults
|
|
973
|
+
}
|
|
974
|
+
) {
|
|
975
|
+
let messageObject = {
|
|
976
|
+
objType,
|
|
977
|
+
objInstanceFull: objectForCreate.objInstanceFull,
|
|
978
|
+
deepPathConditional: requestParams.deepPathConditional || [],
|
|
979
|
+
setFieldConditionals: requestParams.setFieldConditionals || [],
|
|
980
|
+
relationships: requestParams.relationships || [],
|
|
981
|
+
targetIdentifiers: requestParams.targetIdentifiers,
|
|
982
|
+
parentFlowId: parentFlowId,
|
|
983
|
+
settings: { updatePropertiesOnMatch: false },
|
|
984
|
+
originTimestamp: Date.now()
|
|
985
|
+
};
|
|
986
|
+
|
|
987
|
+
const callbackProps = resolveCallbackRbacProperties(
|
|
988
|
+
phaseResults,
|
|
989
|
+
requestParams
|
|
990
|
+
);
|
|
991
|
+
|
|
992
|
+
messageObject =
|
|
993
|
+
callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject(
|
|
994
|
+
messageObject,
|
|
995
|
+
callingFlowSharedLib.addParentCallingFlowConfig(
|
|
996
|
+
callingFlowConfig,
|
|
997
|
+
callingFlowSharedLib.createCallingFlowConfig(
|
|
998
|
+
//(<callingFlowProperties>)
|
|
999
|
+
await lambdaSharedLib.lambdaFunctionName(
|
|
1000
|
+
_izContext,
|
|
1001
|
+
<% if (plan.hasCreateNodeCallback) { -%>
|
|
1002
|
+
'CreateNodeCallbackHdrSqs'
|
|
1003
|
+
<% } else { -%>
|
|
1004
|
+
TOPIC_NAME_GENERATE_CODE.createNodeComplete
|
|
1005
|
+
<% } -%>
|
|
1006
|
+
),
|
|
1007
|
+
<% if (plan.hasCreateNodeCallback) { -%>
|
|
1008
|
+
callbackProps
|
|
1009
|
+
<% } else { -%>
|
|
1010
|
+
{}
|
|
1011
|
+
<% } -%>
|
|
1012
|
+
//(</callingFlowProperties>)
|
|
1013
|
+
)
|
|
1014
|
+
)
|
|
1015
|
+
);
|
|
1016
|
+
|
|
1017
|
+
_izContext.logger.debug(
|
|
1018
|
+
`[${process.env.iz_serviceTag}:<%- flowTag %>LogicalLib] message to graph:`,
|
|
1019
|
+
{
|
|
1020
|
+
graphServiceName,
|
|
1021
|
+
messageObject
|
|
1022
|
+
}
|
|
1023
|
+
);
|
|
1024
|
+
|
|
1025
|
+
await sns.publishAsync(_izContext, {
|
|
1026
|
+
Message: JSON.stringify(messageObject),
|
|
1027
|
+
TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
|
|
1028
|
+
_izContext,
|
|
1029
|
+
TOPIC_NAME_GRAPH_HANDLER.inCreateNode,
|
|
1030
|
+
graphServiceName
|
|
1031
|
+
)
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
/**
|
|
1036
|
+
* Handles creation errors and publishes error response
|
|
1037
|
+
* @param {Object} _izContext - Izara context object
|
|
1038
|
+
* @param {Object} options - Error context details
|
|
1039
|
+
* @returns {Promise<Object>} Error status result
|
|
1040
|
+
*/
|
|
1041
|
+
export async function handleCreateError(
|
|
1042
|
+
_izContext,
|
|
1043
|
+
{
|
|
1044
|
+
objType,
|
|
1045
|
+
objInstanceFull,
|
|
1046
|
+
requestParams,
|
|
1047
|
+
callingFlowConfig,
|
|
1048
|
+
errorsFound
|
|
1049
|
+
}
|
|
1050
|
+
) {
|
|
1051
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>LogicalLib] errorsFound:`, errorsFound);
|
|
1052
|
+
|
|
1053
|
+
await asyncFlowSharedLib.clearAllAwaitingSteps(
|
|
1054
|
+
_izContext,
|
|
1055
|
+
asyncFlowSharedLib.createPendingStepId(
|
|
1056
|
+
_izContext.uniqueRequestId,
|
|
1057
|
+
PREFIX.CREATE_OBJECT_ASYNC_COMPLETE
|
|
1058
|
+
)
|
|
1059
|
+
);
|
|
1060
|
+
|
|
1061
|
+
const messageObject =
|
|
1062
|
+
callingFlowSharedLib.addPassBackPropertiesToSnsResponseMessageObject(
|
|
1063
|
+
callingFlowConfig,
|
|
1064
|
+
{ objType, identifiers: null, errorsFound }
|
|
1065
|
+
);
|
|
1066
|
+
|
|
1067
|
+
await sns.publishAsync(_izContext, {
|
|
1068
|
+
Message: JSON.stringify(messageObject),
|
|
1069
|
+
MessageAttributes: sns.createStringMessageAttributes(
|
|
1070
|
+
_izContext,
|
|
1071
|
+
callingFlowSharedLib.addCallingFlowToSnsResponseMessageAttributes(
|
|
1072
|
+
callingFlowConfig,
|
|
1073
|
+
{}
|
|
1074
|
+
)
|
|
1075
|
+
),
|
|
1076
|
+
TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
|
|
1077
|
+
_izContext,
|
|
1078
|
+
TOPIC_NAME_GENERATE_CODE.outCreateNodeComplete
|
|
1079
|
+
)
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
return {
|
|
1083
|
+
objType,
|
|
1084
|
+
objInstanceFull,
|
|
1085
|
+
relationships: requestParams.relationships || [],
|
|
1086
|
+
targetIdentifiers: requestParams.targetIdentifiers,
|
|
1087
|
+
status: 'error',
|
|
1088
|
+
errorsFound
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/**
|
|
1093
|
+
* Handles DynamoDB-only complete notification when no Graph callback is required
|
|
1094
|
+
* @param {Object} _izContext - Izara context object
|
|
1095
|
+
* @param {Object} options - Completion parameters
|
|
1096
|
+
* @returns {Promise<void>}
|
|
1097
|
+
*/
|
|
1098
|
+
export async function handleDynamoDbOnlyComplete(
|
|
1099
|
+
_izContext,
|
|
1100
|
+
{
|
|
1101
|
+
objType,
|
|
1102
|
+
objInstanceFullForDynamoDb,
|
|
1103
|
+
callingFlowConfig
|
|
1104
|
+
}
|
|
1105
|
+
) {
|
|
1106
|
+
const outMessage =
|
|
1107
|
+
callingFlowSharedLib.addPassBackPropertiesToSnsResponseMessageObject(
|
|
1108
|
+
callingFlowConfig,
|
|
1109
|
+
{
|
|
1110
|
+
objType: objType,
|
|
1111
|
+
objInstanceFull: {
|
|
1112
|
+
identifiers: objInstanceFullForDynamoDb.identifiers,
|
|
1113
|
+
fields: objInstanceFullForDynamoDb.fields
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
);
|
|
1117
|
+
|
|
1118
|
+
if (
|
|
1119
|
+
callingFlowConfig[callingFlowSharedLib.consts.CALLINGFLOW_PROPERTYNAME]
|
|
1120
|
+
) {
|
|
1121
|
+
_izContext.logger.debug('HAVE CallingFlow');
|
|
1122
|
+
await sns.publishAsync(_izContext, {
|
|
1123
|
+
Message: JSON.stringify(outMessage),
|
|
1124
|
+
MessageAttributes: sns.createStringMessageAttributes(
|
|
1125
|
+
_izContext,
|
|
1126
|
+
callingFlowSharedLib.addCallingFlowToSnsResponseMessageAttributes(
|
|
1127
|
+
callingFlowConfig,
|
|
1128
|
+
{}
|
|
1129
|
+
)
|
|
1130
|
+
),
|
|
1131
|
+
TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
|
|
1132
|
+
_izContext,
|
|
1133
|
+
TOPIC_NAME_GENERATE_CODE.outCreateNodeComplete
|
|
1134
|
+
)
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
// ── 6. Main Logical Function ──────────────────────────────────────────────────
|
|
1140
|
+
|
|
64
1141
|
/**
|
|
65
|
-
* Core logical processing library
|
|
1142
|
+
* Core logical processing library for <%- flowTag %> flow
|
|
66
1143
|
* @param {Object} _izContext - Context object with logger
|
|
67
|
-
* @param {Object} requestParams - { fieldNames, relationships[] }
|
|
1144
|
+
* @param {Object} requestParams - Request parameters { fieldNames, relationships[] }
|
|
68
1145
|
* @param {Object} callingFlowConfig - Calling flow configuration
|
|
1146
|
+
* @param {Array<string>} errorsFound - Array of preceding errors
|
|
69
1147
|
* @param {Object} phaseResults - Results from preceding phases
|
|
70
1148
|
* @returns {Promise<Object>} Result with objType, objInstanceFull, relationships, status, errorsFound
|
|
71
1149
|
*/
|
|
@@ -77,7 +1155,7 @@ export async function <%- flowTag %>LogicalLib(
|
|
|
77
1155
|
phaseResults = {}
|
|
78
1156
|
) {
|
|
79
1157
|
try {
|
|
80
|
-
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>LogicalLib] started`,
|
|
1158
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>LogicalLib] started`, {
|
|
81
1159
|
requestParams,
|
|
82
1160
|
callingFlowConfig,
|
|
83
1161
|
phaseResults,
|
|
@@ -87,47 +1165,23 @@ export async function <%- flowTag %>LogicalLib(
|
|
|
87
1165
|
//(<beforeCreateMainLogical>)
|
|
88
1166
|
//(</beforeCreateMainLogical>)
|
|
89
1167
|
|
|
90
|
-
|
|
1168
|
+
// 1. Load and resolve Schema
|
|
1169
|
+
const { objType, objectSchema, objectSchemas, parentObject } =
|
|
1170
|
+
await resolveObjectSchemas(_izContext, requestParams.objectType);
|
|
91
1171
|
|
|
92
|
-
// ── Load schema ───────────────────────────────────────────────────────────
|
|
93
|
-
const objectSchema = await getObjectSchema.getObjSchemaS3WithHierarchy(
|
|
94
|
-
_izContext,
|
|
95
|
-
objType
|
|
96
|
-
);
|
|
97
|
-
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>LogicalLib] objectSchema:`, objectSchema);
|
|
98
|
-
|
|
99
|
-
const objectSchemas =
|
|
100
|
-
uploadUseCase.generateObjectSchemaForCombineFieldNames(
|
|
101
|
-
_izContext,
|
|
102
|
-
objectSchema
|
|
103
|
-
);
|
|
104
|
-
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>LogicalLib] objectSchemas:`, objectSchemas);
|
|
105
|
-
|
|
106
|
-
// ── Parent object ─────────────────────────────────────────────────────────
|
|
107
|
-
let parentObject;
|
|
108
|
-
if (objectSchemas.hasOwnProperty('extendObjType')) {
|
|
109
|
-
parentObject = await getObjectSchema.getObjSchemaS3WithHierarchy(
|
|
110
|
-
_izContext,
|
|
111
|
-
objectSchema.extendObjType
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// ── Detect invocation type ────────────────────────────────────────────────
|
|
116
1172
|
const directInvoke =
|
|
117
1173
|
requestParams.hasOwnProperty('__context__') &&
|
|
118
1174
|
requestParams.izEventSource?.eventSourceTag === 'DirectInvoke';
|
|
119
1175
|
|
|
120
|
-
// ── Build createDataDetails ───────────────────────────────────────────────
|
|
121
1176
|
const createDataDetails = await createDataDetailsLib(
|
|
122
1177
|
_izContext,
|
|
123
1178
|
objectSchemas
|
|
124
1179
|
);
|
|
125
1180
|
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>LogicalLib] createDataDetails:`, createDataDetails);
|
|
126
1181
|
|
|
127
|
-
//
|
|
1182
|
+
// 2. Prepare objInstanceFull
|
|
128
1183
|
const objInstanceFull = { identifiers: {}, fields: {} };
|
|
129
|
-
|
|
130
|
-
for (const fieldName of Object.keys(requestParams.fieldNames)) {
|
|
1184
|
+
for (const fieldName of Object.keys(requestParams.fieldNames || {})) {
|
|
131
1185
|
if (objectSchemas.identifierFieldNames.includes(fieldName)) {
|
|
132
1186
|
Object.assign(objInstanceFull.identifiers, {
|
|
133
1187
|
[fieldName]: requestParams.fieldNames[fieldName]
|
|
@@ -135,29 +1189,6 @@ export async function <%- flowTag %>LogicalLib(
|
|
|
135
1189
|
}
|
|
136
1190
|
}
|
|
137
1191
|
|
|
138
|
-
// ── Helper: extract fields per storageTag ─────────────────────────────────
|
|
139
|
-
function createObjInstanceFullFieldsByStorageTag(
|
|
140
|
-
_izContext,
|
|
141
|
-
storageTag,
|
|
142
|
-
createDataDetail
|
|
143
|
-
) {
|
|
144
|
-
const fields = {};
|
|
145
|
-
for (const fieldName of Object.keys(requestParams.fieldNames)) {
|
|
146
|
-
if (
|
|
147
|
-
createDataDetails[storageTag].fieldNames.includes(fieldName) &&
|
|
148
|
-
createDataDetail.fieldNames.includes(fieldName) &&
|
|
149
|
-
!objInstanceFull.identifiers.hasOwnProperty(fieldName) &&
|
|
150
|
-
!fields.hasOwnProperty(fieldName)
|
|
151
|
-
) {
|
|
152
|
-
Object.assign(fields, {
|
|
153
|
-
[fieldName]: requestParams.fieldNames[fieldName]
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
return fields;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// ── Process storage resources ─────────────────────────────────────────────
|
|
161
1192
|
const objInstanceFullForDynamoDb = structuredClone(objInstanceFull);
|
|
162
1193
|
const objInstanceFullForGraph = structuredClone(objInstanceFull);
|
|
163
1194
|
const objInstanceFullForExternalTopic = structuredClone(objInstanceFull);
|
|
@@ -166,17 +1197,20 @@ export async function <%- flowTag %>LogicalLib(
|
|
|
166
1197
|
const listOfObjectForCreates = [];
|
|
167
1198
|
const listOfRecords = [];
|
|
168
1199
|
|
|
1200
|
+
// 3. Process storage resources
|
|
169
1201
|
if (errorsFound.length === 0) {
|
|
170
1202
|
//(<validateBeforeCreate>)
|
|
171
1203
|
//(</validateBeforeCreate>)
|
|
172
1204
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
)
|
|
1205
|
+
const hasGraphStorage = Object.values(createDataDetails).some(
|
|
1206
|
+
(detail) => detail.storageType === consts.STORAGE_TYPES.graph
|
|
1207
|
+
);
|
|
1208
|
+
|
|
1209
|
+
for (const [storageTag, createDataDetail] of Object.entries(createDataDetails)) {
|
|
176
1210
|
//(<beforeCreate>)
|
|
177
1211
|
//(</beforeCreate>)
|
|
178
1212
|
|
|
179
|
-
// ── DynamoDB
|
|
1213
|
+
// ── DynamoDB ──
|
|
180
1214
|
if (createDataDetail.storageType === consts.STORAGE_TYPES.dynamoDB) {
|
|
181
1215
|
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>LogicalLib] DynamoDB:`, {
|
|
182
1216
|
storageTag,
|
|
@@ -197,18 +1231,16 @@ export async function <%- flowTag %>LogicalLib(
|
|
|
197
1231
|
|
|
198
1232
|
Object.assign(
|
|
199
1233
|
objInstanceFullForDynamoDb.fields,
|
|
200
|
-
|
|
1234
|
+
extractFieldsByStorageTag(
|
|
201
1235
|
_izContext,
|
|
202
1236
|
storageTag,
|
|
203
|
-
|
|
1237
|
+
createDataDetails,
|
|
1238
|
+
createDataDetail,
|
|
1239
|
+
requestParams,
|
|
1240
|
+
objInstanceFull
|
|
204
1241
|
)
|
|
205
1242
|
);
|
|
206
1243
|
|
|
207
|
-
_izContext.logger.debug(
|
|
208
|
-
'objInstanceFull before create item in dynamoDB',
|
|
209
|
-
objInstanceFullForDynamoDb
|
|
210
|
-
);
|
|
211
|
-
|
|
212
1244
|
const identifiersForCreateData =
|
|
213
1245
|
await dynamoDbIdentifiersByStorageResource(
|
|
214
1246
|
_izContext,
|
|
@@ -216,20 +1248,11 @@ export async function <%- flowTag %>LogicalLib(
|
|
|
216
1248
|
createDataDetails,
|
|
217
1249
|
objInstanceFullForDynamoDb.identifiers
|
|
218
1250
|
);
|
|
219
|
-
_izContext.logger.debug(
|
|
220
|
-
'identifiersForCreateData',
|
|
221
|
-
identifiersForCreateData
|
|
222
|
-
);
|
|
223
1251
|
|
|
224
1252
|
//(<beforeCreateRecordDynamo>)
|
|
225
1253
|
//(</beforeCreateRecordDynamo>)
|
|
226
1254
|
|
|
227
|
-
if (
|
|
228
|
-
Object.values(createDataDetails).some(
|
|
229
|
-
(createDataDetail) =>
|
|
230
|
-
createDataDetail.storageType === consts.STORAGE_TYPES.graph
|
|
231
|
-
)
|
|
232
|
-
) {
|
|
1255
|
+
if (hasGraphStorage) {
|
|
233
1256
|
_izContext.logger.debug('must process graph first');
|
|
234
1257
|
listOfRecords.push({
|
|
235
1258
|
[storageTag]: {
|
|
@@ -258,76 +1281,22 @@ export async function <%- flowTag %>LogicalLib(
|
|
|
258
1281
|
|
|
259
1282
|
objInstanceFullForDynamoDb.fields = {};
|
|
260
1283
|
|
|
261
|
-
//
|
|
262
|
-
|
|
263
|
-
objectSchema
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
objectSchema?.objectType !== 'user' &&
|
|
268
|
-
objectSchema?.generatedBy !== 'systemNoUser';
|
|
269
|
-
|
|
270
|
-
if (isRootRbacObject) {
|
|
271
|
-
try {
|
|
272
|
-
const submittedByUserId = requestParams.submittedByUserId || _izContext.correlationIds.get(coreConsts.SUBMITTED_BY_USER_ID);
|
|
273
|
-
if (submittedByUserId) {
|
|
274
|
-
const targetIdentKeys = Object.entries(identifiersForCreateData)
|
|
275
|
-
.map(([k, v]) => `${k}#${v}`)
|
|
276
|
-
.join('_');
|
|
277
|
-
const targetIdentifiersConcat = targetIdentKeys || (identifiersForCreateData[Object.keys(identifiersForCreateData)[0]] ? `${Object.keys(identifiersForCreateData)[0]}#${identifiersForCreateData[Object.keys(identifiersForCreateData)[0]]}` : '');
|
|
278
|
-
|
|
279
|
-
if (targetIdentifiersConcat) {
|
|
280
|
-
const roleIdKey = `${targetIdentifiersConcat}_owner`;
|
|
281
|
-
_izContext.logger.debug('[CreateLogicalLib] Auto-provisioning owner TargetRole:', {
|
|
282
|
-
identifiersConcat: targetIdentifiersConcat,
|
|
283
|
-
roleIdKey,
|
|
284
|
-
submittedByUserId,
|
|
285
|
-
roleName: 'owner'
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
await dynamodbSharedLib.putItem(
|
|
289
|
-
_izContext,
|
|
290
|
-
dynamodbSharedLib.tableName(_izContext, 'TargetRoles'),
|
|
291
|
-
{
|
|
292
|
-
identifiersConcat: targetIdentifiersConcat,
|
|
293
|
-
roleIdKey,
|
|
294
|
-
submittedByUserId,
|
|
295
|
-
roleName: 'owner',
|
|
296
|
-
assignedAt: new Date().toISOString()
|
|
297
|
-
}
|
|
298
|
-
).catch(() => {});
|
|
299
|
-
|
|
300
|
-
await dynamodbSharedLib.transactWriteItems(_izContext, [
|
|
301
|
-
{
|
|
302
|
-
Put: {
|
|
303
|
-
TableName: dynamodbSharedLib.tableName(_izContext, 'RoleUsers'),
|
|
304
|
-
Item: { roleIdKey, submittedByUserId }
|
|
305
|
-
}
|
|
306
|
-
},
|
|
307
|
-
{
|
|
308
|
-
Put: {
|
|
309
|
-
TableName: dynamodbSharedLib.tableName(_izContext, 'UserRoles'),
|
|
310
|
-
Item: { submittedByUserId, roleIdKey }
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
]).catch(() => {});
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
} catch (roleError) {
|
|
317
|
-
_izContext.logger.warn('[CreateLogicalLib] Failed to auto-provision owner TargetRole:', { roleError });
|
|
318
|
-
}
|
|
319
|
-
}
|
|
1284
|
+
// Auto-provision owner TargetRole for DynamoDB-only
|
|
1285
|
+
await autoProvisionOwnerTargetRoleForDynamoOnly(_izContext, {
|
|
1286
|
+
objectSchema,
|
|
1287
|
+
identifiersForCreateData,
|
|
1288
|
+
requestParams
|
|
1289
|
+
});
|
|
320
1290
|
}
|
|
321
1291
|
|
|
322
1292
|
//(<afterCreateRecordDynamo>)
|
|
323
1293
|
//(</afterCreateRecordDynamo>)
|
|
324
1294
|
|
|
325
|
-
|
|
326
|
-
} else if (
|
|
327
|
-
createDataDetail.storageType === consts.STORAGE_TYPES.graph
|
|
328
|
-
) {
|
|
1295
|
+
// ── Graph ──
|
|
1296
|
+
} else if (createDataDetail.storageType === consts.STORAGE_TYPES.graph) {
|
|
329
1297
|
//(<beforeCreateNode>)
|
|
330
1298
|
//(</beforeCreateNode>)
|
|
1299
|
+
|
|
331
1300
|
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>LogicalLib] Graph:`, {
|
|
332
1301
|
storageTag,
|
|
333
1302
|
objInstanceFull
|
|
@@ -335,16 +1304,15 @@ export async function <%- flowTag %>LogicalLib(
|
|
|
335
1304
|
|
|
336
1305
|
Object.assign(
|
|
337
1306
|
objInstanceFullForGraph.fields,
|
|
338
|
-
|
|
1307
|
+
extractFieldsByStorageTag(
|
|
339
1308
|
_izContext,
|
|
340
1309
|
storageTag,
|
|
341
|
-
|
|
1310
|
+
createDataDetails,
|
|
1311
|
+
createDataDetail,
|
|
1312
|
+
requestParams,
|
|
1313
|
+
objInstanceFull
|
|
342
1314
|
)
|
|
343
1315
|
);
|
|
344
|
-
_izContext.logger.debug(
|
|
345
|
-
'objInstanceFull before send to Graph',
|
|
346
|
-
objInstanceFullForGraph
|
|
347
|
-
);
|
|
348
1316
|
|
|
349
1317
|
//(<additionalAttributeCreateNode>)
|
|
350
1318
|
const additionalAttributes = {};
|
|
@@ -372,29 +1340,22 @@ export async function <%- flowTag %>LogicalLib(
|
|
|
372
1340
|
}
|
|
373
1341
|
});
|
|
374
1342
|
|
|
375
|
-
|
|
376
|
-
} else if (
|
|
377
|
-
createDataDetail.storageType === consts.STORAGE_TYPES.externalTopic
|
|
378
|
-
) {
|
|
379
|
-
_izContext.logger.debug('::::::externalTopic::::::', {
|
|
380
|
-
storageTag,
|
|
381
|
-
createDataDetail
|
|
382
|
-
});
|
|
1343
|
+
// ── External Topic ──
|
|
1344
|
+
} else if (createDataDetail.storageType === consts.STORAGE_TYPES.externalTopic) {
|
|
383
1345
|
//(<beforeSendMessageToExternalTopic>)
|
|
384
1346
|
//(</beforeSendMessageToExternalTopic>)
|
|
385
1347
|
|
|
386
1348
|
Object.assign(
|
|
387
1349
|
objInstanceFullForExternalTopic.fields,
|
|
388
|
-
|
|
1350
|
+
extractFieldsByStorageTag(
|
|
389
1351
|
_izContext,
|
|
390
1352
|
storageTag,
|
|
391
|
-
|
|
1353
|
+
createDataDetails,
|
|
1354
|
+
createDataDetail,
|
|
1355
|
+
requestParams,
|
|
1356
|
+
objInstanceFull
|
|
392
1357
|
)
|
|
393
1358
|
);
|
|
394
|
-
_izContext.logger.debug(
|
|
395
|
-
'objInstanceFull before send to external topic',
|
|
396
|
-
objInstanceFullForExternalTopic
|
|
397
|
-
);
|
|
398
1359
|
|
|
399
1360
|
allAwaitingStepsId.push({
|
|
400
1361
|
awaitingStepId: await asyncFlowSharedLib.createAwaitingStepId(
|
|
@@ -416,9 +1377,8 @@ export async function <%- flowTag %>LogicalLib(
|
|
|
416
1377
|
}
|
|
417
1378
|
}
|
|
418
1379
|
|
|
419
|
-
//
|
|
1380
|
+
// 4. Create awaiting multiple steps
|
|
420
1381
|
if (allAwaitingStepsId.length > 0) {
|
|
421
|
-
_izContext.logger.debug('not api will create awaitingStep');
|
|
422
1382
|
await asyncFlowSharedLib.createAwaitingMultipleSteps(
|
|
423
1383
|
_izContext,
|
|
424
1384
|
asyncFlowSharedLib.createPendingStepId(
|
|
@@ -434,155 +1394,57 @@ export async function <%- flowTag %>LogicalLib(
|
|
|
434
1394
|
);
|
|
435
1395
|
}
|
|
436
1396
|
|
|
437
|
-
//
|
|
438
|
-
let messageObject = {};
|
|
1397
|
+
// 5. Dispatch to Graph Service
|
|
439
1398
|
for (const objectCreate of listOfObjectForCreates) {
|
|
440
|
-
for (const [graphServiceName, objectForCreate] of Object.entries(
|
|
441
|
-
|
|
442
|
-
)) {
|
|
443
|
-
_izContext.logger.debug('Loop each graph:', {
|
|
444
|
-
[graphServiceName]: objectForCreate
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
if (!objectForCreate.allStorageTagComplete) {
|
|
1399
|
+
for (const [graphServiceName, objectForCreate] of Object.entries(objectCreate)) {
|
|
1400
|
+
if (!objectForCreate.allStorageTagComplete && errorsFound.length === 0) {
|
|
448
1401
|
//(<beforeSendMessageToGraph>)
|
|
449
1402
|
//(</beforeSendMessageToGraph>)
|
|
450
|
-
|
|
1403
|
+
|
|
1404
|
+
await dispatchToGraphEngine(_izContext, {
|
|
1405
|
+
objType,
|
|
1406
|
+
objectForCreate,
|
|
1407
|
+
graphServiceName,
|
|
1408
|
+
requestParams,
|
|
1409
|
+
parentFlowId,
|
|
1410
|
+
callingFlowConfig,
|
|
1411
|
+
phaseResults
|
|
1412
|
+
});
|
|
1413
|
+
|
|
1414
|
+
//(<afterSendMessageToGraph>)
|
|
1415
|
+
//(</afterSendMessageToGraph>)
|
|
1416
|
+
|
|
1417
|
+
return {
|
|
451
1418
|
objType,
|
|
452
|
-
objInstanceFull:
|
|
453
|
-
deepPathConditional: requestParams.deepPathConditional || [],
|
|
454
|
-
setFieldConditionals: requestParams.setFieldConditionals || [],
|
|
1419
|
+
objInstanceFull: objInstanceFullForGraph,
|
|
455
1420
|
relationships: requestParams.relationships || [],
|
|
456
|
-
|
|
1421
|
+
targetIdentifiers: requestParams.targetIdentifiers,
|
|
1422
|
+
status: 'complete',
|
|
1423
|
+
errorsFound
|
|
457
1424
|
};
|
|
458
1425
|
}
|
|
459
|
-
|
|
460
|
-
if (errorsFound.length > 0) break;
|
|
461
|
-
|
|
462
|
-
Object.assign(messageObject, {
|
|
463
|
-
settings: { updatePropertiesOnMatch: false },
|
|
464
|
-
originTimestamp: Date.now()
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
messageObject =
|
|
468
|
-
callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject(
|
|
469
|
-
messageObject,
|
|
470
|
-
callingFlowSharedLib.addParentCallingFlowConfig(
|
|
471
|
-
callingFlowConfig,
|
|
472
|
-
callingFlowSharedLib.createCallingFlowConfig(
|
|
473
|
-
//(<callingFlowProperties>)
|
|
474
|
-
await lambdaSharedLib.lambdaFunctionName(
|
|
475
|
-
_izContext,
|
|
476
|
-
TOPIC_NAME_GENERATE_CODE.createNodeComplete
|
|
477
|
-
),
|
|
478
|
-
{}
|
|
479
|
-
//(</callingFlowProperties>)
|
|
480
|
-
)
|
|
481
|
-
)
|
|
482
|
-
);
|
|
483
|
-
|
|
484
|
-
await sns.publishAsync(_izContext, {
|
|
485
|
-
Message: JSON.stringify(messageObject),
|
|
486
|
-
TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
|
|
487
|
-
_izContext,
|
|
488
|
-
TOPIC_NAME_GRAPH_HANDLER.inCreateNode,
|
|
489
|
-
graphServiceName
|
|
490
|
-
)
|
|
491
|
-
});
|
|
492
|
-
|
|
493
|
-
//(<afterSendMessageToGraph>)
|
|
494
|
-
//(</afterSendMessageToGraph>)
|
|
495
|
-
return {
|
|
496
|
-
objType,
|
|
497
|
-
objInstanceFull: objInstanceFullForGraph,
|
|
498
|
-
relationships: requestParams.relationships || [],
|
|
499
|
-
targetIdentifiers: requestParams.targetIdentifiers,
|
|
500
|
-
status: 'complete',
|
|
501
|
-
errorsFound
|
|
502
|
-
};
|
|
503
1426
|
}
|
|
504
1427
|
if (errorsFound.length > 0) break;
|
|
505
1428
|
}
|
|
506
1429
|
|
|
507
|
-
//
|
|
1430
|
+
// 6. Handle Errors
|
|
508
1431
|
if (errorsFound.length > 0) {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
await asyncFlowSharedLib.clearAllAwaitingSteps(
|
|
512
|
-
_izContext,
|
|
513
|
-
asyncFlowSharedLib.createPendingStepId(
|
|
514
|
-
_izContext.uniqueRequestId,
|
|
515
|
-
PREFIX.CREATE_OBJECT_ASYNC_COMPLETE
|
|
516
|
-
)
|
|
517
|
-
);
|
|
518
|
-
|
|
519
|
-
messageObject =
|
|
520
|
-
callingFlowSharedLib.addPassBackPropertiesToSnsResponseMessageObject(
|
|
521
|
-
callingFlowConfig,
|
|
522
|
-
{ objType, identifiers: null, errorsFound }
|
|
523
|
-
);
|
|
524
|
-
|
|
525
|
-
await sns.publishAsync(_izContext, {
|
|
526
|
-
Message: JSON.stringify(messageObject),
|
|
527
|
-
MessageAttributes: sns.createStringMessageAttributes(
|
|
528
|
-
_izContext,
|
|
529
|
-
callingFlowSharedLib.addCallingFlowToSnsResponseMessageAttributes(
|
|
530
|
-
callingFlowConfig,
|
|
531
|
-
{}
|
|
532
|
-
)
|
|
533
|
-
),
|
|
534
|
-
TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
|
|
535
|
-
_izContext,
|
|
536
|
-
TOPIC_NAME_GENERATE_CODE.outCreateNodeComplete
|
|
537
|
-
)
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
return {
|
|
1432
|
+
return await handleCreateError(_izContext, {
|
|
541
1433
|
objType,
|
|
542
1434
|
objInstanceFull,
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
status: 'error',
|
|
1435
|
+
requestParams,
|
|
1436
|
+
callingFlowConfig,
|
|
546
1437
|
errorsFound
|
|
547
|
-
};
|
|
1438
|
+
});
|
|
548
1439
|
}
|
|
549
1440
|
|
|
550
|
-
//
|
|
551
|
-
if (
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
callingFlowConfig,
|
|
558
|
-
{
|
|
559
|
-
objType: objType,
|
|
560
|
-
objInstanceFull: {
|
|
561
|
-
identifiers: objInstanceFullForDynamoDb.identifiers,
|
|
562
|
-
fields: objInstanceFullForDynamoDb.fields
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
);
|
|
566
|
-
|
|
567
|
-
if (
|
|
568
|
-
callingFlowConfig[callingFlowSharedLib.consts.CALLINGFLOW_PROPERTYNAME]
|
|
569
|
-
) {
|
|
570
|
-
_izContext.logger.debug('HAVE CallingFlow');
|
|
571
|
-
await sns.publishAsync(_izContext, {
|
|
572
|
-
Message: JSON.stringify(outMessage),
|
|
573
|
-
MessageAttributes: sns.createStringMessageAttributes(
|
|
574
|
-
_izContext,
|
|
575
|
-
callingFlowSharedLib.addCallingFlowToSnsResponseMessageAttributes(
|
|
576
|
-
callingFlowConfig,
|
|
577
|
-
{}
|
|
578
|
-
)
|
|
579
|
-
),
|
|
580
|
-
TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
|
|
581
|
-
_izContext,
|
|
582
|
-
TOPIC_NAME_GENERATE_CODE.outCreateNodeComplete
|
|
583
|
-
)
|
|
584
|
-
});
|
|
585
|
-
}
|
|
1441
|
+
// 7. Handle DynamoDB-only Complete Path
|
|
1442
|
+
if (allAwaitingStepsId.length === 0 && listOfObjectForCreates.length === 0) {
|
|
1443
|
+
await handleDynamoDbOnlyComplete(_izContext, {
|
|
1444
|
+
objType,
|
|
1445
|
+
objInstanceFullForDynamoDb,
|
|
1446
|
+
callingFlowConfig
|
|
1447
|
+
});
|
|
586
1448
|
}
|
|
587
1449
|
|
|
588
1450
|
return {
|