@izara_project/izara-core-generate-service-code 1.0.60 → 1.0.62

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 (52) hide show
  1. package/package.json +11 -8
  2. package/src/codeGenerators/app/initial_setup/InitialSetupGenerator.js +100 -150
  3. package/src/codeGenerators/app/initial_setup/templates/InitialSetup_LambdaRole.ejs +27 -20
  4. package/src/codeGenerators/app/sls_yaml/FunctionYamlGenerator.js +12 -22
  5. package/src/codeGenerators/app/sls_yaml/RoleNameConfigGenerator.js +13 -72
  6. package/src/codeGenerators/app/sls_yaml/_policy/PolicyEmitter.js +3 -2
  7. package/src/codeGenerators/app/sls_yaml/_policy/__tests__/PolicyEmitter.test.js +3 -3
  8. package/src/codeGenerators/app/sls_yaml/_policy/templates/ManagedPolicy_Yaml.ejs +2 -2
  9. package/src/codeGenerators/app/sls_yaml/templates/SharedResource_Yaml.ejs +1 -1
  10. package/src/codeGenerators/app/src/generatedCode/Flow/FlowObjects/EndpointsGenerator.js +3 -2
  11. package/src/codeGenerators/app/src/generatedCode/Flow/FlowRbac/RbacFlowGenerator.js +4 -0
  12. package/src/codeGenerators/app/src/generatedCode/Flow/FlowRelationships/RelationshipFlowGenerator.js +2 -1
  13. package/src/codeGenerators/app/src/generatedCode/Flow/FlowSchemas/templates/endpoint/FlowEndpoint_HdrWbs.ejs +2 -2
  14. package/src/codeGenerators/app/src/generatedCode/Flow/FlowSchemas/templates/endpoint/FlowEndpoint_Main.ejs +8 -4
  15. package/src/codeGenerators/app/src/generatedCode/Flow/FlowSchemas/templates/endpoint/FlowMain_Wbs.ejs +4 -4
  16. package/src/codeGenerators/app/src/generatedCode/Flow/FlowSchemas/templates/s3/CreatePreSignUrl_Main.ejs +1 -1
  17. package/src/codeGenerators/app/src/generatedCode/Flow/FlowSchemas/templates/s3/ProcessS3File_Main.ejs +1 -1
  18. package/src/codeGenerators/app/src/generatedCode/Flow/FlowSchemas/templates/step/FlowEndpointComplete_Main.ejs +11 -12
  19. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/events/BaseDsqHandler.js +55 -0
  20. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/events/BaseSqsHandler.js +20 -21
  21. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/events/__tests__/BaseDsqHandler.test.js +93 -0
  22. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/events/dsq.js +5 -0
  23. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/events/lambdaSyncApi.js +10 -19
  24. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/events/lambdaSyncInv.js +10 -19
  25. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/events/ownTopic.js +16 -4
  26. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/events/registry.js +3 -1
  27. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/handlers/FlowHandler_HdrApi.ejs +3 -3
  28. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/handlers/FlowHandler_HdrDsq.ejs +137 -0
  29. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/handlers/FlowHandler_HdrInv.ejs +3 -3
  30. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/handlers/FlowHandler_HdrSqs.ejs +62 -51
  31. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/shared/eventTypes.js +2 -2
  32. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/shared/flowEntryPointBase.js +8 -4
  33. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/shared/flowMainFunctionBase.js +28 -36
  34. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/shared/flowNaming.js +16 -0
  35. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/shared/flowSelection.js +0 -1
  36. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/shared/flowStepBase.js +11 -5
  37. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/shared/flowStepNormalizer.js +48 -1
  38. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/shared/flowValidator.js +7 -21
  39. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/shared/propertyNormalizer.js +20 -0
  40. package/src/codeGenerators/app/src/generatedCode/Flow/_shared/shared/roleNameDetection.js +49 -0
  41. package/src/codeGenerators/app/src/generatedCode/libs/templates/Consts.ejs +10 -10
  42. package/src/codeGenerators/resource/sls_yaml/DynamoDBGenerator.js +1 -10
  43. package/src/codeGenerators/resource/sls_yaml/FlowOutGenerator.js +1 -10
  44. package/src/codeGenerators/resource/sls_yaml/FlowResourceYamlGenerator.js +3 -12
  45. package/src/core/validate.js +9 -5
  46. package/src/generate.js +1 -1
  47. package/src/generateCode.js +90 -24
  48. package/src/schemaGenerators/app/src/schemas/FlowSchemas/FlowSchemaGenerator.js +0 -43
  49. package/src/schemaGenerators/app/src/schemas/FlowSchemas/templates/DynamicFlowSchemaTemplate.ejs +3 -3
  50. package/src/schemaGenerators/app/src/schemas/FlowSchemas/templates/DynamicRbacFlowSchemaTemplate.ejs +1 -1
  51. package/src/schemaGenerators/app/src/schemas/FlowSchemas/templates/RelationshipFlowSchemaTemplate.ejs +2 -2
  52. package/src/schemaGenerators/app/src/schemas/FlowSchemas/templates/UserRbacFlowSchemaTemplate.ejs +1 -1
