@izara_project/izara-core-generate-service-code 1.0.98 → 1.0.99

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/package.json +1 -1
  2. package/src/compile/flowValidator.js +45 -8
  3. package/src/compile/loadSchemas.js +0 -3
  4. package/src/emit/application/coreFunction/emitCoreFunctionArtifacts.js +15 -1
  5. package/src/emit/application/coreFunction/templates/InitialSetupConfig.ejs +8 -4
  6. package/src/emit/application/crud/templates/CreateEndpoint_Main.ejs +4 -4
  7. package/src/emit/application/crud/templates/CreateLogicalLib.ejs +60 -8
  8. package/src/emit/application/crud/templates/DeleteEndpoint_Main.ejs +4 -4
  9. package/src/emit/application/crud/templates/UpdateEndpoint_Main.ejs +4 -4
  10. package/src/emit/application/custom/customFlowS3.js +4 -4
  11. package/src/emit/application/custom/templates/FlowEndpoint_Main.ejs +2 -2
  12. package/src/emit/application/rbac/emitDynamicRbacFlowSchemas.js +4 -1
  13. package/src/emit/application/rbac/emitRbacApplicationArtifacts.js +76 -1
  14. package/src/emit/application/rbac/templates/ReservedDynamicUsage_Main.ejs +2 -0
  15. package/src/emit/application/rbac/templates/ReservedUsageTtlExpired_HdrDss.ejs +176 -0
  16. package/src/emit/infrastructure/functions/functionYamlArtifact.js +28 -0
  17. package/src/emit/infrastructure/functions/templates/DssFunction_Yaml.ejs +21 -0
  18. package/src/emit/infrastructure/resources/iamResourceArtifacts.js +45 -5
  19. package/src/emit/infrastructure/resources/templates/GeneratedLambdaRoleServiceConfig.ejs +5 -3
  20. package/src/emit/schemas/attributeTree/emitAttributeTreeSchemas.js +62 -1
  21. package/src/generate.js +2 -6
  22. package/src/output/mergeHooks.js +105 -5
  23. package/src/plan/domainRegistry.js +0 -10
  24. package/task.md +40 -96
  25. package/test/integration/graphDrivenFlowSchema.test.js +58 -0
  26. package/test/unit/application/createTargetRoleHandler.test.js +70 -1
  27. package/test/unit/application/crudApplicationArtifacts.test.js +4 -0
  28. package/test/unit/infrastructure/resourceArtifacts.test.js +52 -0
  29. package/test/unit/output/mergeHooks.test.js +137 -0
  30. package/test/unit/schemas/attributeTreeSchemas.test.js +32 -3
  31. package/test/unit/schemas/attributeTreeSingleService.test.js +175 -0
  32. package/test/unit/templates/hookTagsValidation.test.js +83 -0
  33. package/src/emit/schemas/attributeTree/emitAttributeTreeTargetValueStandard.js +0 -132
  34. package/test/unit/schemas/attributeTreeDirectives.test.js +0 -177
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@izara_project/izara-core-generate-service-code",
3
- "version": "1.0.98",
3
+ "version": "1.0.99",
4
4
  "description": "Code for locally generating per service files",
5
5
  "homepage": "https://bitbucket.org/izara-core-support-services/izara-core-support-services-generate-service-code#readme",
