@bitblit/ratchet-epsilon-common 6.0.146-alpha → 6.0.148-alpha

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 (165) hide show
  1. package/lib/config/cron/abstract-cron-entry.d.ts +1 -1
  2. package/package.json +11 -10
  3. package/src/background/background-dynamo-log-table-handler.ts +44 -0
  4. package/src/background/background-entry.ts +4 -0
  5. package/src/background/background-execution-event-type.ts +9 -0
  6. package/src/background/background-execution-event.ts +9 -0
  7. package/src/background/background-execution-listener.ts +6 -0
  8. package/src/background/background-handler.ts +352 -0
  9. package/src/background/background-http-adapter-handler.ts +166 -0
  10. package/src/background/background-meta-response-internal.ts +5 -0
  11. package/src/background/background-process-handling.ts +6 -0
  12. package/src/background/background-process-log-table-entry.ts +11 -0
  13. package/src/background/background-queue-response-internal.ts +9 -0
  14. package/src/background/background-validator.ts +105 -0
  15. package/src/background/epsilon-background-process-error.ts +110 -0
  16. package/src/background/internal-background-entry.ts +10 -0
  17. package/src/background/manager/abstract-background-manager.ts +120 -0
  18. package/src/background/manager/aws-large-payload-s3-sqs-sns-background-manager.ts +87 -0
  19. package/src/background/manager/aws-sqs-sns-background-manager.ts +201 -0
  20. package/src/background/manager/background-manager-like.ts +44 -0
  21. package/src/background/manager/background-manager.spec.ts +89 -0
  22. package/src/background/manager/single-thread-local-background-manager.ts +58 -0
  23. package/src/background/s3-background-transaction-logger.ts +65 -0
  24. package/src/build/ratchet-epsilon-common-info.ts +19 -0
  25. package/src/built-in/background/echo-processor.ts +17 -0
  26. package/src/built-in/background/log-and-enqueue-echo-processor.ts +14 -0
  27. package/src/built-in/background/log-message-background-error-processor.ts +10 -0
  28. package/src/built-in/background/no-op-processor.ts +12 -0
  29. package/src/built-in/background/retry-processor.ts +51 -0
  30. package/src/built-in/background/sample-delay-processor.ts +15 -0
  31. package/src/built-in/background/sample-input-validated-processor-data.ts +4 -0
  32. package/src/built-in/background/sample-input-validated-processor.ts +14 -0
  33. package/src/built-in/built-in-trace-id-generators.ts +22 -0
  34. package/src/built-in/daemon/daemon-authorizer-function.ts +4 -0
  35. package/src/built-in/daemon/daemon-config.ts +9 -0
  36. package/src/built-in/daemon/daemon-group-selection-function.ts +3 -0
  37. package/src/built-in/daemon/daemon-handler.ts +87 -0
  38. package/src/built-in/daemon/daemon-process-state-list.ts +9 -0
  39. package/src/built-in/http/apollo/apollo-util.ts +43 -0
  40. package/src/built-in/http/apollo/default-epsilon-apollo-context.ts +11 -0
  41. package/src/built-in/http/apollo/epsilon-apollo-context-builder-options.ts +5 -0
  42. package/src/built-in/http/apollo/epsilon-lambda-apollo-context-function-argument.ts +6 -0
  43. package/src/built-in/http/apollo/epsilon-lambda-apollo-options.ts +11 -0
  44. package/src/built-in/http/apollo-filter.ts +151 -0
  45. package/src/built-in/http/built-in-auth-filters.ts +73 -0
  46. package/src/built-in/http/built-in-authorizers.ts +22 -0
  47. package/src/built-in/http/built-in-filters.spec.ts +26 -0
  48. package/src/built-in/http/built-in-filters.ts +300 -0
  49. package/src/built-in/http/built-in-handlers.ts +85 -0
  50. package/src/built-in/http/log-level-manipulation-filter.ts +26 -0
  51. package/src/built-in/http/run-handler-as-filter.spec.ts +67 -0
  52. package/src/built-in/http/run-handler-as-filter.ts +102 -0
  53. package/src/cli/ratchet-cli-handler.ts +23 -0
  54. package/src/cli/run-background-process-from-command-line.ts +32 -0
  55. package/src/config/background/background-aws-config.ts +8 -0
  56. package/src/config/background/background-config.ts +15 -0
  57. package/src/config/background/background-error-processor.ts +5 -0
  58. package/src/config/background/background-processor.ts +14 -0
  59. package/src/config/background/background-transaction-log.ts +9 -0
  60. package/src/config/background/background-transaction-logger.ts +6 -0
  61. package/src/config/cron/abstract-cron-entry.ts +17 -0
  62. package/src/config/cron/cron-background-entry.ts +17 -0
  63. package/src/config/cron/cron-config.ts +10 -0
  64. package/src/config/dynamo-db-config.ts +6 -0
  65. package/src/config/epsilon-config.ts +30 -0
  66. package/src/config/epsilon-lambda-event-handler.ts +12 -0
  67. package/src/config/epsilon-logger-config.ts +23 -0
  68. package/src/config/espilon-server-mode.ts +10 -0
  69. package/src/config/generic-aws-event-handler-function.ts +1 -0
  70. package/src/config/http/authorizer-function.ts +9 -0
  71. package/src/config/http/epsilon-authorization-context.ts +5 -0
  72. package/src/config/http/epsilon-cors-approach.ts +7 -0
  73. package/src/config/http/extended-api-gateway-event.ts +8 -0
  74. package/src/config/http/filter-chain-context.ts +15 -0
  75. package/src/config/http/filter-function.ts +3 -0
  76. package/src/config/http/handler-function.ts +4 -0
  77. package/src/config/http/http-config.ts +27 -0
  78. package/src/config/http/http-processing-config.ts +23 -0
  79. package/src/config/http/mapped-http-processing-config.ts +12 -0
  80. package/src/config/http/null-returned-object-handling.ts +7 -0
  81. package/src/config/inter-api/inter-api-aws-config.ts +5 -0
  82. package/src/config/inter-api/inter-api-config.ts +7 -0
  83. package/src/config/inter-api/inter-api-process-mapping.ts +11 -0
  84. package/src/config/local-server/local-server-event-logging-style.ts +8 -0
  85. package/src/config/local-server/local-server-http-method-handling.ts +7 -0
  86. package/src/config/local-server/local-server-options.ts +12 -0
  87. package/src/config/logging-trace-id-generator.ts +3 -0
  88. package/src/config/no-handlers-found-error.ts +6 -0
  89. package/src/config/open-api/open-api-document-components.ts +4 -0
  90. package/src/config/open-api/open-api-document.ts +7 -0
  91. package/src/config/s3-config.ts +8 -0
  92. package/src/config/sns-config.ts +7 -0
  93. package/src/config/sqs-config.ts +7 -0
  94. package/src/epsilon-build-properties.ts +21 -0
  95. package/src/epsilon-constants.ts +62 -0
  96. package/src/epsilon-global-handler.ts +238 -0
  97. package/src/epsilon-instance.ts +20 -0
  98. package/src/epsilon-logging-extension-processor.ts +19 -0
  99. package/src/http/auth/api-gateway-adapter-authentication-handler.ts +95 -0
  100. package/src/http/auth/auth0-web-token-manipulator.ts +69 -0
  101. package/src/http/auth/basic-auth-token.ts +7 -0
  102. package/src/http/auth/google-web-token-manipulator.spec.ts +15 -0
  103. package/src/http/auth/google-web-token-manipulator.ts +80 -0
  104. package/src/http/auth/jwt-ratchet-local-web-token-manipulator.ts +37 -0
  105. package/src/http/auth/local-web-token-manipulator.spec.ts +34 -0
  106. package/src/http/auth/local-web-token-manipulator.ts +114 -0
  107. package/src/http/auth/web-token-manipulator.ts +9 -0
  108. package/src/http/error/bad-gateway.ts +11 -0
  109. package/src/http/error/bad-request-error.ts +11 -0
  110. package/src/http/error/conflict-error.ts +12 -0
  111. package/src/http/error/forbidden-error.ts +12 -0
  112. package/src/http/error/gateway-timeout.ts +12 -0
  113. package/src/http/error/method-not-allowed-error.ts +12 -0
  114. package/src/http/error/misconfigured-error.ts +12 -0
  115. package/src/http/error/not-found-error.ts +12 -0
  116. package/src/http/error/not-implemented.ts +12 -0
  117. package/src/http/error/request-timeout-error.ts +12 -0
  118. package/src/http/error/service-unavailable.ts +12 -0
  119. package/src/http/error/too-many-requests-error.ts +12 -0
  120. package/src/http/error/unauthorized-error.ts +12 -0
  121. package/src/http/event-util.spec.ts +190 -0
  122. package/src/http/event-util.ts +272 -0
  123. package/src/http/response-util.spec.ts +117 -0
  124. package/src/http/response-util.ts +164 -0
  125. package/src/http/route/epsilon-router.ts +9 -0
  126. package/src/http/route/extended-auth-response-context.ts +7 -0
  127. package/src/http/route/route-and-parse.ts +8 -0
  128. package/src/http/route/route-mapping.ts +21 -0
  129. package/src/http/route/route-validator-config.ts +5 -0
  130. package/src/http/route/router-util.spec.ts +33 -0
  131. package/src/http/route/router-util.ts +314 -0
  132. package/src/http/web-handler.spec.ts +99 -0
  133. package/src/http/web-handler.ts +157 -0
  134. package/src/http/web-v2-handler.ts +34 -0
  135. package/src/inter-api/inter-api-entry.ts +8 -0
  136. package/src/inter-api/inter-api-util.spec.ts +77 -0
  137. package/src/inter-api/inter-api-util.ts +71 -0
  138. package/src/inter-api-manager.ts +75 -0
  139. package/src/lambda-event-handler/cron-epsilon-lambda-event-handler.spec.ts +130 -0
  140. package/src/lambda-event-handler/cron-epsilon-lambda-event-handler.ts +132 -0
  141. package/src/lambda-event-handler/dynamo-epsilon-lambda-event-handler.ts +42 -0
  142. package/src/lambda-event-handler/generic-sns-epsilon-lambda-event-handler.ts +38 -0
  143. package/src/lambda-event-handler/generic-sqs-epsilon-lambda-event-handler.ts +43 -0
  144. package/src/lambda-event-handler/inter-api-epsilon-lambda-event-handler.ts +33 -0
  145. package/src/lambda-event-handler/s3-epsilon-lambda-event-handler.ts +50 -0
  146. package/src/local-container-server.ts +128 -0
  147. package/src/local-server.spec.ts +16 -0
  148. package/src/local-server.ts +426 -0
  149. package/src/open-api-util/open-api-doc-modifications.ts +9 -0
  150. package/src/open-api-util/open-api-doc-modifier.spec.ts +22 -0
  151. package/src/open-api-util/open-api-doc-modifier.ts +90 -0
  152. package/src/open-api-util/yaml-combiner.spec.ts +26 -0
  153. package/src/open-api-util/yaml-combiner.ts +35 -0
  154. package/src/sample/sample-server-components-with-apollo.ts +87 -0
  155. package/src/sample/sample-server-components.ts +183 -0
  156. package/src/sample/sample-server-static-files.ts +614 -0
  157. package/src/sample/test-error-server.ts +140 -0
  158. package/src/util/aws-util.ts +89 -0
  159. package/src/util/context-global-data.ts +13 -0
  160. package/src/util/context-util.ts +156 -0
  161. package/src/util/cron-util.spec.ts +190 -0
  162. package/src/util/cron-util.ts +86 -0
  163. package/src/util/epsilon-config-parser.ts +90 -0
  164. package/src/util/epsilon-server-util.spec.ts +18 -0
  165. package/src/util/epsilon-server-util.ts +16 -0
