@bitblit/ratchet-epsilon-common 6.0.146-alpha → 6.0.147-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 (164) hide show
  1. package/package.json +10 -9
  2. package/src/background/background-dynamo-log-table-handler.ts +44 -0
  3. package/src/background/background-entry.ts +4 -0
  4. package/src/background/background-execution-event-type.ts +9 -0
  5. package/src/background/background-execution-event.ts +9 -0
  6. package/src/background/background-execution-listener.ts +6 -0
  7. package/src/background/background-handler.ts +352 -0
  8. package/src/background/background-http-adapter-handler.ts +166 -0
  9. package/src/background/background-meta-response-internal.ts +5 -0
  10. package/src/background/background-process-handling.ts +6 -0
  11. package/src/background/background-process-log-table-entry.ts +11 -0
  12. package/src/background/background-queue-response-internal.ts +9 -0
  13. package/src/background/background-validator.ts +105 -0
  14. package/src/background/epsilon-background-process-error.ts +110 -0
  15. package/src/background/internal-background-entry.ts +10 -0
  16. package/src/background/manager/abstract-background-manager.ts +120 -0
  17. package/src/background/manager/aws-large-payload-s3-sqs-sns-background-manager.ts +87 -0
  18. package/src/background/manager/aws-sqs-sns-background-manager.ts +201 -0
  19. package/src/background/manager/background-manager-like.ts +44 -0
  20. package/src/background/manager/background-manager.spec.ts +89 -0
  21. package/src/background/manager/single-thread-local-background-manager.ts +58 -0
  22. package/src/background/s3-background-transaction-logger.ts +65 -0
  23. package/src/build/ratchet-epsilon-common-info.ts +19 -0
  24. package/src/built-in/background/echo-processor.ts +17 -0
  25. package/src/built-in/background/log-and-enqueue-echo-processor.ts +14 -0
  26. package/src/built-in/background/log-message-background-error-processor.ts +10 -0
  27. package/src/built-in/background/no-op-processor.ts +12 -0
  28. package/src/built-in/background/retry-processor.ts +51 -0
  29. package/src/built-in/background/sample-delay-processor.ts +15 -0
  30. package/src/built-in/background/sample-input-validated-processor-data.ts +4 -0
  31. package/src/built-in/background/sample-input-validated-processor.ts +14 -0
  32. package/src/built-in/built-in-trace-id-generators.ts +22 -0
  33. package/src/built-in/daemon/daemon-authorizer-function.ts +4 -0
  34. package/src/built-in/daemon/daemon-config.ts +9 -0
  35. package/src/built-in/daemon/daemon-group-selection-function.ts +3 -0
  36. package/src/built-in/daemon/daemon-handler.ts +87 -0
  37. package/src/built-in/daemon/daemon-process-state-list.ts +9 -0
  38. package/src/built-in/http/apollo/apollo-util.ts +43 -0
  39. package/src/built-in/http/apollo/default-epsilon-apollo-context.ts +11 -0
  40. package/src/built-in/http/apollo/epsilon-apollo-context-builder-options.ts +5 -0
  41. package/src/built-in/http/apollo/epsilon-lambda-apollo-context-function-argument.ts +6 -0
  42. package/src/built-in/http/apollo/epsilon-lambda-apollo-options.ts +11 -0
  43. package/src/built-in/http/apollo-filter.ts +151 -0
  44. package/src/built-in/http/built-in-auth-filters.ts +73 -0
  45. package/src/built-in/http/built-in-authorizers.ts +22 -0
  46. package/src/built-in/http/built-in-filters.spec.ts +26 -0
  47. package/src/built-in/http/built-in-filters.ts +300 -0
  48. package/src/built-in/http/built-in-handlers.ts +85 -0
  49. package/src/built-in/http/log-level-manipulation-filter.ts +26 -0
  50. package/src/built-in/http/run-handler-as-filter.spec.ts +67 -0
  51. package/src/built-in/http/run-handler-as-filter.ts +102 -0
  52. package/src/cli/ratchet-cli-handler.ts +23 -0
  53. package/src/cli/run-background-process-from-command-line.ts +32 -0
  54. package/src/config/background/background-aws-config.ts +8 -0
  55. package/src/config/background/background-config.ts +15 -0
  56. package/src/config/background/background-error-processor.ts +5 -0
  57. package/src/config/background/background-processor.ts +14 -0
  58. package/src/config/background/background-transaction-log.ts +9 -0
  59. package/src/config/background/background-transaction-logger.ts +6 -0
  60. package/src/config/cron/abstract-cron-entry.ts +17 -0
  61. package/src/config/cron/cron-background-entry.ts +17 -0
  62. package/src/config/cron/cron-config.ts +10 -0
  63. package/src/config/dynamo-db-config.ts +6 -0
  64. package/src/config/epsilon-config.ts +30 -0
  65. package/src/config/epsilon-lambda-event-handler.ts +12 -0
  66. package/src/config/epsilon-logger-config.ts +23 -0
  67. package/src/config/espilon-server-mode.ts +10 -0
  68. package/src/config/generic-aws-event-handler-function.ts +1 -0
  69. package/src/config/http/authorizer-function.ts +9 -0
  70. package/src/config/http/epsilon-authorization-context.ts +5 -0
  71. package/src/config/http/epsilon-cors-approach.ts +7 -0
  72. package/src/config/http/extended-api-gateway-event.ts +8 -0
  73. package/src/config/http/filter-chain-context.ts +15 -0
  74. package/src/config/http/filter-function.ts +3 -0
  75. package/src/config/http/handler-function.ts +4 -0
  76. package/src/config/http/http-config.ts +27 -0
  77. package/src/config/http/http-processing-config.ts +23 -0
  78. package/src/config/http/mapped-http-processing-config.ts +12 -0
  79. package/src/config/http/null-returned-object-handling.ts +7 -0
  80. package/src/config/inter-api/inter-api-aws-config.ts +5 -0
  81. package/src/config/inter-api/inter-api-config.ts +7 -0
  82. package/src/config/inter-api/inter-api-process-mapping.ts +11 -0
  83. package/src/config/local-server/local-server-event-logging-style.ts +8 -0
  84. package/src/config/local-server/local-server-http-method-handling.ts +7 -0
  85. package/src/config/local-server/local-server-options.ts +12 -0
  86. package/src/config/logging-trace-id-generator.ts +3 -0
  87. package/src/config/no-handlers-found-error.ts +6 -0
  88. package/src/config/open-api/open-api-document-components.ts +4 -0
  89. package/src/config/open-api/open-api-document.ts +7 -0
  90. package/src/config/s3-config.ts +8 -0
  91. package/src/config/sns-config.ts +7 -0
  92. package/src/config/sqs-config.ts +7 -0
  93. package/src/epsilon-build-properties.ts +21 -0
  94. package/src/epsilon-constants.ts +62 -0
  95. package/src/epsilon-global-handler.ts +238 -0
  96. package/src/epsilon-instance.ts +20 -0
  97. package/src/epsilon-logging-extension-processor.ts +19 -0
  98. package/src/http/auth/api-gateway-adapter-authentication-handler.ts +95 -0
  99. package/src/http/auth/auth0-web-token-manipulator.ts +69 -0
  100. package/src/http/auth/basic-auth-token.ts +7 -0
  101. package/src/http/auth/google-web-token-manipulator.spec.ts +15 -0
  102. package/src/http/auth/google-web-token-manipulator.ts +80 -0
  103. package/src/http/auth/jwt-ratchet-local-web-token-manipulator.ts +37 -0
  104. package/src/http/auth/local-web-token-manipulator.spec.ts +34 -0
  105. package/src/http/auth/local-web-token-manipulator.ts +114 -0
  106. package/src/http/auth/web-token-manipulator.ts +9 -0
  107. package/src/http/error/bad-gateway.ts +11 -0
  108. package/src/http/error/bad-request-error.ts +11 -0
  109. package/src/http/error/conflict-error.ts +12 -0
  110. package/src/http/error/forbidden-error.ts +12 -0
  111. package/src/http/error/gateway-timeout.ts +12 -0
  112. package/src/http/error/method-not-allowed-error.ts +12 -0
  113. package/src/http/error/misconfigured-error.ts +12 -0
  114. package/src/http/error/not-found-error.ts +12 -0
  115. package/src/http/error/not-implemented.ts +12 -0
  116. package/src/http/error/request-timeout-error.ts +12 -0
  117. package/src/http/error/service-unavailable.ts +12 -0
  118. package/src/http/error/too-many-requests-error.ts +12 -0
  119. package/src/http/error/unauthorized-error.ts +12 -0
  120. package/src/http/event-util.spec.ts +190 -0
  121. package/src/http/event-util.ts +272 -0
  122. package/src/http/response-util.spec.ts +117 -0
  123. package/src/http/response-util.ts +164 -0
  124. package/src/http/route/epsilon-router.ts +9 -0
  125. package/src/http/route/extended-auth-response-context.ts +7 -0
  126. package/src/http/route/route-and-parse.ts +8 -0
  127. package/src/http/route/route-mapping.ts +21 -0
  128. package/src/http/route/route-validator-config.ts +5 -0
  129. package/src/http/route/router-util.spec.ts +33 -0
  130. package/src/http/route/router-util.ts +314 -0
  131. package/src/http/web-handler.spec.ts +99 -0
  132. package/src/http/web-handler.ts +157 -0
  133. package/src/http/web-v2-handler.ts +34 -0
  134. package/src/inter-api/inter-api-entry.ts +8 -0
  135. package/src/inter-api/inter-api-util.spec.ts +77 -0
  136. package/src/inter-api/inter-api-util.ts +71 -0
  137. package/src/inter-api-manager.ts +75 -0
  138. package/src/lambda-event-handler/cron-epsilon-lambda-event-handler.spec.ts +130 -0
  139. package/src/lambda-event-handler/cron-epsilon-lambda-event-handler.ts +132 -0
  140. package/src/lambda-event-handler/dynamo-epsilon-lambda-event-handler.ts +42 -0
  141. package/src/lambda-event-handler/generic-sns-epsilon-lambda-event-handler.ts +38 -0
  142. package/src/lambda-event-handler/generic-sqs-epsilon-lambda-event-handler.ts +43 -0
  143. package/src/lambda-event-handler/inter-api-epsilon-lambda-event-handler.ts +33 -0
  144. package/src/lambda-event-handler/s3-epsilon-lambda-event-handler.ts +50 -0
  145. package/src/local-container-server.ts +128 -0
  146. package/src/local-server.spec.ts +16 -0
  147. package/src/local-server.ts +426 -0
  148. package/src/open-api-util/open-api-doc-modifications.ts +9 -0
  149. package/src/open-api-util/open-api-doc-modifier.spec.ts +22 -0
  150. package/src/open-api-util/open-api-doc-modifier.ts +90 -0
  151. package/src/open-api-util/yaml-combiner.spec.ts +26 -0
  152. package/src/open-api-util/yaml-combiner.ts +35 -0
  153. package/src/sample/sample-server-components-with-apollo.ts +87 -0
  154. package/src/sample/sample-server-components.ts +183 -0
  155. package/src/sample/sample-server-static-files.ts +614 -0
  156. package/src/sample/test-error-server.ts +140 -0
  157. package/src/util/aws-util.ts +89 -0
  158. package/src/util/context-global-data.ts +13 -0
  159. package/src/util/context-util.ts +156 -0
  160. package/src/util/cron-util.spec.ts +190 -0
  161. package/src/util/cron-util.ts +86 -0
  162. package/src/util/epsilon-config-parser.ts +90 -0
  163. package/src/util/epsilon-server-util.spec.ts +18 -0
  164. package/src/util/epsilon-server-util.ts +16 -0
