@izara_project/izara-core-generate-service-code 1.0.73 → 1.0.75

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 (60) hide show
  1. package/package.json +1 -1
  2. package/src/emit/coreFunction/emitCoreFunctionArtifacts.js +3 -0
  3. package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +100 -28
  4. package/src/emit/coreFunction/templates/InitialSetupRunner_uploadSchema.ejs +2 -0
  5. package/src/emit/coreFunction/templates/UploadSchema_Main.ejs +2 -0
  6. package/src/emit/crud/emitCrudApplicationArtifacts.js +2 -2
  7. package/src/emit/crud/emitDynamicCrudFlowSchemas.js +43 -2
  8. package/src/emit/crud/templates/CreateComplete_HdrSqs.ejs +99 -0
  9. package/src/emit/crud/templates/CreateComplete_Main.ejs +188 -0
  10. package/src/emit/crud/templates/DeleteComplete_HdrSqs.ejs +99 -0
  11. package/src/emit/crud/templates/DeleteComplete_Main.ejs +188 -0
  12. package/src/emit/crud/templates/FlowEndpointComplete_Main.ejs +1 -1
  13. package/src/emit/crud/templates/UpdateComplete_HdrSqs.ejs +99 -0
  14. package/src/emit/crud/templates/UpdateComplete_Main.ejs +188 -0
  15. package/src/emit/custom/customFlows.js +56 -0
  16. package/src/emit/custom/emitCustomFlowApplicationArtifacts.js +24 -9
  17. package/src/emit/custom/templates/FlowEndpoint_Main.ejs +1 -1
  18. package/src/emit/custom/templates/triggerCache/CheckTriggerCache_HdrSqs.ejs +2 -1
  19. package/src/emit/function/functionYamlArtifact.js +8 -3
  20. package/src/emit/relationship/emitDynamicRelationshipFlowSchemas.js +21 -1
  21. package/src/emit/relationship/emitRelationshipApplicationArtifacts.js +1 -1
  22. package/src/emit/relationship/templates/ChangeRelationshipComplete_HdrSqs.ejs +99 -0
  23. package/src/emit/relationship/templates/ChangeRelationshipComplete_Main.ejs +99 -100
  24. package/src/emit/relationship/templates/ChangeRelationship_Main.ejs +5 -14
  25. package/src/emit/relationship/templates/CreateRelationshipComplete_HdrSqs.ejs +99 -0
  26. package/src/emit/relationship/templates/CreateRelationshipComplete_Main.ejs +98 -99
  27. package/src/emit/relationship/templates/CreateRelationship_Main.ejs +7 -16
  28. package/src/emit/relationship/templates/DeleteRelationshipComplete_HdrSqs.ejs +99 -0
  29. package/src/emit/relationship/templates/DeleteRelationshipComplete_Main.ejs +93 -85
  30. package/src/emit/relationship/templates/DeleteRelationship_Main.ejs +6 -24
  31. package/src/emit/relationship/templates/GetRelationshipComplete_HdrSqs.ejs +99 -0
  32. package/src/emit/relationship/templates/GetRelationshipComplete_Main.ejs +10 -4
  33. package/src/emit/relationship/templates/GetRelationship_Main.ejs +2 -20
  34. package/src/emit/relationship/templates/MoveRelationshipComplete_HdrSqs.ejs +99 -0
  35. package/src/emit/relationship/templates/MoveRelationshipComplete_Main.ejs +101 -104
  36. package/src/emit/relationship/templates/MoveRelationship_Main.ejs +6 -15
  37. package/src/emit/relationship/templates/UpdateRelationshipComplete_HdrSqs.ejs +99 -0
  38. package/src/emit/relationship/templates/UpdateRelationshipComplete_Main.ejs +92 -110
  39. package/src/emit/relationship/templates/UpdateRelationship_Main.ejs +6 -15
  40. package/src/emit/resource/emitResourceArtifacts.js +25 -1
  41. package/src/emit/resource/templates/DynamoDBTable_Yaml.ejs +3 -0
  42. package/src/emit/shared/templates/FindData_Main.ejs +1 -1
  43. package/src/emit/shared/templates/FlowHandler_HdrApi.ejs +1 -1
  44. package/src/emit/shared/templates/FlowHandler_HdrDsq.ejs +2 -1
  45. package/src/emit/shared/templates/FlowHandler_HdrInv.ejs +1 -1
  46. package/src/emit/shared/templates/FlowHandler_HdrSqs.ejs +2 -1
  47. package/src/emit/system/emitSystemWebSocketArtifacts.js +54 -2
  48. package/src/emit/system/templates/RegisterComplete_HdrSqs.ejs +64 -0
  49. package/src/emit/system/templates/RegisterComplete_Main.ejs +154 -0
  50. package/src/emit/system/templates/RegisterComplete_Yaml.ejs +17 -0
  51. package/src/emit/system/templates/RegisterWbs_Yaml.ejs +7 -0
  52. package/src/emit/system/templates/Register_HdrWbs.ejs +56 -0
  53. package/src/emit/system/templates/Register_Main.ejs +148 -0
  54. package/src/emit/system/templates/WebSocketComplete_Yaml.ejs +2 -0
  55. package/test/crudApplicationArtifacts.test.js +22 -0
  56. package/test/customFlowApplicationArtifacts.test.js +147 -1
  57. package/test/functionYamlArtifact.test.js +5 -2
  58. package/test/publicApi.test.js +50 -0
  59. package/test/relationshipApplicationArtifacts.test.js +42 -8
  60. package/test/resourceArtifacts.test.js +54 -16