@@ -0,0 +1,77 @@
1
+ import { SNSEvent } from 'aws-lambda';
2
+ import { InterApiUtil } from './inter-api-util.js';
3
+ import { InterApiConfig } from '../config/inter-api/inter-api-config.js';
4
+
5
+ import { BackgroundManagerLike } from '../background/manager/background-manager-like.js';
6
+ import { SNSClient } from '@aws-sdk/client-sns';
7
+ import { SQSClient } from '@aws-sdk/client-sqs';
8
+ import { beforeEach, describe, expect, test } from 'vitest';
9
+ import { mock } from 'vitest-mock-extended';
10
+
11
+ describe('#interApiUtil', function () {
12
+ let _mockSns;
13
+ let _mockSqs;
14
+ let mockBgMgr;
15
+
16
+ const evt: SNSEvent = {
17
+ Records: [
18
+ {
19
+ EventSource: 'aws:sns',
20
+ EventVersion: '1.0',
21
+ EventSubscriptionArn: 'arn:aws:sns:us-east-1:012345678901:GenericApiEventTopicDev:6efec6a5-1f02-4fc5-b0f7-fa7c013cf8bb',
22
+ Sns: {
23
+ Type: 'Notification',
24
+ MessageId: '205de1e8-7ba6-52f5-b706-b815f442c512',
25
+ TopicArn: 'arn:aws:sns:us-east-1:012345678901:GenericApiEventTopicDev',
26
+ Subject: null,
27
+ Message:
28
+ '{"type":"EPSILON_INTER_API_EVENT","interApiEvent":{"source":"OriginalApi","type":"Sample","data":{"notes":"SOURCE API: OriginalApi","timestampEpochMS":1636011428200}}}',
29
+ Timestamp: '2021-11-04T07:37:08.241Z',
30
+ SignatureVersion: '1',
31
+ Signature: 'LyS2ybM/Epsq5sFqPJd==',
32
+ SigningCertUrl: 'https://sns.us-east-1.amazonaws.com/SimpleNotificationService-7ff5318490ec183fbaddaa2a969abfda.pem',
33
+ UnsubscribeUrl:
34
+ 'https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:012345678901:GenericApiEventTopicDev:6efec6a5-1f02-4fc5-b0f7-fa7c013cf8bb',
35
+ MessageAttributes: {},
36
+ },
37
+ },
38
+ ],
39
+ };
40
+
41
+ beforeEach(() => {
42
+ _mockSns = mock<SNSClient>();
43
+ _mockSqs = mock<SQSClient>();
44
+ mockBgMgr = mock<BackgroundManagerLike>(); //new AwsSqsSnsBackgroundManager({} as BackgroundAwsConfig, mockSqs, mockSns);
45
+ });
46
+
47
+ test('should translate processes', async () => {
48
+ mockBgMgr.createEntry.mockResolvedValue({ t: 1 });
49
+ mockBgMgr.addEntriesToQueue.mockResolvedValue(['a]']);
50
+
51
+ const cfg: InterApiConfig = {
52
+ aws: {
53
+ source: 'test',
54
+ snsArn: 'test',
55
+ localMode: true,
56
+ },
57
+ processMappings: [
58
+ {
59
+ typeRegex: '.*',
60
+ sourceRegex: '.*',
61
+ disabled: false,
62
+ backgroundProcessTypes: ['TESTBG'],
63
+ },
64
+ ],
65
+ };
66
+
67
+ const output: string[] = await InterApiUtil.processInterApiEvent(evt, cfg, mockBgMgr);
68
+
69
+ expect(output).not.toBeNull();
70
+ expect(output.length).toEqual(1);
71
+ });
72
+
73
+ test('should verify that an event is an inter-api even', async () => {
74
+ const res: boolean = InterApiUtil.isInterApiSnsEvent(evt);
75
+ expect(res).toBeTruthy();
76
+ }, 500);
77
+ });
@@ -0,0 +1,71 @@
1
+ import { LambdaEventDetector } from '@bitblit/ratchet-aws/lambda/lambda-event-detector';
2
+ import { SNSEvent } from 'aws-lambda';
3
+ import { EpsilonConstants } from '../epsilon-constants.js';
4
+ import { InterApiEntry } from './inter-api-entry.js';
5
+ import { RequireRatchet } from '@bitblit/ratchet-common/lang/require-ratchet';
6
+
7
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
8
+ import { StringRatchet } from '@bitblit/ratchet-common/lang/string-ratchet';
9
+ import { BackgroundEntry } from '../background/background-entry.js';
10
+ import { InterApiConfig } from '../config/inter-api/inter-api-config.js';
11
+ import { ContextUtil } from '../util/context-util.js';
12
+ import { BackgroundManagerLike } from '../background/manager/background-manager-like.js';
13
+
14
+ export class InterApiUtil {
15
+ public static isInterApiSnsEvent(event: any): boolean {
16
+ return !!InterApiUtil.extractEntryFromEvent(event);
17
+ }
18
+
19
+ public static extractEntryFromEvent(evt: SNSEvent): InterApiEntry<any> {
20
+ let rval: InterApiEntry<any> = null;
21
+
22
+ if (evt) {
23
+ if (LambdaEventDetector.isSingleSnsEvent(evt)) {
24
+ const cast: SNSEvent = evt as SNSEvent;
25
+ const msg: string = cast.Records[0].Sns.Message;
26
+ if (StringRatchet.trimToNull(msg)) {
27
+ const parsed: any = JSON.parse(msg);
28
+ if (!!parsed && parsed['type'] === EpsilonConstants.INTER_API_SNS_EVENT) {
29
+ rval = parsed['interApiEvent'];
30
+ }
31
+ }
32
+ }
33
+ }
34
+
35
+ return rval;
36
+ }
37
+
38
+ public static async processInterApiEvent(evt: SNSEvent, cfg: InterApiConfig, mgr: BackgroundManagerLike): Promise<string[]> {
39
+ let rval: string[] = [];
40
+ RequireRatchet.notNullOrUndefined(evt, 'InterApiEntry');
41
+ RequireRatchet.notNullOrUndefined(mgr, 'BackgroundManager');
42
+
43
+ const interApiEntry: InterApiEntry<any> = InterApiUtil.extractEntryFromEvent(evt);
44
+ ContextUtil.setOverrideTraceFromInterApiEntry(interApiEntry);
45
+ Logger.info('Processing inter-api event : %j', evt);
46
+ const backgroundEntries: BackgroundEntry<any>[] = [];
47
+ cfg.processMappings.forEach((map) => {
48
+ if (!map.disabled && interApiEntry.source.match(map.sourceRegex) && interApiEntry.type.match(map.typeRegex)) {
49
+ map.backgroundProcessTypes.forEach((taskName) => {
50
+ const entry: BackgroundEntry<any> = mgr.createEntry(taskName, interApiEntry.data);
51
+ backgroundEntries.push(entry);
52
+ });
53
+ }
54
+ });
55
+ if (backgroundEntries.length > 0) {
56
+ Logger.info('Adding %d entries to queue', backgroundEntries.length);
57
+ rval = await mgr.addEntriesToQueue(backgroundEntries, true);
58
+ } else {
59
+ Logger.info('No entries mapped for this event');
60
+ }
61
+ return rval;
62
+ }
63
+
64
+ public static addTraceToInterApiEntry(ent: InterApiEntry<any>): InterApiEntry<any> {
65
+ if (ent) {
66
+ ent.traceId = ent.traceId || ContextUtil.currentTraceId();
67
+ ent.traceDepth = ent.traceDepth || ContextUtil.currentTraceDepth() + 1;
68
+ }
69
+ return ent;
70
+ }
71
+ }
@@ -0,0 +1,75 @@
1
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
2
+ import { EpsilonConstants } from './epsilon-constants.js';
3
+ import { InterApiEntry } from './inter-api/inter-api-entry.js';
4
+ import { InterApiAwsConfig } from './config/inter-api/inter-api-aws-config.js';
5
+ import { InterApiUtil } from './inter-api/inter-api-util.js';
6
+ import { PublishCommand, PublishCommandOutput, SNSClient } from '@aws-sdk/client-sns';
7
+
8
+ /**
9
+ * Handles all submission of events to the inter-api SNS topic (if any)
10
+ */
11
+ export class InterApiManager {
12
+ constructor(
13
+ private _aws: InterApiAwsConfig,
14
+ private _sns: SNSClient,
15
+ ) {}
16
+
17
+ public get config(): InterApiAwsConfig {
18
+ return this._aws;
19
+ }
20
+
21
+ public get sns(): SNSClient {
22
+ return this._sns;
23
+ }
24
+
25
+ public createEntry<T>(type: string, data?: T): InterApiEntry<T> {
26
+ const rval: InterApiEntry<T> = {
27
+ source: this._aws.source,
28
+ type: type,
29
+ data: data,
30
+ };
31
+ return rval;
32
+ }
33
+
34
+ public async fireInterApiEventByParts<T>(type: string, data?: T): Promise<string> {
35
+ const entry: InterApiEntry<T> = this.createEntry(type, data);
36
+ const rval: string = await this.fireInterApiEvent(entry);
37
+ return rval;
38
+ }
39
+
40
+ public async fireInterApiEvent<T>(entry: InterApiEntry<T>): Promise<string> {
41
+ let rval: string = null;
42
+ if (this.config.localMode) {
43
+ Logger.info('Fire inter-api event ignored because running locally (was %j)', entry);
44
+ rval = 'INTER-API-IGNORED';
45
+ } else {
46
+ try {
47
+ // Guard against bad entries up front
48
+ Logger.info('Firing inter-api event (remote) : %j ', entry);
49
+ const toWrite: any = {
50
+ type: EpsilonConstants.INTER_API_SNS_EVENT,
51
+ interApiEvent: InterApiUtil.addTraceToInterApiEntry(entry),
52
+ };
53
+ const msg: string = JSON.stringify(toWrite);
54
+ const snsId: string = await this.writeMessageToSnsTopic(msg);
55
+ Logger.debug('Inter-api Wrote message : %s to SNS : %s', rval, msg, snsId);
56
+ } catch (err) {
57
+ Logger.error('Failed to fireImmediateProcessRequest : %s', err, err);
58
+ }
59
+ }
60
+ return rval;
61
+ }
62
+
63
+ public async writeMessageToSnsTopic(message: string): Promise<string> {
64
+ let rval: string = null;
65
+ const params = {
66
+ Message: message,
67
+ TopicArn: this._aws.snsArn,
68
+ };
69
+
70
+ Logger.debug('Writing message to SNS topic : j', params);
71
+ const result: PublishCommandOutput = await this.sns.send(new PublishCommand(params));
72
+ rval = result.MessageId;
73
+ return rval;
74
+ }
75
+ }
@@ -0,0 +1,130 @@
1
+ import { ScheduledEvent } from 'aws-lambda';
2
+ import { BackgroundHandler } from '../background/background-handler.js';
3
+ import { CronConfig } from '../config/cron/cron-config.js';
4
+ import { BackgroundConfig } from '../config/background/background-config.js';
5
+
6
+ import { CronEpsilonLambdaEventHandler } from './cron-epsilon-lambda-event-handler.js';
7
+ import { BackgroundManagerLike } from '../background/manager/background-manager-like.js';
8
+ import { SingleThreadLocalBackgroundManager } from '../background/manager/single-thread-local-background-manager.js';
9
+ import { SQSClient } from '@aws-sdk/client-sqs';
10
+ import { SNSClient } from '@aws-sdk/client-sns';
11
+ import { beforeEach, describe, expect, test, vi } from 'vitest';
12
+ import { mock } from 'vitest-mock-extended';
13
+ import { DateTime } from 'luxon';
14
+
15
+ // jest.mock('@bitblit/background');
16
+
17
+ describe('#cronEpsilonLambdaEventHandler', function () {
18
+ let _mockSqs;
19
+ let _mockSns;
20
+
21
+ beforeEach(() => {
22
+ _mockSqs = mock<SQSClient>();
23
+ _mockSns = mock<SNSClient>();
24
+ });
25
+
26
+ // CAW 2021-03-10 : Disabling for now since jest mock not working when run in batch from command line...unclear why
27
+ test.skip('should verify that cron data functions get executed', async () => {
28
+ // Logger.setLevel(LoggerLevelName.silly);
29
+ const evt: ScheduledEvent = {
30
+ id: '1',
31
+ version: '1',
32
+ account: 'test',
33
+ time: 'test',
34
+ region: '',
35
+ resources: ['test'],
36
+ source: null,
37
+ detail: {},
38
+ 'detail-type': null,
39
+ };
40
+ const cronConfig: CronConfig = {
41
+ timezone: 'America/Los_Angeles',
42
+ context: 'Test',
43
+ entries: [
44
+ {
45
+ backgroundTaskType: 'test',
46
+ fireImmediate: true,
47
+ data: () => {
48
+ return { curDate: new Date().toISOString(), fixed: 'abc' };
49
+ },
50
+ },
51
+ ],
52
+ };
53
+ const smConfig: BackgroundConfig = {
54
+ processors: [],
55
+ httpSubmissionPath: '/background/',
56
+ implyTypeFromPathSuffix: true,
57
+ httpMetaEndpoint: '/background-meta',
58
+ };
59
+ const background = new BackgroundHandler(null, null);
60
+ background.getConfig = vi.fn(() => smConfig);
61
+
62
+ const backgroundManager: BackgroundManagerLike = new SingleThreadLocalBackgroundManager();
63
+
64
+ const res: boolean = await CronEpsilonLambdaEventHandler.processCronEvent(evt, cronConfig, backgroundManager, background);
65
+ expect(res).toBeTruthy();
66
+ }, 500);
67
+ });
68
+
69
+ describe('cronEpsilonLambdaEventHandler.getCronTimeToUse', () => {
70
+ const currentTimestampEpochMS = new Date().getTime();
71
+
72
+ const sampleEvent: ScheduledEvent = {
73
+ version: '0',
74
+ id: '403a3159-cf4d-4cbe-315d-ed688c429988',
75
+ 'detail-type': 'Scheduled Event',
76
+ source: 'aws.events',
77
+ account: '000011112222',
78
+ time: '2024-08-30T13:16:24Z',
79
+ region: 'us-east-1',
80
+ resources: ['arn:aws:events:us-east-1:000011112222:rule/MyRule1'],
81
+ detail: {},
82
+ };
83
+
84
+ // @ts-expect-error private method
85
+ const getCronTimeToUse = CronEpsilonLambdaEventHandler.getCronTimeToUse;
86
+
87
+ test('should return current timestamp when evt is undefined', () => {
88
+ const result = getCronTimeToUse(undefined, currentTimestampEpochMS);
89
+ expect(result).toBe(currentTimestampEpochMS);
90
+ });
91
+
92
+ test('should return current timestamp when evt.time is undefined', () => {
93
+ const result = getCronTimeToUse({} as ScheduledEvent, currentTimestampEpochMS);
94
+ expect(result).toBe(currentTimestampEpochMS);
95
+ });
96
+
97
+ test('should return event time in milliseconds when evt.time is a valid ISO string', () => {
98
+ const recentISOTime = DateTime.fromMillis(currentTimestampEpochMS).toUTC().plus({ seconds: 30 }).toISO();
99
+ const eventTimeInMillis = DateTime.fromISO(recentISOTime).toMillis();
100
+ const result = getCronTimeToUse(
101
+ {
102
+ ...sampleEvent,
103
+ time: recentISOTime,
104
+ },
105
+ currentTimestampEpochMS,
106
+ );
107
+ expect(result).toBe(eventTimeInMillis);
108
+ });
109
+
110
+ test('should return current timestamp when evt.time is an invalid ISO string', () => {
111
+ const invalidISOTime = 'invalid-time';
112
+ const result = getCronTimeToUse(
113
+ {
114
+ ...sampleEvent,
115
+ time: invalidISOTime,
116
+ },
117
+ currentTimestampEpochMS,
118
+ );
119
+ expect(result).toBe(currentTimestampEpochMS);
120
+ });
121
+
122
+ test('should return current timestamp when time difference exceeds threshold', () => {
123
+ const oldISOTime = '2023-10-10T10:00:00.000Z';
124
+ const largeTimeDifference =
125
+ currentTimestampEpochMS + (CronEpsilonLambdaEventHandler.CRON_EVENT_TIMESTAMP_MISMATCH_MAX_THRESHOLD_MINUTES + 1) * 60 * 1000;
126
+
127
+ const result = getCronTimeToUse({ ...sampleEvent, time: oldISOTime }, largeTimeDifference);
128
+ expect(result).toBe(largeTimeDifference);
129
+ });
130
+ });
@@ -0,0 +1,132 @@
1
+ import { EpsilonLambdaEventHandler } from '../config/epsilon-lambda-event-handler.js';
2
+ import { Context, ProxyResult, ScheduledEvent } from 'aws-lambda';
3
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
4
+ import { AwsUtil } from '../util/aws-util.js';
5
+ import { EpsilonInstance } from '../epsilon-instance.js';
6
+ import { CronConfig } from '../config/cron/cron-config.js';
7
+ import { BackgroundHandler } from '../background/background-handler.js';
8
+ import { BackgroundEntry } from '../background/background-entry.js';
9
+ import { CronUtil } from '../util/cron-util.js';
10
+ import { BackgroundManagerLike } from '../background/manager/background-manager-like.js';
11
+ import { LambdaEventDetector } from '@bitblit/ratchet-aws/lambda/lambda-event-detector';
12
+ import { DateTime } from 'luxon';
13
+
14
+ export class CronEpsilonLambdaEventHandler implements EpsilonLambdaEventHandler<ScheduledEvent> {
15
+ public static readonly CRON_EVENT_TIMESTAMP_MISMATCH_MAX_THRESHOLD_MINUTES = 5;
16
+
17
+ constructor(private _epsilon: EpsilonInstance) {}
18
+
19
+ public extractLabel(evt: ScheduledEvent, _context: Context): string {
20
+ return 'CronEvt:' + evt.source;
21
+ }
22
+
23
+ public handlesEvent(evt: any): boolean {
24
+ return LambdaEventDetector.isValidCronEvent(evt);
25
+ }
26
+
27
+ public async processEvent(evt: ScheduledEvent, _context: Context): Promise<ProxyResult> {
28
+ let rval: ProxyResult = null;
29
+ Logger.debug('Epsilon: CRON: %j', evt);
30
+ if (!this._epsilon.config.cron) {
31
+ Logger.debug('Skipping - CRON disabled');
32
+ rval = {
33
+ statusCode: 200,
34
+ body: JSON.stringify({ message: 'CRON skipped - disabled' }),
35
+ isBase64Encoded: false,
36
+ };
37
+ } else {
38
+ const _output: boolean = await CronEpsilonLambdaEventHandler.processCronEvent(
39
+ evt,
40
+ this._epsilon.config.cron,
41
+ this._epsilon.backgroundManager,
42
+ this._epsilon.backgroundHandler,
43
+ );
44
+ rval = {
45
+ statusCode: 200,
46
+ body: JSON.stringify({ message: 'CRON complete' }),
47
+ isBase64Encoded: false,
48
+ };
49
+ }
50
+ return rval;
51
+ }
52
+
53
+ public static async processCronEvent(
54
+ evt: ScheduledEvent,
55
+ cronConfig: CronConfig,
56
+ backgroundManager: BackgroundManagerLike,
57
+ background: BackgroundHandler,
58
+ ): Promise<boolean> {
59
+ let rval: boolean = false;
60
+ if (cronConfig && evt && evt.resources[0]) {
61
+ // Run all the background ones
62
+ if (cronConfig.entries) {
63
+ if (background) {
64
+ const cronTimestampEpochMS = CronEpsilonLambdaEventHandler.getCronTimeToUse(evt);
65
+
66
+ const toEnqueue: BackgroundEntry<any>[] = [];
67
+ for (const smCronEntry of cronConfig.entries) {
68
+ //for (let i = 0; i < cronConfig.entries.length; i++) {
69
+ //const smCronEntry: CronBackgroundEntry = cronConfig.entries[i];
70
+ if (CronUtil.eventMatchesEntry(evt, smCronEntry, cronConfig, cronTimestampEpochMS)) {
71
+ Logger.info('CRON Firing : %s', CronUtil.cronEntryName(smCronEntry));
72
+
73
+ const backgroundEntry: BackgroundEntry<any> = {
74
+ type: smCronEntry.backgroundTaskType,
75
+ data: AwsUtil.resolvePotentialFunctionToResult<any>(smCronEntry.data, {}),
76
+ };
77
+ Logger.silly('Resolved entry : %j', backgroundEntry);
78
+ if (smCronEntry.fireImmediate) {
79
+ await backgroundManager.fireImmediateProcessRequest(backgroundEntry);
80
+ rval = true;
81
+ } else {
82
+ toEnqueue.push(backgroundEntry);
83
+ }
84
+ }
85
+ }
86
+ if (toEnqueue.length > 0) {
87
+ await backgroundManager.addEntriesToQueue(toEnqueue, true);
88
+ rval = true;
89
+ }
90
+ } else {
91
+ Logger.warn('Cron defines background tasks, but no background manager provided');
92
+ }
93
+ }
94
+ }
95
+ return rval;
96
+ }
97
+
98
+ private static getCronTimeToUse(evt?: ScheduledEvent, currentTimestampEpochMS: number = new Date().getTime()): number {
99
+ let rval = currentTimestampEpochMS;
100
+
101
+ if (!evt?.time) {
102
+ return rval;
103
+ }
104
+
105
+ try {
106
+ const dateTimeOfEvent = DateTime.fromISO(evt.time);
107
+ if (!dateTimeOfEvent.isValid) {
108
+ throw new Error('Invalid date');
109
+ }
110
+ rval = dateTimeOfEvent.toMillis();
111
+ if (isNaN(rval)) {
112
+ throw new Error('Invalid date');
113
+ }
114
+ } catch (err) {
115
+ Logger.warn('Could not parse event time : %s, using system time instead', evt.time, err);
116
+ return rval;
117
+ }
118
+
119
+ if (
120
+ Math.abs(rval - currentTimestampEpochMS) >
121
+ CronEpsilonLambdaEventHandler.CRON_EVENT_TIMESTAMP_MISMATCH_MAX_THRESHOLD_MINUTES * 60 * 1000
122
+ ) {
123
+ Logger.warn(
124
+ 'Event time and current time mismatch by more than %d minutes, using current time instead',
125
+ CronEpsilonLambdaEventHandler.CRON_EVENT_TIMESTAMP_MISMATCH_MAX_THRESHOLD_MINUTES,
126
+ );
127
+ rval = currentTimestampEpochMS;
128
+ }
129
+
130
+ return rval;
131
+ }
132
+ }
@@ -0,0 +1,42 @@
1
+ import { EpsilonLambdaEventHandler } from '../config/epsilon-lambda-event-handler.js';
2
+ import { Context, DynamoDBStreamEvent, ProxyResult } from 'aws-lambda';
3
+ import { GenericAwsEventHandlerFunction } from '../config/generic-aws-event-handler-function.js';
4
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
5
+ import { AwsUtil } from '../util/aws-util.js';
6
+ import { EpsilonInstance } from '../epsilon-instance.js';
7
+ import { LambdaEventDetector } from '@bitblit/ratchet-aws/lambda/lambda-event-detector';
8
+ import { NoHandlersFoundError } from '../config/no-handlers-found-error.js';
9
+
10
+ export class DynamoEpsilonLambdaEventHandler implements EpsilonLambdaEventHandler<DynamoDBStreamEvent> {
11
+ constructor(private _epsilon: EpsilonInstance) {}
12
+
13
+ public extractLabel(evt: DynamoDBStreamEvent, _context: Context): string {
14
+ return 'DDBEvt:' + evt.Records[0].eventName + ':' + evt.Records[0].eventSource;
15
+ }
16
+
17
+ public handlesEvent(evt: any): boolean {
18
+ return LambdaEventDetector.isValidDynamoDBEvent(evt);
19
+ }
20
+
21
+ public async processEvent(evt: DynamoDBStreamEvent, _context: Context): Promise<ProxyResult> {
22
+ let rval: any = null;
23
+ if (this._epsilon.config && this._epsilon.config.dynamoDb && evt && evt.Records && evt.Records.length > 0) {
24
+ const finder: string = evt.Records[0].eventSourceARN;
25
+ const handler: GenericAwsEventHandlerFunction<DynamoDBStreamEvent> = AwsUtil.findInMap<
26
+ GenericAwsEventHandlerFunction<DynamoDBStreamEvent>
27
+ >(finder, this._epsilon.config.dynamoDb.handlers);
28
+ if (handler) {
29
+ rval = await handler(evt);
30
+ } else {
31
+ Logger.info('Found no Dynamo handler for : %s', finder);
32
+ throw new NoHandlersFoundError();
33
+ }
34
+ }
35
+ return rval;
36
+ }
37
+
38
+ public async processUncaughtError(event: DynamoDBStreamEvent, context: Context, err: any): Promise<ProxyResult> {
39
+ Logger.error('Error slipped out to outer edge (Dynamo). Logging and rethrowing : %s', err, err);
40
+ throw err;
41
+ }
42
+ }
@@ -0,0 +1,38 @@
1
+ import { EpsilonLambdaEventHandler } from '../config/epsilon-lambda-event-handler.js';
2
+ import { Context, ProxyResult, SNSEvent } from 'aws-lambda';
3
+ import { GenericAwsEventHandlerFunction } from '../config/generic-aws-event-handler-function.js';
4
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
5
+ import { AwsUtil } from '../util/aws-util.js';
6
+ import { EpsilonInstance } from '../epsilon-instance.js';
7
+ import { LambdaEventDetector } from '@bitblit/ratchet-aws/lambda/lambda-event-detector';
8
+ import { NoHandlersFoundError } from '../config/no-handlers-found-error.js';
9
+
10
+ export class GenericSnsEpsilonLambdaEventHandler implements EpsilonLambdaEventHandler<SNSEvent> {
11
+ constructor(private _epsilon: EpsilonInstance) {}
12
+
13
+ public extractLabel(evt: SNSEvent, _context: Context): string {
14
+ return 'SNSEvt:' + evt.Records[0].EventSource;
15
+ }
16
+
17
+ public handlesEvent(evt: any): boolean {
18
+ return LambdaEventDetector.isValidSnsEvent(evt);
19
+ }
20
+
21
+ public async processEvent(evt: SNSEvent, _context: Context): Promise<ProxyResult> {
22
+ let rval: any = null;
23
+ if (this._epsilon.config && this._epsilon.config.sns && evt && evt.Records.length > 0) {
24
+ const finder: string = evt.Records[0].Sns.TopicArn;
25
+ const handler: GenericAwsEventHandlerFunction<SNSEvent> = AwsUtil.findInMap<GenericAwsEventHandlerFunction<SNSEvent>>(
26
+ finder,
27
+ this._epsilon.config.sns.handlers,
28
+ );
29
+ if (handler) {
30
+ rval = await handler(evt);
31
+ } else {
32
+ Logger.info('Found no SNS handler for : %s', finder);
33
+ throw new NoHandlersFoundError();
34
+ }
35
+ }
36
+ return rval;
37
+ }
38
+ }
@@ -0,0 +1,43 @@
1
+ import { EpsilonLambdaEventHandler } from '../config/epsilon-lambda-event-handler.js';
2
+ import { Context, ProxyResult, SQSEvent } from 'aws-lambda';
3
+ import { GenericAwsEventHandlerFunction } from '../config/generic-aws-event-handler-function.js';
4
+ import { AwsUtil } from '../util/aws-util.js';
5
+ import { EpsilonInstance } from '../epsilon-instance.js';
6
+ import { NoHandlersFoundError } from '../config/no-handlers-found-error.js';
7
+ import { LambdaEventDetector } from '@bitblit/ratchet-aws/lambda/lambda-event-detector';
8
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
9
+
10
+ export class GenericSqsEpsilonLambdaEventHandler implements EpsilonLambdaEventHandler<SQSEvent> {
11
+ constructor(private _epsilon: EpsilonInstance) {}
12
+
13
+ public extractLabel(evt: SQSEvent, _context: Context): string {
14
+ return 'SQSEvt:' + evt.Records[0].eventSourceARN;
15
+ }
16
+
17
+ public handlesEvent(evt: any): boolean {
18
+ return LambdaEventDetector.isValidSqsEvent(evt);
19
+ }
20
+
21
+ public async processEvent(evt: SQSEvent, _context: Context): Promise<ProxyResult> {
22
+ let rval: any = null;
23
+ if (this._epsilon.config && this._epsilon.config.sqs && evt && evt.Records.length > 0) {
24
+ const finder: string = evt.Records[0].eventSourceARN;
25
+ const handler: GenericAwsEventHandlerFunction<SQSEvent> = AwsUtil.findInMap<GenericAwsEventHandlerFunction<SQSEvent>>(
26
+ finder,
27
+ this._epsilon.config.sqs.handlers,
28
+ );
29
+ if (handler) {
30
+ rval = await handler(evt);
31
+ } else {
32
+ Logger.info('Found no SQS handler for : %s', finder);
33
+ throw new NoHandlersFoundError();
34
+ }
35
+ }
36
+ return rval;
37
+ }
38
+
39
+ public async processUncaughtError(event: SQSEvent, context: Context, err: any): Promise<ProxyResult> {
40
+ Logger.error('Error slipped out to outer edge (SQS). Logging and rethrowing : %s', err, err);
41
+ throw err;
42
+ }
43
+ }
@@ -0,0 +1,33 @@
1
+ import { EpsilonLambdaEventHandler } from '../config/epsilon-lambda-event-handler.js';
2
+ import { Context, ProxyResult, SNSEvent } from 'aws-lambda';
3
+ import { EpsilonInstance } from '../epsilon-instance.js';
4
+ import { InterApiUtil } from '../inter-api/inter-api-util.js';
5
+ import { InterApiEntry } from '../inter-api/inter-api-entry.js';
6
+
7
+ export class InterApiEpsilonLambdaEventHandler implements EpsilonLambdaEventHandler<SNSEvent> {
8
+ constructor(private _epsilon: EpsilonInstance) {}
9
+
10
+ public extractLabel(evt: SNSEvent, _context: Context): string {
11
+ const ent: InterApiEntry<any> = InterApiUtil.extractEntryFromEvent(evt);
12
+ return 'InterApi:' + ent.source + ':' + ent.type;
13
+ }
14
+
15
+ public handlesEvent(evt: any): boolean {
16
+ return this._epsilon.config.interApiConfig && InterApiUtil.isInterApiSnsEvent(evt);
17
+ }
18
+
19
+ public async processEvent(evt: SNSEvent, _context: Context): Promise<ProxyResult> {
20
+ const tmp: string[] = await InterApiUtil.processInterApiEvent(
21
+ evt,
22
+ this._epsilon.config.interApiConfig,
23
+ this._epsilon.backgroundManager,
24
+ );
25
+ const rval: ProxyResult = {
26
+ statusCode: 200,
27
+ body: JSON.stringify(tmp),
28
+ isBase64Encoded: false,
29
+ };
30
+
31
+ return rval;
32
+ }
33
+ }