@izara_project/izara-core-generate-service-code 1.0.83 → 1.0.84
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 +4 -4
- package/src/compile/flowValidator.js +13 -2
- package/src/compile/loadSchemas.js +116 -7
- package/src/emit/attributeTree/emitAttributeTreeSchemas.js +388 -0
- package/src/emit/coreFunction/emitCoreFunctionArtifacts.js +29 -1
- package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +16 -3
- package/src/emit/crud/crudActionPlan.js +38 -5
- package/src/emit/crud/emitCrudApplicationArtifacts.js +19 -4
- package/src/emit/crud/emitDynamicCrudFlowSchemas.js +27 -6
- package/src/emit/crud/generateCrudApplicationArtifacts.js +3 -2
- package/src/emit/crud/generateDynamicCrudFlowSchemas.js +3 -2
- package/src/emit/crud/loadLocalSchemaArrays.js +3 -3
- package/src/emit/crud/templates/CreateComplete_Main.ejs +1 -1
- package/src/emit/crud/templates/CreateEndpoint_Main.ejs +50 -19
- package/src/emit/crud/templates/CreateLogicalLib.ejs +73 -8
- package/src/emit/crud/templates/DeleteComplete_Main.ejs +1 -1
- package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +33 -8
- package/src/emit/crud/templates/DeleteLogicalLib.ejs +6 -3
- package/src/emit/crud/templates/FlowEndpointRbacCallback_Main.ejs +2 -2
- package/src/emit/crud/templates/FlowEndpoint_HdrWbs.ejs +7 -0
- package/src/emit/crud/templates/FlowMain_Wbs.ejs +1 -1
- package/src/emit/crud/templates/GetEndpoint_Main.ejs +13 -0
- package/src/emit/crud/templates/UpdateComplete_Main.ejs +1 -1
- package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +52 -3
- package/src/emit/crud/templates/UpdateLogicalLib.ejs +4 -3
- package/src/emit/custom/customFlows.js +47 -0
- package/src/emit/custom/emitCustomFlowApplicationArtifacts.js +168 -49
- package/src/emit/custom/generateCustomFlowApplicationArtifacts.js +1 -1
- package/src/emit/custom/templates/FlowEndpointRbacCallback_Main.ejs +194 -0
- package/src/emit/custom/templates/FlowEndpoint_Main.ejs +14 -22
- package/src/emit/custom/templates/FlowStep_Main.ejs +1 -1
- package/src/emit/function/templates/WbsFunction_Yaml.ejs +0 -2
- package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +33 -8
- package/src/emit/rbac/emitRbacApplicationArtifacts.js +204 -7
- package/src/emit/rbac/templates/CancelUsage_Main.ejs +103 -0
- package/src/emit/rbac/templates/ConfirmUsage_Main.ejs +71 -0
- package/src/emit/rbac/templates/CreateTargetRole_Main.ejs +49 -6
- package/src/emit/rbac/templates/ListRbac_Main.ejs +9 -3
- package/src/emit/rbac/templates/RbacMutation_Main.ejs +1 -1
- package/src/emit/rbac/templates/ReservedDynamicUsage_Main.ejs +147 -0
- package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +59 -23
- package/src/emit/rbac/templates/UserRoleMutation_Main.ejs +1 -1
- package/src/emit/relationship/emitDynamicRelationshipFlowSchemas.js +1 -1
- package/src/emit/relationship/templates/ChangeRelationship_Main.ejs +4 -4
- package/src/emit/resource/emitResourceArtifacts.js +342 -108
- package/src/emit/resource/templates/SystemDynamoDB_Yaml.ejs +53 -2
- package/src/emit/system/emitSystemWebSocketArtifacts.js +6 -5
- package/src/emit/system/templates/WebSocketConnect.ejs +71 -12
- package/src/emit/system/templates/WebSocketConnect_Yaml.ejs +7 -0
- package/src/emit/systemText/emitSystemTextSchemas.js +1 -1
- package/src/generate.js +41 -10
- package/test/attributeTreeSchemas.test.js +219 -0
- package/test/createTargetRoleHandler.test.js +59 -4
- package/test/crudApplicationArtifacts.test.js +20 -3
- package/test/customFlowApplicationArtifacts.test.js +5 -5
- package/test/dynamicCrudFlowSchemas.test.js +91 -4
- package/test/dynamicRbacFlowSchemas.test.js +15 -2
- package/test/publicApi.test.js +6 -6
- package/test/resourceArtifacts.test.js +68 -27
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (C) 2020 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 dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
|
|
19
|
+
import snsSharedLib from '@izara_project/izara-core-library-sns';
|
|
20
|
+
import { sns } from '@izara_project/izara-core-library-external-request';
|
|
21
|
+
import { consts, NoRetryError } from '@izara_project/izara-core-library-core';
|
|
22
|
+
|
|
23
|
+
export default async function CancelUsage(_izContext, requestParams) {
|
|
24
|
+
try {
|
|
25
|
+
const submittedByUserId =
|
|
26
|
+
requestParams.submittedByUserId ||
|
|
27
|
+
_izContext.correlationIds?.get(consts.SUBMITTED_BY_USER_ID);
|
|
28
|
+
const { limitTag, uniqueIdentify } = requestParams;
|
|
29
|
+
|
|
30
|
+
const targetIdentifiers = requestParams.targetIdentifiers;
|
|
31
|
+
let identifiersConcat = requestParams.identifiersConcat;
|
|
32
|
+
if (!identifiersConcat && targetIdentifiers && typeof targetIdentifiers === 'object') {
|
|
33
|
+
identifiersConcat = Object.entries(targetIdentifiers)
|
|
34
|
+
.map(([k, v]) => `${k}#${v}`)
|
|
35
|
+
.join('_');
|
|
36
|
+
}
|
|
37
|
+
if (!identifiersConcat) {
|
|
38
|
+
identifiersConcat = submittedByUserId;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!identifiersConcat || !limitTag || !uniqueIdentify) {
|
|
42
|
+
throw new NoRetryError(
|
|
43
|
+
'identifiersConcat, limitTag, and uniqueIdentify are required.'
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const runningTotalReservedId = `${identifiersConcat}_${limitTag}`;
|
|
48
|
+
const reservedRecord = await dynamodbSharedLib.getItem(
|
|
49
|
+
_izContext,
|
|
50
|
+
dynamodbSharedLib.tableName(_izContext, 'RunningTotalReservedUsage'),
|
|
51
|
+
{
|
|
52
|
+
runningTotalReservedId,
|
|
53
|
+
usedTimestampId: uniqueIdentify
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const usageCount = reservedRecord?.usageCount || 0;
|
|
58
|
+
|
|
59
|
+
await dynamodbSharedLib.updateItem(
|
|
60
|
+
_izContext,
|
|
61
|
+
dynamodbSharedLib.tableName(_izContext, 'RunningTotalReservedUsage'),
|
|
62
|
+
{
|
|
63
|
+
runningTotalReservedId,
|
|
64
|
+
usedTimestampId: uniqueIdentify
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
runningTotalUsageStatus: 'cancelled'
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
if (usageCount > 0) {
|
|
72
|
+
const usageRecord = await dynamodbSharedLib.getItem(
|
|
73
|
+
_izContext,
|
|
74
|
+
dynamodbSharedLib.tableName(_izContext, 'RunningTotalUsage'),
|
|
75
|
+
{ identifiersConcat, limitTag }
|
|
76
|
+
);
|
|
77
|
+
if (usageRecord) {
|
|
78
|
+
const newTotal = Math.max(0, (usageRecord.totalCount || 0) - usageCount);
|
|
79
|
+
await dynamodbSharedLib.putItem(
|
|
80
|
+
_izContext,
|
|
81
|
+
dynamodbSharedLib.tableName(_izContext, 'RunningTotalUsage'),
|
|
82
|
+
{
|
|
83
|
+
identifiersConcat,
|
|
84
|
+
limitTag,
|
|
85
|
+
totalCount: newTotal,
|
|
86
|
+
updateUuid: _izContext.uniqueRequestId || `${Date.now()}`
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
await sns.publishMessage(_izContext, {
|
|
93
|
+
message: { returnResult: { status: true, uniqueIdentify } },
|
|
94
|
+
TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
|
|
95
|
+
_izContext,
|
|
96
|
+
'CancelUsage_Out'
|
|
97
|
+
)
|
|
98
|
+
});
|
|
99
|
+
} catch (error) {
|
|
100
|
+
_izContext.logger.error(`[${process.env.iz_serviceTag}:CancelUsage] error:`, error);
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (C) 2020 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 dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
|
|
19
|
+
import snsSharedLib from '@izara_project/izara-core-library-sns';
|
|
20
|
+
import { sns } from '@izara_project/izara-core-library-external-request';
|
|
21
|
+
import { consts, NoRetryError } from '@izara_project/izara-core-library-core';
|
|
22
|
+
|
|
23
|
+
export default async function ConfirmUsage(_izContext, requestParams) {
|
|
24
|
+
try {
|
|
25
|
+
const submittedByUserId =
|
|
26
|
+
requestParams.submittedByUserId ||
|
|
27
|
+
_izContext.correlationIds?.get(consts.SUBMITTED_BY_USER_ID);
|
|
28
|
+
const { limitTag, uniqueIdentify } = requestParams;
|
|
29
|
+
|
|
30
|
+
const targetIdentifiers = requestParams.targetIdentifiers;
|
|
31
|
+
let identifiersConcat = requestParams.identifiersConcat;
|
|
32
|
+
if (!identifiersConcat && targetIdentifiers && typeof targetIdentifiers === 'object') {
|
|
33
|
+
identifiersConcat = Object.entries(targetIdentifiers)
|
|
34
|
+
.map(([k, v]) => `${k}#${v}`)
|
|
35
|
+
.join('_');
|
|
36
|
+
}
|
|
37
|
+
if (!identifiersConcat) {
|
|
38
|
+
identifiersConcat = submittedByUserId;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!identifiersConcat || !limitTag || !uniqueIdentify) {
|
|
42
|
+
throw new NoRetryError(
|
|
43
|
+
'identifiersConcat, limitTag, and uniqueIdentify are required.'
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const runningTotalReservedId = `${identifiersConcat}_${limitTag}`;
|
|
48
|
+
await dynamodbSharedLib.updateItem(
|
|
49
|
+
_izContext,
|
|
50
|
+
dynamodbSharedLib.tableName(_izContext, 'RunningTotalReservedUsage'),
|
|
51
|
+
{
|
|
52
|
+
runningTotalReservedId,
|
|
53
|
+
usedTimestampId: uniqueIdentify
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
runningTotalUsageStatus: 'confirmed'
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
await sns.publishMessage(_izContext, {
|
|
61
|
+
message: { returnResult: { status: true, uniqueIdentify } },
|
|
62
|
+
TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
|
|
63
|
+
_izContext,
|
|
64
|
+
'ConfirmUsage_Out'
|
|
65
|
+
)
|
|
66
|
+
});
|
|
67
|
+
} catch (error) {
|
|
68
|
+
_izContext.logger.error(`[${process.env.iz_serviceTag}:ConfirmUsage] error:`, error);
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -23,23 +23,66 @@ import { consts } from '@izara_project/izara-core-library-core';
|
|
|
23
23
|
|
|
24
24
|
export default async function CreateTargetRole(_izContext, requestParams) {
|
|
25
25
|
try {
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
const submittedByUserId =
|
|
27
|
+
requestParams.submittedByUserId ||
|
|
28
|
+
_izContext.correlationIds?.get(consts.SUBMITTED_BY_USER_ID);
|
|
29
|
+
|
|
30
|
+
const targetIdentifiers = requestParams.targetIdentifiers;
|
|
31
|
+
let identifiersConcat = requestParams.identifiersConcat;
|
|
32
|
+
if (!identifiersConcat && targetIdentifiers && typeof targetIdentifiers === 'object') {
|
|
33
|
+
identifiersConcat = Object.entries(targetIdentifiers)
|
|
34
|
+
.map(([k, v]) => `${k}#${v}`)
|
|
35
|
+
.join('_');
|
|
36
|
+
}
|
|
37
|
+
if (!identifiersConcat) {
|
|
38
|
+
identifiersConcat = submittedByUserId;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const { roleName = 'owner', roleId: inputRoleId } = requestParams;
|
|
42
|
+
const roleId = inputRoleId || roleName || identifierUuid();
|
|
43
|
+
const roleIdKey = `${identifiersConcat}_${roleId}`;
|
|
44
|
+
|
|
29
45
|
await dynamodbSharedLib.putItem(
|
|
30
46
|
_izContext,
|
|
31
47
|
dynamodbSharedLib.tableName(_izContext, 'TargetRoles'),
|
|
32
|
-
{
|
|
48
|
+
{
|
|
49
|
+
identifiersConcat,
|
|
50
|
+
roleIdKey,
|
|
51
|
+
roleName,
|
|
52
|
+
submittedByUserId
|
|
53
|
+
}
|
|
33
54
|
);
|
|
55
|
+
|
|
56
|
+
if (submittedByUserId) {
|
|
57
|
+
try {
|
|
58
|
+
await dynamodbSharedLib.transactWriteItems(_izContext, [
|
|
59
|
+
{
|
|
60
|
+
Put: {
|
|
61
|
+
TableName: dynamodbSharedLib.tableName(_izContext, 'RoleUsers'),
|
|
62
|
+
Item: { roleIdKey, submittedByUserId }
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
Put: {
|
|
67
|
+
TableName: dynamodbSharedLib.tableName(_izContext, 'UserRoles'),
|
|
68
|
+
Item: { submittedByUserId, roleIdKey }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
]);
|
|
72
|
+
} catch (assignError) {
|
|
73
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:CreateTargetRole] UserRoles transactWrite ignored:', assignError);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
34
77
|
await sns.publishMessage(_izContext, {
|
|
35
|
-
message: { returnResult: { roleId } },
|
|
78
|
+
message: { returnResult: { roleId, roleIdKey } },
|
|
36
79
|
TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
|
|
37
80
|
_izContext,
|
|
38
81
|
'CreateTargetRole_Out'
|
|
39
82
|
)
|
|
40
83
|
});
|
|
41
84
|
} catch (error) {
|
|
42
|
-
_izContext.logger.error(
|
|
85
|
+
_izContext.logger.error(`[${process.env.iz_serviceTag}:CreateTargetRole] error:`, error);
|
|
43
86
|
throw error;
|
|
44
87
|
}
|
|
45
88
|
}
|
|
@@ -22,8 +22,14 @@ import { consts, NoRetryError } from '@izara_project/izara-core-library-core';
|
|
|
22
22
|
|
|
23
23
|
export default async function <%- flowTag %>(_izContext, requestParams) {
|
|
24
24
|
try {
|
|
25
|
-
<% if (
|
|
26
|
-
const userId = _izContext.correlationIds.get(consts.
|
|
25
|
+
<% if (flowTag === 'ListTargetRole') { -%>
|
|
26
|
+
const userId = requestParams.submittedByUserId || _izContext.correlationIds.get(consts.SUBMITTED_BY_USER_ID);
|
|
27
|
+
const identifiersConcat =
|
|
28
|
+
requestParams.identifiersConcat || requestParams.targetId || userId;
|
|
29
|
+
if (!identifiersConcat) throw new NoRetryError('Identifier not found.');
|
|
30
|
+
const queryParams = { identifiersConcat };
|
|
31
|
+
<% } else if (useContextUserId) { -%>
|
|
32
|
+
const userId = requestParams.submittedByUserId || _izContext.correlationIds.get(consts.SUBMITTED_BY_USER_ID);
|
|
27
33
|
if (!userId) throw new NoRetryError('User Id not found.');
|
|
28
34
|
const queryParams = { userId };
|
|
29
35
|
<% } else { -%>
|
|
@@ -43,7 +49,7 @@ export default async function <%- flowTag %>(_izContext, requestParams) {
|
|
|
43
49
|
)
|
|
44
50
|
});
|
|
45
51
|
} catch (error) {
|
|
46
|
-
_izContext.logger.error(
|
|
52
|
+
_izContext.logger.error(`[${process.env.iz_serviceTag}:<%- flowTag %>] error:`, error);
|
|
47
53
|
throw error;
|
|
48
54
|
}
|
|
49
55
|
}
|
|
@@ -36,7 +36,7 @@ export default async function <%- flowTag %>(_izContext, requestParams) {
|
|
|
36
36
|
)
|
|
37
37
|
});
|
|
38
38
|
} catch (error) {
|
|
39
|
-
_izContext.logger.error(
|
|
39
|
+
_izContext.logger.error(`[${process.env.iz_serviceTag}:<%- flowTag %>] error:`, error);
|
|
40
40
|
throw error;
|
|
41
41
|
}
|
|
42
42
|
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (C) 2020 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 dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
|
|
19
|
+
import snsSharedLib from '@izara_project/izara-core-library-sns';
|
|
20
|
+
import { sns } from '@izara_project/izara-core-library-external-request';
|
|
21
|
+
import { consts, NoRetryError } from '@izara_project/izara-core-library-core';
|
|
22
|
+
|
|
23
|
+
export default async function ReservedDynamicUsage(_izContext, requestParams) {
|
|
24
|
+
try {
|
|
25
|
+
const submittedByUserId =
|
|
26
|
+
requestParams.submittedByUserId ||
|
|
27
|
+
_izContext.correlationIds?.get(consts.SUBMITTED_BY_USER_ID);
|
|
28
|
+
const {
|
|
29
|
+
limitTag,
|
|
30
|
+
value = 1,
|
|
31
|
+
usedTimestamp = new Date().toISOString(),
|
|
32
|
+
roleLimitId
|
|
33
|
+
} = requestParams;
|
|
34
|
+
|
|
35
|
+
const targetIdentifiers = requestParams.targetIdentifiers;
|
|
36
|
+
let identifiersConcat = requestParams.identifiersConcat;
|
|
37
|
+
if (!identifiersConcat && targetIdentifiers && typeof targetIdentifiers === 'object') {
|
|
38
|
+
identifiersConcat = Object.entries(targetIdentifiers)
|
|
39
|
+
.map(([k, v]) => `${k}#${v}`)
|
|
40
|
+
.join('_');
|
|
41
|
+
}
|
|
42
|
+
if (!identifiersConcat) {
|
|
43
|
+
identifiersConcat = submittedByUserId;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!identifiersConcat) {
|
|
47
|
+
throw new NoRetryError('Identifier not found.');
|
|
48
|
+
}
|
|
49
|
+
if (!limitTag) {
|
|
50
|
+
throw new NoRetryError('limitTag is required.');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const uniqueIdentify = `${usedTimestamp}_${_izContext.uniqueRequestId || Date.now()}`;
|
|
54
|
+
const roleLimitKeys = Array.isArray(requestParams.roleLimitIds)
|
|
55
|
+
? requestParams.roleLimitIds
|
|
56
|
+
: [roleLimitId || `${identifiersConcat}_${limitTag}`];
|
|
57
|
+
|
|
58
|
+
let maxLimit = Infinity;
|
|
59
|
+
const limitRecords = [];
|
|
60
|
+
|
|
61
|
+
for (const key of roleLimitKeys) {
|
|
62
|
+
const record = await dynamodbSharedLib.getItem(
|
|
63
|
+
_izContext,
|
|
64
|
+
dynamodbSharedLib.tableName(_izContext, 'RoleLimit'),
|
|
65
|
+
{ roleLimitId: key, limitType: 'runningTotal' }
|
|
66
|
+
);
|
|
67
|
+
if (record && typeof record.limitValue === 'number') {
|
|
68
|
+
limitRecords.push(record);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (limitRecords.length > 0) {
|
|
73
|
+
const isDecrease = (rec) =>
|
|
74
|
+
rec.effect === 'decrease' ||
|
|
75
|
+
rec.actionType === 'decrease' ||
|
|
76
|
+
rec.action === 'decrease';
|
|
77
|
+
|
|
78
|
+
const increaseLimits = limitRecords.filter((rec) => !isDecrease(rec));
|
|
79
|
+
const decreaseLimits = limitRecords.filter((rec) => isDecrease(rec));
|
|
80
|
+
|
|
81
|
+
const maxIncrease =
|
|
82
|
+
increaseLimits.length > 0
|
|
83
|
+
? Math.max(...increaseLimits.map((r) => r.limitValue))
|
|
84
|
+
: Infinity;
|
|
85
|
+
const minDecrease =
|
|
86
|
+
decreaseLimits.length > 0
|
|
87
|
+
? Math.min(...decreaseLimits.map((r) => r.limitValue))
|
|
88
|
+
: Infinity;
|
|
89
|
+
|
|
90
|
+
maxLimit = Math.min(maxIncrease, minDecrease);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const usageRecord = await dynamodbSharedLib.getItem(
|
|
94
|
+
_izContext,
|
|
95
|
+
dynamodbSharedLib.tableName(_izContext, 'RunningTotalUsage'),
|
|
96
|
+
{ identifiersConcat, limitTag }
|
|
97
|
+
);
|
|
98
|
+
const currentTotal = usageRecord?.totalCount || 0;
|
|
99
|
+
|
|
100
|
+
let passStatus = false;
|
|
101
|
+
if (currentTotal + value <= maxLimit) {
|
|
102
|
+
passStatus = true;
|
|
103
|
+
const ttlTimestamp = Math.floor(Date.now() / 1000) + 3600;
|
|
104
|
+
|
|
105
|
+
await dynamodbSharedLib.putItem(
|
|
106
|
+
_izContext,
|
|
107
|
+
dynamodbSharedLib.tableName(_izContext, 'RunningTotalReservedUsage'),
|
|
108
|
+
{
|
|
109
|
+
runningTotalReservedId: `${identifiersConcat}_${limitTag}`,
|
|
110
|
+
usedTimestampId: uniqueIdentify,
|
|
111
|
+
usageCount: value,
|
|
112
|
+
runningTotalUsageStatus: 'reserved',
|
|
113
|
+
ttlTimestamp
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
await dynamodbSharedLib.putItem(
|
|
118
|
+
_izContext,
|
|
119
|
+
dynamodbSharedLib.tableName(_izContext, 'RunningTotalUsage'),
|
|
120
|
+
{
|
|
121
|
+
identifiersConcat,
|
|
122
|
+
limitTag,
|
|
123
|
+
totalCount: currentTotal + value,
|
|
124
|
+
updateUuid: _izContext.uniqueRequestId || `${Date.now()}`
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const returnResult = {
|
|
130
|
+
passStatus,
|
|
131
|
+
uniqueIdentify,
|
|
132
|
+
limitTag,
|
|
133
|
+
identifiersConcat
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
await sns.publishMessage(_izContext, {
|
|
137
|
+
message: { returnResult },
|
|
138
|
+
TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
|
|
139
|
+
_izContext,
|
|
140
|
+
'ReservedDynamicUsage_Out'
|
|
141
|
+
)
|
|
142
|
+
});
|
|
143
|
+
} catch (error) {
|
|
144
|
+
_izContext.logger.error(`[${process.env.iz_serviceTag}:ReservedDynamicUsage] error:`, error);
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -48,11 +48,11 @@ function _setL1Cache(key, value) {
|
|
|
48
48
|
// ── L2 Cache (Shared DynamoDB TTL Cache) ──────────────────────────────────────
|
|
49
49
|
const L2_TTL_SECONDS = 5 * 60; // 5 minutes
|
|
50
50
|
|
|
51
|
-
async function _getL2Cache(_izContext,
|
|
51
|
+
async function _getL2Cache(_izContext, submittedByUserId, cacheSubKey) {
|
|
52
52
|
try {
|
|
53
53
|
const tableName = dynamodbSharedLib.tableName(_izContext, 'RbacPermissionCaches');
|
|
54
54
|
const result = await dynamodbSharedLib.getItem(_izContext, tableName, {
|
|
55
|
-
|
|
55
|
+
submittedByUserId,
|
|
56
56
|
cacheKey: cacheSubKey
|
|
57
57
|
});
|
|
58
58
|
if (result && result.ttlTimestamp && result.ttlTimestamp > Math.floor(Date.now() / 1000)) {
|
|
@@ -72,12 +72,12 @@ async function _getL2Cache(_izContext, userId, cacheSubKey) {
|
|
|
72
72
|
return null;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
async function _setL2Cache(_izContext,
|
|
75
|
+
async function _setL2Cache(_izContext, submittedByUserId, cacheSubKey, value) {
|
|
76
76
|
try {
|
|
77
77
|
const tableName = dynamodbSharedLib.tableName(_izContext, 'RbacPermissionCaches');
|
|
78
78
|
const ttlTimestamp = Math.floor(Date.now() / 1000) + L2_TTL_SECONDS;
|
|
79
79
|
await dynamodbSharedLib.putItem(_izContext, tableName, {
|
|
80
|
-
|
|
80
|
+
submittedByUserId,
|
|
81
81
|
cacheKey: cacheSubKey,
|
|
82
82
|
permission: value.returnValue ? value.returnValue.permission : value.permission,
|
|
83
83
|
roleIdKey: value.returnValue ? value.returnValue.roleIdKey : value.roleIdKey,
|
|
@@ -90,10 +90,17 @@ async function _setL2Cache(_izContext, userId, cacheSubKey, value) {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
export default async function <%- flowTag %>(_izContext, requestParams = {}) {
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
const submittedByUserId =
|
|
94
|
+
requestParams.submittedByUserId ||
|
|
95
|
+
_izContext.correlationIds.get(consts.SUBMITTED_BY_USER_ID);
|
|
96
|
+
|
|
97
|
+
const targetIdentifiers = requestParams.targetIdentifiers;
|
|
98
|
+
let targetId = targetIdentifiers && typeof targetIdentifiers === 'object'
|
|
99
|
+
? Object.entries(targetIdentifiers).map(([k, v]) => `${k}#${v}`).join('_')
|
|
100
|
+
: undefined;
|
|
101
|
+
|
|
102
|
+
if (!submittedByUserId || !targetIdentifiers) {
|
|
103
|
+
throw new Error('Missing required request parameters: submittedByUserId or targetIdentifiers');
|
|
97
104
|
}
|
|
98
105
|
|
|
99
106
|
const { flowTypeConcat, inputParameterGroups, parentFlowId } = requestParams;
|
|
@@ -101,40 +108,69 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
|
|
|
101
108
|
throw new Error('Missing flowTypeConcat in requestParams');
|
|
102
109
|
}
|
|
103
110
|
|
|
104
|
-
_izContext.logger.debug(
|
|
105
|
-
|
|
111
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] requestParams:', {
|
|
112
|
+
submittedByUserId,
|
|
106
113
|
targetId,
|
|
107
114
|
flowTypeConcat,
|
|
108
115
|
inputParameterGroups
|
|
109
116
|
});
|
|
110
117
|
|
|
111
118
|
const cacheSubKey = `${targetId}#${flowTypeConcat}#${JSON.stringify(inputParameterGroups || {})}`;
|
|
112
|
-
const fullCacheKey = `${
|
|
119
|
+
const fullCacheKey = `${submittedByUserId}:${cacheSubKey}`;
|
|
120
|
+
|
|
121
|
+
// ── Fast-path for User Self-Action (submittedByUserId === targetId) ───────────────────
|
|
122
|
+
const isSelfUser = Boolean(
|
|
123
|
+
submittedByUserId &&
|
|
124
|
+
targetId &&
|
|
125
|
+
(submittedByUserId === targetId ||
|
|
126
|
+
targetId === `user#${submittedByUserId}` ||
|
|
127
|
+
targetId === `submittedByUserId#${submittedByUserId}`)
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
if (isSelfUser) {
|
|
131
|
+
_izContext.logger.debug(
|
|
132
|
+
'[<%- flowTag %>] Fast-path triggered: user self-action',
|
|
133
|
+
{ submittedByUserId, targetId }
|
|
134
|
+
);
|
|
135
|
+
const result = {
|
|
136
|
+
returnValue: {
|
|
137
|
+
permission: true,
|
|
138
|
+
roleIdKey: 'self_user',
|
|
139
|
+
rbacType: 'self_user'
|
|
140
|
+
},
|
|
141
|
+
errorsFound: [],
|
|
142
|
+
status: 'Completed'
|
|
143
|
+
};
|
|
144
|
+
_setL1Cache(fullCacheKey, result);
|
|
145
|
+
await _setL2Cache(_izContext, submittedByUserId, cacheSubKey, result);
|
|
146
|
+
await _publicMessageOut(_izContext, { ...result, parentFlowId });
|
|
147
|
+
return 'Completed';
|
|
148
|
+
}
|
|
113
149
|
|
|
114
150
|
// 1. Check L1 In-Memory Cache
|
|
115
151
|
const l1Hit = _getL1Cache(fullCacheKey);
|
|
116
152
|
if (l1Hit) {
|
|
117
|
-
_izContext.logger.debug(
|
|
153
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] L1 Cache hit:', l1Hit);
|
|
118
154
|
await _publicMessageOut(_izContext, { ...l1Hit, parentFlowId });
|
|
119
155
|
return;
|
|
120
156
|
}
|
|
121
157
|
|
|
122
158
|
// 2. Check L2 Shared DynamoDB Cache
|
|
123
|
-
const l2Hit = await _getL2Cache(_izContext,
|
|
159
|
+
const l2Hit = await _getL2Cache(_izContext, submittedByUserId, cacheSubKey);
|
|
124
160
|
if (l2Hit) {
|
|
125
|
-
_izContext.logger.debug(
|
|
161
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] L2 Cache hit:', l2Hit);
|
|
126
162
|
_setL1Cache(fullCacheKey, l2Hit);
|
|
127
163
|
await _publicMessageOut(_izContext, { ...l2Hit, parentFlowId });
|
|
128
164
|
return;
|
|
129
165
|
}
|
|
130
166
|
|
|
131
|
-
_izContext.logger.debug(
|
|
167
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] Cache miss — querying DynamoDB');
|
|
132
168
|
|
|
133
169
|
// 3. Fallback to DB Query
|
|
134
170
|
const userRolesResult = await dynamodbSharedLib.query(
|
|
135
171
|
_izContext,
|
|
136
172
|
dynamodbSharedLib.tableName(_izContext, 'UserRoles'),
|
|
137
|
-
{
|
|
173
|
+
{ submittedByUserId }
|
|
138
174
|
);
|
|
139
175
|
|
|
140
176
|
let userRoles = [];
|
|
@@ -144,7 +180,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
|
|
|
144
180
|
userRoles = userRolesResult.Items;
|
|
145
181
|
}
|
|
146
182
|
|
|
147
|
-
_izContext.logger.debug(
|
|
183
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] userRoles fetched:', { userRoles });
|
|
148
184
|
|
|
149
185
|
const targetIdPrefix = `${targetId}_`;
|
|
150
186
|
const targetRoles = [];
|
|
@@ -154,13 +190,13 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
|
|
|
154
190
|
}
|
|
155
191
|
}
|
|
156
192
|
|
|
157
|
-
_izContext.logger.debug(
|
|
193
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] targetRoles filtered:', {
|
|
158
194
|
targetIdPrefix,
|
|
159
195
|
targetRoles
|
|
160
196
|
});
|
|
161
197
|
|
|
162
198
|
if (targetRoles.length === 0) {
|
|
163
|
-
_izContext.logger.debug(
|
|
199
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] no targetRoles found, permission denied');
|
|
164
200
|
const result = {
|
|
165
201
|
returnValue: {
|
|
166
202
|
permission: false,
|
|
@@ -171,7 +207,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
|
|
|
171
207
|
status: 'Completed'
|
|
172
208
|
};
|
|
173
209
|
_setL1Cache(fullCacheKey, result);
|
|
174
|
-
await _setL2Cache(_izContext,
|
|
210
|
+
await _setL2Cache(_izContext, submittedByUserId, cacheSubKey, result);
|
|
175
211
|
await _publicMessageOut(_izContext, { ...result, parentFlowId });
|
|
176
212
|
return;
|
|
177
213
|
}
|
|
@@ -189,7 +225,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
|
|
|
189
225
|
})
|
|
190
226
|
);
|
|
191
227
|
|
|
192
|
-
_izContext.logger.debug(
|
|
228
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] rolePermissions fetched:', {
|
|
193
229
|
rolePermissions
|
|
194
230
|
});
|
|
195
231
|
|
|
@@ -217,11 +253,11 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
|
|
|
217
253
|
status: 'Completed'
|
|
218
254
|
};
|
|
219
255
|
|
|
220
|
-
_izContext.logger.debug(
|
|
256
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] validation result:', cacheResult);
|
|
221
257
|
|
|
222
258
|
// Store in L1 & L2 Cache
|
|
223
259
|
_setL1Cache(fullCacheKey, cacheResult);
|
|
224
|
-
await _setL2Cache(_izContext,
|
|
260
|
+
await _setL2Cache(_izContext, submittedByUserId, cacheSubKey, cacheResult);
|
|
225
261
|
|
|
226
262
|
await _publicMessageOut(_izContext, { ...cacheResult, parentFlowId });
|
|
227
263
|
|
|
@@ -47,7 +47,7 @@ export default async function <%- flowTag %>(_izContext, requestParams) {
|
|
|
47
47
|
)
|
|
48
48
|
});
|
|
49
49
|
} catch (error) {
|
|
50
|
-
_izContext.logger.error(
|
|
50
|
+
_izContext.logger.error(`[${process.env.iz_serviceTag}:<%- flowTag %>] error:`, error);
|
|
51
51
|
throw error;
|
|
52
52
|
}
|
|
53
53
|
}
|
|
@@ -78,7 +78,7 @@ export default async function <%- functionName %>(
|
|
|
78
78
|
|
|
79
79
|
let originTimestamp = Date.now();
|
|
80
80
|
let changedByUserId = _izContext.correlationIds.get(
|
|
81
|
-
coreConsts.
|
|
81
|
+
coreConsts.SUBMITTED_BY_USER_ID
|
|
82
82
|
);
|
|
83
83
|
const {
|
|
84
84
|
firstObject,
|
|
@@ -191,14 +191,14 @@ export default async function <%- functionName %>(
|
|
|
191
191
|
*/
|
|
192
192
|
// ? [Fix]: The legacy logic incorrectly blocked "user" from regular execution and allowed non-user values.
|
|
193
193
|
// ? It has been corrected to require changeBy === "user" for normal execution.
|
|
194
|
-
const isUserProcess = Boolean(_izContext.correlationIds?.get(consts.
|
|
194
|
+
const isUserProcess = Boolean(_izContext.correlationIds?.get(consts.SUBMITTED_BY_USER_ID));
|
|
195
195
|
if (isUserProcess) {
|
|
196
196
|
if (canChangeToRelType.changeBy !== 'user' && canChangeToRelType.changeBy !== 'systemWithUser') {
|
|
197
197
|
errorsFound.push(`CanChangeToRelType changeBy is not user or systemWithUser`);
|
|
198
198
|
}
|
|
199
199
|
} else {
|
|
200
|
-
if (canChangeToRelType.changeBy !== '
|
|
201
|
-
errorsFound.push(`CanChangeToRelType changeBy is not
|
|
200
|
+
if (canChangeToRelType.changeBy !== 'systemNoUser') {
|
|
201
|
+
errorsFound.push(`CanChangeToRelType changeBy is not systemNoUser`);
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
}
|