@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
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (C) 2021 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 asyncFlowSharedLib from '@izara_project/izara-core-library-asynchronous-flow';
|
|
19
|
+
import callingFlowSharedLib from '@izara_project/izara-core-library-calling-flow';
|
|
20
|
+
import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
|
|
21
|
+
import lambdaSharedLib from '@izara_project/izara-core-library-lambda';
|
|
22
|
+
import snsSharedLib from '@izara_project/izara-core-library-sns';
|
|
23
|
+
import { sns } from '@izara_project/izara-core-library-external-request';
|
|
24
|
+
import { consts } from '@izara_project/izara-core-library-service-schemas';
|
|
25
|
+
import {
|
|
26
|
+
consts as coreConsts,
|
|
27
|
+
NoRetryError
|
|
28
|
+
} from '@izara_project/izara-core-library-core';
|
|
29
|
+
|
|
30
|
+
import {
|
|
31
|
+
saveDynamoDbRecords,
|
|
32
|
+
autoProvisionOwnerTargetRole,
|
|
33
|
+
resolveQuotaDeductionKeys,
|
|
34
|
+
forwardToQuotaConfirmation
|
|
35
|
+
} from './CreateLogicalLib.js';
|
|
36
|
+
|
|
37
|
+
//(<optionalRequire>)
|
|
38
|
+
//(</optionalRequire>)
|
|
39
|
+
|
|
40
|
+
const PREFIX = { ASYNC: 'CreateAsyncEndpoint' };
|
|
41
|
+
|
|
42
|
+
function empty(value) {
|
|
43
|
+
return (
|
|
44
|
+
value == null ||
|
|
45
|
+
(typeof value === 'object' && Object.keys(value).length === 0)
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function validateInput(returnValue, status, graphServiceTag, errorsFound) {
|
|
50
|
+
if (!returnValue?.queryResult)
|
|
51
|
+
errorsFound.push(
|
|
52
|
+
'message is not have property queryResult, should be not happen'
|
|
53
|
+
);
|
|
54
|
+
if (!returnValue?.requestParams?.objType)
|
|
55
|
+
errorsFound.push(
|
|
56
|
+
'message is not have property objType, should be not happen'
|
|
57
|
+
);
|
|
58
|
+
if (!returnValue?.requestParams?.objInstanceFull)
|
|
59
|
+
errorsFound.push(
|
|
60
|
+
'message is not have property objInstance, should be not objInstance'
|
|
61
|
+
);
|
|
62
|
+
if (status === 'error' && errorsFound.length === 0)
|
|
63
|
+
errorsFound.push(
|
|
64
|
+
'[invalid]status is error not have errorsFound form graphService'
|
|
65
|
+
);
|
|
66
|
+
if (status === 'complete' && errorsFound.length > 0)
|
|
67
|
+
errorsFound.push(
|
|
68
|
+
'[Invalid] status is complete should be not have errorsFound'
|
|
69
|
+
);
|
|
70
|
+
if (graphServiceTag == null)
|
|
71
|
+
errorsFound.push('[Invalid] graphServerTag is not empty string');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Handles callback after Graph creation node completion
|
|
76
|
+
* @param {Object} _izContext - Izara context object
|
|
77
|
+
* @param {Object} requestParams - Callback request parameters
|
|
78
|
+
* @param {Object} passBackProperties - Pass-back properties
|
|
79
|
+
* @returns {Promise<Object>} Result object
|
|
80
|
+
*/
|
|
81
|
+
export default async function CreateNodeCallbackMain(
|
|
82
|
+
_izContext,
|
|
83
|
+
requestParams,
|
|
84
|
+
passBackProperties
|
|
85
|
+
//(<additionalParams>)
|
|
86
|
+
//(</additionalParams>)
|
|
87
|
+
) {
|
|
88
|
+
try {
|
|
89
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:CreateNodeCallback] started`, {
|
|
90
|
+
requestParams,
|
|
91
|
+
passBackProperties
|
|
92
|
+
});
|
|
93
|
+
const {
|
|
94
|
+
returnValue,
|
|
95
|
+
status,
|
|
96
|
+
errorsFound = [],
|
|
97
|
+
graphServiceTag,
|
|
98
|
+
parentFlowId
|
|
99
|
+
} = requestParams;
|
|
100
|
+
if (empty(returnValue?.requestParams))
|
|
101
|
+
throw new NoRetryError('message not an object');
|
|
102
|
+
validateInput(returnValue, status, graphServiceTag, errorsFound);
|
|
103
|
+
if (errorsFound.length > 0) errorsFound.push(errorsFound.join(','));
|
|
104
|
+
|
|
105
|
+
const awaitingStepId = await asyncFlowSharedLib.createAwaitingStepId(
|
|
106
|
+
parentFlowId,
|
|
107
|
+
PREFIX.ASYNC
|
|
108
|
+
);
|
|
109
|
+
const pending =
|
|
110
|
+
await asyncFlowSharedLib.findPendingStepAwaitingMultipleSteps(
|
|
111
|
+
_izContext,
|
|
112
|
+
awaitingStepId
|
|
113
|
+
);
|
|
114
|
+
_izContext.logger.debug('[CreateNodeCallback] awaiting step', {
|
|
115
|
+
awaitingStepId,
|
|
116
|
+
pending
|
|
117
|
+
});
|
|
118
|
+
if (!pending) {
|
|
119
|
+
_izContext.logger.warn(
|
|
120
|
+
`No pending step found for awaitingStepId: ${awaitingStepId}`
|
|
121
|
+
);
|
|
122
|
+
return { returnValue, status, errorsFound };
|
|
123
|
+
}
|
|
124
|
+
const result =
|
|
125
|
+
await asyncFlowSharedLib.checkAllAwaitingStepsFinishedWithReturnParams(
|
|
126
|
+
_izContext,
|
|
127
|
+
pending.pendingStepId,
|
|
128
|
+
awaitingStepId,
|
|
129
|
+
errorsFound
|
|
130
|
+
);
|
|
131
|
+
_izContext.logger.debug(
|
|
132
|
+
'[CreateNodeCallback] all awaiting steps result',
|
|
133
|
+
{
|
|
134
|
+
isComplete: result.isComplete,
|
|
135
|
+
awaitingStepId,
|
|
136
|
+
pendingStepId: pending.pendingStepId
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
if (result.isComplete) {
|
|
141
|
+
const nodeResultKey = `returnCreatedNode`;
|
|
142
|
+
const fallbackNodeResultKey = `returnCreateNode`;
|
|
143
|
+
const nodeResult =
|
|
144
|
+
returnValue.queryResult[nodeResultKey] ??
|
|
145
|
+
returnValue.queryResult[fallbackNodeResultKey] ??
|
|
146
|
+
{};
|
|
147
|
+
const node = nodeResult.createNodeResult ?? nodeResult;
|
|
148
|
+
const existsNode =
|
|
149
|
+
node.existsNode && Object.keys(node.existsNode).length
|
|
150
|
+
? {
|
|
151
|
+
objType: returnValue.requestParams.objType,
|
|
152
|
+
objInstanceFull: node.existsNode.objInstanceFull
|
|
153
|
+
}
|
|
154
|
+
: null;
|
|
155
|
+
|
|
156
|
+
// ── Step 1: Persist DynamoDB records ──────────────────────────────────
|
|
157
|
+
if (status === 'complete' && errorsFound.length === 0) {
|
|
158
|
+
const listOfRecords = result?.additionalAttributes?.listOfRecords ?? [];
|
|
159
|
+
await saveDynamoDbRecords(_izContext, listOfRecords);
|
|
160
|
+
|
|
161
|
+
// ── Step 2: Auto-provision TargetRole (Owner) after DynamoDB write ─
|
|
162
|
+
const submittedByUserId =
|
|
163
|
+
returnValue?.requestParams?.submittedByUserId ||
|
|
164
|
+
node?.objInstanceFull?.identifiers?.submittedByUserId ||
|
|
165
|
+
requestParams?.passBackProperties?.submittedByUserId ||
|
|
166
|
+
passBackProperties?.passBackProperties?.submittedByUserId ||
|
|
167
|
+
_izContext.correlationIds?.get(coreConsts.SUBMITTED_BY_USER_ID) ||
|
|
168
|
+
_izContext.correlationIds?.get('submittedByUserId');
|
|
169
|
+
|
|
170
|
+
await autoProvisionOwnerTargetRole(_izContext, {
|
|
171
|
+
node,
|
|
172
|
+
submittedByUserId
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const callingFlowConfig =
|
|
177
|
+
result?.additionalAttributes?.callingFlowConfig ||
|
|
178
|
+
requestParams?.passBackProperties?.parentPassBackProperties ||
|
|
179
|
+
passBackProperties?.passBackProperties?.parentPassBackProperties ||
|
|
180
|
+
passBackProperties;
|
|
181
|
+
|
|
182
|
+
const actionData = {
|
|
183
|
+
objType: returnValue.requestParams.objType,
|
|
184
|
+
objInstanceFull: node.objInstanceFull,
|
|
185
|
+
relationships: node.relationships,
|
|
186
|
+
parentFlowId: parentFlowId || returnValue?.parentFlowId,
|
|
187
|
+
existsNode,
|
|
188
|
+
status,
|
|
189
|
+
errorsFound
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// ── Step 3: Resolve Quota deduction keys and forward confirmation ─────
|
|
193
|
+
const quotaKeys = resolveQuotaDeductionKeys(
|
|
194
|
+
requestParams,
|
|
195
|
+
passBackProperties,
|
|
196
|
+
returnValue,
|
|
197
|
+
callingFlowConfig,
|
|
198
|
+
_izContext
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
await forwardToQuotaConfirmation(_izContext, {
|
|
202
|
+
status,
|
|
203
|
+
errorsFound,
|
|
204
|
+
quotaKeys,
|
|
205
|
+
actionData,
|
|
206
|
+
callingFlowConfig
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
await asyncFlowSharedLib.removeAwaitingMultipleStep(
|
|
211
|
+
_izContext,
|
|
212
|
+
awaitingStepId,
|
|
213
|
+
pending.pendingStepId,
|
|
214
|
+
errorsFound
|
|
215
|
+
);
|
|
216
|
+
return { returnValue, status, errorsFound };
|
|
217
|
+
} catch (error) {
|
|
218
|
+
_izContext.logger.error('[CreateNodeCallback] error:', error);
|
|
219
|
+
throw error;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -85,6 +85,7 @@ export default async function <%- flowTag %>(
|
|
|
85
85
|
_izContext.correlationIds.get(coreConsts.SUBMITTED_BY_USER_ID);
|
|
86
86
|
|
|
87
87
|
targetIdentifiers = requestParams.targetIdentifiers;
|
|
88
|
+
delete requestParams.targetIdentifiers;
|
|
88
89
|
|
|
89
90
|
if (!targetIdentifiers && objectSchema.belongTo?.objectType === 'user' && submittedByUserId) {
|
|
90
91
|
targetIdentifiers = { submittedByUserId };
|
|
@@ -99,12 +100,11 @@ export default async function <%- flowTag %>(
|
|
|
99
100
|
if (!targetIdentifiers.submittedByUserId && submittedByUserId) {
|
|
100
101
|
targetIdentifiers.submittedByUserId = submittedByUserId;
|
|
101
102
|
}
|
|
102
|
-
requestParams.targetIdentifiers = targetIdentifiers;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] Identity & Target Context Resolved:`, {
|
|
106
106
|
submittedByUserId: requestParams.submittedByUserId,
|
|
107
|
-
targetIdentifiers
|
|
107
|
+
targetIdentifiers,
|
|
108
108
|
belongTo: objectSchema.belongTo,
|
|
109
109
|
generatedBy: objectSchema.generatedBy
|
|
110
110
|
});
|
|
@@ -116,6 +116,37 @@ export default async function <%- flowTag %>(
|
|
|
116
116
|
// ── Build identifiers used by asynchronous processing ────────────────────
|
|
117
117
|
const identifiers = requestParams.identifiers;
|
|
118
118
|
|
|
119
|
+
if (!objectSchema.hasOwnProperty('belongTo')) {
|
|
120
|
+
if (!targetIdentifiers && identifiers) {
|
|
121
|
+
targetIdentifiers = { ...identifiers };
|
|
122
|
+
} else if (targetIdentifiers && identifiers) {
|
|
123
|
+
for (const [key, value] of Object.entries(identifiers)) {
|
|
124
|
+
if (targetIdentifiers[key] === undefined && value !== undefined) {
|
|
125
|
+
targetIdentifiers[key] = value;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (targetIdentifiers) {
|
|
130
|
+
if (!targetIdentifiers.submittedByUserId && submittedByUserId) {
|
|
131
|
+
targetIdentifiers.submittedByUserId = submittedByUserId;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (targetIdentifiers && identifiers) {
|
|
137
|
+
for (const [key, value] of Object.entries(identifiers)) {
|
|
138
|
+
if (
|
|
139
|
+
targetIdentifiers[key] !== undefined &&
|
|
140
|
+
value !== undefined &&
|
|
141
|
+
targetIdentifiers[key] !== value
|
|
142
|
+
) {
|
|
143
|
+
errorsFound.push(
|
|
144
|
+
`targetIdentifiers.${key} must match identifiers.${key}`
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
119
150
|
if (errorsFound.length > 0)
|
|
120
151
|
return await <%- flowTag %>LogicalLib(
|
|
121
152
|
_izContext,
|
|
@@ -131,8 +162,8 @@ export default async function <%- flowTag %>(
|
|
|
131
162
|
for (const o of rbacObjects) {
|
|
132
163
|
const belongToTargets = o?.belongTo
|
|
133
164
|
? Array.isArray(o.belongTo)
|
|
134
|
-
|
|
135
|
-
|
|
165
|
+
? o.belongTo
|
|
166
|
+
: [o.belongTo]
|
|
136
167
|
: [];
|
|
137
168
|
if (belongToTargets.length > 0 && belongToTargets[0]?.serviceTag) {
|
|
138
169
|
rbacServiceTagMap[o.objectType] = `'${belongToTargets[0].serviceTag}'`;
|
|
@@ -164,6 +195,7 @@ export default async function <%- flowTag %>(
|
|
|
164
195
|
},
|
|
165
196
|
parameterGroups,
|
|
166
197
|
{
|
|
198
|
+
targetIdentifiers,
|
|
167
199
|
rbacObjType: {
|
|
168
200
|
serviceTag: <%- rbacServiceTags.length === 1 ? rbacServiceTags[0] : `{ ${Object.entries(rbacServiceTagMap).map(([k, v]) => `'${k}': ${v}`).join(', ')} }[requestParams.objectType] || process.env.iz_serviceTag` %>
|
|
169
201
|
},
|
|
@@ -30,7 +30,11 @@ import { sns } from '@izara_project/izara-core-library-external-request';
|
|
|
30
30
|
|
|
31
31
|
const { createObjType } = utils;
|
|
32
32
|
|
|
33
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
<%- flowTag %>LogicalLib,
|
|
35
|
+
collectRbacAwaitingResults,
|
|
36
|
+
assertRbacPermission
|
|
37
|
+
} from './<%- upperCaseFlowTag %>LogicalLib.js';
|
|
34
38
|
|
|
35
39
|
//(<optionalRequire>)
|
|
36
40
|
//(</optionalRequire>)
|
|
@@ -39,6 +43,7 @@ import { <%- flowTag %>LogicalLib } from './<%- upperCaseFlowTag %>LogicalLib.js
|
|
|
39
43
|
* Handles callback after RbacCallback phase — verifies awaitingStep then proceeds to next phase
|
|
40
44
|
* @param {Object} _izContext - Context object with logger
|
|
41
45
|
* @param {Object} requestParams - { fieldNames, relationships[] }
|
|
46
|
+
* @param {string} flowTag - Flow tag name
|
|
42
47
|
* @param {Object} callingFlowConfig - Calling flow configuration
|
|
43
48
|
* @returns {Promise<Object>}
|
|
44
49
|
*/
|
|
@@ -49,149 +54,44 @@ export default async function <%- flowTag %>RbacCallbackMain(
|
|
|
49
54
|
callingFlowConfig = {}
|
|
50
55
|
) {
|
|
51
56
|
try {
|
|
52
|
-
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>RbacCallback] Started with params:`,
|
|
57
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>RbacCallback] Started with params:`, {
|
|
53
58
|
requestParams,
|
|
54
59
|
flowTag,
|
|
55
60
|
callingFlowConfig
|
|
56
61
|
});
|
|
57
62
|
|
|
58
|
-
|
|
59
|
-
const rbacType = parts.pop();
|
|
60
|
-
const rbacPendingStepId = '<%- flowTag %>RbacCallback_' + parts.join('_');
|
|
61
|
-
const currentAwaitingStepId = requestParams.parentFlowId;
|
|
62
|
-
|
|
63
|
-
_izContext.logger.debug(
|
|
64
|
-
'[<%- flowTag %>RbacCallbackMain] Parsed RBAC pending details:',
|
|
65
|
-
{
|
|
66
|
-
rbacType,
|
|
67
|
-
rbacPendingStepId,
|
|
68
|
-
currentAwaitingStepId,
|
|
69
|
-
parentFlowId: requestParams.parentFlowId
|
|
70
|
-
}
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
const rbacAwaitingStepsResult =
|
|
74
|
-
await asyncFlowSharedLib.checkAllAwaitingStepsFinishedWithReturnParams(
|
|
75
|
-
_izContext,
|
|
76
|
-
rbacPendingStepId,
|
|
77
|
-
currentAwaitingStepId,
|
|
78
|
-
requestParams.errorsFound,
|
|
79
|
-
{
|
|
80
|
-
returnValue: requestParams.returnValue,
|
|
81
|
-
rbacType
|
|
82
|
-
}
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
_izContext.logger.debug(
|
|
86
|
-
'[<%- flowTag %>RbacCallbackMain] checkAllAwaitingStepsFinishedWithReturnParams result:',
|
|
87
|
-
{
|
|
88
|
-
rbacAwaitingStepsResult
|
|
89
|
-
}
|
|
90
|
-
);
|
|
91
|
-
|
|
63
|
+
// 1. Wait until all awaiting steps (App/Target RBAC) are complete
|
|
92
64
|
const {
|
|
93
65
|
isComplete,
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
} =
|
|
66
|
+
rbacResults,
|
|
67
|
+
originalRequestParams,
|
|
68
|
+
collectedErrors = []
|
|
69
|
+
} = await collectRbacAwaitingResults(_izContext, requestParams);
|
|
98
70
|
|
|
99
71
|
if (isComplete === false) {
|
|
100
|
-
_izContext.logger.debug(
|
|
101
|
-
'[<%- flowTag %>RbacCallbackMain] Awaiting steps still pending completion:',
|
|
102
|
-
{
|
|
103
|
-
rbacPendingStepId,
|
|
104
|
-
isComplete
|
|
105
|
-
}
|
|
106
|
-
);
|
|
107
|
-
|
|
108
72
|
return 'all task not completed';
|
|
109
73
|
}
|
|
110
74
|
|
|
111
|
-
|
|
112
|
-
'[<%- flowTag %>RbacCallbackMain] All awaiting steps completed — clearing awaiting steps:',
|
|
113
|
-
{
|
|
114
|
-
rbacPendingStepId
|
|
115
|
-
}
|
|
116
|
-
);
|
|
75
|
+
const errorsFound = [...collectedErrors];
|
|
117
76
|
|
|
118
|
-
|
|
77
|
+
// 2. Validate RBAC permission results
|
|
78
|
+
const { hasError, errorResult, rbacCallbackResults } = await assertRbacPermission(
|
|
119
79
|
_izContext,
|
|
120
|
-
rbacPendingStepId
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
const originalRequestParams = additionalAttributes?.requestParams || {};
|
|
124
|
-
|
|
125
|
-
_izContext.logger.debug(
|
|
126
|
-
'[<%- flowTag %>RbacCallbackMain] Retrieved originalRequestParams:',
|
|
127
80
|
{
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
const rbacResults = Object.fromEntries(
|
|
133
|
-
Object.entries(collectedReturnValueByAwaitingStepId || {}).map(
|
|
134
|
-
([stepId, attrs]) => {
|
|
135
|
-
const type = stepId.split('_').pop();
|
|
136
|
-
return [type, attrs];
|
|
137
|
-
}
|
|
138
|
-
)
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
const rbacCallbackResults = {};
|
|
142
|
-
const hasError = Object.entries(rbacResults).some(([type, result]) => {
|
|
143
|
-
if (result && result.error) {
|
|
144
|
-
errorsFound.push(result.error);
|
|
145
|
-
return true;
|
|
146
|
-
}
|
|
147
|
-
rbacCallbackResults[type] = result;
|
|
148
|
-
return false;
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
const isAllowed = Object.values(rbacCallbackResults).some(
|
|
152
|
-
item => item?.returnValue?.permission === true
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
if (!isAllowed) {
|
|
156
|
-
errorsFound.push('Permission denied');
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
_izContext.logger.debug(
|
|
160
|
-
'[<%- flowTag %>RbacCallbackMain] Final rbacCallbackResults before calling <%- flowTag %>MainLogical:',
|
|
161
|
-
{
|
|
162
|
-
rbacCallbackResults,
|
|
163
|
-
isAllowed,
|
|
81
|
+
rbacResults,
|
|
82
|
+
originalRequestParams,
|
|
164
83
|
errorsFound
|
|
165
84
|
}
|
|
166
85
|
);
|
|
167
86
|
|
|
168
|
-
if (hasError
|
|
169
|
-
const errorResult = {
|
|
170
|
-
errorsFound,
|
|
171
|
-
status: 'rejected',
|
|
172
|
-
requestParams: originalRequestParams
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
_izContext.logger.debug(
|
|
176
|
-
'[<%- flowTag %>RbacCallbackMain] RBAC check failed, publishing rejected status:',
|
|
177
|
-
errorResult
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
await sns.publishAsync(_izContext, {
|
|
181
|
-
TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
|
|
182
|
-
_izContext,
|
|
183
|
-
'<%- upperCaseFlowTag %>_Out',
|
|
184
|
-
process.env.iz_serviceTag
|
|
185
|
-
),
|
|
186
|
-
Message: JSON.stringify(errorResult)
|
|
187
|
-
});
|
|
188
|
-
|
|
87
|
+
if (hasError) {
|
|
189
88
|
return errorResult;
|
|
190
89
|
}
|
|
191
90
|
|
|
192
91
|
//(<rbacCallbackMain>)
|
|
193
92
|
//(</rbacCallbackMain>)
|
|
194
93
|
|
|
94
|
+
// 3. Permission granted -> Proceed with core logical processing
|
|
195
95
|
const logicalResult = await <%- flowTag %>LogicalLib(
|
|
196
96
|
_izContext,
|
|
197
97
|
originalRequestParams,
|
|
@@ -209,7 +109,7 @@ export default async function <%- flowTag %>RbacCallbackMain(
|
|
|
209
109
|
|
|
210
110
|
return logicalResult;
|
|
211
111
|
} catch (err) {
|
|
212
|
-
_izContext.logger.error(`[${process.env.iz_serviceTag}:<%- flowTag %>RbacCallback] Error caught:`,
|
|
112
|
+
_izContext.logger.error(`[${process.env.iz_serviceTag}:<%- flowTag %>RbacCallback] Error caught:`, {
|
|
213
113
|
error: err
|
|
214
114
|
});
|
|
215
115
|
throw err;
|
|
@@ -39,7 +39,7 @@ import <%- typeof functionName !== 'undefined' ? functionName : upperCaseFlowTag
|
|
|
39
39
|
//(</optionalRequire>)
|
|
40
40
|
|
|
41
41
|
export const main = middleware.wrap(async (event, context) => {
|
|
42
|
-
event._izContext.logger.debug('
|
|
42
|
+
event._izContext.logger.debug('[FlowEndpoint_HdrWbs] WebSocket event received', { event });
|
|
43
43
|
|
|
44
44
|
try {
|
|
45
45
|
if (event.requestContext) {
|
|
@@ -49,12 +49,21 @@ export const main = middleware.wrap(async (event, context) => {
|
|
|
49
49
|
consts.X_CORRELATION_ID
|
|
50
50
|
);
|
|
51
51
|
|
|
52
|
+
event._izContext.logger.debug('[FlowEndpoint_HdrWbs] request context info', {
|
|
53
|
+
route,
|
|
54
|
+
connectionId,
|
|
55
|
+
correlationId
|
|
56
|
+
});
|
|
57
|
+
|
|
52
58
|
switch (route) {
|
|
53
59
|
case '$default':
|
|
54
|
-
|
|
60
|
+
event._izContext.logger.debug('[FlowEndpoint_HdrWbs] Received unknown route', { route });
|
|
55
61
|
break;
|
|
56
62
|
case '<%- flowTag %>':
|
|
57
|
-
|
|
63
|
+
event._izContext.logger.debug('[FlowEndpoint_HdrWbs] matched route <%- flowTag %>', {
|
|
64
|
+
route,
|
|
65
|
+
body: event.body
|
|
66
|
+
});
|
|
58
67
|
let eventParams = JSON.parse(event.body);
|
|
59
68
|
|
|
60
69
|
//(<createIdentifiersFlow>)
|
|
@@ -69,11 +78,22 @@ export const main = middleware.wrap(async (event, context) => {
|
|
|
69
78
|
}
|
|
70
79
|
);
|
|
71
80
|
|
|
81
|
+
event._izContext.logger.debug('[FlowEndpoint_HdrWbs] flow schema resolved', {
|
|
82
|
+
flowTag: '<%- flowTag %>',
|
|
83
|
+
statusType: flowSchema?.statusType
|
|
84
|
+
});
|
|
85
|
+
|
|
72
86
|
if (
|
|
73
87
|
flowSchema.statusType === 'statusField' ||
|
|
74
88
|
flowSchema.statusType === 'storedCache' ||
|
|
75
89
|
flowSchema.statusType === 'triggerCache'
|
|
76
90
|
) {
|
|
91
|
+
const taskKey = `${createFlowTypeConcat(event._izContext, { flowTag: '<%- flowTag %>', serviceTag: process.env.iz_serviceTag })}${identifiers}`;
|
|
92
|
+
event._izContext.logger.debug('[FlowEndpoint_HdrWbs] registering WebSocketTask with ttl', {
|
|
93
|
+
taskKey,
|
|
94
|
+
connectionId,
|
|
95
|
+
ttl: 600
|
|
96
|
+
});
|
|
77
97
|
await dynamodbSharedLib.putItem(
|
|
78
98
|
event._izContext,
|
|
79
99
|
await dynamodbSharedLib.tableName(
|
|
@@ -81,12 +101,17 @@ export const main = middleware.wrap(async (event, context) => {
|
|
|
81
101
|
'WebSocketTask'
|
|
82
102
|
),
|
|
83
103
|
{
|
|
84
|
-
taskKey
|
|
104
|
+
taskKey,
|
|
85
105
|
connectionId: connectionId,
|
|
86
106
|
ttl: 600
|
|
87
107
|
}
|
|
88
108
|
);
|
|
89
109
|
} else if (flowSchema.statusType === 'none') {
|
|
110
|
+
const taskKey = `${createFlowTypeConcat(event._izContext, { flowTag: '<%- flowTag %>', serviceTag: process.env.iz_serviceTag })}${correlationId}`;
|
|
111
|
+
event._izContext.logger.debug('[FlowEndpoint_HdrWbs] registering WebSocketTask for status none', {
|
|
112
|
+
taskKey,
|
|
113
|
+
connectionId
|
|
114
|
+
});
|
|
90
115
|
await dynamodbSharedLib.putItem(
|
|
91
116
|
event._izContext,
|
|
92
117
|
await dynamodbSharedLib.tableName(
|
|
@@ -94,22 +119,52 @@ export const main = middleware.wrap(async (event, context) => {
|
|
|
94
119
|
'WebSocketTask'
|
|
95
120
|
),
|
|
96
121
|
{
|
|
97
|
-
taskKey
|
|
122
|
+
taskKey,
|
|
98
123
|
connectionId: connectionId
|
|
99
124
|
}
|
|
100
125
|
);
|
|
101
126
|
}
|
|
102
127
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
128
|
+
const authorizerUserId =
|
|
129
|
+
event.requestContext?.authorizer?.submittedByUserId ||
|
|
130
|
+
event.requestContext?.authorizer?.principalId;
|
|
131
|
+
const resolvedUserId =
|
|
132
|
+
authorizerUserId ||
|
|
133
|
+
eventParams.submittedByUserId ||
|
|
134
|
+
event._izContext?.correlationIds?.get(consts.SUBMITTED_BY_USER_ID);
|
|
135
|
+
|
|
136
|
+
if (resolvedUserId) {
|
|
137
|
+
if (event._izContext?.correlationIds) {
|
|
138
|
+
event._izContext.correlationIds.set(
|
|
139
|
+
consts.SUBMITTED_BY_USER_ID,
|
|
140
|
+
resolvedUserId
|
|
141
|
+
);
|
|
107
142
|
}
|
|
143
|
+
if (
|
|
144
|
+
!eventParams.targetIdentifiers ||
|
|
145
|
+
typeof eventParams.targetIdentifiers !== 'object'
|
|
146
|
+
) {
|
|
147
|
+
eventParams.targetIdentifiers = {};
|
|
148
|
+
}
|
|
149
|
+
eventParams.targetIdentifiers.submittedByUserId = resolvedUserId;
|
|
108
150
|
}
|
|
109
151
|
|
|
152
|
+
event._izContext.logger.debug('[FlowEndpoint_HdrWbs] resolved user and target identifiers', {
|
|
153
|
+
authorizerUserId,
|
|
154
|
+
resolvedUserId,
|
|
155
|
+
targetIdentifiers: eventParams.targetIdentifiers
|
|
156
|
+
});
|
|
157
|
+
|
|
110
158
|
let callingFlow = eventParams?.callingFlow
|
|
111
159
|
? eventParams.callingFlow
|
|
112
160
|
: {};
|
|
161
|
+
|
|
162
|
+
event._izContext.logger.debug('[FlowEndpoint_HdrWbs] invoking main function', {
|
|
163
|
+
flowTag: '<%- flowTag %>',
|
|
164
|
+
eventParams,
|
|
165
|
+
callingFlow
|
|
166
|
+
});
|
|
167
|
+
|
|
113
168
|
await <%- typeof functionName !== 'undefined' ? functionName : upperCaseFlowTag %>(
|
|
114
169
|
event._izContext,
|
|
115
170
|
eventParams,
|
|
@@ -122,16 +177,23 @@ export const main = middleware.wrap(async (event, context) => {
|
|
|
122
177
|
}
|
|
123
178
|
} else {
|
|
124
179
|
const connId = event.connId;
|
|
125
|
-
|
|
180
|
+
event._izContext.logger.debug('[FlowEndpoint_HdrWbs] direct postToConnection', {
|
|
181
|
+
connId,
|
|
182
|
+
message: event.message
|
|
183
|
+
});
|
|
126
184
|
await postToConnection(event._izContext, { message: event.message }, connId);
|
|
127
185
|
}
|
|
128
186
|
|
|
129
187
|
return response.webSocketSuccess();
|
|
130
188
|
} catch (err) {
|
|
131
|
-
const connId = event.requestContext
|
|
132
|
-
event._izContext.logger.error('Error, WebSocket: ',
|
|
133
|
-
|
|
134
|
-
|
|
189
|
+
const connId = event.requestContext?.connectionId;
|
|
190
|
+
event._izContext.logger.error('[FlowEndpoint_HdrWbs] Error, WebSocket: ', {
|
|
191
|
+
error: err,
|
|
192
|
+
connectionId: connId
|
|
193
|
+
});
|
|
194
|
+
if (connId) {
|
|
195
|
+
await postToConnection(event._izContext, { message: err.message }, connId);
|
|
196
|
+
}
|
|
135
197
|
return response.failure(err);
|
|
136
198
|
}
|
|
137
199
|
});
|