@izara_project/izara-core-generate-service-code 1.0.99 → 1.0.101
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/compile/compileProject.js +16 -5
- package/src/compile/flowValidator.js +3 -1
- package/src/compile/loadSchemas.js +18 -4
- package/src/compile/schemaValidatorSetup.js +16 -0
- package/src/emit/application/coreFunction/emitCoreFunctionArtifacts.js +108 -108
- package/src/emit/application/coreFunction/templates/InitialSetupRunner.ejs +2 -2
- package/src/emit/application/crud/templates/CreateComplete_Main.ejs +2 -0
- package/src/emit/application/crud/templates/CreateLogicalLib.ejs +20 -7
- package/src/emit/application/crud/templates/DeleteComplete_Main.ejs +2 -0
- package/src/emit/application/crud/templates/FlowEndpoint_HdrWbs.ejs +7 -5
- package/src/emit/application/crud/templates/UpdateComplete_Main.ejs +2 -0
- package/src/emit/application/custom/customFlowPaths.js +13 -16
- package/src/emit/application/custom/customFlowS3.js +456 -63
- package/src/emit/application/custom/customFlows.js +131 -0
- package/src/emit/application/custom/emitCustomFlowApplicationArtifacts.js +7 -4
- package/src/emit/application/custom/templates/s3/ConfirmAfterUpload_HdrSqs.ejs +111 -0
- package/src/emit/application/custom/templates/s3/ConfirmAfterUpload_Main.ejs +89 -0
- package/src/emit/application/custom/templates/s3/ConfirmComplete_HdrSqs.ejs +111 -0
- package/src/emit/application/custom/templates/s3/ConfirmComplete_Main.ejs +100 -0
- package/src/emit/application/custom/templates/s3/CreateObject_HdrWbs.ejs +2 -1
- package/src/emit/application/custom/templates/s3/CreateObject_Main.ejs +4 -0
- package/src/emit/application/custom/templates/s3/CreatePreSignUrl_HdrSqs.ejs +42 -0
- package/src/emit/application/custom/templates/s3/CreatePreSignUrl_Main.ejs +23 -0
- package/src/emit/application/custom/templates/s3/ProcessS3File_Main.ejs +240 -27
- package/src/emit/application/custom/templates/s3/Start_Main.ejs +216 -0
- package/src/emit/application/rbac/emitDynamicRbacFlowSchemas.js +5 -1
- package/src/emit/application/rbac/emitRbacApplicationArtifacts.js +67 -60
- package/src/emit/application/rbac/templates/ReservedDynamicUsage_Main.ejs +27 -9
- package/src/emit/application/rbac/templates/UserRbacFlow_Main.ejs +4 -3
- package/src/emit/application/system/templates/Register_HdrWbs.ejs +2 -1
- package/src/emit/infrastructure/functions/functionYamlArtifact.js +3 -8
- package/src/emit/infrastructure/functions/templates/S3Function_Yaml.ejs +2 -3
- package/src/emit/infrastructure/resources/iamResourceArtifacts.js +22 -0
- package/src/emit/infrastructure/resources/resourceState.js +15 -5
- package/src/emit/infrastructure/resources/s3ResourceArtifacts.js +19 -3
- package/src/emit/infrastructure/resources/templates/S3Bucket_Yaml.ejs +1 -1
- package/src/emit/infrastructure/resources/templates/SystemDynamoDB_Yaml.ejs +2 -2
- package/src/emit/schemas/error/emitErrorSchemas.js +120 -10
- package/src/emit/schemas/error/templates/errorConsts.ejs +12 -0
- package/src/generate.js +32 -30
- package/src/plan/domainRegistry.js +2 -1
- package/test/integration/publicApi.test.js +33 -24
- package/test/unit/application/createTargetRoleHandler.test.js +33 -10
- package/test/unit/application/customFlowApplicationArtifacts.test.js +6 -6
- package/test/unit/application/customFlowS3.test.js +858 -0
- package/test/unit/infrastructure/functionYamlArtifact.test.js +6 -4
- package/test/unit/infrastructure/resourceArtifacts.test.js +51 -0
- package/test/unit/schemas/dynamicRbacFlowSchemas.test.js +8 -6
- package/test/unit/schemas/dynamicRelationshipFlowSchemas.test.js +51 -0
- package/test/unit/schemas/errorSchemas.test.js +84 -5
|
@@ -161,7 +161,7 @@ export default async function ReservedDynamicUsage(_izContext, requestParams) {
|
|
|
161
161
|
let passStatus = false;
|
|
162
162
|
if (currentTotal + value <= maxLimit) {
|
|
163
163
|
passStatus = true;
|
|
164
|
-
const
|
|
164
|
+
const expirationTime = Math.floor(Date.now() / 1000) + 3600;
|
|
165
165
|
|
|
166
166
|
await dynamodbSharedLib.putItem(
|
|
167
167
|
_izContext,
|
|
@@ -173,7 +173,7 @@ export default async function ReservedDynamicUsage(_izContext, requestParams) {
|
|
|
173
173
|
flowTypeConcat,
|
|
174
174
|
usageCount: value,
|
|
175
175
|
runningTotalUsageStatus: 'reserved',
|
|
176
|
-
|
|
176
|
+
expirationTime
|
|
177
177
|
}
|
|
178
178
|
);
|
|
179
179
|
|
|
@@ -196,13 +196,31 @@ export default async function ReservedDynamicUsage(_izContext, requestParams) {
|
|
|
196
196
|
identifiersConcat
|
|
197
197
|
};
|
|
198
198
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
199
|
+
const eventSourceTag =
|
|
200
|
+
_izContext.izEventSource?.eventSourceTag ||
|
|
201
|
+
requestParams?.izEventSource?.eventSourceTag;
|
|
202
|
+
|
|
203
|
+
let isSync = false;
|
|
204
|
+
if (
|
|
205
|
+
eventSourceTag === 'DirectInvoke' ||
|
|
206
|
+
eventSourceTag === 'ApiGateway' ||
|
|
207
|
+
eventSourceTag === 'ApiGatewayV2' ||
|
|
208
|
+
requestParams?.hasOwnProperty('__context__')
|
|
209
|
+
) {
|
|
210
|
+
isSync = true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (!isSync) {
|
|
214
|
+
await sns.publishMessage(_izContext, {
|
|
215
|
+
message: { returnResult },
|
|
216
|
+
TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
|
|
217
|
+
_izContext,
|
|
218
|
+
'ReservedDynamicUsage_Out'
|
|
219
|
+
)
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return returnResult;
|
|
206
224
|
} catch (error) {
|
|
207
225
|
_izContext.logger.error(`[${process.env.iz_serviceTag}:ReservedDynamicUsage] error:`, error);
|
|
208
226
|
throw error;
|
|
@@ -55,7 +55,8 @@ async function _getL2Cache(_izContext, submittedByUserId, cacheSubKey) {
|
|
|
55
55
|
userId: submittedByUserId,
|
|
56
56
|
cacheKey: cacheSubKey
|
|
57
57
|
});
|
|
58
|
-
|
|
58
|
+
const expTime = result && (result.expirationTime || result.ttlTimestamp);
|
|
59
|
+
if (expTime && expTime > Math.floor(Date.now() / 1000)) {
|
|
59
60
|
return {
|
|
60
61
|
returnValue: {
|
|
61
62
|
permission: Boolean(result.permission),
|
|
@@ -75,14 +76,14 @@ async function _getL2Cache(_izContext, submittedByUserId, cacheSubKey) {
|
|
|
75
76
|
async function _setL2Cache(_izContext, submittedByUserId, cacheSubKey, value) {
|
|
76
77
|
try {
|
|
77
78
|
const tableName = dynamodbSharedLib.tableName(_izContext, 'RbacPermissionCaches');
|
|
78
|
-
const
|
|
79
|
+
const expirationTime = Math.floor(Date.now() / 1000) + L2_TTL_SECONDS;
|
|
79
80
|
await dynamodbSharedLib.putItem(_izContext, tableName, {
|
|
80
81
|
userId: submittedByUserId,
|
|
81
82
|
cacheKey: cacheSubKey,
|
|
82
83
|
permission: value.returnValue ? value.returnValue.permission : value.permission,
|
|
83
84
|
roleIdKey: value.returnValue ? value.returnValue.roleIdKey : value.roleIdKey,
|
|
84
85
|
rbacType: 'target',
|
|
85
|
-
|
|
86
|
+
expirationTime
|
|
86
87
|
});
|
|
87
88
|
} catch (error) {
|
|
88
89
|
// Safely ignore L2 write errors if table is not configured
|
|
@@ -27,7 +27,8 @@ export const main = middleware.wrap(async (event, context) => {
|
|
|
27
27
|
await dynamodbSharedLib.tableName(event._izContext, 'WebSocketTask'),
|
|
28
28
|
{
|
|
29
29
|
taskKey: correlationId,
|
|
30
|
-
connectionId: connectionId
|
|
30
|
+
connectionId: connectionId,
|
|
31
|
+
expirationTime: Math.floor(Date.now() / 1000) + 7200
|
|
31
32
|
}
|
|
32
33
|
);
|
|
33
34
|
|
|
@@ -181,6 +181,7 @@ export function renderS3FunctionYaml({
|
|
|
181
181
|
handlerPath,
|
|
182
182
|
roleName,
|
|
183
183
|
bucketName,
|
|
184
|
+
s3KeyPrefix,
|
|
184
185
|
flowTag,
|
|
185
186
|
flowStepName
|
|
186
187
|
}) {
|
|
@@ -189,6 +190,7 @@ export function renderS3FunctionYaml({
|
|
|
189
190
|
handlerPath,
|
|
190
191
|
roleName,
|
|
191
192
|
bucketName,
|
|
193
|
+
s3KeyPrefix,
|
|
192
194
|
environmentSetting: _environmentSetting(
|
|
193
195
|
functionName,
|
|
194
196
|
flowTag,
|
|
@@ -342,15 +344,8 @@ function _sqsFilterPatterns(
|
|
|
342
344
|
- body:
|
|
343
345
|
MessageAttributes:
|
|
344
346
|
callingFlow:
|
|
345
|
-
|
|
347
|
+
StringValue:
|
|
346
348
|
- \${self:custom.iz_resourcePrefix}${callingFlowName}
|
|
347
|
-
- body:
|
|
348
|
-
MessageAttributes:
|
|
349
|
-
callingFlow:
|
|
350
|
-
- exists: false
|
|
351
|
-
- body:
|
|
352
|
-
MessageAttributes:
|
|
353
|
-
- exists: false
|
|
354
349
|
#<#/${hookFunctionName}FilterPatterns#>
|
|
355
350
|
`;
|
|
356
351
|
}
|
|
@@ -8,9 +8,8 @@
|
|
|
8
8
|
bucket: <%= bucketName %>
|
|
9
9
|
event: s3:ObjectCreated:*
|
|
10
10
|
existing: true
|
|
11
|
-
rules
|
|
11
|
+
rules:<% if (s3KeyPrefix) { %>
|
|
12
|
+
- prefix: <%- s3KeyPrefix %><% } %>
|
|
12
13
|
#<#<%= functionName %>rules#>
|
|
13
14
|
#<#/<%= functionName %>rules#>
|
|
14
|
-
- prefix: uploads/
|
|
15
|
-
- suffix: .csv
|
|
16
15
|
forceDeploy: true
|
|
@@ -139,6 +139,25 @@ export function roleStatements(state, roleName) {
|
|
|
139
139
|
resource: ['arn:aws:s3:::${self:custom.iz_serviceSchemaBucketName}']
|
|
140
140
|
});
|
|
141
141
|
}
|
|
142
|
+
const s3FlowsForRole = (state.flows || []).filter((candidate) => {
|
|
143
|
+
const roleCandidate = customFlowRoleName(candidate?.flowTag ?? '');
|
|
144
|
+
return (
|
|
145
|
+
roleCandidate === roleName &&
|
|
146
|
+
includesEvent(candidate, 's3') &&
|
|
147
|
+
Boolean(candidate.bucketName)
|
|
148
|
+
);
|
|
149
|
+
});
|
|
150
|
+
if (s3FlowsForRole.length > 0) {
|
|
151
|
+
const bucketResources = [
|
|
152
|
+
...new Set(
|
|
153
|
+
s3FlowsForRole.map((flow) => `arn:aws:s3:::${flow.bucketName}/*`)
|
|
154
|
+
)
|
|
155
|
+
].sort();
|
|
156
|
+
statements.push({
|
|
157
|
+
action: ['s3:GetObject'],
|
|
158
|
+
resource: bucketResources
|
|
159
|
+
});
|
|
160
|
+
}
|
|
142
161
|
addStatement(statements, dynamoActionsForRole(roleName), [
|
|
143
162
|
...dynamoArns(roleTableNames(state, roleName)),
|
|
144
163
|
...externalDynamoArns(roleExternalDynamoTables(state, roleName))
|
|
@@ -479,6 +498,9 @@ export function roleTopicNames(state, roleName) {
|
|
|
479
498
|
if (flowHasOwnTopic(flow)) {
|
|
480
499
|
topics.push(`${name}_In`);
|
|
481
500
|
}
|
|
501
|
+
if (includesEvent(flow, 's3')) {
|
|
502
|
+
topics.push('Create_In');
|
|
503
|
+
}
|
|
482
504
|
return topics;
|
|
483
505
|
}
|
|
484
506
|
return [];
|
|
@@ -4,7 +4,8 @@ import {
|
|
|
4
4
|
flowExtTopicSteps,
|
|
5
5
|
generatedDsqFlows,
|
|
6
6
|
generatedOwnTopicFlows,
|
|
7
|
-
supportedCustomFlows
|
|
7
|
+
supportedCustomFlows,
|
|
8
|
+
resolveS3Parts
|
|
8
9
|
} from '../../application/custom/customFlows.js';
|
|
9
10
|
import { hasSystemWebSocket } from '../../application/system/emitSystemWebSocketArtifacts.js';
|
|
10
11
|
import { rbacFlowTag } from '../../application/rbac/rbacFlowTag.js';
|
|
@@ -161,9 +162,12 @@ export function createResourceState({ objects, relationships, flows }) {
|
|
|
161
162
|
|
|
162
163
|
for (const flow of flows.filter((flow) => includesEvent(flow, 's3'))) {
|
|
163
164
|
const tag = upperFirst(flow.flowTag);
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
const parts = resolveS3Parts(flow);
|
|
166
|
+
for (const part of parts) {
|
|
167
|
+
if (part.type === 'sqs') {
|
|
168
|
+
customSqsQueues.push(`${tag}${part.action}HdrSqs`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
167
171
|
}
|
|
168
172
|
|
|
169
173
|
const customDsqQueues = flows
|
|
@@ -281,8 +285,14 @@ export function createResourceState({ objects, relationships, flows }) {
|
|
|
281
285
|
topicName = `Out${upperFirst(flow.outputTopic)}`;
|
|
282
286
|
}
|
|
283
287
|
}
|
|
288
|
+
let targetAction = 'ReservedLimit';
|
|
289
|
+
const parts = resolveS3Parts(flow);
|
|
290
|
+
const firstSqsPart = parts.find((p) => p.type === 'sqs');
|
|
291
|
+
if (firstSqsPart && firstSqsPart.action) {
|
|
292
|
+
targetAction = firstSqsPart.action;
|
|
293
|
+
}
|
|
284
294
|
const queueName =
|
|
285
|
-
flow.s3Subscription?.queueName || `${tag}
|
|
295
|
+
flow.s3Subscription?.queueName || `${tag}${targetAction}HdrSqs`;
|
|
286
296
|
const subscriptionName =
|
|
287
297
|
flow.s3Subscription?.subscriptionName ||
|
|
288
298
|
`Subscription${topicName}To${queueName}`;
|
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
import { indentYaml, renderResourceTemplate } from './resourceTemplate.js';
|
|
2
2
|
|
|
3
|
+
export function toAlphanumericPascalCase(str) {
|
|
4
|
+
if (!str) {
|
|
5
|
+
return '';
|
|
6
|
+
}
|
|
7
|
+
const parts = str.split(/[^a-zA-Z0-9]+/);
|
|
8
|
+
const validParts = parts.filter(Boolean);
|
|
9
|
+
const capitalizedParts = validParts.map((word) => {
|
|
10
|
+
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
11
|
+
});
|
|
12
|
+
return capitalizedParts.join('');
|
|
13
|
+
}
|
|
14
|
+
|
|
3
15
|
export async function renderS3(state) {
|
|
4
16
|
if (state.s3Buckets.length === 0) {
|
|
5
17
|
return '';
|
|
6
18
|
}
|
|
7
19
|
const renderedBuckets = await Promise.all(
|
|
8
|
-
state.s3Buckets.map((bucketName) =>
|
|
9
|
-
|
|
10
|
-
|
|
20
|
+
state.s3Buckets.map((bucketName) => {
|
|
21
|
+
const bucketLogicalId = `${toAlphanumericPascalCase(bucketName)}S3Bucket`;
|
|
22
|
+
return renderResourceTemplate('S3Bucket_Yaml.ejs', {
|
|
23
|
+
bucketName,
|
|
24
|
+
bucketLogicalId
|
|
25
|
+
});
|
|
26
|
+
})
|
|
11
27
|
);
|
|
12
28
|
const body = renderedBuckets.join('\n');
|
|
13
29
|
return `Resources:\n${indentYaml(body, 2)}`;
|
|
@@ -230,7 +230,7 @@ Resources:
|
|
|
230
230
|
- AttributeName: cacheKey
|
|
231
231
|
KeyType: RANGE
|
|
232
232
|
TimeToLiveSpecification:
|
|
233
|
-
AttributeName:
|
|
233
|
+
AttributeName: expirationTime
|
|
234
234
|
Enabled: true
|
|
235
235
|
|
|
236
236
|
RunningTotalUsageTable:
|
|
@@ -265,7 +265,7 @@ Resources:
|
|
|
265
265
|
- AttributeName: usedTimestampId
|
|
266
266
|
KeyType: RANGE
|
|
267
267
|
TimeToLiveSpecification:
|
|
268
|
-
AttributeName:
|
|
268
|
+
AttributeName: expirationTime
|
|
269
269
|
Enabled: true
|
|
270
270
|
StreamSpecification:
|
|
271
271
|
StreamViewType: NEW_AND_OLD_IMAGES
|
|
@@ -1,19 +1,43 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
1
2
|
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
2
4
|
import ejsLib from 'ejs';
|
|
3
5
|
|
|
4
6
|
const TEMPLATE_URL = new URL('./templates/', import.meta.url);
|
|
5
7
|
export const ERROR_SCHEMA_PATH =
|
|
6
|
-
'app/src/schemas/ErrorSchemas/generateCode/
|
|
8
|
+
'app/src/schemas/ErrorSchemas/generateCode/errorConsts.js';
|
|
9
|
+
export const ERROR_CONSTS_PATH = ERROR_SCHEMA_PATH;
|
|
10
|
+
|
|
11
|
+
export const SHARED_ERROR_PACKAGE_PATH = 'libs/errorLib/package.json';
|
|
12
|
+
export const SHARED_ERROR_INDEX_PATH = 'libs/errorLib/index.js';
|
|
13
|
+
export const SHARED_ERROR_CONSTS_PATH = 'libs/errorLib/src/errorConsts.js';
|
|
7
14
|
|
|
8
15
|
export function errorSchemaManagedPaths(errors = []) {
|
|
9
16
|
if (!errors || errors.length === 0) {
|
|
10
17
|
return [];
|
|
11
18
|
}
|
|
12
|
-
|
|
19
|
+
const paths = [ERROR_SCHEMA_PATH];
|
|
20
|
+
const safeErrors = Array.isArray(errors) ? errors : [];
|
|
21
|
+
let hasShared = false;
|
|
22
|
+
for (const item of safeErrors) {
|
|
23
|
+
if (item && item.sharedLibrary === true) {
|
|
24
|
+
hasShared = true;
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (hasShared) {
|
|
29
|
+
paths.push(
|
|
30
|
+
SHARED_ERROR_PACKAGE_PATH,
|
|
31
|
+
SHARED_ERROR_INDEX_PATH,
|
|
32
|
+
SHARED_ERROR_CONSTS_PATH
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
return paths;
|
|
13
36
|
}
|
|
14
37
|
|
|
15
38
|
/**
|
|
16
|
-
* Emits
|
|
39
|
+
* Emits errorConsts.js artifact and shared library artifacts based on
|
|
40
|
+
* loaded ErrorSchemas
|
|
17
41
|
*
|
|
18
42
|
* @param {Object} params
|
|
19
43
|
* @param {Object} params.project
|
|
@@ -31,21 +55,28 @@ export async function emitErrorSchemas({
|
|
|
31
55
|
return [];
|
|
32
56
|
}
|
|
33
57
|
|
|
34
|
-
const defaultGraphServiceTag =
|
|
35
|
-
objectGraphServiceTags[0] || 'GraphHandler';
|
|
36
58
|
const defaultServiceTag = project?.serviceTag || '';
|
|
37
59
|
|
|
38
60
|
const errorTypes = {};
|
|
61
|
+
const sharedErrorTypes = {};
|
|
62
|
+
|
|
39
63
|
for (const item of safeErrors) {
|
|
40
64
|
if (!item || typeof item !== 'object' || !item.errorTag) {
|
|
41
65
|
continue;
|
|
42
66
|
}
|
|
43
67
|
const errorTag = item.errorTag;
|
|
44
|
-
|
|
45
|
-
|
|
68
|
+
let serviceTag = item.serviceTag;
|
|
69
|
+
if (!serviceTag) {
|
|
70
|
+
serviceTag = defaultServiceTag;
|
|
71
|
+
}
|
|
72
|
+
const errorEntry = {
|
|
46
73
|
errorTag,
|
|
47
74
|
serviceTag
|
|
48
75
|
};
|
|
76
|
+
errorTypes[errorTag] = errorEntry;
|
|
77
|
+
if (item.sharedLibrary === true) {
|
|
78
|
+
sharedErrorTypes[errorTag] = errorEntry;
|
|
79
|
+
}
|
|
49
80
|
}
|
|
50
81
|
|
|
51
82
|
const sortedKeys = Object.keys(errorTypes).sort();
|
|
@@ -58,16 +89,95 @@ export async function emitErrorSchemas({
|
|
|
58
89
|
sortedErrorTypes[key] = errorTypes[key];
|
|
59
90
|
}
|
|
60
91
|
|
|
61
|
-
const templatePath = new URL('
|
|
92
|
+
const templatePath = new URL('errorConsts.ejs', TEMPLATE_URL);
|
|
62
93
|
const template = await readFile(templatePath, 'utf8');
|
|
63
94
|
const content = ejsLib.render(template, {
|
|
64
|
-
errorTypes: sortedErrorTypes
|
|
95
|
+
errorTypes: sortedErrorTypes,
|
|
96
|
+
headerComment: null
|
|
65
97
|
});
|
|
66
98
|
|
|
67
|
-
|
|
99
|
+
const artifacts = [
|
|
68
100
|
{
|
|
69
101
|
path: ERROR_SCHEMA_PATH,
|
|
70
102
|
content
|
|
71
103
|
}
|
|
72
104
|
];
|
|
105
|
+
|
|
106
|
+
const sortedSharedKeys = Object.keys(sharedErrorTypes).sort();
|
|
107
|
+
if (sortedSharedKeys.length > 0) {
|
|
108
|
+
const sortedSharedErrorTypes = {};
|
|
109
|
+
for (const key of sortedSharedKeys) {
|
|
110
|
+
sortedSharedErrorTypes[key] = sharedErrorTypes[key];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const sharedContent = ejsLib.render(template, {
|
|
114
|
+
errorTypes: sortedSharedErrorTypes,
|
|
115
|
+
headerComment: '// only generate error that sharedLibrary: true'
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
let serviceName = 'service';
|
|
119
|
+
if (project?.serviceTag) {
|
|
120
|
+
serviceName = project.serviceTag.toLowerCase();
|
|
121
|
+
}
|
|
122
|
+
const packageName = `@izara_project/izara-${serviceName}-library-errorlib`;
|
|
123
|
+
|
|
124
|
+
let packageJson = {
|
|
125
|
+
name: packageName,
|
|
126
|
+
version: '1.0.0',
|
|
127
|
+
description: '',
|
|
128
|
+
type: 'module',
|
|
129
|
+
main: 'index.js',
|
|
130
|
+
scripts: {},
|
|
131
|
+
keywords: [],
|
|
132
|
+
author: '',
|
|
133
|
+
license: 'ISC',
|
|
134
|
+
devDependencies: {},
|
|
135
|
+
dependencies: {}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
let targetDir = project?.outputPath;
|
|
139
|
+
if (!targetDir) {
|
|
140
|
+
targetDir = project?.rootPath;
|
|
141
|
+
}
|
|
142
|
+
if (targetDir) {
|
|
143
|
+
const existingPkgPath = join(targetDir, SHARED_ERROR_PACKAGE_PATH);
|
|
144
|
+
if (existsSync(existingPkgPath)) {
|
|
145
|
+
try {
|
|
146
|
+
const raw = readFileSync(existingPkgPath, 'utf8');
|
|
147
|
+
const parsed = JSON.parse(raw);
|
|
148
|
+
if (parsed && typeof parsed === 'object') {
|
|
149
|
+
packageJson = {
|
|
150
|
+
...parsed,
|
|
151
|
+
name: packageName,
|
|
152
|
+
type: 'module',
|
|
153
|
+
main: 'index.js'
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
} catch {
|
|
157
|
+
// If parsing fails (e.g. comments in JSON), use default packageJson
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const packageJsonContent = `${JSON.stringify(packageJson, null, 2)}\n`;
|
|
163
|
+
const indexContent =
|
|
164
|
+
"import * as errorLib from './src/errorConsts.js';\n\nexport { errorLib };\n";
|
|
165
|
+
|
|
166
|
+
artifacts.push(
|
|
167
|
+
{
|
|
168
|
+
path: SHARED_ERROR_PACKAGE_PATH,
|
|
169
|
+
content: packageJsonContent
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
path: SHARED_ERROR_INDEX_PATH,
|
|
173
|
+
content: indexContent
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
path: SHARED_ERROR_CONSTS_PATH,
|
|
177
|
+
content: sharedContent
|
|
178
|
+
}
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return artifacts;
|
|
73
183
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<% if (locals.headerComment) { -%>
|
|
2
|
+
<%- headerComment %>
|
|
3
|
+
<% } -%>
|
|
4
|
+
export const ERROR_TYPES = {
|
|
5
|
+
<% const keys = Object.keys(errorTypes); -%>
|
|
6
|
+
<% keys.forEach((key, index) => { -%>
|
|
7
|
+
<%- key %>: {
|
|
8
|
+
errorTag: '<%- errorTypes[key].errorTag %>',
|
|
9
|
+
serviceTag: '<%- errorTypes[key].serviceTag %>'
|
|
10
|
+
}<%- index < keys.length - 1 ? ',' : '' %>
|
|
11
|
+
<% }); -%>
|
|
12
|
+
};
|
package/src/generate.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { mkdir } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
|
|
1
4
|
import { buildGenerationPlan } from './plan/buildGenerationPlan.js';
|
|
2
5
|
import { resolveEmissionGroups } from './plan/domainRegistry.js';
|
|
3
6
|
import { commitArtifacts } from './output/commitArtifacts.js';
|
|
@@ -13,32 +16,32 @@ export async function generate(rootPath, options = {}) {
|
|
|
13
16
|
console.log(`[INFO] [Generate] STATUS=STARTED | rootPath=${rootPath}`);
|
|
14
17
|
try {
|
|
15
18
|
const project = compileProject({ ...options, rootPath });
|
|
19
|
+
await mkdir(join(project.schemaRoot, 'ErrorSchemas'), {
|
|
20
|
+
recursive: true
|
|
21
|
+
});
|
|
16
22
|
const schemaLoadOptions = {
|
|
17
23
|
skipValidation: options.skipValidation
|
|
18
24
|
};
|
|
19
|
-
const [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
'NotificationSchemas',
|
|
36
|
-
schemaLoadOptions
|
|
37
|
-
),
|
|
38
|
-
loadLocalSchemaDirectory(project, 'ErrorSchemas', schemaLoadOptions)
|
|
39
|
-
]);
|
|
25
|
+
const [rawObjects, relationships, flows, notifications, errors] =
|
|
26
|
+
await Promise.all([
|
|
27
|
+
loadLocalSchemaDirectory(project, 'ObjectSchemas', schemaLoadOptions),
|
|
28
|
+
loadLocalSchemaDirectory(
|
|
29
|
+
project,
|
|
30
|
+
'RelationshipSchemas',
|
|
31
|
+
schemaLoadOptions
|
|
32
|
+
),
|
|
33
|
+
loadLocalSchemaDirectory(project, 'FlowSchemas', schemaLoadOptions),
|
|
34
|
+
loadLocalSchemaDirectory(
|
|
35
|
+
project,
|
|
36
|
+
'NotificationSchemas',
|
|
37
|
+
schemaLoadOptions
|
|
38
|
+
),
|
|
39
|
+
loadLocalSchemaDirectory(project, 'ErrorSchemas', schemaLoadOptions)
|
|
40
|
+
]);
|
|
40
41
|
const schemaBucketName =
|
|
41
|
-
options.schemaBucketName ||
|
|
42
|
+
options.schemaBucketName ||
|
|
43
|
+
project.schemaBucketName ||
|
|
44
|
+
process.env.iz_serviceSchemaBucketName;
|
|
42
45
|
const objects = await resolveExtendedObjectSchemas(
|
|
43
46
|
rawObjects,
|
|
44
47
|
schemaBucketName
|
|
@@ -58,17 +61,17 @@ export async function generate(rootPath, options = {}) {
|
|
|
58
61
|
}
|
|
59
62
|
console.log(
|
|
60
63
|
`[INFO] [Generate] STATUS=SCHEMAS_LOADED | ` +
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
`objects=${objects.length}${objectSummary} ` +
|
|
65
|
+
`relationships=${relationships.length} flows=${flows.length} ` +
|
|
66
|
+
`notifications=${notifications.length} ` +
|
|
67
|
+
`errors=${errors.length}`
|
|
65
68
|
);
|
|
66
69
|
if (options.verbose || process.env.GENERATE_VERBOSE) {
|
|
67
70
|
for (const objectSchema of objects) {
|
|
68
71
|
const objectType = objectSchema.objectType || 'unknown';
|
|
69
72
|
console.log(
|
|
70
73
|
`[DEBUG] [Generate] OBJECT_SCHEMA | objectType=${objectType}\n` +
|
|
71
|
-
|
|
74
|
+
JSON.stringify(objectSchema, null, 2)
|
|
72
75
|
);
|
|
73
76
|
}
|
|
74
77
|
}
|
|
@@ -107,17 +110,16 @@ export async function generate(rootPath, options = {}) {
|
|
|
107
110
|
const durationMs = Date.now() - startedAt;
|
|
108
111
|
console.log(
|
|
109
112
|
`[INFO] [Generate] STATUS=FINISHED | ` +
|
|
110
|
-
|
|
113
|
+
`files=${files.length} durationMs=${durationMs}`
|
|
111
114
|
);
|
|
112
115
|
|
|
113
116
|
return { project, durationMs, files };
|
|
114
117
|
} catch (error) {
|
|
115
118
|
console.log(
|
|
116
119
|
`[ERROR] [Generate] STATUS=FAILED | ` +
|
|
117
|
-
|
|
120
|
+
`durationMs=${Date.now() - startedAt}`,
|
|
118
121
|
error
|
|
119
122
|
);
|
|
120
123
|
throw error;
|
|
121
124
|
}
|
|
122
125
|
}
|
|
123
|
-
|
|
@@ -23,7 +23,7 @@ test('generate commits schemas, application artifacts, and resources in one publ
|
|
|
23
23
|
const rootPath = await createCrudProject();
|
|
24
24
|
const result = await generate(rootPath);
|
|
25
25
|
|
|
26
|
-
assert.strictEqual(result.files.length,
|
|
26
|
+
assert.strictEqual(result.files.length, 59);
|
|
27
27
|
assert.equal(result.project.rootPath, rootPath);
|
|
28
28
|
assert.match(
|
|
29
29
|
await readFile(
|
|
@@ -366,7 +366,7 @@ test('generate removes stale CRUD schemas and application artifacts', async () =
|
|
|
366
366
|
|
|
367
367
|
const result = await generate(rootPath);
|
|
368
368
|
|
|
369
|
-
assert.equal(result.files.length,
|
|
369
|
+
assert.equal(result.files.length, 23);
|
|
370
370
|
await assert.rejects(
|
|
371
371
|
readFile(
|
|
372
372
|
join(
|
|
@@ -448,36 +448,45 @@ async function createTriggerCacheProject() {
|
|
|
448
448
|
}
|
|
449
449
|
|
|
450
450
|
test(
|
|
451
|
-
'emits ignoreExternalServices hooks in
|
|
452
|
-
'commands/uploadSchema.js',
|
|
451
|
+
'emits ignoreExternalServices hooks in commands/uploadSchema.js',
|
|
453
452
|
async () => {
|
|
454
453
|
const rootPath = await createCrudProject();
|
|
455
454
|
await generate(rootPath);
|
|
456
455
|
|
|
457
|
-
const uploadSchemaMain = await readFile(
|
|
456
|
+
// const uploadSchemaMain = await readFile(
|
|
457
|
+
// join(
|
|
458
|
+
// rootPath,
|
|
459
|
+
// 'app/src/generatedCode/CoreFunctions/UploadSchema_Main.js'
|
|
460
|
+
// ),
|
|
461
|
+
// 'utf8'
|
|
462
|
+
// );
|
|
463
|
+
// assert.match(uploadSchemaMain, /\/\/\(<ignoreExternalServices>\)/);
|
|
464
|
+
// assert.match(uploadSchemaMain, /\/\/\(<\/ignoreExternalServices>\)/);
|
|
465
|
+
|
|
466
|
+
// const coreResource = await readFile(
|
|
467
|
+
// join(
|
|
468
|
+
// rootPath,
|
|
469
|
+
// 'app/sls_yaml/generatedCode/source/core-resource.yml'
|
|
470
|
+
// ),
|
|
471
|
+
// 'utf8'
|
|
472
|
+
// );
|
|
473
|
+
// assert.match(
|
|
474
|
+
// coreResource,
|
|
475
|
+
// /UserAccount\$\{self:custom\.iz_stage\}AddLinkCreateByRelHdrInv/
|
|
476
|
+
// );
|
|
477
|
+
// assert.match(
|
|
478
|
+
// coreResource,
|
|
479
|
+
// /UserAccount\$\{self:custom\.iz_stage\}AddLinkBelongToHdrInv/
|
|
480
|
+
// );
|
|
481
|
+
|
|
482
|
+
const uploadSchemaCommand = await readFile(
|
|
458
483
|
join(
|
|
459
484
|
rootPath,
|
|
460
|
-
'app/
|
|
485
|
+
'app/initial_setup/generatedCode/source/commands/uploadSchema.js'
|
|
461
486
|
),
|
|
462
487
|
'utf8'
|
|
463
488
|
);
|
|
464
|
-
assert.match(
|
|
465
|
-
assert.match(
|
|
466
|
-
|
|
467
|
-
const coreResource = await readFile(
|
|
468
|
-
join(
|
|
469
|
-
rootPath,
|
|
470
|
-
'app/sls_yaml/generatedCode/source/core-resource.yml'
|
|
471
|
-
),
|
|
472
|
-
'utf8'
|
|
473
|
-
);
|
|
474
|
-
assert.match(
|
|
475
|
-
coreResource,
|
|
476
|
-
/UserAccount\$\{self:custom\.iz_stage\}AddLinkCreateByRelHdrInv/
|
|
477
|
-
);
|
|
478
|
-
assert.match(
|
|
479
|
-
coreResource,
|
|
480
|
-
/UserAccount\$\{self:custom\.iz_stage\}AddLinkBelongToHdrInv/
|
|
481
|
-
);
|
|
489
|
+
assert.match(uploadSchemaCommand, /\/\/\(<ignoreExternalServices>\)/);
|
|
490
|
+
assert.match(uploadSchemaCommand, /\/\/\(<\/ignoreExternalServices>\)/);
|
|
482
491
|
}
|
|
483
492
|
);
|