@@ -0,0 +1,140 @@
1
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
2
+ import { IncomingMessage, Server, ServerResponse } from 'http';
3
+ import net from 'net';
4
+
5
+ /**
6
+ * A simplistic server for testing your lambdas locally
7
+ */
8
+ export class TestErrorServer {
9
+ private server: Server;
10
+ private aborted: boolean = false;
11
+
12
+ constructor(private port: number = 9999) {}
13
+
14
+ async runServer(): Promise<boolean> {
15
+ Logger.info('Starting Test Error net server on port %d', this.port);
16
+
17
+ return new Promise<boolean>((_res, _rej) => {
18
+ const server = new net.Server({});
19
+ // The server listens to a socket for a client to make a connection request.
20
+ // Think of a socket as an end point.
21
+ server.listen(this.port, () => {
22
+ Logger.info('Server listening for connection requests on socket localhost: %s', this.port);
23
+ });
24
+
25
+ // When a client requests a connection with the server, the server creates a new
26
+ // socket dedicated to that client.
27
+ server.on('connection', async (socket) => {
28
+ Logger.info('X: A new connection has been established.');
29
+
30
+ //await PromiseRatchet.wait(30000);
31
+ // Now that a TCP connection has been established, the server can send data to
32
+ // the client by writing to its socket.
33
+ socket.write('Hello, client.');
34
+
35
+ // The server can also receive data from the client by reading from its socket.
36
+ socket.on('data', (chunk) => {
37
+ Logger.info('Data received from client: %s', chunk);
38
+ });
39
+
40
+ // When the client requests to end the TCP connection with the server, the server
41
+ // ends the connection.
42
+ socket.on('end', () => {
43
+ Logger.info('Closing connection with the client');
44
+ });
45
+
46
+ // Don't forget to catch error, for your own sake.
47
+ socket.on('error', (err) => {
48
+ Logger.info('Error: %s', err);
49
+ });
50
+ });
51
+
52
+ /*
53
+ this.server = http.createServer(this.requestHandler.bind(this)).listen(this.port);
54
+ Logger.info('Test Error server is listening');
55
+
56
+ // Also listen for SIGINT
57
+ process.on('SIGINT', () => {
58
+ Logger.info('Caught SIGINT - shutting down test server...');
59
+ this.aborted = true;
60
+ });
61
+
62
+ return this.checkFinished();
63
+
64
+ */
65
+ });
66
+ }
67
+
68
+ async requestHandler(request: IncomingMessage, response: ServerResponse): Promise<any> {
69
+ Logger.info('Got request %d - closing socket', request);
70
+ request.setTimeout(100);
71
+ //await PromiseRatchet.wait(3000);
72
+ response.end('x');
73
+ /*response.socket.end(() => {
74
+ //return null;
75
+ });
76
+
77
+ */
78
+ /*(e) => {
79
+ Logger.info('Out: %s', e);
80
+ });
81
+
82
+ */
83
+ /*const context: Context = {
84
+ awsRequestId: 'LOCAL-' + StringRatchet.createType4Guid(),
85
+ getRemainingTimeInMillis(): number {
86
+ return 300000;
87
+ },
88
+ } as Context; //TBD
89
+ const evt: APIGatewayEvent = await this.messageToApiGatewayEvent(request, context);
90
+ const logEventLevel: LoggerLevelName = EventUtil.eventIsAGraphQLIntrospection(evt) ? LoggerLevelName.silly : LoggerLevelName.info;
91
+
92
+ Logger.logByLevel(logEventLevel, 'Processing event: %j', evt);
93
+
94
+ if (evt.path == '/epsilon-poison-pill') {
95
+ this.aborted = true;
96
+ return true;
97
+ } else {
98
+ const result: ProxyResult = await this.globalHandler.lambdaHandler(evt, context);
99
+ const written: boolean = await this.writeProxyResultToServerResponse(result, response);
100
+ return written;
101
+ }
102
+
103
+ */
104
+ }
105
+
106
+ /*
107
+ private async writeProxyResultToServerResponse(proxyResult: ProxyResult, response: ServerResponse): Promise<boolean> {
108
+ const isGraphQLSchemaResponse: boolean = !!proxyResult && !!proxyResult.body && proxyResult.body.indexOf('{"data":{"__schema"') > -1;
109
+
110
+ if (!isGraphQLSchemaResponse) {
111
+ Logger.debug('Result: %j', proxyResult);
112
+ }
113
+
114
+ response.statusCode = proxyResult.statusCode;
115
+ if (proxyResult.headers) {
116
+ Object.keys(proxyResult.headers).forEach((hk) => {
117
+ response.setHeader(hk, String(proxyResult.headers[hk]));
118
+ });
119
+ }
120
+ if (proxyResult.multiValueHeaders) {
121
+ Object.keys(proxyResult.multiValueHeaders).forEach((hk) => {
122
+ response.setHeader(hk, proxyResult.multiValueHeaders[hk].join(','));
123
+ });
124
+ }
125
+ const toWrite: Buffer = proxyResult.isBase64Encoded ? Buffer.from(proxyResult.body, 'base64') : Buffer.from(proxyResult.body);
126
+
127
+ response.end(toWrite);
128
+ return !!proxyResult.body;
129
+ }
130
+
131
+ */
132
+
133
+ public static async runFromCliArgs(_args: string[]): Promise<void> {
134
+ Logger.info('test-error-server requested (cli is %s) - starting', process?.argv);
135
+ const testServer: TestErrorServer = new TestErrorServer();
136
+ await testServer.runServer();
137
+ Logger.info('Got res server');
138
+ process.exit(0);
139
+ }
140
+ }
@@ -0,0 +1,89 @@
1
+ import {
2
+ APIGatewayEvent,
3
+ APIGatewayEventRequestContextV2,
4
+ APIGatewayEventRequestContextWithAuthorizer,
5
+ APIGatewayProxyEventV2,
6
+ } from 'aws-lambda';
7
+
8
+ // This class holds any random stuff I need to deal with AWS weirdness
9
+ export class AwsUtil {
10
+ public static apiGatewayV2ToApiGatewayV1(srcEvt: APIGatewayProxyEventV2): APIGatewayEvent {
11
+ const rval: APIGatewayEvent = {
12
+ requestContext: AwsUtil.apiGatewayV2RequestContextToApiGatewayV1RequestContext(srcEvt.requestContext),
13
+ httpMethod: srcEvt.requestContext.http.method,
14
+ path: srcEvt.requestContext.http.path,
15
+ queryStringParameters: srcEvt.queryStringParameters,
16
+ headers: srcEvt.headers,
17
+ body: srcEvt.body,
18
+ isBase64Encoded: srcEvt.isBase64Encoded,
19
+ multiValueHeaders: null,
20
+ multiValueQueryStringParameters: null,
21
+ pathParameters: srcEvt.pathParameters,
22
+ stageVariables: srcEvt.stageVariables,
23
+ resource: null,
24
+ };
25
+ return rval;
26
+ }
27
+
28
+ public static apiGatewayV2RequestContextToApiGatewayV1RequestContext(
29
+ srcEvt: APIGatewayEventRequestContextV2,
30
+ ): APIGatewayEventRequestContextWithAuthorizer<any> {
31
+ const rval: APIGatewayEventRequestContextWithAuthorizer<any> = {
32
+ accountId: srcEvt.accountId,
33
+ apiId: srcEvt.apiId,
34
+ authorizer: null, //srcEvtTAuthorizerContext;
35
+
36
+ domainName: srcEvt.domainName,
37
+ domainPrefix: srcEvt.domainPrefix,
38
+
39
+ requestId: srcEvt.requestId,
40
+ routeKey: srcEvt.routeKey,
41
+ stage: srcEvt.stage,
42
+
43
+ requestTime: srcEvt.time,
44
+ requestTimeEpoch: srcEvt.timeEpoch,
45
+
46
+ //connectedAt?: number | undefined;
47
+ //connectionId?: string | undefined;
48
+ //eventType?: string | undefined;
49
+ //extendedRequestId?: string | undefined;
50
+ protocol: srcEvt.http.protocol,
51
+ httpMethod: srcEvt.http.method,
52
+ identity: null, // APIGatewayEventIdentity;
53
+ //messageDirection?: string | undefined;
54
+ //messageId?: string | null | undefined;
55
+ path: srcEvt.http.path,
56
+ resourceId: null, //string;
57
+ resourcePath: null, //string;
58
+ };
59
+ return rval;
60
+ }
61
+
62
+ public static findInMap<T>(toFind: string, map: Map<string, T>): T {
63
+ let rval: T = null;
64
+ map.forEach((val, key) => {
65
+ if (AwsUtil.matchExact(key, toFind)) {
66
+ rval = val;
67
+ }
68
+ });
69
+ return rval;
70
+ }
71
+
72
+ public static matchExact(r, str) {
73
+ const match = str.match(r);
74
+ return match != null && str == match[0];
75
+ }
76
+
77
+ // Returns either the value if non-function, the result if function, and default if neither
78
+ public static resolvePotentialFunctionToResult<T>(src: any, def: T): T {
79
+ let rval: T = def;
80
+ if (src) {
81
+ if (typeof src === 'function') {
82
+ rval = src();
83
+ } else {
84
+ rval = src;
85
+ }
86
+ }
87
+ return rval;
88
+ }
89
+ }
@@ -0,0 +1,13 @@
1
+ import { EpsilonInstance } from '../epsilon-instance';
2
+ import { Context } from 'aws-lambda';
3
+
4
+ export interface ContextGlobalData {
5
+ epsilonInstance: EpsilonInstance;
6
+ context: Context;
7
+ event: any;
8
+ logVariables: Record<string, string | number | boolean>;
9
+ processLabel: string;
10
+
11
+ overrideTraceId: string;
12
+ overrideTraceDepth: number;
13
+ }
@@ -0,0 +1,156 @@
1
+ import { Context, ProxyResult } from 'aws-lambda';
2
+ import { StringRatchet } from '@bitblit/ratchet-common/lang/string-ratchet';
3
+ import { ErrorRatchet } from '@bitblit/ratchet-common/lang/error-ratchet';
4
+ import { NumberRatchet } from '@bitblit/ratchet-common/lang/number-ratchet';
5
+ import { EpsilonInstance } from '../epsilon-instance.js';
6
+ import { LoggingTraceIdGenerator } from '../config/logging-trace-id-generator.js';
7
+ //import { BuiltInTraceIdGenerators } from '../built-in/built-in-trace-id-generators.js';
8
+ import { InternalBackgroundEntry } from '../background/internal-background-entry.js';
9
+ import { InterApiEntry } from '../inter-api/inter-api-entry.js';
10
+ import { ContextGlobalData } from './context-global-data.js';
11
+ import { GlobalRatchet } from '@bitblit/ratchet-common/lang/global-ratchet';
12
+
13
+ // This class serves as a static holder for the AWS Lambda context, and also adds some
14
+ // simple helper functions
15
+ export class ContextUtil {
16
+ // This only really works because Node is single-threaded - otherwise need some kind of thread local
17
+ public static readonly CONTEXT_DATA_GLOBAL_NAME = 'EpsilonGlobalContextData';
18
+ /*
19
+ private static CURRENT_EPSILON_REFERENCE: EpsilonInstance;
20
+ private static CURRENT_CONTEXT: Context;
21
+ private static CURRENT_EVENT: any;
22
+ private static CURRENT_LOG_VARS: Record<string, string | number | boolean> = {};
23
+ private static CURRENT_PROCESS_LABEL: string;
24
+
25
+ private static CURRENT_OVERRIDE_TRACE_ID: string;
26
+ private static CURRENT_OVERRIDE_TRACE_DEPTH: number;*/
27
+
28
+ // Prevent instantiation
29
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
30
+ private constructor() {}
31
+
32
+ private static fetchContextData(): ContextGlobalData {
33
+ return GlobalRatchet.fetchGlobalVar(ContextUtil.CONTEXT_DATA_GLOBAL_NAME, {
34
+ epsilonInstance: null,
35
+ context: null,
36
+ event: null,
37
+ logVariables: {},
38
+ processLabel: 'UNSET',
39
+ overrideTraceDepth: null,
40
+ overrideTraceId: null,
41
+ });
42
+ }
43
+
44
+ public static initContext(epsilon: EpsilonInstance, evt: any, ctx: Context, processLabel: string): void {
45
+ const cd: ContextGlobalData = ContextUtil.fetchContextData();
46
+ cd.epsilonInstance = epsilon;
47
+ cd.context = ctx;
48
+ cd.event = evt;
49
+ cd.processLabel = processLabel;
50
+ }
51
+
52
+ public static clearContext() {
53
+ GlobalRatchet.setGlobalVar(ContextUtil.CONTEXT_DATA_GLOBAL_NAME, null);
54
+ }
55
+
56
+ public static setOverrideTrace(traceId: string, traceDepth: number): void {
57
+ const cd: ContextGlobalData = ContextUtil.fetchContextData();
58
+ cd.overrideTraceId = traceId || cd.overrideTraceId;
59
+ cd.overrideTraceDepth = traceDepth || cd.overrideTraceDepth;
60
+ }
61
+
62
+ public static setOverrideTraceFromInternalBackgroundEntry(entry: InternalBackgroundEntry<any>): void {
63
+ ContextUtil.setOverrideTrace(entry.traceId, entry.traceDepth);
64
+ }
65
+
66
+ public static setOverrideTraceFromInterApiEntry(interApiEntry: InterApiEntry<any>): void {
67
+ ContextUtil.setOverrideTrace(interApiEntry.traceId, interApiEntry.traceDepth);
68
+ }
69
+
70
+ public static addHeadersToRecord(input: Record<string, any>, depthOffset: number = 0): void {
71
+ if (input) {
72
+ input[ContextUtil.traceHeaderName()] = ContextUtil.currentTraceId();
73
+ input[ContextUtil.traceDepthHeaderName()] = StringRatchet.safeString(ContextUtil.currentTraceDepth() + depthOffset);
74
+ } else {
75
+ ErrorRatchet.throwFormattedErr('Cannot add headers to null/undefined input');
76
+ }
77
+ }
78
+
79
+ public static addTraceToProxyResult(pr: ProxyResult): void {
80
+ pr.headers = pr.headers || {};
81
+ ContextUtil.addHeadersToRecord(pr.headers);
82
+ }
83
+
84
+ public static addTraceToHttpRequestInit(ri: RequestInit): void {
85
+ ri.headers = ri.headers || {};
86
+ ContextUtil.addHeadersToRecord(ri.headers, 1);
87
+ }
88
+
89
+ public static setProcessLabel(processLabel: string): void {
90
+ const cd: ContextGlobalData = ContextUtil.fetchContextData();
91
+ cd.processLabel = processLabel;
92
+ }
93
+
94
+ public static currentRequestId(): string {
95
+ const cd: ContextGlobalData = ContextUtil.fetchContextData();
96
+ return cd?.context?.awsRequestId;
97
+ }
98
+
99
+ public static defaultedCurrentRequestId(defaultValueIfMissing: string = StringRatchet.createType4Guid()): string {
100
+ return ContextUtil.currentRequestId() ?? defaultValueIfMissing;
101
+ }
102
+
103
+ public static remainingTimeMS(): number {
104
+ const cd: ContextGlobalData = ContextUtil.fetchContextData();
105
+ return cd?.context?.getRemainingTimeInMillis();
106
+ }
107
+
108
+ public static currentProcessLabel(): string {
109
+ const cd: ContextGlobalData = ContextUtil.fetchContextData();
110
+ return cd?.processLabel || 'unset';
111
+ }
112
+
113
+ private static traceHeaderName(): string {
114
+ const cd: ContextGlobalData = ContextUtil.fetchContextData();
115
+ const headerName: string = cd?.epsilonInstance?.config?.loggerConfig?.traceHeaderName || 'X-TRACE-ID';
116
+ return headerName;
117
+ }
118
+
119
+ private static traceDepthHeaderName(): string {
120
+ const cd: ContextGlobalData = ContextUtil.fetchContextData();
121
+ const headerName: string = cd?.epsilonInstance?.config?.loggerConfig?.traceDepthHeaderName || 'X-TRACE-DEPTH';
122
+ return headerName;
123
+ }
124
+
125
+ public static currentTraceId(): string {
126
+ const cd: ContextGlobalData = ContextUtil.fetchContextData();
127
+
128
+ const traceFn: LoggingTraceIdGenerator =
129
+ cd?.epsilonInstance?.config?.loggerConfig?.traceIdGenerator || ContextUtil.defaultedCurrentRequestId;
130
+ const traceId: string = cd?.overrideTraceId || cd?.event?.headers?.[ContextUtil.traceHeaderName()] || traceFn(cd?.event, cd?.context);
131
+ return traceId;
132
+ }
133
+
134
+ public static currentTraceDepth(): number {
135
+ const cd: ContextGlobalData = ContextUtil.fetchContextData();
136
+
137
+ const caller: number =
138
+ cd?.overrideTraceDepth || NumberRatchet.safeNumber(cd?.event?.headers?.[ContextUtil.traceDepthHeaderName()]) || 1;
139
+ return caller;
140
+ }
141
+
142
+ public static addLogVariable(name: string, val: string | number | boolean): void {
143
+ const cd: ContextGlobalData = ContextUtil.fetchContextData();
144
+ cd.logVariables[name] = val;
145
+ }
146
+
147
+ public static fetchLogVariable(name: string): string | number | boolean {
148
+ const cd: ContextGlobalData = ContextUtil.fetchContextData();
149
+ return cd?.logVariables?.[name];
150
+ }
151
+
152
+ public static fetchLogVariables(): Record<string, string | number | boolean> {
153
+ const cd: ContextGlobalData = ContextUtil.fetchContextData();
154
+ return Object.assign({}, cd?.logVariables || {});
155
+ }
156
+ }
@@ -0,0 +1,190 @@
1
+ import { ScheduledEvent } from 'aws-lambda';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { CronUtil } from './cron-util.js';
5
+ import { CronConfig } from '../config/cron/cron-config.js';
6
+ import { AbstractCronEntry } from '../config/cron/abstract-cron-entry.js';
7
+ import { EsmRatchet } from '@bitblit/ratchet-common/lang/esm-ratchet';
8
+ import { describe, expect, test } from 'vitest';
9
+
10
+ describe('#cronUtil', function () {
11
+ test('should test matching event to entry', async () => {
12
+ const sEvent: ScheduledEvent = JSON.parse(
13
+ fs
14
+ .readFileSync(
15
+ path.join(EsmRatchet.fetchDirName(import.meta.url), '../../../../test-data/epsilon/sample-json/sample-schedule-event-1.json'),
16
+ )
17
+ .toString(),
18
+ );
19
+
20
+ const cfg: CronConfig = {
21
+ context: 'prod',
22
+ timezone: 'America/Los_Angeles',
23
+ entries: [], // only works since this isn't checked
24
+ };
25
+
26
+ const cron1: AbstractCronEntry = {
27
+ contextMatchFilter: new RegExp('prod'),
28
+ };
29
+
30
+ const match1: boolean = CronUtil.eventMatchesEntry(sEvent, cron1, cfg);
31
+ expect(match1).toBeTruthy();
32
+
33
+ const cron2: AbstractCronEntry = {
34
+ contextMatchFilter: new RegExp('dev'),
35
+ };
36
+
37
+ const match2: boolean = CronUtil.eventMatchesEntry(sEvent, cron2, cfg);
38
+ expect(match2).toBeFalsy();
39
+
40
+ const cron3: AbstractCronEntry = {
41
+ eventFilter: new RegExp('.*MyScheduledRule.*'),
42
+ };
43
+
44
+ const match3: boolean = CronUtil.eventMatchesEntry(sEvent, cron3, cfg);
45
+ expect(match3).toBeTruthy();
46
+
47
+ const cron4: AbstractCronEntry = {
48
+ eventFilter: new RegExp('.*NotMyRule.*'),
49
+ };
50
+
51
+ const match4: boolean = CronUtil.eventMatchesEntry(sEvent, cron4, cfg);
52
+ expect(match4).toBeFalsy();
53
+ });
54
+
55
+ test('should match times', async () => {
56
+ const sEvent: ScheduledEvent = JSON.parse(
57
+ fs
58
+ .readFileSync(
59
+ path.join(EsmRatchet.fetchDirName(import.meta.url), '../../../../test-data/epsilon/sample-json/sample-schedule-event-1.json'),
60
+ )
61
+ .toString(),
62
+ );
63
+ const cfg: CronConfig = {
64
+ context: 'prod',
65
+ timezone: 'America/Los_Angeles',
66
+ entries: [], // only works since this isn't checked
67
+ };
68
+
69
+ const _entry1: AbstractCronEntry = {};
70
+
71
+ const cron1: AbstractCronEntry = {
72
+ contextMatchFilter: new RegExp('prod'),
73
+ };
74
+
75
+ const match1: boolean = CronUtil.eventMatchesEntry(sEvent, cron1, cfg);
76
+ expect(match1).toBeTruthy();
77
+
78
+ const cron2: AbstractCronEntry = {
79
+ contextMatchFilter: new RegExp('dev'),
80
+ };
81
+
82
+ const match2: boolean = CronUtil.eventMatchesEntry(sEvent, cron2, cfg);
83
+ expect(match2).toBeFalsy();
84
+
85
+ const cron3: AbstractCronEntry = {
86
+ eventFilter: new RegExp('.*MyScheduledRule.*'),
87
+ };
88
+
89
+ const match3: boolean = CronUtil.eventMatchesEntry(sEvent, cron3, cfg);
90
+ expect(match3).toBeTruthy();
91
+
92
+ const cron4: AbstractCronEntry = {
93
+ eventFilter: new RegExp('.*NotMyRule.*'),
94
+ };
95
+
96
+ const match4: boolean = CronUtil.eventMatchesEntry(sEvent, cron4, cfg);
97
+ expect(match4).toBeFalsy();
98
+ });
99
+
100
+ test('should match time with override', async () => {
101
+ const sEvent: ScheduledEvent = JSON.parse(
102
+ fs
103
+ .readFileSync(
104
+ path.join(EsmRatchet.fetchDirName(import.meta.url), '../../../../test-data/epsilon/sample-json/sample-schedule-event-1.json'),
105
+ )
106
+ .toString(),
107
+ );
108
+ const cfg: CronConfig = {
109
+ context: 'prod',
110
+ timezone: 'America/Los_Angeles',
111
+ entries: [], // only works since this isn't checked
112
+ };
113
+
114
+ const _entry1: AbstractCronEntry = {};
115
+
116
+ const cron1: AbstractCronEntry = {
117
+ hourFilter: [3],
118
+ };
119
+
120
+ const cron2: AbstractCronEntry = {
121
+ overrideTimezone: 'etc/utc',
122
+ hourFilter: [3],
123
+ };
124
+
125
+ //Timestamp in milliseconds: 1652931000000
126
+ //Date and time (GMT): Thursday, May 19, 2022 3:30:00 AM
127
+ const testTimestampEpochMS: number = 1652931000000;
128
+
129
+ const match1: boolean = CronUtil.eventMatchesEntry(sEvent, cron1, cfg, testTimestampEpochMS);
130
+ const match2: boolean = CronUtil.eventMatchesEntry(sEvent, cron2, cfg, testTimestampEpochMS);
131
+ expect(match1).toBeFalsy();
132
+ expect(match2).toBeTruthy();
133
+ });
134
+
135
+ test('should match day of month filters', async () => {
136
+ const sEvent: ScheduledEvent = JSON.parse(
137
+ fs
138
+ .readFileSync(
139
+ path.join(EsmRatchet.fetchDirName(import.meta.url), '../../../../test-data/epsilon/sample-json/sample-schedule-event-1.json'),
140
+ )
141
+ .toString(),
142
+ );
143
+ const cfg: CronConfig = {
144
+ context: 'prod',
145
+ timezone: 'etc/utc',
146
+ entries: [], // only works since this isn't checked
147
+ };
148
+
149
+ const _entry1: AbstractCronEntry = {};
150
+
151
+ //Timestamp in milliseconds: 1652931000000
152
+ //Date and time (GMT): Thursday, May 19, 2022 3:30:00 AM
153
+ const testTimestampEpochMS: number = 1652931000000;
154
+
155
+ const dayOfMonth: AbstractCronEntry = {
156
+ dayOfMonthFilter: [19],
157
+ };
158
+
159
+ const matchDay: boolean = CronUtil.eventMatchesEntry(sEvent, dayOfMonth, cfg, testTimestampEpochMS);
160
+ expect(matchDay).toBeTruthy();
161
+ });
162
+
163
+ test('should match month of year filter', async () => {
164
+ const sEvent: ScheduledEvent = JSON.parse(
165
+ fs
166
+ .readFileSync(
167
+ path.join(EsmRatchet.fetchDirName(import.meta.url), '../../../../test-data/epsilon/sample-json/sample-schedule-event-1.json'),
168
+ )
169
+ .toString(),
170
+ );
171
+ const cfg: CronConfig = {
172
+ context: 'prod',
173
+ timezone: 'etc/utc',
174
+ entries: [], // only works since this isn't checked
175
+ };
176
+
177
+ const _entry1: AbstractCronEntry = {};
178
+
179
+ //Timestamp in milliseconds: 1652931000000
180
+ //Date and time (GMT): Thursday, May 19, 2022 3:30:00 AM
181
+ const testTimestampEpochMS: number = 1652931000000;
182
+
183
+ const monthOfYear: AbstractCronEntry = {
184
+ monthOfYearFilter: [5],
185
+ };
186
+
187
+ const matchMonth: boolean = CronUtil.eventMatchesEntry(sEvent, monthOfYear, cfg, testTimestampEpochMS);
188
+ expect(matchMonth).toBeTruthy();
189
+ });
190
+ });
@@ -0,0 +1,86 @@
1
+ import { RequireRatchet } from '@bitblit/ratchet-common/lang/require-ratchet';
2
+ import { ErrorRatchet } from '@bitblit/ratchet-common/lang/error-ratchet';
3
+ import { StringRatchet } from '@bitblit/ratchet-common/lang/string-ratchet';
4
+ import { ScheduledEvent } from 'aws-lambda';
5
+ import { DateTime } from 'luxon';
6
+ import { AbstractCronEntry } from '../config/cron/abstract-cron-entry.js';
7
+ import { CronConfig } from '../config/cron/cron-config.js';
8
+
9
+ export class CronUtil {
10
+ public static everyNMinuteFilter(n: number): number[] {
11
+ return CronUtil.everyNElementFilter(n, 60);
12
+ }
13
+
14
+ public static everyNDaysOfYearFilter(n: number): number[] {
15
+ return CronUtil.everyNElementFilter(n, 365);
16
+ }
17
+
18
+ public static everyNElementFilter(n: number, m: number): number[] {
19
+ RequireRatchet.notNullOrUndefined(n);
20
+ RequireRatchet.notNullOrUndefined(m);
21
+ const half: number = Math.floor(m / 2);
22
+
23
+ if (!n || n < 2 || n > half || m % n !== 0) {
24
+ ErrorRatchet.throwFormattedErr(
25
+ 'Invalid config - this function only makes sense for 2 < N < %d and %d evenly divisible by N',
26
+ half,
27
+ m,
28
+ );
29
+ throw new Error('Invalid config - this function only makes sense for 2 < N < 31 and 60 evenly divisible by N');
30
+ }
31
+ const rval: number[] = [];
32
+ for (let i = 0; i < 60; i += n) {
33
+ rval.push(i);
34
+ }
35
+ return rval;
36
+ }
37
+
38
+ public static numberMatchesFilter(num: number, filter: number[]): boolean {
39
+ return !filter || filter.length === 0 || filter.includes(num);
40
+ }
41
+
42
+ public static eventMatchesEntry(
43
+ event: ScheduledEvent,
44
+ entry: AbstractCronEntry,
45
+ cfg: CronConfig,
46
+ testTimeEpochMS: number = new Date().getTime(),
47
+ ): boolean {
48
+ let rval: boolean = false;
49
+ if (!!event && !!entry && !!cfg.timezone) {
50
+ if (!!event.resources && event.resources.length > 0) {
51
+ const eventSourceName: string = event.resources[0];
52
+ const targetTZ: string = StringRatchet.trimToNull(entry.overrideTimezone) || cfg.timezone;
53
+ const nowInTZ: DateTime = DateTime.fromMillis(testTimeEpochMS).setZone(targetTZ);
54
+ rval = !entry.eventFilter || entry.eventFilter.test(eventSourceName);
55
+ rval = rval && CronUtil.numberMatchesFilter(nowInTZ.minute, entry.minuteFilter);
56
+ rval = rval && CronUtil.numberMatchesFilter(nowInTZ.hour, entry.hourFilter);
57
+ rval = rval && CronUtil.numberMatchesFilter(nowInTZ.weekday, entry.dayOfWeekFilter);
58
+ rval = rval && CronUtil.numberMatchesFilter(nowInTZ.day, entry.dayOfMonthFilter);
59
+ rval = rval && CronUtil.numberMatchesFilter(nowInTZ.month, entry.monthOfYearFilter);
60
+ rval = rval && (!entry.contextMatchFilter || entry.contextMatchFilter.test(StringRatchet.trimToEmpty(cfg.context)));
61
+ rval = rval && (!entry.contextNoMatchFilter || !entry.contextNoMatchFilter.test(StringRatchet.trimToEmpty(cfg.context)));
62
+ }
63
+ }
64
+
65
+ return rval;
66
+ }
67
+
68
+ public static cronEntryName(entry: AbstractCronEntry, idx: number = null): string {
69
+ RequireRatchet.notNullOrUndefined(entry);
70
+ let rval: string = null;
71
+ if (entry) {
72
+ rval = entry.name;
73
+ rval = rval || entry['backgroundTaskType'];
74
+ if (!rval && !!entry['directHandler']) {
75
+ if (idx) {
76
+ rval = 'Direct Entry ' + idx;
77
+ } else {
78
+ rval = 'Direct Entry (No idx specified)';
79
+ }
80
+ }
81
+ } else {
82
+ rval = 'ERROR: no entry passed';
83
+ }
84
+ return rval;
85
+ }
86
+ }