6
6
  "repository": {
@@ -9,6 +9,15 @@ export const VALID_EVENT_ENUM = [
9
9
  'generateCode'
10
10
  ];
11
11
 
12
+ export const VALID_S3_STEP_NAMES = [
13
+ 'CreateObject',
14
+ 'ReservedLimit',
15
+ 'CreatePreSignUrl',
16
+ 'ProcessAfterUpload',
17
+ 'ConfirmAfterUpload'
18
+ ];
19
+
20
+
12
21
  export function validateFlowSchema(flowSchema) {
13
22
  if (!flowSchema || typeof flowSchema !== 'object') {
14
23
  throw new TypeError('flowSchema must be an object');
@@ -188,14 +197,19 @@ export function validateFlowSchema(flowSchema) {
188
197
  }
189
198
  }
190
199
 
200
+ const flowEvents = flowSchema.event;
201
+ const isS3Flow = Array.isArray(flowEvents) && flowEvents.includes('s3');
202
+
191
203
  const steps = flowSchema.flowSteps;
192
204
  if (!Array.isArray(steps) || steps.length === 0) {
205
+ if (isS3Flow) {
206
+ return true;
207
+ }
193
208
  throw new Error(
194
209
  'FlowSchema validation error: flowSteps must be a non-empty array'
195
210
  );
196
211
  }
197
212
 
198
- const flowEvents = flowSchema.event;
199
213
  const syncEvents = ['lambdaSyncInv', 'lambdaSyncApi'];
200
214
  let hasSyncEvent = false;
201
215
  for (const eventName of flowEvents) {
@@ -220,16 +234,28 @@ export function validateFlowSchema(flowSchema) {
220
234
  }
221
235
 
222
236
  const entryStep = steps[0];
223
- if (
224
- !entryStep ||
225
- typeof entryStep !== 'object' ||
226
- entryStep.stepName !== 'Start'
227
- ) {
237
+ if (!entryStep || typeof entryStep !== 'object') {
228
238
  throw new Error(
229
- "FlowSchema validation error: Entry step flowSteps[0] must have stepName fixed to 'Start'"
239
+ 'FlowSchema validation error: flowSteps[0] must be an object'
230
240
  );
231
241
  }
232
242
 
243
+ if (isS3Flow) {
244
+ const validS3EntrySteps = ['Start', 'CreateObject'];
245
+ if (!validS3EntrySteps.includes(entryStep.stepName)) {
246
+ throw new Error(
247
+ `FlowSchema validation error: Entry step flowSteps[0] for s3 flow ` +
248
+ `must have stepName 'CreateObject' or 'Start', got '${entryStep.stepName}'`
249
+ );
250
+ }
251
+ } else {
252
+ if (entryStep.stepName !== 'Start') {
253
+ throw new Error(
254
+ "FlowSchema validation error: Entry step flowSteps[0] must have stepName fixed to 'Start'"
255
+ );
256
+ }
257
+ }
258
+
233
259
  if (
234
260
  entryStep.previousSteps &&
235
261
  Array.isArray(entryStep.previousSteps) &&
@@ -240,7 +266,7 @@ export function validateFlowSchema(flowSchema) {
240
266
  );
241
267
  }
242
268
 
243
- if (entryStep.event) {
269
+ if (!isS3Flow && entryStep.event) {
244
270
  throw new Error(
245
271
  "FlowSchema validation error: Entry step flowSteps[0] must not define step-level 'event'"
246
272
  );
@@ -266,6 +292,17 @@ export function validateFlowSchema(flowSchema) {
266
292
  );
267
293
  }
268
294
 
295
+ if (
296
+ isS3Flow &&
297
+ step.stepName !== 'Start' &&
298
+ !VALID_S3_STEP_NAMES.includes(step.stepName)
299
+ ) {
300
+ throw new Error(
301
+ `FlowSchema validation error: flowSteps[${idx}].stepName '${step.stepName}' ` +
302
+ `is invalid for s3 flow. Must be one of ${JSON.stringify(VALID_S3_STEP_NAMES)}`
303
+ );
304
+ }
305
+
269
306
  if (step.stepName.toLowerCase().includes('checktrigger')) {
270
307
  if (
271
308
  !Array.isArray(flowSchema.triggerFlowTypes) ||
@@ -28,9 +28,6 @@ const consoleLogger = {
28
28
 
29
29
  export { resolveExtendedObjectSchemas, resolveGraphServiceTags };
30
30
 
31
- export async function loadGenerateDirectives(project, options = {}) {
32
- return loadLocalSchemaDirectory(project, 'GenerateDirectives', options);
33
- }
34
31
 
35
32
  export async function loadLocalSchemaDirectory(
36
33
  project,
@@ -206,7 +206,21 @@ export async function emitCoreFunctionArtifacts({ project, objects = [], relatio
206
206
  if (typeof flow.flowTag === 'string' && flow.flowTag) {
207
207
  roleName = flow.flowTag[0].toUpperCase() + flow.flowTag.slice(1);
208
208
  }
209
- return { roleName };
209
+ let quotaServiceTag = flow.quotaServiceTag;
210
+ if (!quotaServiceTag && flow.belongTo) {
211
+ if (Array.isArray(flow.belongTo)) {
212
+ quotaServiceTag = flow.belongTo[0]?.serviceTag;
213
+ } else if (flow.belongTo.serviceTag) {
214
+ quotaServiceTag = flow.belongTo.serviceTag;
215
+ }
216
+ }
217
+ if (!quotaServiceTag && flow.objType?.serviceTag) {
218
+ quotaServiceTag = flow.objType.serviceTag;
219
+ }
220
+ if (!quotaServiceTag) {
221
+ quotaServiceTag = project?.serviceTag || 'Rbac';
222
+ }
223
+ return { roleName, quotaServiceTag };
210
224
  });
211
225
 
212
226
  const rbac =
@@ -277,7 +277,9 @@ function roleConfigs() {
277
277
  `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, 'ConfirmUsageHdrInv', 'Rbac')}`,
278
278
  `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, 'CancelUsageHdrInv', 'Rbac')}`<% if (rbac) { %>,
279
279
  `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, 'ValidatorRbacHdrInv')}`<% } %><% (rbacValidatorServiceTags || []).forEach(function(tag) { %>,
280
- `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, 'ValidatorRbacHdrInv', '<%= tag %>')}`<% }); %>
280
+ `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, 'ValidatorRbacHdrInv', '<%= tag %>')}`,
281
+ `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, 'ConfirmUsageHdrInv', '<%= tag %>')}`,
282
+ `arn:aws:lambda:${process.env.iz_region}:${process.env.iz_accountId}:function:${lambdaSharedLib.lambdaFunctionName(_izContext, 'CancelUsageHdrInv', '<%= tag %>')}`<% }); %>
281
283
  ]
282
284
  },
283
285
 
@@ -290,7 +292,9 @@ function roleConfigs() {
290
292
  `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ConfirmUsage_In', 'Rbac')}`,
291
293
  `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'CancelUsage_In', 'Rbac')}`<% if (rbac) { %>,
292
294
  `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In')}`<% } %><% (rbacValidatorServiceTags || []).forEach(function(tag) { %>,
293
- `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', '<%= tag %>')}`<% }); %>
295
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', '<%= tag %>')}`,
296
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ConfirmUsage_In', '<%= tag %>')}`,
297
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'CancelUsage_In', '<%= tag %>')}`<% }); %>
294
298
  ]
295
299
  },
296
300
  <% } -%>
@@ -390,7 +394,7 @@ function roleConfigs() {
390
394
  Effect: 'Allow',
391
395
  Action: ['sns:Publish'],
392
396
  Resource: [
393
- `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ReservedDynamicUsage_In', 'AccountLimits')}`
397
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ReservedDynamicUsage_In', '<%= flow.quotaServiceTag || 'Rbac' %>')}`
394
398
  ]
395
399
  },
396
400
 
@@ -399,7 +403,7 @@ function roleConfigs() {
399
403
  Effect: 'Allow',
400
404
  Action: ['sns:Publish'],
401
405
  Resource: [
402
- `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ConfirmDynamicAfterUpload_In', 'AccountLimits')}`
406
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ConfirmDynamicAfterUpload_In', '<%= flow.quotaServiceTag || 'Rbac' %>')}`
403
407
  ]
404
408
  }
405
409
  //(<create<%= flow.roleName %>Role>)
@@ -142,9 +142,9 @@ export default async function <%- flowTag %>(
142
142
  const { checkRbacPermission } =
143
143
  await import('@izara_project/izara-authorization');
144
144
 
145
- //(<rbacParameterGroups_<%- flowTag %>)
145
+ //(<rbacParameterGroups_<%- flowTag %>>)
146
146
  const parameterGroups = {};
147
- //(</rbacParameterGroups_<%- flowTag %>)
147
+ //(</rbacParameterGroups_<%- flowTag %>>)
148
148
 
149
149
  const isBillable = objectSchema.addOnDataStructure?.some(
150
150
  (item) => item.type === 'rbac' && item.billable === true
@@ -187,8 +187,8 @@ export default async function <%- flowTag %>(
187
187
  );
188
188
  }
189
189
 
190
- //(<rbacPublish_<%- flowTag %>)
191
- //(</rbacPublish_<%- flowTag %>)
190
+ //(<rbacPublish_<%- flowTag %>>)
191
+ //(</rbacPublish_<%- flowTag %>>)
192
192
  }
193
193
  <%_ } _%>
194
194
 
@@ -499,6 +499,24 @@ const ROOT_RBAC_OBJECT_TYPES = new Set([
499
499
  <%_ } _%>
500
500
  ]);
501
501
 
502
+ <%_
503
+ const objectQuotaServiceTags = {};
504
+ for (const obj of objects || []) {
505
+ if (obj?.objectType) {
506
+ let serviceTag = null;
507
+ if (obj.belongTo) {
508
+ if (Array.isArray(obj.belongTo)) {
509
+ serviceTag = obj.belongTo[0]?.serviceTag || null;
510
+ } else if (obj.belongTo.serviceTag) {
511
+ serviceTag = obj.belongTo.serviceTag;
512
+ }
513
+ }
514
+ objectQuotaServiceTags[obj.objectType] = serviceTag;
515
+ }
516
+ }
517
+ _%>
518
+ const OBJECT_QUOTA_SERVICE_TAGS = <%- JSON.stringify(objectQuotaServiceTags) %>;
519
+
502
520
  /**
503
521
  * Auto-provisions owner TargetRole for Root RBAC object after Graph and DynamoDB write
504
522
  * @param {Object} _izContext - Izara context object
@@ -772,6 +790,30 @@ export function resolveQuotaDeductionKeys(
772
790
  passBackProperties?.rbacType ||
773
791
  callingFlowConfig?.rbacType;
774
792
 
793
+ const objType =
794
+ returnValue?.requestParams?.objType ||
795
+ requestParams?.objType ||
796
+ passBackProperties?.objType;
797
+ let objectType = null;
798
+ if (typeof objType === 'string') {
799
+ objectType = objType;
800
+ } else if (objType && objType.objectType) {
801
+ objectType = objType.objectType;
802
+ }
803
+
804
+ let quotaServiceTag =
805
+ requestParams?.passBackProperties?.quotaServiceTag ||
806
+ passBackProperties?.passBackProperties?.quotaServiceTag ||
807
+ passBackProperties?.quotaServiceTag ||
808
+ callingFlowConfig?.quotaServiceTag;
809
+
810
+ if (!quotaServiceTag && objectType && OBJECT_QUOTA_SERVICE_TAGS[objectType]) {
811
+ quotaServiceTag = OBJECT_QUOTA_SERVICE_TAGS[objectType];
812
+ }
813
+ if (!quotaServiceTag) {
814
+ quotaServiceTag = process.env.iz_serviceTag;
815
+ }
816
+
775
817
  return {
776
818
  limitTag,
777
819
  flowTypeConcat,
@@ -780,7 +822,8 @@ export function resolveQuotaDeductionKeys(
780
822
  runningTotalReservedId,
781
823
  identifiersConcat,
782
824
  targetIdentifiers,
783
- rbacType
825
+ rbacType,
826
+ quotaServiceTag
784
827
  };
785
828
  }
786
829
 
@@ -808,10 +851,15 @@ export async function forwardToQuotaConfirmation(
808
851
  targetFunction = 'ConfirmUsageHdrInv';
809
852
  }
810
853
 
854
+ let quotaServiceTag = quotaKeys.quotaServiceTag;
855
+ if (!quotaServiceTag) {
856
+ quotaServiceTag = process.env.iz_serviceTag;
857
+ }
858
+
811
859
  const functionName = await lambdaSharedLib.lambdaFunctionName(
812
860
  _izContext,
813
861
  targetFunction,
814
- 'Rbac'
862
+ quotaServiceTag
815
863
  );
816
864
 
817
865
  const forwardMessage = {
@@ -820,11 +868,15 @@ export async function forwardToQuotaConfirmation(
820
868
  callingFlowConfig
821
869
  };
822
870
 
823
- _izContext.logger.debug('[CreateLogicalLib] invoking RBAC quota sync', {
824
- targetFunction,
825
- functionName,
826
- forwardMessage
827
- });
871
+ _izContext.logger.debug(
872
+ `[CreateLogicalLib] invoking ${quotaServiceTag} quota sync`,
873
+ {
874
+ targetFunction,
875
+ functionName,
876
+ quotaServiceTag,
877
+ forwardMessage
878
+ }
879
+ );
828
880
 
829
881
  try {
830
882
  quotaResult = await lambda.invokeSync(
@@ -842,7 +894,7 @@ export async function forwardToQuotaConfirmation(
842
894
  const cancelFunctionName = await lambdaSharedLib.lambdaFunctionName(
843
895
  _izContext,
844
896
  'CancelUsageHdrInv',
845
- 'Rbac'
897
+ quotaServiceTag
846
898
  );
847
899
  await lambda.invokeSync(
848
900
  _izContext,
@@ -144,9 +144,9 @@ export default async function <%- flowTag %>(
144
144
  const { checkRbacPermission } =
145
145
  await import('@izara_project/izara-authorization');
146
146
 
147
- //(<rbacParameterGroups_<%- flowTag %>)
147
+ //(<rbacParameterGroups_<%- flowTag %>>)
148
148
  const parameterGroups = {};
149
- //(</rbacParameterGroups_<%- flowTag %>)
149
+ //(</rbacParameterGroups_<%- flowTag %>>)
150
150
 
151
151
  const resultPermissionCheck = await checkRbacPermission(
152
152
  _izContext,
@@ -181,8 +181,8 @@ export default async function <%- flowTag %>(
181
181
  );
182
182
  }
183
183
 
184
- //(<rbacPublish_<%- flowTag %>)
185
- //(</rbacPublish_<%- flowTag %>)
184
+ //(<rbacPublish_<%- flowTag %>>)
185
+ //(</rbacPublish_<%- flowTag %>>)
186
186
  }
187
187
  <%_ } _%>
188
188
 
@@ -156,9 +156,9 @@ export default async function <%- flowTag %>(
156
156
  const { checkRbacPermission } =
157
157
  await import('@izara_project/izara-authorization');
158
158
 
159
- //(<rbacParameterGroups_<%- flowTag %>)
159
+ //(<rbacParameterGroups_<%- flowTag %>>)
160
160
  const parameterGroups = {};
161
- //(</rbacParameterGroups_<%- flowTag %>)
161
+ //(</rbacParameterGroups_<%- flowTag %>>)
162
162
 
163
163
  const resultPermissionCheck = await checkRbacPermission(
164
164
  _izContext,
@@ -193,8 +193,8 @@ export default async function <%- flowTag %>(
193
193
  );
194
194
  }
195
195
 
196
- //(<rbacPublish_<%- flowTag %>)
197
- //(</rbacPublish_<%- flowTag %>)
196
+ //(<rbacPublish_<%- flowTag %>>)
197
+ //(</rbacPublish_<%- flowTag %>>)
198
198
  }
199
199
  <%_ } _%>
200
200
 
@@ -73,7 +73,7 @@ export async function emitS3Flow(flow, source, handlerRoot) {
73
73
  ...descriptor,
74
74
  routeName: tag,
75
75
  flowTag: flow.flowTag,
76
- flowStepName: ''
76
+ flowStepName: action
77
77
  });
78
78
  } else if (type === 'sqs') {
79
79
  let filterPatternFunctionName = descriptor.functionName;
@@ -84,7 +84,7 @@ export async function emitS3Flow(flow, source, handlerRoot) {
84
84
  ...descriptor,
85
85
  queueName: descriptor.functionName,
86
86
  flowTag: flow.flowTag,
87
- flowStepName: '',
87
+ flowStepName: action,
88
88
  filterPatternFunctionName
89
89
  });
90
90
  } else if (type === 's3') {
@@ -101,13 +101,13 @@ export async function emitS3Flow(flow, source, handlerRoot) {
101
101
  ...descriptor,
102
102
  bucketName: flow.bucketName,
103
103
  flowTag: flow.flowTag,
104
- flowStepName: ''
104
+ flowStepName: action
105
105
  });
106
106
  } else {
107
107
  functionYaml = renderInvFunctionYaml({
108
108
  ...descriptor,
109
109
  flowTag: flow.flowTag,
110
- flowStepName: ''
110
+ flowStepName: action
111
111
  });
112
112
  }
113
113
  return {
@@ -247,8 +247,8 @@ export default async function <%- typeof functionName !== 'undefined' ? function
247
247
  resultPermissionCheck
248
248
  );
249
249
 
250
- //(<rbacPublish_<%- flowTag %>)
251
- //(</rbacPublish_<%- flowTag %>)
250
+ //(<rbacPublish_<%- flowTag %>>)
251
+ //(</rbacPublish_<%- flowTag %>>)
252
252
 
253
253
  return resultPermissionCheck;
254
254
  <% } else { -%>
@@ -18,7 +18,10 @@ const RBAC_CORE_FLOW_DEFINITIONS = [
18
18
  ];
19
19
 
20
20
  const LIMIT_FLOW_DEFINITIONS = [
21
- { flowTag: 'ReservedDynamicUsage', event: ['ownTopic'] },
21
+ {
22
+ flowTag: 'ReservedDynamicUsage',
23
+ event: ['ownTopic', 'lambdaSyncInv', 'lambdaSyncApi']
24
+ },
22
25
  {
23
26
  flowTag: 'ConfirmUsage',
24
27
  event: ['ownTopic', 'lambdaSyncInv', 'lambdaSyncApi']
@@ -3,6 +3,7 @@ import { readFile } from 'node:fs/promises';
3
3
 
4
4
  import {
5
5
  renderApiFunctionYaml,
6
+ renderDssFunctionYaml,
6
7
  renderInvFunctionYaml,
7
8
  renderSqsFunctionYaml,
8
9
  renderWbsFunctionYaml
@@ -53,7 +54,10 @@ export async function emitRbacApplicationArtifacts(objects) {
53
54
 
54
55
  const limitHandlers = await Promise.all(
55
56
  hasSelfRbac
56
- ? ACCOUNT_LIMIT_HANDLERS.map(createAccountLimitHandler)
57
+ ? [
58
+ ...ACCOUNT_LIMIT_HANDLERS.map(createAccountLimitHandler),
59
+ createReservedUsageTtlExpiredHandler()
60
+ ]
57
61
  : []
58
62
  );
59
63
 
@@ -173,9 +177,12 @@ const STATIC_LIMIT_APPLICATION_PATHS = [
173
177
  ...ACCOUNT_LIMIT_HANDLERS.flatMap((flowTag) => [
174
178
  `app/src/generatedCode/FlowLimit/${flowTag}/source/${flowTag}_Main.js`,
175
179
  `app/src/generatedCode/FlowLimit/${flowTag}/source/${flowTag}_HdrSqs.js`,
180
+ `app/src/generatedCode/FlowLimit/${flowTag}/source/${flowTag}_HdrInv.js`,
181
+ `app/src/generatedCode/FlowLimit/${flowTag}/source/${flowTag}_HdrApi.js`,
176
182
  `app/src/generatedCode/FlowLimit/${flowTag}/source/${flowTag}_WbsMain.js`,
177
183
  `app/src/generatedCode/FlowLimit/${flowTag}/source/${flowTag}_HdrWbs.js`
178
184
  ]),
185
+ 'app/src/generatedCode/FlowLimit/ReservedUsageTtlExpired/source/ReservedUsageTtlExpired_HdrDss.js',
179
186
  FLOW_LIMITS_YAML_PATH
180
187
  ];
181
188
 
@@ -210,6 +217,12 @@ async function createAccountLimitHandler(flowTag) {
210
217
  sourcePath,
211
218
  handlerPath
212
219
  );
220
+ const shared = {
221
+ flowTag,
222
+ flowStepName: '',
223
+ mainFileName: `${flowTag}_Main`,
224
+ functionName: flowTag
225
+ };
213
226
  return {
214
227
  artifacts: [
215
228
  {
@@ -220,6 +233,14 @@ async function createAccountLimitHandler(flowTag) {
220
233
  path: `${sourcePath}/${flowTag}_HdrSqs.js`,
221
234
  content: await renderRbacTemplate('ListRbac_HdrSqs.ejs', { flowTag })
222
235
  },
236
+ {
237
+ path: `${sourcePath}/${flowTag}_HdrInv.js`,
238
+ content: await renderSharedTemplate('FlowHandler_HdrInv.ejs', shared)
239
+ },
240
+ {
241
+ path: `${sourcePath}/${flowTag}_HdrApi.js`,
242
+ content: await renderSharedTemplate('FlowHandler_HdrApi.ejs', shared)
243
+ },
223
244
  ...webSocket.artifacts
224
245
  ],
225
246
  functions: [
@@ -232,11 +253,64 @@ async function createAccountLimitHandler(flowTag) {
232
253
  flowTag: flowTag,
233
254
  flowStepName: ''
234
255
  },
256
+ {
257
+ type: 'inv',
258
+ functionName: `${flowTag}HdrInv`,
259
+ handlerPath: `${handlerPath}/${flowTag}_HdrInv.main`,
260
+ roleName: 'FlowRbacRole',
261
+ flowTag: flowTag,
262
+ flowStepName: ''
263
+ },
264
+ {
265
+ type: 'api',
266
+ functionName: `${flowTag}HdrApi`,
267
+ handlerPath: `${handlerPath}/${flowTag}_HdrApi.main`,
268
+ roleName: 'FlowRbacRole',
269
+ flowTag: flowTag,
270
+ flowStepName: ''
271
+ },
235
272
  ...webSocket.functions
236
273
  ]
237
274
  };
238
275
  }
239
276
 
277
+ async function createReservedUsageTtlExpiredHandler() {
278
+ const sourcePath =
279
+ 'app/src/generatedCode/FlowLimit/ReservedUsageTtlExpired/source';
280
+ const handlerPath =
281
+ 'src/generatedCode/FlowLimit/ReservedUsageTtlExpired/source';
282
+ return {
283
+ artifacts: [
284
+ {
285
+ path: `${sourcePath}/ReservedUsageTtlExpired_HdrDss.js`,
286
+ content: await renderRbacTemplate('ReservedUsageTtlExpired_HdrDss.ejs')
287
+ }
288
+ ],
289
+ functions: [
290
+ {
291
+ type: 'dss',
292
+ functionName: 'ReservedUsageTtlExpiredHdrDss',
293
+ handlerPath: `${handlerPath}/ReservedUsageTtlExpired_HdrDss.main`,
294
+ roleName: 'FlowRbacRole',
295
+ tableName: 'RunningTotalReservedUsage',
296
+ batchSize: 100,
297
+ startingPosition: 'TRIM_HORIZON',
298
+ filterPatterns: [
299
+ {
300
+ eventName: ['REMOVE'],
301
+ userIdentity: {
302
+ type: ['Service'],
303
+ principalId: ['dynamodb.amazonaws.com']
304
+ }
305
+ }
306
+ ],
307
+ flowTag: 'ReservedUsageTtlExpired',
308
+ flowStepName: ''
309
+ }
310
+ ]
311
+ };
312
+ }
313
+
240
314
 
241
315
  async function createCustomRbacOwnTopicHandler(flowTag, mainTemplateName) {
242
316
  const sourcePath = `app/src/generatedCode/FlowRbac/${flowTag}/source`;
@@ -523,6 +597,7 @@ function renderFunctionYaml(descriptor) {
523
597
  if (descriptor.type === 'api') return renderApiFunctionYaml(descriptor);
524
598
  if (descriptor.type === 'inv') return renderInvFunctionYaml(descriptor);
525
599
  if (descriptor.type === 'wbs') return renderWbsFunctionYaml(descriptor);
600
+ if (descriptor.type === 'dss') return renderDssFunctionYaml(descriptor);
526
601
  return renderSqsFunctionYaml(descriptor);
527
602
  }
528
603
 
@@ -169,6 +169,8 @@ export default async function ReservedDynamicUsage(_izContext, requestParams) {
169
169
  {
170
170
  runningTotalReservedId: `${identifiersConcat}_${flowTypeConcat}`,
171
171
  usedTimestampId: uniqueIdentify,
172
+ identifiersConcat,
173
+ flowTypeConcat,
172
174
  usageCount: value,
173
175
  runningTotalUsageStatus: 'reserved',
174
176
  ttlTimestamp