@@ -0,0 +1,154 @@
1
+ import { NoRetryError } from '@izara_project/izara-core-library-core';
2
+ import { objectHash as hash } from '@izara_project/izara-shared-core';
3
+ import { getObjectSchema, identifiersObject } from '@izara_project/izara-core-library-service-schemas';
4
+ import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
5
+ import lambdaSharedLib from '@izara_project/izara-core-library-lambda';
6
+ import { lambda, webSocket } from '@izara_project/izara-core-library-external-request';
7
+ const { postToConnection } = webSocket;
8
+
9
+ /**
10
+ * Register complete main handler
11
+ * @param {Object} _izContext
12
+ * @param {string} topicArn
13
+ * @param {Object} requestParams
14
+ * @param {Object} callingFlowConfig
15
+ */
16
+ export default async function registerCompleteMain(
17
+ _izContext,
18
+ topicArn,
19
+ requestParams,
20
+ callingFlowConfig = {}
21
+ ) {
22
+ try {
23
+ _izContext.logger.debug('WebSocketInvoke _izContext', _izContext);
24
+ _izContext.logger.debug('WebSocketInvoke requestParams', requestParams);
25
+ _izContext.logger.debug('WebSocketInvoke callingFlowConfig', callingFlowConfig);
26
+
27
+ if (!topicArn) {
28
+ throw new NoRetryError('invalid not have topicArn to get flowSchema');
29
+ }
30
+
31
+ let topicName = topicArn.split(':')[5].split('_');
32
+ let flowSchema = await getObjectSchema.getFlowSchemaS3WithCache(_izContext, {
33
+ flowTag: topicName[2],
34
+ serviceTag: topicName[0]
35
+ });
36
+
37
+ if (!flowSchema) {
38
+ throw new NoRetryError('not have flowSchema in S3');
39
+ }
40
+
41
+ let identifiersTask = '';
42
+
43
+ if (!requestParams.identifiersTask && flowSchema.objType) {
44
+ identifiersTask = await identifiersObject.identifiersConcatFromIdentifiers(
45
+ _izContext,
46
+ flowSchema.objType,
47
+ requestParams
48
+ );
49
+ _izContext.logger.debug('identifiersTask from request params', identifiersTask);
50
+
51
+ Object.assign(requestParams, {
52
+ identifiersTask: identifiersTask
53
+ });
54
+ }
55
+
56
+ if (!requestParams.identifiersTask) {
57
+ _izContext.logger.warn('invalid identifiersTask');
58
+ return 'invalid identifiersTask';
59
+ }
60
+
61
+ const getResult = async (objType) => {
62
+ const objectSchema = await getObjectSchema.getObjSchemaS3WithCache(
63
+ _izContext,
64
+ {
65
+ objectType: objType.objectType,
66
+ serviceTag: objType.serviceTag
67
+ }
68
+ );
69
+
70
+ _izContext.logger.debug('==> objectSchema', objectSchema);
71
+
72
+ let tasks = await dynamodbSharedLib.query(
73
+ _izContext,
74
+ await dynamodbSharedLib.tableName(_izContext, 'RegisterRecords'),
75
+ {
76
+ identifiersTask: identifiersTask
77
+ }
78
+ );
79
+
80
+ if (tasks.Items && tasks.Items.length > 0) {
81
+ const result = await lambda.invokeSync(
82
+ _izContext,
83
+ await lambdaSharedLib.lambdaFunctionName(_izContext, 'GetHdrInv'),
84
+ {
85
+ identifiers: await identifiersObject.identifiersFromIdentifiersConcat(
86
+ _izContext,
87
+ objType,
88
+ requestParams.identifiersTask
89
+ ),
90
+ objectType: objType.objectType
91
+ }
92
+ );
93
+ await Promise.all(
94
+ tasks.Items.map(async (task) => {
95
+ if (!result) {
96
+ await postToConnection({ message: 'not have record' }, task.connectionId);
97
+ } else {
98
+ await postToConnection({ message: result }, task.connectionId);
99
+ }
100
+ })
101
+ );
102
+ }
103
+ };
104
+
105
+ switch (flowSchema.statusType) {
106
+ case 'statusField':
107
+ await getResult(flowSchema.objType);
108
+ break;
109
+ case 'storedCache':
110
+ await getResult(flowSchema.objType);
111
+ break;
112
+ case 'triggerCache':
113
+ await getResult(flowSchema.objType);
114
+ break;
115
+ case 'none': {
116
+ let identifier = {};
117
+ if (
118
+ requestParams.hasOwnProperty('identifiersField') &&
119
+ Array.isArray(requestParams.identifiersField)
120
+ ) {
121
+ for (const fieldNames of requestParams.identifiersField) {
122
+ Object.assign(identifier, {
123
+ [fieldNames]: requestParams[fieldNames]
124
+ });
125
+ }
126
+ } else {
127
+ identifier = requestParams;
128
+ }
129
+
130
+ let tasks = await dynamodbSharedLib.query(
131
+ _izContext,
132
+ await dynamodbSharedLib.tableName(_izContext, 'RegisterRecords'),
133
+ {
134
+ identifiersTask: hash(identifier)
135
+ }
136
+ );
137
+
138
+ if (tasks.Items && tasks.Items.length > 0) {
139
+ await Promise.all(
140
+ tasks.Items.map(async (task) => {
141
+ await postToConnection({ message: task }, task.connectionId);
142
+ })
143
+ );
144
+ }
145
+ break;
146
+ }
147
+ default:
148
+ throw new NoRetryError(`statusType not found ${flowSchema.statusType}`);
149
+ }
150
+ } catch (err) {
151
+ _izContext.logger.error('error WebSocketInvoke: ', err);
152
+ throw err;
153
+ }
154
+ }
@@ -0,0 +1,17 @@
1
+ RegisterCompleteHdrSqs:
2
+ handler: src/generatedCode/SystemFlowSchemas/Register/source/RegisterComplete_HdrSqs.main
3
+ name: ${self:custom.iz_resourcePrefix}RegisterCompleteHdrSqs
4
+ role: RegisterRole
5
+ events:
6
+ - sqs:
7
+ arn: arn:aws:sqs:${self:custom.iz_region}:${self:custom.iz_accountId}:${self:custom.iz_resourcePrefix}RegisterComplete
8
+ batchSize: 10
9
+
10
+ RegisterCompleteDsq:
11
+ handler: src/generatedCode/SystemFlowSchemas/Register/source/RegisterComplete_HdrDsq.main
12
+ name: ${self:custom.iz_resourcePrefix}RegisterCompleteDsq
13
+ role: RegisterRole
14
+ events:
15
+ - sqs:
16
+ arn: arn:aws:sqs:${self:custom.iz_region}:${self:custom.iz_accountId}:${self:custom.iz_resourcePrefix}RegisterCompleteDLQ
17
+ batchSize: 10
@@ -0,0 +1,7 @@
1
+ RegisterWbs:
2
+ handler: src/generatedCode/SystemFlowSchemas/Register/source/Register_HdrWbs.main
3
+ name: ${self:custom.iz_resourcePrefix}RegisterWbs
4
+ role: RegisterRole
5
+ events:
6
+ - websocket:
7
+ route: register
@@ -0,0 +1,56 @@
1
+ import { middlewareHandler as middleware, consts, response } from '@izara_project/izara-middleware';
2
+ import callingFlowSharedLib from '@izara_project/izara-core-library-calling-flow';
3
+ import registerMain from './Register_Main.js';
4
+ import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
5
+ import { webSocket } from '@izara_project/izara-core-library-external-request';
6
+ const { postToConnection } = webSocket;
7
+
8
+ export const main = middleware.wrap(async (event, context) => {
9
+ event._izContext.logger.debug('Event:', event);
10
+
11
+ try {
12
+ if (event.requestContext) {
13
+ const route = event.requestContext.routeKey;
14
+ const connectionId = event.requestContext.connectionId;
15
+ let correlationId = event._izContext.correlationIds.get(consts.X_CORRELATION_ID);
16
+
17
+ switch (route) {
18
+ case '$default':
19
+ console.log('Received unknown route:', route);
20
+ break;
21
+ case 'register':
22
+ console.log('event in route register', event);
23
+ let eventParams = JSON.parse(event.body);
24
+
25
+ await dynamodbSharedLib.putItem(
26
+ event._izContext,
27
+ await dynamodbSharedLib.tableName(event._izContext, 'WebSocketTask'),
28
+ {
29
+ taskKey: correlationId,
30
+ connectionId: connectionId
31
+ }
32
+ );
33
+
34
+ let callingFlow = eventParams?.callingFlow ? eventParams.callingFlow : {};
35
+
36
+ await registerMain(
37
+ event._izContext,
38
+ eventParams,
39
+ callingFlowSharedLib.addCallingFlowToPassOnProperties(callingFlow)
40
+ );
41
+ break;
42
+ }
43
+ } else {
44
+ const connId = event.connId;
45
+ await postToConnection({ message: event.message }, connId);
46
+ }
47
+ return response.webSocketSuccess();
48
+ } catch (err) {
49
+ const connId = event.requestContext?.connectionId;
50
+ event._izContext.logger.error('Error, WebSocket: ', err);
51
+ if (connId) {
52
+ await postToConnection({ message: err.message }, connId);
53
+ }
54
+ return response.failure(err);
55
+ }
56
+ });
@@ -0,0 +1,148 @@
1
+ import { objectHash as hash } from '@izara_project/izara-shared-core';
2
+ import { getObjectSchema, identifiersObject } from '@izara_project/izara-core-library-service-schemas';
3
+ import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
4
+ import lambdaSharedLib from '@izara_project/izara-core-library-lambda';
5
+ import { lambda, webSocket } from '@izara_project/izara-core-library-external-request';
6
+ import { NoRetryError } from '@izara_project/izara-core-library-core';
7
+ import { consts } from '@izara_project/izara-middleware';
8
+ const { postToConnection } = webSocket;
9
+
10
+ /**
11
+ * Register main handler
12
+ * @param {Object} _izContext
13
+ * @param {Object} requestParams
14
+ * @param {Object} callingFlowConfig
15
+ */
16
+ export default async function registerMain(
17
+ _izContext,
18
+ requestParams,
19
+ callingFlowConfig = {}
20
+ ) {
21
+ try {
22
+ let connectionId = _izContext.correlationIds.get(consts.CONNECTION_ID);
23
+ let flowSchema = await getObjectSchema.getFlowSchemaS3WithCache(_izContext, {
24
+ flowTag: requestParams.flowTag,
25
+ serviceTag: process.env.iz_serviceTag
26
+ });
27
+
28
+ _izContext.logger.debug('flowSchema', flowSchema);
29
+ if (!flowSchema) {
30
+ throw new NoRetryError(`flowTag not found ${requestParams.flowTag}`);
31
+ }
32
+
33
+ const getResult = async (objType) => {
34
+ const objectSchema = await getObjectSchema.getObjSchemaS3WithCache(
35
+ _izContext,
36
+ {
37
+ objectType: objType.objectType,
38
+ serviceTag: objType.serviceTag
39
+ }
40
+ );
41
+
42
+ _izContext.logger.debug('==> objectSchema', objectSchema);
43
+
44
+ const result = await lambda.invokeSync(
45
+ _izContext,
46
+ await lambdaSharedLib.lambdaFunctionName(_izContext, 'GetHdrInv'),
47
+ {
48
+ identifiers: await identifiersObject.identifiersFromIdentifiersConcat(
49
+ _izContext,
50
+ objType,
51
+ requestParams.identifiersTask
52
+ ),
53
+ objectType: objType.objectType
54
+ }
55
+ );
56
+
57
+ if (!result) {
58
+ await postToConnection({ message: 'not have record' }, connectionId);
59
+ } else {
60
+ for (const [fieldName, fieldNameSetting] of Object.entries(
61
+ objectSchema.fieldNames
62
+ )) {
63
+ if (
64
+ fieldNameSetting.hasOwnProperty('statusField') &&
65
+ fieldNameSetting.statusField === true
66
+ ) {
67
+ if (
68
+ result.fields[fieldName] !== 'complete' &&
69
+ result.fields[fieldName] !== 'error'
70
+ ) {
71
+ await dynamodbSharedLib.putItem(
72
+ _izContext,
73
+ await dynamodbSharedLib.tableName(_izContext, 'RegisterRecords'),
74
+ {
75
+ identifiersTask: requestParams.identifiersTask,
76
+ connectionId: connectionId
77
+ }
78
+ );
79
+ } else {
80
+ await postToConnection({ message: result }, connectionId);
81
+ }
82
+ }
83
+ }
84
+ }
85
+ };
86
+
87
+ switch (flowSchema.statusType) {
88
+ case 'statusField':
89
+ await getResult(flowSchema.objType);
90
+ break;
91
+ case 'storedCache':
92
+ await getResult(flowSchema.objType);
93
+ break;
94
+ case 'triggerCache':
95
+ await getResult(flowSchema.objType);
96
+ break;
97
+ case 'none': {
98
+ let identifier = {};
99
+ if (
100
+ requestParams.hasOwnProperty('identifiersField') &&
101
+ Array.isArray(requestParams.identifiersField)
102
+ ) {
103
+ for (const fieldNames of requestParams.identifiersField) {
104
+ Object.assign(identifier, {
105
+ [fieldNames]: requestParams[fieldNames]
106
+ });
107
+ }
108
+ } else {
109
+ let pureRequestParams = structuredClone(requestParams);
110
+ delete pureRequestParams.flowTag;
111
+ identifier = pureRequestParams;
112
+ }
113
+
114
+ let tasks = await dynamodbSharedLib.query(
115
+ _izContext,
116
+ await dynamodbSharedLib.tableName(_izContext, 'RegisterRecords'),
117
+ {
118
+ identifiersTask: hash(identifier)
119
+ }
120
+ );
121
+ if (tasks.Items && tasks.Items.length > 0) {
122
+ await Promise.all(
123
+ tasks.Items.map(async (task) => {
124
+ await postToConnection({ message: task }, task.connectionId);
125
+ })
126
+ );
127
+ await postToConnection({ message: tasks.Items[0] }, connectionId);
128
+ return tasks.Items[0];
129
+ } else {
130
+ await dynamodbSharedLib.putItem(
131
+ _izContext,
132
+ await dynamodbSharedLib.tableName(_izContext, 'RegisterRecords'),
133
+ {
134
+ identifiersTask: hash(identifier),
135
+ connectionId: connectionId
136
+ }
137
+ );
138
+ }
139
+ break;
140
+ }
141
+ default:
142
+ throw new NoRetryError(`statusType not found ${flowSchema.statusType}`);
143
+ }
144
+ } catch (err) {
145
+ _izContext.logger.error('error WebSocketInvoke: ', err);
146
+ throw err;
147
+ }
148
+ }
@@ -6,6 +6,7 @@ WebSocketCompleteHdrSqs:
6
6
  - sqs:
7
7
  arn: arn:aws:sqs:${self:custom.iz_region}:${self:custom.iz_accountId}:${self:custom.iz_resourcePrefix}WebSocketCompleteHdrSqs
8
8
  batchSize: 10
9
+ #<#WebSocketCompleteHdrSqsFilterPatterns#>
9
10
  filterPatterns:
10
11
  - body:
11
12
  MessageAttributes:
@@ -17,3 +18,4 @@ WebSocketCompleteHdrSqs:
17
18
  callingFlow:
18
19
  Value:
19
20
  - exists: false
21
+ #<#/WebSocketCompleteHdrSqsFilterPatterns#>
@@ -137,6 +137,27 @@ test('preserves function-setting hooks across CRUD regeneration', async () => {
137
137
  /#<#CreateCompleteHdrSqsFunctionSetting#>\n memorySize: 512\n #<#\/CreateCompleteHdrSqsFunctionSetting#>/
138
138
  );
139
139
 
140
+ const customFilterHook =
141
+ ' #<#CreateCompleteHdrSqsFilterPatterns#>\n filterPatterns:\n - body: custom\n #<#/CreateCompleteHdrSqsFilterPatterns#>';
142
+ const withFilterHook = yaml.replace(
143
+ / #<#CreateCompleteHdrSqsFilterPatterns#>[\s\S]*?#<#\/CreateCompleteHdrSqsFilterPatterns#>/,
144
+ customFilterHook
145
+ );
146
+ assert.notEqual(
147
+ yaml,
148
+ withFilterHook,
149
+ 'filterPatterns hook placeholder must exist in generated YAML'
150
+ );
151
+ await writeFile(yamlPath, withFilterHook);
152
+
153
+ await generateCrudApplicationArtifacts(rootPath, { serviceTag: 'orders' });
154
+
155
+ yaml = await readFile(yamlPath, 'utf8');
156
+ assert.match(
157
+ yaml,
158
+ /#<#CreateCompleteHdrSqsFilterPatterns#>\n filterPatterns:\n - body: custom\n #<#\/CreateCompleteHdrSqsFilterPatterns#>/
159
+ );
160
+
140
161
  const conflictPath = join(
141
162
  rootPath,
142
163
  'app/src/generatedCode/FlowObjects/Create/source/Create_Main.js'
@@ -151,6 +172,7 @@ test('preserves function-setting hooks across CRUD regeneration', async () => {
151
172
  assert.equal(await readFile(yamlPath, 'utf8'), yaml);
152
173
  });
153
174
 
175
+
154
176
  test('removes managed CRUD artifacts when no applicable object remains', async () => {
155
177
  const rootPath = await createProject(true);
156
178
  await generateCrudApplicationArtifacts(rootPath, { serviceTag: 'orders' });
@@ -36,7 +36,17 @@ test('emits handlers, function YAML, and IAM roles for supported custom flow eve
36
36
  const result = await generateCustomFlowApplicationArtifacts(rootPath);
37
37
  await generateResourceArtifacts(rootPath);
38
38
 
39
- assert.equal(result.files.length, 38);
39
+ assert.equal(result.files.length, 43);
40
+ assert.match(
41
+ await readFile(
42
+ join(
43
+ rootPath,
44
+ 'app/src/generatedCode/SystemFlowSchemas/Register/source/Register_Main.js'
45
+ ),
46
+ 'utf8'
47
+ ),
48
+ /registerMain/
49
+ );
40
50
  assert.match(
41
51
  await readFile(
42
52
  join(
@@ -673,3 +683,139 @@ test('supports step-level required overrides in flowSteps properties and message
673
683
  /messageAttributeValidatorSchema = {[\s\S]*required: \[\]/
674
684
  );
675
685
  });
686
+
687
+ test(
688
+ 'picks properties from inProcess flowStep and renders default keys ' +
689
+ 'in handler validator schema',
690
+ async () => {
691
+ const rootPath = await mkdtemp(
692
+ join(tmpdir(), 'generate-service-code-v2-inprocess-')
693
+ );
694
+ roots.add(rootPath);
695
+ const schemaRoot = join(rootPath, 'app/src/schemas/FlowSchemas');
696
+ await mkdir(schemaRoot, { recursive: true });
697
+
698
+ await writeFile(
699
+ join(schemaRoot, 'InProcessFlow.js'),
700
+ `export default {
701
+ flowTag: 'InProcessFlow',
702
+ event: ['extTopic', 'lambdaSyncApi'],
703
+ stepProperties: {
704
+ status: { propertyName: 'status', type: 'string', default: 'ACTIVE' },
705
+ retryCount: { propertyName: 'retryCount', type: 'number', default: 0 }
706
+ },
707
+ flowSteps: [
708
+ {
709
+ stepName: 'inProcess',
710
+ properties: ['status', { key: 'retryCount', default: 3 }]
711
+ }
712
+ ]
713
+ };\n`
714
+ );
715
+
716
+ const result = await generateCustomFlowApplicationArtifacts(rootPath);
717
+ assert.ok(result);
718
+
719
+ const sqsHdrContent = await readFile(
720
+ join(
721
+ rootPath,
722
+ 'app/src/generatedCode/FlowSchemas/InProcessFlow/source/' +
723
+ 'InProcessFlow_HdrSqs.js'
724
+ ),
725
+ 'utf8'
726
+ );
727
+ assert.match(
728
+ sqsHdrContent,
729
+ /status: {[\s\S]*type: 'string',[\s\S]*default: 'ACTIVE'/
730
+ );
731
+ assert.match(
732
+ sqsHdrContent,
733
+ /retryCount: {[\s\S]*type: 'number',[\s\S]*default: 3/
734
+ );
735
+
736
+ const apiHdrContent = await readFile(
737
+ join(
738
+ rootPath,
739
+ 'app/src/generatedCode/FlowSchemas/InProcessFlow/source/' +
740
+ 'InProcessFlow_HdrApi.js'
741
+ ),
742
+ 'utf8'
743
+ );
744
+ assert.match(
745
+ apiHdrContent,
746
+ /status: { type: 'string', default: 'ACTIVE' }/
747
+ );
748
+ assert.match(apiHdrContent, /retryCount: { type: 'number', default: 3 }/);
749
+ }
750
+ );
751
+
752
+ test('renders nextSteps IAM permissions in InitialSetupConfig', async () => {
753
+ const { generate } = await import('../src/generate.js');
754
+ const rootPath = await mkdtemp(
755
+ join(tmpdir(), 'generate-service-code-v2-nextsteps-')
756
+ );
757
+ roots.add(rootPath);
758
+ const schemaRoot = join(rootPath, 'app/src/schemas/FlowSchemas');
759
+ await mkdir(schemaRoot, { recursive: true });
760
+
761
+ await writeFile(
762
+ join(schemaRoot, 'Reservations.js'),
763
+ `export default {
764
+ flowTag: 'Reservations',
765
+ event: ['ownTopic'],
766
+ flowSteps: [
767
+ {
768
+ stepName: 'Start',
769
+ nextSteps: [
770
+ {
771
+ targetType: 'extTopic',
772
+ serviceTag: 'GraphHandler',
773
+ topic: 'GetDeepPathSearch_In'
774
+ }
775
+ ]
776
+ },
777
+ {
778
+ stepName: 'GetDeepPathSearch',
779
+ event: ['extTopic'],
780
+ nextSteps: [
781
+ {
782
+ targetType: 'ownTopic',
783
+ topic: 'Create_In'
784
+ }
785
+ ]
786
+ },
787
+ {
788
+ stepName: 'Complete',
789
+ event: ['extTopic']
790
+ }
791
+ ]
792
+ };\n`
793
+ );
794
+
795
+ const result = await generate(rootPath, {
796
+ serviceTag: 'ReservationsService'
797
+ });
798
+ assert.ok(result);
799
+
800
+ const initialSetupConfigContent = await readFile(
801
+ join(
802
+ rootPath,
803
+ 'app/src/generatedCode/CoreFunctions/InitialSetupConfig.js'
804
+ ),
805
+ 'utf8'
806
+ );
807
+ assert.match(initialSetupConfigContent, /roleName: 'Reservations'/);
808
+ assert.match(
809
+ initialSetupConfigContent,
810
+ /ReservationsPublishNextStepsTopics/
811
+ );
812
+ assert.match(
813
+ initialSetupConfigContent,
814
+ /\/\/\(<createReservationsRole>\)/
815
+ );
816
+ assert.match(
817
+ initialSetupConfigContent,
818
+ /\/\/\(<\/createReservationsRole>\)/
819
+ );
820
+ });
821
+
@@ -80,12 +80,15 @@ test('filters all SQS functions by their calling flow', () => {
80
80
 
81
81
  assert.match(
82
82
  completion,
83
- /filterPatterns:[\s\S]*\$\{self:custom\.iz_resourcePrefix\}CreateCompleteHdrSqs/
83
+ /#<#CreateCompleteHdrSqsFilterPatterns#>[\s\S]*filterPatterns:[\s\S]*\$\{self:custom\.iz_resourcePrefix\}CreateCompleteHdrSqs/
84
84
  );
85
+ assert.match(completion, /#<#\/CreateCompleteHdrSqsFilterPatterns#>/);
85
86
  assert.match(completion, /exists: false/);
86
87
  assert.match(
87
88
  ordinary,
88
- /filterPatterns:[\s\S]*\$\{self:custom\.iz_resourcePrefix\}CreateHdrSqs/
89
+ /#<#CreateHdrSqsFilterPatterns#>[\s\S]*filterPatterns:[\s\S]*\$\{self:custom\.iz_resourcePrefix\}CreateHdrSqs/
89
90
  );
91
+ assert.match(ordinary, /#<#\/CreateHdrSqsFilterPatterns#>/);
90
92
  assert.match(ordinary, /exists: false/);
91
93
  });
94
+
@@ -99,6 +99,27 @@ test('generate commits the system WebSocket completion path for own-topic flows'
99
99
  'app/src/generatedCode/SystemFlowSchemas/WebSocketMain/source/WebSocketComplete_HdrSqs.js'
100
100
  )
101
101
  );
102
+ assert.ok(
103
+ result.files.some(
104
+ (file) =>
105
+ file.path ===
106
+ 'app/src/generatedCode/SystemFlowSchemas/Register/source/Register_Main.js'
107
+ )
108
+ );
109
+ assert.ok(
110
+ result.files.some(
111
+ (file) =>
112
+ file.path ===
113
+ 'app/src/generatedCode/SystemFlowSchemas/Register/source/Register_HdrWbs.js'
114
+ )
115
+ );
116
+ assert.ok(
117
+ result.files.some(
118
+ (file) =>
119
+ file.path ===
120
+ 'app/src/generatedCode/SystemFlowSchemas/Register/source/RegisterComplete_Main.js'
121
+ )
122
+ );
102
123
  assert.ok(
103
124
  result.files.some(
104
125
  (file) =>
@@ -347,3 +368,32 @@ async function createTriggerCacheProject() {
347
368
  );
348
369
  return rootPath;
349
370
  }
371
+
372
+ test(
373
+ 'emits ignoreExternalServices hooks in UploadSchema_Main.js and ' +
374
+ 'commands/uploadSchema.js',
375
+ async () => {
376
+ const rootPath = await createCrudProject();
377
+ await generate(rootPath);
378
+
379
+ const uploadSchemaMain = await readFile(
380
+ join(
381
+ rootPath,
382
+ 'app/src/generatedCode/CoreFunctions/UploadSchema_Main.js'
383
+ ),
384
+ 'utf8'
385
+ );
386
+ assert.match(uploadSchemaMain, /\/\/\(<ignoreExternalServices>\)/);
387
+ assert.match(uploadSchemaMain, /\/\/\(<\/ignoreExternalServices>\)/);
388
+
389
+ const commandUploadSchema = await readFile(
390
+ join(
391
+ rootPath,
392
+ 'app/initial_setup/generatedCode/source/commands/uploadSchema.js'
393
+ ),
394
+ 'utf8'
395
+ );
396
+ assert.match(commandUploadSchema, /\/\/\(<ignoreExternalServices>\)/);
397
+ assert.match(commandUploadSchema, /\/\/\(<\/ignoreExternalServices>\)/);
398
+ }
399
+ );