@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.
Files changed (51) hide show
  1. package/package.json +1 -1
  2. package/src/compile/compileProject.js +16 -5
  3. package/src/compile/flowValidator.js +3 -1
  4. package/src/compile/loadSchemas.js +18 -4
  5. package/src/compile/schemaValidatorSetup.js +16 -0
  6. package/src/emit/application/coreFunction/emitCoreFunctionArtifacts.js +108 -108
  7. package/src/emit/application/coreFunction/templates/InitialSetupRunner.ejs +2 -2
  8. package/src/emit/application/crud/templates/CreateComplete_Main.ejs +2 -0
  9. package/src/emit/application/crud/templates/CreateLogicalLib.ejs +20 -7
  10. package/src/emit/application/crud/templates/DeleteComplete_Main.ejs +2 -0
  11. package/src/emit/application/crud/templates/FlowEndpoint_HdrWbs.ejs +7 -5
  12. package/src/emit/application/crud/templates/UpdateComplete_Main.ejs +2 -0
  13. package/src/emit/application/custom/customFlowPaths.js +13 -16
  14. package/src/emit/application/custom/customFlowS3.js +456 -63
  15. package/src/emit/application/custom/customFlows.js +131 -0
  16. package/src/emit/application/custom/emitCustomFlowApplicationArtifacts.js +7 -4
  17. package/src/emit/application/custom/templates/s3/ConfirmAfterUpload_HdrSqs.ejs +111 -0
  18. package/src/emit/application/custom/templates/s3/ConfirmAfterUpload_Main.ejs +89 -0
  19. package/src/emit/application/custom/templates/s3/ConfirmComplete_HdrSqs.ejs +111 -0
  20. package/src/emit/application/custom/templates/s3/ConfirmComplete_Main.ejs +100 -0
  21. package/src/emit/application/custom/templates/s3/CreateObject_HdrWbs.ejs +2 -1
  22. package/src/emit/application/custom/templates/s3/CreateObject_Main.ejs +4 -0
  23. package/src/emit/application/custom/templates/s3/CreatePreSignUrl_HdrSqs.ejs +42 -0
  24. package/src/emit/application/custom/templates/s3/CreatePreSignUrl_Main.ejs +23 -0
  25. package/src/emit/application/custom/templates/s3/ProcessS3File_Main.ejs +240 -27
  26. package/src/emit/application/custom/templates/s3/Start_Main.ejs +216 -0
  27. package/src/emit/application/rbac/emitDynamicRbacFlowSchemas.js +5 -1
  28. package/src/emit/application/rbac/emitRbacApplicationArtifacts.js +67 -60
  29. package/src/emit/application/rbac/templates/ReservedDynamicUsage_Main.ejs +27 -9
  30. package/src/emit/application/rbac/templates/UserRbacFlow_Main.ejs +4 -3
  31. package/src/emit/application/system/templates/Register_HdrWbs.ejs +2 -1
  32. package/src/emit/infrastructure/functions/functionYamlArtifact.js +3 -8
  33. package/src/emit/infrastructure/functions/templates/S3Function_Yaml.ejs +2 -3
  34. package/src/emit/infrastructure/resources/iamResourceArtifacts.js +22 -0
  35. package/src/emit/infrastructure/resources/resourceState.js +15 -5
  36. package/src/emit/infrastructure/resources/s3ResourceArtifacts.js +19 -3
  37. package/src/emit/infrastructure/resources/templates/S3Bucket_Yaml.ejs +1 -1
  38. package/src/emit/infrastructure/resources/templates/SystemDynamoDB_Yaml.ejs +2 -2
  39. package/src/emit/schemas/error/emitErrorSchemas.js +120 -10
  40. package/src/emit/schemas/error/templates/errorConsts.ejs +12 -0
  41. package/src/generate.js +32 -30
  42. package/src/plan/domainRegistry.js +2 -1
  43. package/test/integration/publicApi.test.js +33 -24
  44. package/test/unit/application/createTargetRoleHandler.test.js +33 -10
  45. package/test/unit/application/customFlowApplicationArtifacts.test.js +6 -6
  46. package/test/unit/application/customFlowS3.test.js +858 -0
  47. package/test/unit/infrastructure/functionYamlArtifact.test.js +6 -4
  48. package/test/unit/infrastructure/resourceArtifacts.test.js +51 -0
  49. package/test/unit/schemas/dynamicRbacFlowSchemas.test.js +8 -6
  50. package/test/unit/schemas/dynamicRelationshipFlowSchemas.test.js +51 -0
  51. 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 ttlTimestamp = Math.floor(Date.now() / 1000) + 3600;
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
- ttlTimestamp
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
- await sns.publishMessage(_izContext, {
200
- message: { returnResult },
201
- TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
202
- _izContext,
203
- 'ReservedDynamicUsage_Out'
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
- if (result && result.ttlTimestamp && result.ttlTimestamp > Math.floor(Date.now() / 1000)) {
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 ttlTimestamp = Math.floor(Date.now() / 1000) + L2_TTL_SECONDS;
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
- ttlTimestamp
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
- Value:
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
- customSqsQueues.push(`${tag}CreatePreSignUrlHdrSqs`);
165
- customSqsQueues.push(`${tag}ReservedLimitHdrSqs`);
166
- customSqsQueues.push(`${tag}ConfirmAfterUploadHdrSqs`);
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}ReservedLimitHdrSqs`;
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
- renderResourceTemplate('S3Bucket_Yaml.ejs', { bucketName })
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)}`;
@@ -1,5 +1,5 @@
1
1
  ##===== [S3 Bucket: <%- bucketName %>]
2
- <%- bucketName %>S3Bucket:
2
+ <%- bucketLogicalId %>:
3
3
  Type: AWS::S3::Bucket
4
4
  Properties:
5
5
  BucketName: <%- bucketName %>
@@ -230,7 +230,7 @@ Resources:
230
230
  - AttributeName: cacheKey
231
231
  KeyType: RANGE
232
232
  TimeToLiveSpecification:
233
- AttributeName: ttlTimestamp
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: ttlTimestamp
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/errorSchema.js';
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
- return [ERROR_SCHEMA_PATH];
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 errorSchema.js artifact based on loaded ErrorSchemas
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
- const serviceTag = item.serviceTag || defaultServiceTag;
45
- errorTypes[errorTag] = {
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('errorSchema.ejs', TEMPLATE_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
- return [
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
- rawObjects,
21
- relationships,
22
- flows,
23
- notifications,
24
- errors
25
- ] = await Promise.all([
26
- loadLocalSchemaDirectory(project, 'ObjectSchemas', schemaLoadOptions),
27
- loadLocalSchemaDirectory(
28
- project,
29
- 'RelationshipSchemas',
30
- schemaLoadOptions
31
- ),
32
- loadLocalSchemaDirectory(project, 'FlowSchemas', schemaLoadOptions),
33
- loadLocalSchemaDirectory(
34
- project,
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 || process.env.iz_serviceSchemaBucketName;
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
- `objects=${objects.length}${objectSummary} ` +
62
- `relationships=${relationships.length} flows=${flows.length} ` +
63
- `notifications=${notifications.length} ` +
64
- `errors=${errors.length}`
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
- JSON.stringify(objectSchema, null, 2)
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
- `files=${files.length} durationMs=${durationMs}`
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
- `durationMs=${Date.now() - startedAt}`,
120
+ `durationMs=${Date.now() - startedAt}`,
118
121
  error
119
122
  );
120
123
  throw error;
121
124
  }
122
125
  }
123
-
@@ -145,7 +145,8 @@ export async function resolveEmissionGroups(context) {
145
145
  ]);
146
146
 
147
147
  const custom = await emitCustomFlowApplicationArtifacts(flows, {
148
- objects
148
+ objects,
149
+ relationships
149
150
  });
150
151
 
151
152
  let systemFunctions = [];
@@ -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, 74);
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, 38);
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 UploadSchema_Main.js and ' +
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/src/generatedCode/CoreFunctions/UploadSchema_Main.js'
485
+ 'app/initial_setup/generatedCode/source/commands/uploadSchema.js'
461
486
  ),
462
487
  'utf8'
463
488
  );
464
- assert.match(uploadSchemaMain, /\/\/\(<ignoreExternalServices>\)/);
465
- assert.match(uploadSchemaMain, /\/\/\(<\/ignoreExternalServices>\)/);
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
  );