@izara_project/izara-core-generate-service-code 1.0.98 → 1.0.99
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/flowValidator.js +45 -8
- package/src/compile/loadSchemas.js +0 -3
- package/src/emit/application/coreFunction/emitCoreFunctionArtifacts.js +15 -1
- package/src/emit/application/coreFunction/templates/InitialSetupConfig.ejs +8 -4
- package/src/emit/application/crud/templates/CreateEndpoint_Main.ejs +4 -4
- package/src/emit/application/crud/templates/CreateLogicalLib.ejs +60 -8
- package/src/emit/application/crud/templates/DeleteEndpoint_Main.ejs +4 -4
- package/src/emit/application/crud/templates/UpdateEndpoint_Main.ejs +4 -4
- package/src/emit/application/custom/customFlowS3.js +4 -4
- package/src/emit/application/custom/templates/FlowEndpoint_Main.ejs +2 -2
- package/src/emit/application/rbac/emitDynamicRbacFlowSchemas.js +4 -1
- package/src/emit/application/rbac/emitRbacApplicationArtifacts.js +76 -1
- package/src/emit/application/rbac/templates/ReservedDynamicUsage_Main.ejs +2 -0
- package/src/emit/application/rbac/templates/ReservedUsageTtlExpired_HdrDss.ejs +176 -0
- package/src/emit/infrastructure/functions/functionYamlArtifact.js +28 -0
- package/src/emit/infrastructure/functions/templates/DssFunction_Yaml.ejs +21 -0
- package/src/emit/infrastructure/resources/iamResourceArtifacts.js +45 -5
- package/src/emit/infrastructure/resources/templates/GeneratedLambdaRoleServiceConfig.ejs +5 -3
- package/src/emit/schemas/attributeTree/emitAttributeTreeSchemas.js +62 -1
- package/src/generate.js +2 -6
- package/src/output/mergeHooks.js +105 -5
- package/src/plan/domainRegistry.js +0 -10
- package/task.md +40 -96
- package/test/integration/graphDrivenFlowSchema.test.js +58 -0
- package/test/unit/application/createTargetRoleHandler.test.js +70 -1
- package/test/unit/application/crudApplicationArtifacts.test.js +4 -0
- package/test/unit/infrastructure/resourceArtifacts.test.js +52 -0
- package/test/unit/output/mergeHooks.test.js +137 -0
- package/test/unit/schemas/attributeTreeSchemas.test.js +32 -3
- package/test/unit/schemas/attributeTreeSingleService.test.js +175 -0
- package/test/unit/templates/hookTagsValidation.test.js +83 -0
- package/src/emit/schemas/attributeTree/emitAttributeTreeTargetValueStandard.js +0 -132
- package/test/unit/schemas/attributeTreeDirectives.test.js +0 -177
|
@@ -0,0 +1,176 @@
|
|
|
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 { middlewareHandler as middleware } from '@izara_project/izara-middleware';
|
|
19
|
+
import Logger from '@izara_project/izara-core-library-logger';
|
|
20
|
+
import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* DynamoDB Stream handler for TTL expired records in RunningTotalReservedUsage.
|
|
24
|
+
* Automatically refunds abandoned reservations with atomic decrement.
|
|
25
|
+
*/
|
|
26
|
+
export const main = middleware.wrap(async (event, context) => {
|
|
27
|
+
try {
|
|
28
|
+
if (!event || !Array.isArray(event.Records)) {
|
|
29
|
+
Logger.debug('No records in DynamoDB stream event');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let _izContext = event._izContext;
|
|
34
|
+
if (!_izContext) {
|
|
35
|
+
_izContext = {
|
|
36
|
+
logger: Logger,
|
|
37
|
+
correlationIds: new Map()
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
for (const record of event.Records) {
|
|
42
|
+
let recordContext = record._izContext;
|
|
43
|
+
if (!recordContext) {
|
|
44
|
+
recordContext = _izContext;
|
|
45
|
+
}
|
|
46
|
+
let recordLogger = recordContext.logger;
|
|
47
|
+
if (!recordLogger) {
|
|
48
|
+
recordLogger = Logger;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const isTtlDelete =
|
|
52
|
+
record.eventName === 'REMOVE' &&
|
|
53
|
+
record.userIdentity &&
|
|
54
|
+
record.userIdentity.type === 'Service' &&
|
|
55
|
+
record.userIdentity.principalId === 'dynamodb.amazonaws.com';
|
|
56
|
+
|
|
57
|
+
if (!isTtlDelete) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const oldImage = record.dynamodb && record.dynamodb.OldImage;
|
|
62
|
+
if (!oldImage) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let oldStatus = null;
|
|
67
|
+
if (oldImage.runningTotalUsageStatus && oldImage.runningTotalUsageStatus.S) {
|
|
68
|
+
oldStatus = oldImage.runningTotalUsageStatus.S;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (oldStatus !== 'reserved') {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let identifiersConcat = null;
|
|
76
|
+
if (oldImage.identifiersConcat && oldImage.identifiersConcat.S) {
|
|
77
|
+
identifiersConcat = oldImage.identifiersConcat.S;
|
|
78
|
+
}
|
|
79
|
+
let flowTypeConcat = null;
|
|
80
|
+
if (oldImage.flowTypeConcat && oldImage.flowTypeConcat.S) {
|
|
81
|
+
flowTypeConcat = oldImage.flowTypeConcat.S;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if ((!identifiersConcat || !flowTypeConcat) && oldImage.runningTotalReservedId && oldImage.runningTotalReservedId.S) {
|
|
85
|
+
const reservedId = oldImage.runningTotalReservedId.S;
|
|
86
|
+
const lastUnderscoreIndex = reservedId.lastIndexOf('_');
|
|
87
|
+
if (lastUnderscoreIndex > 0) {
|
|
88
|
+
if (!identifiersConcat) {
|
|
89
|
+
identifiersConcat = reservedId.substring(0, lastUnderscoreIndex);
|
|
90
|
+
}
|
|
91
|
+
if (!flowTypeConcat) {
|
|
92
|
+
flowTypeConcat = reservedId.substring(lastUnderscoreIndex + 1);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let usageCount = 1;
|
|
98
|
+
if (oldImage.usageCount && oldImage.usageCount.N) {
|
|
99
|
+
usageCount = Number(oldImage.usageCount.N);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (identifiersConcat && flowTypeConcat && usageCount > 0) {
|
|
103
|
+
const usageTable = dynamodbSharedLib.tableName(
|
|
104
|
+
recordContext,
|
|
105
|
+
'RunningTotalUsage'
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const usageRecord = await dynamodbSharedLib.getItem(
|
|
109
|
+
recordContext,
|
|
110
|
+
usageTable,
|
|
111
|
+
{
|
|
112
|
+
identifiersConcat: identifiersConcat,
|
|
113
|
+
flowTypeConcat: flowTypeConcat
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
if (usageRecord) {
|
|
118
|
+
let currentTotal = 0;
|
|
119
|
+
if (typeof usageRecord.totalCount === 'number') {
|
|
120
|
+
currentTotal = usageRecord.totalCount;
|
|
121
|
+
}
|
|
122
|
+
const newTotal = Math.max(0, currentTotal - usageCount);
|
|
123
|
+
const updateUuid = recordContext.uniqueRequestId || `${Date.now()}`;
|
|
124
|
+
|
|
125
|
+
const queryElementsWhenUpdate = {
|
|
126
|
+
additionalAttributes: { currentUuid: usageRecord.updateUuid },
|
|
127
|
+
logicalElements: [
|
|
128
|
+
{
|
|
129
|
+
type: 'logical',
|
|
130
|
+
comparison: 'equals',
|
|
131
|
+
lhsAttribute: 'updateUuid',
|
|
132
|
+
rhsReference: 'currentUuid'
|
|
133
|
+
}
|
|
134
|
+
],
|
|
135
|
+
returnValues: 'ALL_NEW'
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const updateAttributes = [
|
|
139
|
+
{ attributeName: 'totalCount', action: 'set', value: newTotal },
|
|
140
|
+
{ attributeName: 'updateUuid', action: 'set', value: updateUuid }
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
await dynamodbSharedLib.updateItem(
|
|
144
|
+
recordContext,
|
|
145
|
+
usageTable,
|
|
146
|
+
{
|
|
147
|
+
identifiersConcat: identifiersConcat,
|
|
148
|
+
flowTypeConcat: flowTypeConcat
|
|
149
|
+
},
|
|
150
|
+
updateAttributes,
|
|
151
|
+
queryElementsWhenUpdate,
|
|
152
|
+
{
|
|
153
|
+
complexAttributes: true,
|
|
154
|
+
errorOnConditionalExpNotPass: false
|
|
155
|
+
}
|
|
156
|
+
).catch(() => null);
|
|
157
|
+
|
|
158
|
+
recordLogger.info({
|
|
159
|
+
message: '[ReservedUsageTtlExpired] Auto-refunded expired reservation',
|
|
160
|
+
identifiersConcat: identifiersConcat,
|
|
161
|
+
flowTypeConcat: flowTypeConcat,
|
|
162
|
+
usageCount: usageCount,
|
|
163
|
+
previousTotal: currentTotal,
|
|
164
|
+
newTotal: newTotal
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
} catch (err) {
|
|
170
|
+
Logger.error({
|
|
171
|
+
message: 'Unhandled error in ReservedUsageTtlExpired_HdrDss',
|
|
172
|
+
error: err
|
|
173
|
+
});
|
|
174
|
+
throw err;
|
|
175
|
+
}
|
|
176
|
+
});
|
|
@@ -198,6 +198,34 @@ export function renderS3FunctionYaml({
|
|
|
198
198
|
});
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
export function renderDssFunctionYaml({
|
|
202
|
+
functionName,
|
|
203
|
+
handlerPath,
|
|
204
|
+
roleName,
|
|
205
|
+
tableName,
|
|
206
|
+
batchSize = 100,
|
|
207
|
+
startingPosition = 'TRIM_HORIZON',
|
|
208
|
+
filterPatterns = [],
|
|
209
|
+
flowTag,
|
|
210
|
+
flowStepName
|
|
211
|
+
}) {
|
|
212
|
+
return _renderFunctionTemplate('DssFunction_Yaml.ejs', {
|
|
213
|
+
functionName,
|
|
214
|
+
handlerPath,
|
|
215
|
+
roleName,
|
|
216
|
+
tableName,
|
|
217
|
+
batchSize,
|
|
218
|
+
startingPosition,
|
|
219
|
+
filterPatterns,
|
|
220
|
+
environmentSetting: _environmentSetting(
|
|
221
|
+
functionName,
|
|
222
|
+
flowTag,
|
|
223
|
+
flowStepName
|
|
224
|
+
),
|
|
225
|
+
functionSetting: _functionSetting(functionName)
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
201
229
|
export function renderEventBridgeFunctionYaml({
|
|
202
230
|
functionName,
|
|
203
231
|
handlerPath,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<%= functionName %>:
|
|
2
|
+
handler: <%= handlerPath %>
|
|
3
|
+
name: ${self:custom.iz_resourcePrefix}<%= functionName %>
|
|
4
|
+
role: <%= roleName %>
|
|
5
|
+
<%- environmentSetting %><%- functionSetting %>
|
|
6
|
+
events:
|
|
7
|
+
- stream:
|
|
8
|
+
type: dynamodb
|
|
9
|
+
arn:
|
|
10
|
+
Fn::GetAtt: [<%= tableName %>Table, StreamArn]
|
|
11
|
+
batchSize: <%= batchSize %>
|
|
12
|
+
startingPosition: <%= startingPosition %>
|
|
13
|
+
<% if (Array.isArray(filterPatterns) && filterPatterns.length > 0) { -%>
|
|
14
|
+
filterPatterns:
|
|
15
|
+
<% filterPatterns.forEach(function(pattern) { -%>
|
|
16
|
+
- eventName: <%- JSON.stringify(pattern.eventName) %>
|
|
17
|
+
userIdentity:
|
|
18
|
+
type: <%- JSON.stringify(pattern.userIdentity.type) %>
|
|
19
|
+
principalId: <%- JSON.stringify(pattern.userIdentity.principalId) %>
|
|
20
|
+
<% }); -%>
|
|
21
|
+
<% } -%>
|
|
@@ -96,9 +96,26 @@ export function getRbacValidatorServiceTags(state) {
|
|
|
96
96
|
export async function renderLambdaRoleConfig(state) {
|
|
97
97
|
const s3Flows = state.flows
|
|
98
98
|
.filter((flow) => includesEvent(flow, 's3'))
|
|
99
|
-
.map((flow) =>
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
.map((flow) => {
|
|
100
|
+
let quotaServiceTag = flow.quotaServiceTag;
|
|
101
|
+
if (!quotaServiceTag && flow.belongTo) {
|
|
102
|
+
if (Array.isArray(flow.belongTo)) {
|
|
103
|
+
quotaServiceTag = flow.belongTo[0]?.serviceTag;
|
|
104
|
+
} else if (flow.belongTo.serviceTag) {
|
|
105
|
+
quotaServiceTag = flow.belongTo.serviceTag;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (!quotaServiceTag && flow.objType?.serviceTag) {
|
|
109
|
+
quotaServiceTag = flow.objType.serviceTag;
|
|
110
|
+
}
|
|
111
|
+
if (!quotaServiceTag) {
|
|
112
|
+
quotaServiceTag = state.project?.serviceTag || 'Rbac';
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
roleName: upperFirst(flow.flowTag),
|
|
116
|
+
quotaServiceTag
|
|
117
|
+
};
|
|
118
|
+
});
|
|
102
119
|
const rbacValidatorServiceTags = getRbacValidatorServiceTags(state);
|
|
103
120
|
return renderResourceTemplate('GeneratedLambdaRoleServiceConfig.ejs', {
|
|
104
121
|
crud: state.crud,
|
|
@@ -160,6 +177,19 @@ export function roleStatements(state, roleName) {
|
|
|
160
177
|
]
|
|
161
178
|
});
|
|
162
179
|
}
|
|
180
|
+
if (roleName === 'FlowRbacRole' && state.selfRbac) {
|
|
181
|
+
statements.push({
|
|
182
|
+
action: [
|
|
183
|
+
'dynamodb:DescribeStream',
|
|
184
|
+
'dynamodb:GetRecords',
|
|
185
|
+
'dynamodb:GetShardIterator',
|
|
186
|
+
'dynamodb:ListStreams'
|
|
187
|
+
],
|
|
188
|
+
resource: [
|
|
189
|
+
'arn:aws:dynamodb:${self:custom.iz_region}:${self:custom.iz_accountId}:table/${self:custom.iz_resourcePrefix}RunningTotalReservedUsage/stream/*'
|
|
190
|
+
]
|
|
191
|
+
});
|
|
192
|
+
}
|
|
163
193
|
return statements;
|
|
164
194
|
}
|
|
165
195
|
|
|
@@ -508,11 +538,16 @@ export function roleExternalTopicArns(state, roleName) {
|
|
|
508
538
|
`arn:aws:sns:\${self:custom.iz_region}:\${self:custom.iz_accountId}:${tag}_\${self:custom.iz_stage}_ValidatorRbac_In`
|
|
509
539
|
);
|
|
510
540
|
if (roleName === 'FlowObjectsRole') {
|
|
541
|
+
const extraCrudTopics = extraTags.flatMap((tag) => [
|
|
542
|
+
`arn:aws:sns:\${self:custom.iz_region}:\${self:custom.iz_accountId}:${tag}_\${self:custom.iz_stage}_ValidatorRbac_In`,
|
|
543
|
+
`arn:aws:sns:\${self:custom.iz_region}:\${self:custom.iz_accountId}:${tag}_\${self:custom.iz_stage}_ConfirmUsage_In`,
|
|
544
|
+
`arn:aws:sns:\${self:custom.iz_region}:\${self:custom.iz_accountId}:${tag}_\${self:custom.iz_stage}_CancelUsage_In`
|
|
545
|
+
]);
|
|
511
546
|
return [
|
|
512
547
|
`arn:aws:sns:\${self:custom.iz_region}:\${self:custom.iz_accountId}:Rbac_\${self:custom.iz_stage}_ValidatorRbac_In`,
|
|
513
548
|
`arn:aws:sns:\${self:custom.iz_region}:\${self:custom.iz_accountId}:Rbac_\${self:custom.iz_stage}_ConfirmUsage_In`,
|
|
514
549
|
`arn:aws:sns:\${self:custom.iz_region}:\${self:custom.iz_accountId}:Rbac_\${self:custom.iz_stage}_CancelUsage_In`,
|
|
515
|
-
...
|
|
550
|
+
...extraCrudTopics
|
|
516
551
|
];
|
|
517
552
|
}
|
|
518
553
|
if (roleName === 'FlowRbacRole' || roleName === 'FlowRelationshipRole') {
|
|
@@ -536,11 +571,16 @@ export function roleExternalLambdaArns(state, roleName) {
|
|
|
536
571
|
`arn:aws:lambda:\${self:custom.iz_region}:\${self:custom.iz_accountId}:function:${tag}\${self:custom.iz_stage}ValidatorRbacHdrInv`
|
|
537
572
|
);
|
|
538
573
|
if (roleName === 'FlowObjectsRole') {
|
|
574
|
+
const extraCrudLambdas = extraTags.flatMap((tag) => [
|
|
575
|
+
`arn:aws:lambda:\${self:custom.iz_region}:\${self:custom.iz_accountId}:function:${tag}\${self:custom.iz_stage}ValidatorRbacHdrInv`,
|
|
576
|
+
`arn:aws:lambda:\${self:custom.iz_region}:\${self:custom.iz_accountId}:function:${tag}\${self:custom.iz_stage}ConfirmUsageHdrInv`,
|
|
577
|
+
`arn:aws:lambda:\${self:custom.iz_region}:\${self:custom.iz_accountId}:function:${tag}\${self:custom.iz_stage}CancelUsageHdrInv`
|
|
578
|
+
]);
|
|
539
579
|
return [
|
|
540
580
|
`arn:aws:lambda:\${self:custom.iz_region}:\${self:custom.iz_accountId}:function:Rbac\${self:custom.iz_stage}ValidatorRbacHdrInv`,
|
|
541
581
|
`arn:aws:lambda:\${self:custom.iz_region}:\${self:custom.iz_accountId}:function:Rbac\${self:custom.iz_stage}ConfirmUsageHdrInv`,
|
|
542
582
|
`arn:aws:lambda:\${self:custom.iz_region}:\${self:custom.iz_accountId}:function:Rbac\${self:custom.iz_stage}CancelUsageHdrInv`,
|
|
543
|
-
...
|
|
583
|
+
...extraCrudLambdas
|
|
544
584
|
];
|
|
545
585
|
}
|
|
546
586
|
if (roleName === 'FlowRbacRole' || roleName === 'FlowRelationshipRole') {
|
|
@@ -60,7 +60,9 @@ export function generatedLambdaRole() {
|
|
|
60
60
|
`arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ConfirmUsage_In', 'Rbac')}`,
|
|
61
61
|
`arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'CancelUsage_In', 'Rbac')}`<% if (rbac) { %>,
|
|
62
62
|
`arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In')}`<% } %><% (rbacValidatorServiceTags || []).forEach(function(tag) { %>,
|
|
63
|
-
`arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', '<%= tag %>')}
|
|
63
|
+
`arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', '<%= tag %>')}`,
|
|
64
|
+
`arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ConfirmUsage_In', '<%= tag %>')}`,
|
|
65
|
+
`arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'CancelUsage_In', '<%= tag %>')}`<% }); %>
|
|
64
66
|
]
|
|
65
67
|
}<% } %>
|
|
66
68
|
//(<createFlowObjectsRole>)
|
|
@@ -144,7 +146,7 @@ export function generatedLambdaRole() {
|
|
|
144
146
|
Effect: 'Allow',
|
|
145
147
|
Action: ['sns:Publish'],
|
|
146
148
|
Resource: [
|
|
147
|
-
`arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ReservedDynamicUsage_In', '
|
|
149
|
+
`arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ReservedDynamicUsage_In', '<%= flow.quotaServiceTag || 'Rbac' %>')}`
|
|
148
150
|
]
|
|
149
151
|
},
|
|
150
152
|
|
|
@@ -153,7 +155,7 @@ export function generatedLambdaRole() {
|
|
|
153
155
|
Effect: 'Allow',
|
|
154
156
|
Action: ['sns:Publish'],
|
|
155
157
|
Resource: [
|
|
156
|
-
`arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ConfirmDynamicAfterUpload_In', '
|
|
158
|
+
`arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ConfirmDynamicAfterUpload_In', '<%= flow.quotaServiceTag || 'Rbac' %>')}`
|
|
157
159
|
]
|
|
158
160
|
}
|
|
159
161
|
//(<create<%= flow.roleName %>Role>)
|
|
@@ -62,6 +62,60 @@ function _renderRelationshipSchema(relationshipTag, content) {
|
|
|
62
62
|
return `export default ${JSON.stringify(wrapper, null, 2)};\n`;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
function _renderValueStandardSchema(tag, storageResources) {
|
|
66
|
+
const storageResourceTags = Object.keys(storageResources);
|
|
67
|
+
if (storageResourceTags.length === 0) {
|
|
68
|
+
storageResourceTags.push('myGraph');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const storageJson = JSON.stringify(storageResources, null, 2)
|
|
72
|
+
.replace(/\n/g, '\n ');
|
|
73
|
+
const tagsJson = JSON.stringify(storageResourceTags);
|
|
74
|
+
|
|
75
|
+
return `export default [
|
|
76
|
+
{
|
|
77
|
+
objectType: '${tag}ValueStandard',
|
|
78
|
+
canDelete: true,
|
|
79
|
+
generatedBy: 'user',
|
|
80
|
+
storageResources: ${storageJson},
|
|
81
|
+
addOnDataStructure: [
|
|
82
|
+
//(<addOnDataStructure${tag}ValueStandard>)
|
|
83
|
+
// {
|
|
84
|
+
// type: 'propertyValue',
|
|
85
|
+
// propertyValuePrefix: '${tag}ValueStandard'
|
|
86
|
+
// }
|
|
87
|
+
//(</addOnDataStructure${tag}ValueStandard>)
|
|
88
|
+
],
|
|
89
|
+
fieldNames: {
|
|
90
|
+
${tag}ValueStandardId: {
|
|
91
|
+
type: 'string',
|
|
92
|
+
randomOnCreate: true,
|
|
93
|
+
canUpdate: false,
|
|
94
|
+
storageResourceTags: ${tagsJson}
|
|
95
|
+
},
|
|
96
|
+
label: {
|
|
97
|
+
type: 'string',
|
|
98
|
+
canUpdate: true,
|
|
99
|
+
storageResourceTags: ${tagsJson}
|
|
100
|
+
},
|
|
101
|
+
value: {
|
|
102
|
+
type: 'string',
|
|
103
|
+
requiredOnCreate: true,
|
|
104
|
+
canUpdate: true,
|
|
105
|
+
storageResourceTags: ${tagsJson}
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
identifiers: [
|
|
109
|
+
{
|
|
110
|
+
type: 'identifier',
|
|
111
|
+
fieldName: '${tag}ValueStandardId'
|
|
112
|
+
}
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
];
|
|
116
|
+
`;
|
|
117
|
+
}
|
|
118
|
+
|
|
65
119
|
export function attributeTreeObjectSchemaManagedPaths(objects = []) {
|
|
66
120
|
const trees = _extractAttributeTrees(objects);
|
|
67
121
|
const paths = [];
|
|
@@ -71,7 +125,8 @@ export function attributeTreeObjectSchemaManagedPaths(objects = []) {
|
|
|
71
125
|
paths.push(
|
|
72
126
|
`app/src/schemas/ObjectSchemas/generatedCode/attributeTree/${tag}Link.js`,
|
|
73
127
|
`app/src/schemas/ObjectSchemas/generatedCode/attributeTree/${tag}Value.js`,
|
|
74
|
-
`app/src/schemas/ObjectSchemas/generatedCode/attributeTree/${tag}Label.js
|
|
128
|
+
`app/src/schemas/ObjectSchemas/generatedCode/attributeTree/${tag}Label.js`,
|
|
129
|
+
`app/src/schemas/ObjectSchemas/generatedCode/attributeTree/${tag}ValueStandard.js`
|
|
75
130
|
);
|
|
76
131
|
}
|
|
77
132
|
|
|
@@ -205,6 +260,12 @@ export function emitAttributeTreeObjectSchemas({ objects = [] } = {}) {
|
|
|
205
260
|
path: `app/src/schemas/ObjectSchemas/generatedCode/attributeTree/${tag}Label.js`,
|
|
206
261
|
content: _renderObjectSchema(labelSchema)
|
|
207
262
|
});
|
|
263
|
+
|
|
264
|
+
// 4. {tag}ValueStandard.js
|
|
265
|
+
artifacts.push({
|
|
266
|
+
path: `app/src/schemas/ObjectSchemas/generatedCode/attributeTree/${tag}ValueStandard.js`,
|
|
267
|
+
content: _renderValueStandardSchema(tag, graphStorageResources)
|
|
268
|
+
});
|
|
208
269
|
}
|
|
209
270
|
|
|
210
271
|
return artifacts;
|
package/src/generate.js
CHANGED
|
@@ -4,7 +4,6 @@ import { commitArtifacts } from './output/commitArtifacts.js';
|
|
|
4
4
|
import { compileProject } from './compile/compileProject.js';
|
|
5
5
|
import {
|
|
6
6
|
loadLocalSchemaDirectory,
|
|
7
|
-
loadGenerateDirectives,
|
|
8
7
|
resolveExtendedObjectSchemas,
|
|
9
8
|
resolveGraphServiceTags
|
|
10
9
|
} from './compile/loadSchemas.js';
|
|
@@ -22,8 +21,7 @@ export async function generate(rootPath, options = {}) {
|
|
|
22
21
|
relationships,
|
|
23
22
|
flows,
|
|
24
23
|
notifications,
|
|
25
|
-
errors
|
|
26
|
-
directives
|
|
24
|
+
errors
|
|
27
25
|
] = await Promise.all([
|
|
28
26
|
loadLocalSchemaDirectory(project, 'ObjectSchemas', schemaLoadOptions),
|
|
29
27
|
loadLocalSchemaDirectory(
|
|
@@ -37,8 +35,7 @@ export async function generate(rootPath, options = {}) {
|
|
|
37
35
|
'NotificationSchemas',
|
|
38
36
|
schemaLoadOptions
|
|
39
37
|
),
|
|
40
|
-
loadLocalSchemaDirectory(project, 'ErrorSchemas', schemaLoadOptions)
|
|
41
|
-
loadGenerateDirectives(project, schemaLoadOptions)
|
|
38
|
+
loadLocalSchemaDirectory(project, 'ErrorSchemas', schemaLoadOptions)
|
|
42
39
|
]);
|
|
43
40
|
const schemaBucketName =
|
|
44
41
|
options.schemaBucketName || process.env.iz_serviceSchemaBucketName;
|
|
@@ -82,7 +79,6 @@ export async function generate(rootPath, options = {}) {
|
|
|
82
79
|
flows,
|
|
83
80
|
notifications,
|
|
84
81
|
errors,
|
|
85
|
-
directives,
|
|
86
82
|
objectGraphServiceTags,
|
|
87
83
|
relationshipGraphServiceTags,
|
|
88
84
|
logger: options.logger
|
package/src/output/mergeHooks.js
CHANGED
|
@@ -40,6 +40,14 @@ async function mergeHook(outputRoot, artifact) {
|
|
|
40
40
|
return artifact.content;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
_validateHookIntegrity(artifact.path, sourceContent);
|
|
44
|
+
_warnOrphanedHooks(
|
|
45
|
+
artifact.path,
|
|
46
|
+
sourceContent,
|
|
47
|
+
artifact.content,
|
|
48
|
+
matchers
|
|
49
|
+
);
|
|
50
|
+
|
|
43
51
|
const tagOccurrences = new Map();
|
|
44
52
|
return artifact.content.replace(
|
|
45
53
|
matchers.anyTag,
|
|
@@ -60,13 +68,105 @@ async function mergeHook(outputRoot, artifact) {
|
|
|
60
68
|
}
|
|
61
69
|
const hookBlock = matches[count];
|
|
62
70
|
if (hookBlock !== null && hookBlock !== undefined) {
|
|
63
|
-
return hookBlock;
|
|
71
|
+
return _normalizeHookBlock(hookBlock, tagName);
|
|
64
72
|
}
|
|
65
73
|
return _match;
|
|
66
74
|
}
|
|
67
75
|
);
|
|
68
76
|
}
|
|
69
77
|
|
|
78
|
+
function _validateHookIntegrity(filePath, content) {
|
|
79
|
+
const lower = filePath.toLowerCase();
|
|
80
|
+
let openCount = 0;
|
|
81
|
+
let closeCount = 0;
|
|
82
|
+
|
|
83
|
+
if (
|
|
84
|
+
lower.endsWith('.js') ||
|
|
85
|
+
lower.endsWith('.mjs') ||
|
|
86
|
+
lower.endsWith('.cjs')
|
|
87
|
+
) {
|
|
88
|
+
const openP1 = content.match(/\/\/\s*\(<\s*[\w.-]+/g);
|
|
89
|
+
const closeP1 = content.match(/\/\/\s*\(<\/\s*[\w.-]+/g);
|
|
90
|
+
const openP2 = content.match(/\/\/\s*<#\s*[\w.-]+/g);
|
|
91
|
+
const closeP2 = content.match(/\/\/\s*<#\/\s*[\w.-]+/g);
|
|
92
|
+
|
|
93
|
+
if (openP1) {
|
|
94
|
+
openCount += openP1.length;
|
|
95
|
+
}
|
|
96
|
+
if (closeP1) {
|
|
97
|
+
closeCount += closeP1.length;
|
|
98
|
+
}
|
|
99
|
+
if (openP2) {
|
|
100
|
+
openCount += openP2.length;
|
|
101
|
+
}
|
|
102
|
+
if (closeP2) {
|
|
103
|
+
closeCount += closeP2.length;
|
|
104
|
+
}
|
|
105
|
+
} else if (lower.endsWith('.yml') || lower.endsWith('.yaml')) {
|
|
106
|
+
const openYml = content.match(/#\s*<#\s*[\w.-]+/g);
|
|
107
|
+
const closeYml = content.match(/#\s*<#\/\s*[\w.-]+/g);
|
|
108
|
+
|
|
109
|
+
if (openYml) {
|
|
110
|
+
openCount += openYml.length;
|
|
111
|
+
}
|
|
112
|
+
if (closeYml) {
|
|
113
|
+
closeCount += closeYml.length;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (openCount !== closeCount) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
`[mergeHooks] Malformed hook tags in '${filePath}': found ` +
|
|
120
|
+
`${openCount} open tag(s) but ${closeCount} close tag(s). ` +
|
|
121
|
+
`Aborting to prevent code loss.`
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function _warnOrphanedHooks(
|
|
127
|
+
filePath,
|
|
128
|
+
sourceContent,
|
|
129
|
+
artifactContent,
|
|
130
|
+
matchers
|
|
131
|
+
) {
|
|
132
|
+
const anyTagRegex = new RegExp(matchers.anyTag.source, 'gm');
|
|
133
|
+
let match = anyTagRegex.exec(sourceContent);
|
|
134
|
+
|
|
135
|
+
while (match !== null) {
|
|
136
|
+
const tagName = _extractTagName(match[1], match[3]);
|
|
137
|
+
let body = '';
|
|
138
|
+
if (typeof match[2] === 'string') {
|
|
139
|
+
body = match[2];
|
|
140
|
+
} else if (typeof match[4] === 'string') {
|
|
141
|
+
body = match[4];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (tagName && body.trim().length > 0) {
|
|
145
|
+
const templatePattern = matchers.tagPattern(tagName);
|
|
146
|
+
const existsInTemplate = templatePattern.test(artifactContent);
|
|
147
|
+
if (!existsInTemplate) {
|
|
148
|
+
console.warn(
|
|
149
|
+
`[WARN] [mergeHooks] Hook '${tagName}' in '${filePath}' ` +
|
|
150
|
+
`contains custom code but is no longer present in template.`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
match = anyTagRegex.exec(sourceContent);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function _normalizeHookBlock(hookBlock, tagName) {
|
|
159
|
+
if (typeof hookBlock !== 'string' || hookBlock.length === 0) {
|
|
160
|
+
return hookBlock;
|
|
161
|
+
}
|
|
162
|
+
const esc = tagName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
163
|
+
const openRegex = new RegExp(`(//\\s*\\(<\\s*${esc}\\s*)(?!>)\\)`);
|
|
164
|
+
const closeRegex = new RegExp(`(//\\s*\\(<\\/\\s*${esc}\\s*)(?!>)\\)`);
|
|
165
|
+
let normalized = hookBlock.replace(openRegex, '$1>)');
|
|
166
|
+
normalized = normalized.replace(closeRegex, '$1>)');
|
|
167
|
+
return normalized;
|
|
168
|
+
}
|
|
169
|
+
|
|
70
170
|
function _extractTagName(p1, p2) {
|
|
71
171
|
if (typeof p1 === 'string' && p1.length > 0) {
|
|
72
172
|
return p1;
|
|
@@ -85,8 +185,8 @@ function hookMatchers(filePath) {
|
|
|
85
185
|
lower.endsWith('.cjs')
|
|
86
186
|
) {
|
|
87
187
|
const jsP1 =
|
|
88
|
-
'\\/\\/\\s*\\(<\\s*([\\w.-]+)\\s
|
|
89
|
-
'\\/\\/\\s*\\(<\\/\\s*\\1\\s
|
|
188
|
+
'\\/\\/\\s*\\(<\\s*([\\w.-]+)\\s*>?\\s*\\)([\\s\\S]*?)' +
|
|
189
|
+
'\\/\\/\\s*\\(<\\/\\s*\\1\\s*>?\\s*\\)';
|
|
90
190
|
const jsP2 =
|
|
91
191
|
'\\/\\/\\s*<#\\s*([\\w.-]+)\\s*#>([\\s\\S]*?)' +
|
|
92
192
|
'\\/\\/\\s*<#\\/\\s*\\3\\s*#>';
|
|
@@ -95,8 +195,8 @@ function hookMatchers(filePath) {
|
|
|
95
195
|
tagPattern: (name) => {
|
|
96
196
|
const esc = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
97
197
|
const p1 =
|
|
98
|
-
`\\/\\/\\s*\\(<\\s*${esc}\\s
|
|
99
|
-
`\\/\\/\\s*\\(<\\/\\s*${esc}\\s
|
|
198
|
+
`\\/\\/\\s*\\(<\\s*${esc}\\s*>?\\s*\\)([\\s\\S]*?)` +
|
|
199
|
+
`\\/\\/\\s*\\(<\\/\\s*${esc}\\s*>?\\s*\\)`;
|
|
100
200
|
const p2 =
|
|
101
201
|
`\\/\\/\\s*<#\\s*${esc}\\s*#>([\\s\\S]*?)` +
|
|
102
202
|
`\\/\\/\\s*<#\\/\\s*${esc}\\s*#>`;
|
|
@@ -52,10 +52,6 @@ import {
|
|
|
52
52
|
attributeTreeObjectSchemaManagedPaths,
|
|
53
53
|
attributeTreeRelationshipSchemaManagedPaths
|
|
54
54
|
} from '../emit/schemas/attributeTree/emitAttributeTreeSchemas.js';
|
|
55
|
-
import {
|
|
56
|
-
emitAttributeTreeTargetValueStandard,
|
|
57
|
-
attributeTreeTargetValueStandardManagedPaths
|
|
58
|
-
} from '../emit/schemas/attributeTree/emitAttributeTreeTargetValueStandard.js';
|
|
59
55
|
import {
|
|
60
56
|
emitLedgerObjectSchemas,
|
|
61
57
|
emitLedgerRelationshipSchemas,
|
|
@@ -81,7 +77,6 @@ export async function resolveEmissionGroups(context) {
|
|
|
81
77
|
flows,
|
|
82
78
|
notifications,
|
|
83
79
|
errors,
|
|
84
|
-
directives = [],
|
|
85
80
|
objectGraphServiceTags,
|
|
86
81
|
relationshipGraphServiceTags,
|
|
87
82
|
logger
|
|
@@ -206,11 +201,6 @@ export async function resolveEmissionGroups(context) {
|
|
|
206
201
|
managedPaths: rbacFlowSchemaManagedPaths(objects),
|
|
207
202
|
artifacts: rbacFlowArtifacts
|
|
208
203
|
},
|
|
209
|
-
{
|
|
210
|
-
scope: 'attribute-tree-target-value-standard',
|
|
211
|
-
managedPaths: attributeTreeTargetValueStandardManagedPaths(directives),
|
|
212
|
-
artifacts: emitAttributeTreeTargetValueStandard({ directives })
|
|
213
|
-
},
|
|
214
204
|
{
|
|
215
205
|
scope: 'attribute-tree-object-schemas',
|
|
216
206
|
managedPaths: attributeTreeObjectSchemaManagedPaths(objects),
|