@@ -35,6 +35,10 @@ export async function generateRbacFlows(allSchemas, options) {
35
35
  path.join(templateDir, '..', '..', '_shared', 'handlers', 'FlowHandler_HdrSqs.ejs'),
36
36
  'utf-8'
37
37
  ),
38
+ dsq: await fs.readFile(
39
+ path.join(templateDir, '..', '..', '_shared', 'handlers', 'FlowHandler_HdrDsq.ejs'),
40
+ 'utf-8'
41
+ ),
38
42
  api: await fs.readFile(
39
43
  path.join(templateDir, '..', '..', '_shared', 'handlers', 'FlowHandler_HdrApi.ejs'),
40
44
  'utf-8'
@@ -25,6 +25,7 @@ export async function generateRelationshipFlows(allSchemas, options) {
25
25
  // Read standard handler templates for reuse
26
26
  const templates = {
27
27
  sqs: await fs.readFile(path.join(templateDir, '..', '..', '_shared', 'handlers', 'FlowHandler_HdrSqs.ejs'), 'utf-8'),
28
+ dsq: await fs.readFile(path.join(templateDir, '..', '..', '_shared', 'handlers', 'FlowHandler_HdrDsq.ejs'), 'utf-8'),
28
29
  api: await fs.readFile(path.join(templateDir, '..', '..', '_shared', 'handlers', 'FlowHandler_HdrApi.ejs'), 'utf-8'),
29
30
  inv: await fs.readFile(path.join(templateDir, '..', '..', '_shared', 'handlers', 'FlowHandler_HdrInv.ejs'), 'utf-8')
30
31
  };
@@ -98,7 +99,7 @@ export async function generateRelationshipFlows(allSchemas, options) {
98
99
  generatedCount++;
99
100
  } else if (event === 'dsq') {
100
101
  handlerFunctionName = `${processFunction}_HdrDsq`;
101
- handlerContent = ejs.render(templates.sqs, {
102
+ handlerContent = ejs.render(templates.dsq || templates.sqs, {
102
103
  flowTag: flowTag,
103
104
  flowStepName: '',
104
105
  mainFileName: actionFileName,
@@ -27,7 +27,7 @@ const { createFlowTypeConcat } = utils;
27
27
  import { webSocket } from '@izara_project/izara-core-library-external-request';
28
28
  const { postToConnection } = webSocket;
29
29
 
30
- import <%- upperCaseFlowTag %> from './<%- upperCaseFlowTag %>_Main.js';
30
+ import <%- typeof functionName !== 'undefined' ? functionName : upperCaseFlowTag %> from './<%- typeof mainFileName !== 'undefined' ? mainFileName : `${upperCaseFlowTag}_Main` %>.js';
31
31
 
32
32
  export const main = middleware.wrap(async (event, context) => {
33
33
 
@@ -82,7 +82,7 @@ export const main = middleware.wrap(async (event, context) => {
82
82
  }
83
83
 
84
84
  let callingFlow = eventParams?.callingFlow ? eventParams.callingFlow : {};
85
- await <%- upperCaseFlowTag %>(
85
+ await <%- typeof functionName !== 'undefined' ? functionName : upperCaseFlowTag %>(
86
86
  event._izContext,
87
87
  eventParams,
88
88
  //(<additionalParams>)
@@ -56,7 +56,7 @@ const { createFlowTypeConcat } = utils;
56
56
  * @returns {object} description of return value
57
57
  */
58
58
 
59
- export default async function Process<%- upperCaseFlowTag %>(
59
+ export default async function <%- typeof functionName !== 'undefined' ? functionName : `Process${upperCaseFlowTag}` %>(
60
60
  _izContext,
61
61
  requestParams,
62
62
  callingFlowConfig = {},
@@ -65,11 +65,15 @@ export default async function Process<%- upperCaseFlowTag %>(
65
65
  ) {
66
66
 
67
67
  try {
68
- _izContext.logger.debug("Process<%- upperCaseFlowTag %> _izContext", _izContext)
69
- _izContext.logger.debug("Process<%- upperCaseFlowTag %> requestParams", requestParams)
70
- _izContext.logger.debug("Process<%- upperCaseFlowTag %> callingFlowConfig", callingFlowConfig)
68
+ _izContext.logger.debug("<%- typeof functionName !== 'undefined' ? functionName : `Process${upperCaseFlowTag}` %> _izContext", _izContext)
69
+ _izContext.logger.debug("<%- typeof functionName !== 'undefined' ? functionName : `Process${upperCaseFlowTag}` %> requestParams", requestParams)
70
+ _izContext.logger.debug("<%- typeof functionName !== 'undefined' ? functionName : `Process${upperCaseFlowTag}` %> callingFlowConfig", callingFlowConfig)
71
71
 
72
72
  <% if (typeof flow !== 'undefined' && flow.statusType === 'storedCache') { %>
73
+
74
+ //(<beforeCheckCache>)
75
+ //(</beforeCheckCache>)
76
+
73
77
  let [ cacheMainStatus, cacheMain, cacheExist ] = await storedCacheSharedLib.checkStoredCacheStatus(
74
78
  _izContext,
75
79
  "<%= tableName %>",
@@ -48,7 +48,7 @@ const { postToConnection } = webSocket;
48
48
  * @returns {object} description of return value
49
49
  */
50
50
 
51
- export default async function <%- upperCaseFlowTag %>(
51
+ export default async function <%- typeof functionName !== 'undefined' ? functionName : upperCaseFlowTag %>(
52
52
  _izContext,
53
53
  requestParams,
54
54
  //(<additionalParams>)
@@ -57,9 +57,9 @@ export default async function <%- upperCaseFlowTag %>(
57
57
  ) {
58
58
 
59
59
  try {
60
- _izContext.logger.debug("<%- upperCaseFlowTag %> _izContext", _izContext)
61
- _izContext.logger.debug("<%- upperCaseFlowTag %> requestParams", requestParams)
62
- _izContext.logger.debug("<%- upperCaseFlowTag %> callingFlowConfig", callingFlowConfig)
60
+ _izContext.logger.debug("<%- typeof functionName !== 'undefined' ? functionName : upperCaseFlowTag %> _izContext", _izContext)
61
+ _izContext.logger.debug("<%- typeof functionName !== 'undefined' ? functionName : upperCaseFlowTag %> requestParams", requestParams)
62
+ _izContext.logger.debug("<%- typeof functionName !== 'undefined' ? functionName : upperCaseFlowTag %> callingFlowConfig", callingFlowConfig)
63
63
 
64
64
  let flowSchema = await getObjectSchema.getFlowSchemaS3WithCache(_izContext, {
65
65
  flowTag: "<%- flowTag %>",
@@ -26,7 +26,7 @@ import { lambda, sns } from '@izara_project/izara-core-library-external-request'
26
26
 
27
27
  import { S3Client } from '@aws-sdk/client-s3';
28
28
  import { createPresignedPost } from '@aws-sdk/s3-presigned-post';
29
- const client = new S3Client({ region: 'us-east-2' });
29
+ const client = new S3Client({ region: process.env.AWS_REGION });
30
30
  import { consts } from '@izara_project/izara-middleware';
31
31
 
32
32
  import { postToConnection, getNestObject } from '../../../../libs/source/GenerateCodeLibs.js'
@@ -16,7 +16,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
16
16
  */
17
17
 
18
18
  import { S3Client, GetObjectCommand, DeleteObjectCommand, PutObjectCommand, CopyObjectCommand } from '@aws-sdk/client-s3';
19
- const s3 = new S3Client({ region: 'us-east-2' });
19
+ const s3 = new S3Client({ region: process.env.AWS_REGION });
20
20
 
21
21
  //(<optionalRequired>)
22
22
  //(</optionalRequired>)
@@ -39,7 +39,7 @@ const PREFIX = {
39
39
  * @param {*} graphServiceTag
40
40
  * @param {*} errorsFound
41
41
  */
42
- function validateInput(returnValue, status, graphServiceTag, errorsFound) {
42
+ function _validateInput(returnValue, status, graphServiceTag, errorsFound) {
43
43
  if (!Object.prototype.hasOwnProperty.call(returnValue, 'queryResult')) {
44
44
  errorsFound.push(
45
45
  'message is not have property queryResult, should be not happen',
@@ -83,7 +83,7 @@ function validateInput(returnValue, status, graphServiceTag, errorsFound) {
83
83
  /**
84
84
  * @param {*} value
85
85
  */
86
- function isEmpty(value) {
86
+ function _isEmpty(value) {
87
87
  if (value == null) return true;
88
88
 
89
89
  if (
@@ -108,7 +108,7 @@ function isEmpty(value) {
108
108
  * @param {*} returnValue
109
109
  * @returns {object|null}
110
110
  */
111
- function buildExistsNode(returnValue) {
111
+ function _buildExistsNode(returnValue) {
112
112
  const { existsNode } = returnValue.queryResult.return<%- upperCaseFlowTag %>Node;
113
113
  if (!existsNode || !Object.keys(existsNode).length) return null;
114
114
 
@@ -129,7 +129,7 @@ function buildExistsNode(returnValue) {
129
129
  * @param {*} errorsFound
130
130
  * @returns {object}
131
131
  */
132
- function buildMessageObject(returnValue, existsNode, status, errorsFound) {
132
+ function _buildMessageObject(returnValue, existsNode, status, errorsFound) {
133
133
  const { objType } = returnValue.requestParams;
134
134
  const { return<%- upperCaseFlowTag %>Node } = returnValue.queryResult;
135
135
 
@@ -152,15 +152,14 @@ function buildMessageObject(returnValue, existsNode, status, errorsFound) {
152
152
  * @param {*} passBackProperties
153
153
  * @param {*} messageObject
154
154
  */
155
- async function sendSnsMessage(_izContext, passBackProperties, messageObject) {
155
+ async function _sendSnsMessage(_izContext, passBackProperties, messageObject) {
156
156
  const enrichedMessage =
157
157
  callingFlowSharedLib.addParentPassBackPropertiesToSnsResponseMessageObject(
158
158
  passBackProperties,
159
159
  messageObject,
160
160
  );
161
161
  const messageAttributes =
162
- callingFlowSharedLib.addParentPassBackCallingFlowToSnsResponseMessageAttribu
163
- tes(
162
+ callingFlowSharedLib.addParentPassBackCallingFlowToSnsResponseMessageAttributes(
164
163
  passBackProperties,
165
164
  {},
166
165
  );
@@ -211,19 +210,19 @@ export default async function <%- functionName %>(
211
210
  passBackProperties,
212
211
  });
213
212
 
214
- if (isEmpty(returnValue.requestParams)) {
213
+ if (_isEmpty(returnValue.requestParams)) {
215
214
  _izContext.logger.debug('message callingFlowProperties is empty');
216
215
  throw new NoRetryError('message not an object');
217
216
  }
218
217
 
219
- validateInput(returnValue, status, graphServiceTag, errorsFound);
218
+ _validateInput(returnValue, status, graphServiceTag, errorsFound);
220
219
 
221
220
  if (errorsFound.length > 0) {
222
221
  _izContext.logger.debug('Have error form graphServerTag:', errorsFound);
223
222
  errorsFound.push(errorsFound.join(','));
224
223
  }
225
224
 
226
- const existsNode = buildExistsNode(returnValue);
225
+ const existsNode = _buildExistsNode(returnValue);
227
226
 
228
227
  const awaitingStepId = await asyncFlowSharedLib.createAwaitingStepId(
229
228
  parentFlowId,
@@ -269,7 +268,7 @@ export default async function <%- functionName %>(
269
268
  }
270
269
  }))
271
270
 
272
- const messageObject = buildMessageObject(
271
+ const messageObject = _buildMessageObject(
273
272
  returnValue,
274
273
  existsNode,
275
274
  status,
@@ -279,7 +278,7 @@ export default async function <%- functionName %>(
279
278
  //(<beforeSendMessage>)
280
279
  //(</beforeSendMessage>)
281
280
 
282
- await sendSnsMessage(_izContext, passBackProperties, messageObject);
281
+ await _sendSnsMessage(_izContext, passBackProperties, messageObject);
283
282
 
284
283
  //(<afterSendMessage>)
285
284
  //(</afterSendMessage>)
@@ -0,0 +1,55 @@
1
+ import ejs from 'ejs';
2
+ import fs from 'fs/promises';
3
+ import path from 'path';
4
+ import { normalizePropertyType } from '../shared/propertyNormalizer.js';
5
+
6
+ export async function generateDsqHandler(flow, stepName, suffix, flowOutputDir, mainFileName, templates) {
7
+ const upperCase = (str) => str.charAt(0).toUpperCase() + str.slice(1);
8
+ const flowTag = flow.flowTag;
9
+
10
+ // For entry point stepName is empty
11
+ const handlerFileName = stepName ? `${flowTag}${stepName}_${suffix}` : `Process${upperCase(flowTag)}_${suffix}`;
12
+
13
+ // Function name: matches default export in the referenced _Main.js
14
+ const functionName = stepName ? `${flowTag}${stepName}` : `Process${upperCase(flowTag)}`;
15
+
16
+ // Queue name: keep the full handler suffix so handler code matches
17
+ const queueNameSuffix = suffix;
18
+ let queueName = '';
19
+ if (stepName) {
20
+ queueName = `${flowTag}${stepName}${queueNameSuffix}`;
21
+ } else {
22
+ if (flow.event && flow.event.includes('ownTopic')) {
23
+ queueName = `${flowTag}${queueNameSuffix}`;
24
+ } else {
25
+ queueName = `${functionName}${queueNameSuffix}`;
26
+ }
27
+ }
28
+
29
+ let startConfig = null;
30
+ if (!stepName && flow.flowSteps) {
31
+ const startStep = flow.flowSteps && flow.flowSteps.length > 0 ? flow.flowSteps[0] : null;
32
+ if (startStep) {
33
+ startConfig = { ...startStep };
34
+ // Map properties
35
+ if (startConfig.properties && Array.isArray(startConfig.properties)) {
36
+ startConfig.properties = startConfig.properties.map(p => normalizePropertyType(p, flow.stepProperties));
37
+ }
38
+ // Map messageAttributes
39
+ if (startConfig.messageAttributes && Array.isArray(startConfig.messageAttributes)) {
40
+ startConfig.messageAttributes = startConfig.messageAttributes.map(p => normalizePropertyType(p, flow.stepMessageAttributes));
41
+ }
42
+ }
43
+ }
44
+
45
+ const handlerContent = ejs.render(templates.dsq || templates.sqs, {
46
+ flowTag: flowTag,
47
+ flowStepName: stepName || '',
48
+ mainFileName: mainFileName,
49
+ functionName: functionName,
50
+ queueName: queueName,
51
+ startConfig: startConfig
52
+ });
53
+
54
+ await fs.writeFile(path.join(flowOutputDir, `${handlerFileName}.js`), handlerContent, 'utf-8');
55
+ }
@@ -1,18 +1,27 @@
1
+ import { isCrudFlow, isRbacFlow, isRelationshipFlow } from '../shared/flowClassifier.js';
2
+ import { ownTopicFunctionName, processFunctionName } from '../shared/flowNaming.js';
1
3
  import ejs from 'ejs';
2
4
  import fs from 'fs/promises';
3
5
  import path from 'path';
6
+ import { normalizePropertyType } from '../shared/propertyNormalizer.js';
4
7
 
5
8
  export async function generateSqsHandler(flow, stepName, suffix, flowOutputDir, mainFileName, templates) {
6
9
  const upperCase = (str) => str.charAt(0).toUpperCase() + str.slice(1);
7
10
  const flowTag = flow.flowTag;
8
11
 
12
+ const isOwnTopic = !stepName && !isCrudFlow(flowTag) && !isRbacFlow(flowTag) && !isRelationshipFlow(flowTag) && flow.event && flow.event.includes('ownTopic');
13
+
9
14
  // For entry point stepName is empty
10
- const handlerFileName = stepName ? `${flowTag}${stepName}_${suffix}` : `Process${upperCase(flowTag)}_${suffix}`;
15
+ const handlerFileName = stepName
16
+ ? `${flowTag}${stepName}_${suffix}`
17
+ : (isOwnTopic ? `${upperCase(flowTag)}_${suffix}` : `Process${upperCase(flowTag)}_${suffix}`);
11
18
 
12
19
  // Function name: matches default export in the referenced _Main.js
13
20
  // For entry point: Process${upperCase(flowTag)} (e.g. ProcessOwnTopicFlow)
14
21
  // For steps: ${flowTag}${flowStepName} (e.g. OwnTopicFlowProcess)
15
- const functionName = stepName ? `${flowTag}${stepName}` : `Process${upperCase(flowTag)}`;
22
+ const functionName = stepName
23
+ ? `${flowTag}${stepName}`
24
+ : (isOwnTopic ? ownTopicFunctionName(flowTag) : processFunctionName(flowTag));
16
25
 
17
26
  // Queue name: keep the full handler suffix so handler code matches
18
27
  // the generated queue/resource names.
@@ -28,28 +37,18 @@ export async function generateSqsHandler(flow, stepName, suffix, flowOutputDir,
28
37
  }
29
38
  }
30
39
 
31
- let inTagConfig = null;
40
+ let startConfig = null;
32
41
  if (!stepName && flow.flowSteps) {
33
- const inTagStep = flow.flowSteps.find(s => s.stepName === 'InTag');
34
- if (inTagStep) {
35
- inTagConfig = { ...inTagStep };
42
+ const startStep = flow.flowSteps && flow.flowSteps.length > 0 ? flow.flowSteps[0] : null;
43
+ if (startStep) {
44
+ startConfig = { ...startStep };
36
45
  // Map properties
37
- if (inTagConfig.properties && Array.isArray(inTagConfig.properties)) {
38
- inTagConfig.properties = inTagConfig.properties.map(p => {
39
- if (typeof p === 'string') {
40
- return (flow.stepProperties && flow.stepProperties[p]) ? flow.stepProperties[p] : { propertyName: p, type: 'string' };
41
- }
42
- return p;
43
- });
46
+ if (startConfig.properties && Array.isArray(startConfig.properties)) {
47
+ startConfig.properties = startConfig.properties.map(p => normalizePropertyType(p, flow.stepProperties));
44
48
  }
45
49
  // Map messageAttributes
46
- if (inTagConfig.messageAttributes && Array.isArray(inTagConfig.messageAttributes)) {
47
- inTagConfig.messageAttributes = inTagConfig.messageAttributes.map(p => {
48
- if (typeof p === 'string') {
49
- return (flow.stepMessageAttributes && flow.stepMessageAttributes[p]) ? flow.stepMessageAttributes[p] : { propertyName: p, type: 'string' };
50
- }
51
- return p;
52
- });
50
+ if (startConfig.messageAttributes && Array.isArray(startConfig.messageAttributes)) {
51
+ startConfig.messageAttributes = startConfig.messageAttributes.map(p => normalizePropertyType(p, flow.stepMessageAttributes));
53
52
  }
54
53
  }
55
54
  }
@@ -60,7 +59,7 @@ export async function generateSqsHandler(flow, stepName, suffix, flowOutputDir,
60
59
  mainFileName: mainFileName,
61
60
  functionName: functionName,
62
61
  queueName: queueName,
63
- inTagConfig: inTagConfig
62
+ startConfig: startConfig
64
63
  });
65
64
 
66
65
  await fs.writeFile(path.join(flowOutputDir, `${handlerFileName}.js`), handlerContent, 'utf-8');
@@ -0,0 +1,93 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
2
+ import fs from 'fs/promises';
3
+ import path from 'path';
4
+ import os from 'os';
5
+ import { fileURLToPath } from 'url';
6
+ import { generateDsqHandler } from '../BaseDsqHandler.js';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ describe('generateDsqHandler', () => {
12
+ let tmpDir;
13
+ beforeEach(async () => {
14
+ tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'iz-dsq-handler-'));
15
+ });
16
+ afterEach(async () => {
17
+ await fs.rm(tmpDir, { recursive: true, force: true });
18
+ });
19
+
20
+ it('generates a DSQ handler file containing reformatDsqMessage and setValidatorSchema', async () => {
21
+ // Read the template
22
+ const templatePath = path.join(__dirname, '..', '..', 'handlers', 'FlowHandler_HdrDsq.ejs');
23
+ const dsqTemplate = await fs.readFile(templatePath, 'utf-8');
24
+ const templates = { dsq: dsqTemplate };
25
+
26
+ const mockFlow = {
27
+ flowTag: 'createOrder',
28
+ event: ['dsq']
29
+ };
30
+
31
+ await generateDsqHandler(
32
+ mockFlow,
33
+ '', // entry point
34
+ 'HdrDsq', // suffix
35
+ tmpDir, // flowOutputDir
36
+ 'ProcessCreateOrder_Main', // mainFileName
37
+ templates
38
+ );
39
+
40
+ const generatedFilePath = path.join(tmpDir, 'ProcessCreateOrder_HdrDsq.js');
41
+ const generatedContent = await fs.readFile(generatedFilePath, 'utf-8');
42
+
43
+ // Verify it contains setValidatorSchema on line 39 (approx or exactly depending on formatting, let's verify substring)
44
+ expect(generatedContent).toContain('middleware.setValidatorSchema(recordHandlerSharedLib.baseValidatorSchema(');
45
+ expect(generatedContent).toContain('type: "object"');
46
+ expect(generatedContent).toContain('required: [\'body\', \'messageAttributes\']');
47
+
48
+ // Verify it contains reformatDsqMessage
49
+ expect(generatedContent).toContain('record = recordHandlerSharedLib.reformatDsqMessage(record._izContext, record);');
50
+ expect(generatedContent).toContain('record._izContext.logger.debug(\'record ReceiveMsgOutHdrSqs\', record);');
51
+ });
52
+
53
+ it('generates a DSQ handler for step containing step properties mapping', async () => {
54
+ const templatePath = path.join(__dirname, '..', '..', 'handlers', 'FlowHandler_HdrDsq.ejs');
55
+ const dsqTemplate = await fs.readFile(templatePath, 'utf-8');
56
+ const templates = { dsq: dsqTemplate };
57
+
58
+ const mockFlow = {
59
+ flowTag: 'createOrder',
60
+ event: ['dsq'],
61
+ flowSteps: [
62
+ {
63
+ stepName: 'Start',
64
+ properties: ['orderId'],
65
+ messageAttributes: ['userId']
66
+ }
67
+ ],
68
+ stepProperties: {
69
+ orderId: { propertyName: 'orderId', type: 'string', required: true }
70
+ },
71
+ stepMessageAttributes: {
72
+ userId: { propertyName: 'userId', type: 'string', required: true }
73
+ }
74
+ };
75
+
76
+ await generateDsqHandler(
77
+ mockFlow,
78
+ 'StepOne', // step name
79
+ 'HdrDsq', // suffix
80
+ tmpDir, // flowOutputDir
81
+ 'CreateOrderStepOne_Main', // mainFileName
82
+ templates
83
+ );
84
+
85
+ const generatedFilePath = path.join(tmpDir, 'createOrderStepOne_HdrDsq.js');
86
+ const generatedContent = await fs.readFile(generatedFilePath, 'utf-8');
87
+
88
+ // Verify template rendering and function names
89
+ expect(generatedContent).toContain('import createOrderStepOne from \'./CreateOrderStepOne_Main.js\';');
90
+ expect(generatedContent).toContain('createOrderStepOne');
91
+ expect(generatedContent).toContain('"createOrderStepOneHdrDsq"');
92
+ });
93
+ });
@@ -0,0 +1,5 @@
1
+ import { generateDsqHandler } from './BaseDsqHandler.js';
2
+
3
+ export default async function generate(flow, stepName, flowOutputDir, mainFileName, templates) {
4
+ await generateDsqHandler(flow, stepName, 'HdrDsq', flowOutputDir, mainFileName, templates);
5
+ }
@@ -1,6 +1,7 @@
1
1
  import ejs from 'ejs';
2
2
  import fs from 'fs/promises';
3
3
  import path from 'path';
4
+ import { normalizePropertyType } from '../shared/propertyNormalizer.js';
4
5
 
5
6
  export default async function generate(flow, stepName, flowOutputDir, mainFileName, templates) {
6
7
  const upperCase = (str) => str.charAt(0).toUpperCase() + str.slice(1);
@@ -13,28 +14,18 @@ export default async function generate(flow, stepName, flowOutputDir, mainFileNa
13
14
  // Function name matches default export of the referenced _Main.js
14
15
  const functionName = stepName ? `${flowTag}${stepName}` : `Process${upperCase(flowTag)}`;
15
16
 
16
- let inTagConfig = null;
17
+ let startConfig = null;
17
18
  if (!stepName && flow.flowSteps) {
18
- const inTagStep = flow.flowSteps.find(s => s.stepName === 'InTag');
19
- if (inTagStep) {
20
- inTagConfig = { ...inTagStep };
19
+ const startStep = flow.flowSteps && flow.flowSteps.length > 0 ? flow.flowSteps[0] : null;
20
+ if (startStep) {
21
+ startConfig = { ...startStep };
21
22
  // Map properties
22
- if (inTagConfig.properties && Array.isArray(inTagConfig.properties)) {
23
- inTagConfig.properties = inTagConfig.properties.map(p => {
24
- if (typeof p === 'string') {
25
- return (flow.stepProperties && flow.stepProperties[p]) ? flow.stepProperties[p] : { propertyName: p, type: 'string' };
26
- }
27
- return p;
28
- });
23
+ if (startConfig.properties && Array.isArray(startConfig.properties)) {
24
+ startConfig.properties = startConfig.properties.map(p => normalizePropertyType(p, flow.stepProperties));
29
25
  }
30
26
  // Map messageAttributes
31
- if (inTagConfig.messageAttributes && Array.isArray(inTagConfig.messageAttributes)) {
32
- inTagConfig.messageAttributes = inTagConfig.messageAttributes.map(p => {
33
- if (typeof p === 'string') {
34
- return (flow.stepMessageAttributes && flow.stepMessageAttributes[p]) ? flow.stepMessageAttributes[p] : { propertyName: p, type: 'string' };
35
- }
36
- return p;
37
- });
27
+ if (startConfig.messageAttributes && Array.isArray(startConfig.messageAttributes)) {
28
+ startConfig.messageAttributes = startConfig.messageAttributes.map(p => normalizePropertyType(p, flow.stepMessageAttributes));
38
29
  }
39
30
  }
40
31
  }
@@ -45,7 +36,7 @@ export default async function generate(flow, stepName, flowOutputDir, mainFileNa
45
36
  mainFileName: mainFileName,
46
37
  functionName: functionName,
47
38
  queueName: queueName,
48
- inTagConfig: inTagConfig
39
+ startConfig: startConfig
49
40
  });
50
41
 
51
42
  await fs.writeFile(path.join(flowOutputDir, `${handlerFileName}.js`), handlerContent, 'utf-8');
@@ -1,6 +1,7 @@
1
1
  import ejs from 'ejs';
2
2
  import fs from 'fs/promises';
3
3
  import path from 'path';
4
+ import { normalizePropertyType } from '../shared/propertyNormalizer.js';
4
5
 
5
6
  export default async function generate(flow, stepName, flowOutputDir, mainFileName, templates) {
6
7
  const upperCase = (str) => str.charAt(0).toUpperCase() + str.slice(1);
@@ -13,28 +14,18 @@ export default async function generate(flow, stepName, flowOutputDir, mainFileNa
13
14
  // Function name matches default export of the referenced _Main.js
14
15
  const functionName = stepName ? `${flowTag}${stepName}` : `Process${upperCase(flowTag)}`;
15
16
 
16
- let inTagConfig = null;
17
+ let startConfig = null;
17
18
  if (!stepName && flow.flowSteps) {
18
- const inTagStep = flow.flowSteps.find(s => s.stepName === 'InTag');
19
- if (inTagStep) {
20
- inTagConfig = { ...inTagStep };
19
+ const startStep = flow.flowSteps && flow.flowSteps.length > 0 ? flow.flowSteps[0] : null;
20
+ if (startStep) {
21
+ startConfig = { ...startStep };
21
22
  // Map properties
22
- if (inTagConfig.properties && Array.isArray(inTagConfig.properties)) {
23
- inTagConfig.properties = inTagConfig.properties.map(p => {
24
- if (typeof p === 'string') {
25
- return (flow.stepProperties && flow.stepProperties[p]) ? flow.stepProperties[p] : { propertyName: p, type: 'string' };
26
- }
27
- return p;
28
- });
23
+ if (startConfig.properties && Array.isArray(startConfig.properties)) {
24
+ startConfig.properties = startConfig.properties.map(p => normalizePropertyType(p, flow.stepProperties));
29
25
  }
30
26
  // Map messageAttributes
31
- if (inTagConfig.messageAttributes && Array.isArray(inTagConfig.messageAttributes)) {
32
- inTagConfig.messageAttributes = inTagConfig.messageAttributes.map(p => {
33
- if (typeof p === 'string') {
34
- return (flow.stepMessageAttributes && flow.stepMessageAttributes[p]) ? flow.stepMessageAttributes[p] : { propertyName: p, type: 'string' };
35
- }
36
- return p;
37
- });
27
+ if (startConfig.messageAttributes && Array.isArray(startConfig.messageAttributes)) {
28
+ startConfig.messageAttributes = startConfig.messageAttributes.map(p => normalizePropertyType(p, flow.stepMessageAttributes));
38
29
  }
39
30
  }
40
31
  }
@@ -45,7 +36,7 @@ export default async function generate(flow, stepName, flowOutputDir, mainFileNa
45
36
  mainFileName: mainFileName,
46
37
  functionName: functionName,
47
38
  queueName: queueName,
48
- inTagConfig: inTagConfig
39
+ startConfig: startConfig
49
40
  });
50
41
 
51
42
  await fs.writeFile(path.join(flowOutputDir, `${handlerFileName}.js`), handlerContent, 'utf-8');
@@ -1,4 +1,6 @@
1
1
  import { generateSqsHandler } from './BaseSqsHandler.js';
2
+ import { isCrudFlow, isRbacFlow, isRelationshipFlow } from '../shared/flowClassifier.js';
3
+ import { ownTopicWebSocketMainFileName, ownTopicWebSocketFunctionName } from '../shared/flowNaming.js';
2
4
  import ejs from 'ejs';
3
5
  import fs from 'fs/promises';
4
6
  import path from 'path';
@@ -15,9 +17,18 @@ export default async function generate(flow, stepName, flowOutputDir, mainFileNa
15
17
  // ownTopic flows ALWAYS generate the WebSocket entry + SNS-publish main file,
16
18
  // regardless of initiateBy (legacy behavior: WebSocket is the universal entry point).
17
19
  if (!stepName) {
20
+ const isOwnTopicConvention = !isCrudFlow(flow.flowTag) && !isRbacFlow(flow.flowTag) && !isRelationshipFlow(flow.flowTag);
21
+ let entryPointFileName;
22
+ let wbsFunctionName;
23
+
24
+ if (isOwnTopicConvention) {
25
+ entryPointFileName = ownTopicWebSocketMainFileName(flow.flowTag);
26
+ wbsFunctionName = ownTopicWebSocketFunctionName(flow.flowTag);
27
+ } else {
28
+ entryPointFileName = `${upperCase(flow.flowTag)}_Main`;
29
+ wbsFunctionName = upperCase(flow.flowTag);
30
+ }
18
31
  const handlerFileName = `${upperCase(flow.flowTag)}_HdrWbs`;
19
- const functionName = `${flow.flowTag}Main`;
20
- const entryPointFileName = `${upperCase(flow.flowTag)}_Main`;
21
32
 
22
33
  // 1. Render HdrWbs.js
23
34
  const handlerContent = ejs.render(templates.endpointHdrWbs, {
@@ -25,7 +36,7 @@ export default async function generate(flow, stepName, flowOutputDir, mainFileNa
25
36
  upperCaseFlowTag: upperCase(flow.flowTag),
26
37
  flowStepName: '',
27
38
  mainFileName: entryPointFileName,
28
- functionName: functionName,
39
+ functionName: wbsFunctionName,
29
40
  route: flow.flowTag
30
41
  });
31
42
  await fs.writeFile(path.join(flowOutputDir, `${handlerFileName}.js`), handlerContent, 'utf-8');
@@ -35,7 +46,8 @@ export default async function generate(flow, stepName, flowOutputDir, mainFileNa
35
46
  flowTag: flow.flowTag,
36
47
  upperCaseFlowTag: upperCase(flow.flowTag),
37
48
  topicArn: `${upperCase(flow.flowTag)}_In`,
38
- generateCode: generateCode
49
+ generateCode: generateCode,
50
+ functionName: wbsFunctionName
39
51
  });
40
52
  await fs.writeFile(path.join(flowOutputDir, `${entryPointFileName}.js`), mainContent, 'utf-8');
41
53
  }
@@ -4,6 +4,7 @@ import lambdaSyncInv from './lambdaSyncInv.js';
4
4
  import lambdaSyncApi from './lambdaSyncApi.js';
5
5
  import eventBridge from './eventBridge.js';
6
6
  import s3 from './s3.js';
7
+ import dsq from './dsq.js';
7
8
 
8
9
  export const EVENT_HANDLERS = {
9
10
  ownTopic,
@@ -11,5 +12,6 @@ export const EVENT_HANDLERS = {
11
12
  lambdaSyncInv,
12
13
  lambdaSyncApi,
13
14
  eventBridge,
14
- s3
15
+ s3,
16
+ dsq
15
17
  };
@@ -5,14 +5,14 @@ import <%= functionName %> from './<%= mainFileName %>.js';
5
5
 
6
6
  const validatorSchema = {
7
7
  //(<validatorSchema>)
8
- <% if (typeof inTagConfig !== 'undefined' && inTagConfig !== null && inTagConfig.properties && inTagConfig.properties.length > 0) { -%>
8
+ <% if (typeof startConfig !== 'undefined' && startConfig !== null && startConfig.properties && startConfig.properties.length > 0) { -%>
9
9
  type: 'object',
10
10
  properties: {
11
- <% inTagConfig.properties.forEach(prop => { -%>
11
+ <% startConfig.properties.forEach(prop => { -%>
12
12
  <%- prop.propertyName %>: { type: '<%- prop.type || 'string' %>' },
13
13
  <% }) -%>
14
14
  },
15
- required: [ <%- inTagConfig.properties.filter(p => p.required !== false).map(p => `'${p.propertyName}'`).join(', ') %> ]
15
+ required: [ <%- startConfig.properties.filter(p => p.required !== false).map(p => `'${p.propertyName}'`).join(', ') %> ]
16
16
  <% } -%>
17
17
  //(</validatorSchema>)
18
18
  };