@bitblit/ratchet-epsilon-common 6.0.145-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,9 @@
1
+ import { BackgroundProcessHandling } from './background-process-handling.js';
2
+
3
+ export interface BackgroundQueueResponseInternal {
4
+ processHandling: BackgroundProcessHandling;
5
+ startProcessorRequested: boolean;
6
+ success: boolean;
7
+ resultId: string;
8
+ error: string;
9
+ }
@@ -0,0 +1,105 @@
1
+ import { ModelValidator } from '@bitblit/ratchet-misc/model-validator/model-validator';
2
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
3
+ import { ErrorRatchet } from '@bitblit/ratchet-common/lang/error-ratchet';
4
+ import { StringRatchet } from '@bitblit/ratchet-common/lang/string-ratchet';
5
+ import { BackgroundConfig } from '../config/background/background-config.js';
6
+ import { BackgroundEntry } from './background-entry.js';
7
+ import { BackgroundProcessor } from '../config/background/background-processor.js';
8
+ import { BackgroundAwsConfig } from '../config/background/background-aws-config.js';
9
+
10
+ /**
11
+ * Handles all submission of work to the background processing system.
12
+ */
13
+ export class BackgroundValidator {
14
+ constructor(
15
+ private cfg: BackgroundConfig,
16
+ private modelValidator: ModelValidator,
17
+ ) {}
18
+
19
+ public findProcessor(typeName: string): BackgroundProcessor<any> {
20
+ const rval: BackgroundProcessor<any> = this.cfg.processors.find((s) => s.typeName === typeName);
21
+ return rval;
22
+ }
23
+
24
+ public validType(type: string): boolean {
25
+ return !!this.findProcessor(type);
26
+ }
27
+
28
+ public validateEntry<T>(entry: BackgroundEntry<T>): string[] {
29
+ const rval: string[] = [];
30
+ if (!entry) {
31
+ rval.push('Entry is null');
32
+ } else if (!StringRatchet.trimToNull(entry.type)) {
33
+ rval.push('Entry type is null or empty');
34
+ const proc: BackgroundProcessor<any> = this.findProcessor(entry.type);
35
+
36
+ if (!proc) {
37
+ rval.push('Entry type is invalid');
38
+ }
39
+ }
40
+ return rval;
41
+ }
42
+
43
+ public validateEntryAndThrowException<T>(entry: BackgroundEntry<T>): void {
44
+ const errors: string[] = this.validateEntry(entry);
45
+ if (errors.length > 0) {
46
+ Logger.warn('Invalid entry %j : errors : %j', entry, errors);
47
+ ErrorRatchet.throwFormattedErr('Invalid entry %j : errors : %j', entry, errors);
48
+ }
49
+ }
50
+
51
+ public static validateAndMapProcessors(
52
+ processorInput: BackgroundProcessor<any>[],
53
+ _modelValidator: ModelValidator,
54
+ ): Map<string, BackgroundProcessor<any>> {
55
+ const rval: Map<string, BackgroundProcessor<any>> = new Map<string, BackgroundProcessor<any>>();
56
+ processorInput.forEach((p, idx) => {
57
+ if (!p) {
58
+ ErrorRatchet.throwFormattedErr('Null processor provided at index %d', idx);
59
+ }
60
+ if (!StringRatchet.trimToNull(p.typeName)) {
61
+ ErrorRatchet.throwFormattedErr('Processor at index %d defines no name', idx);
62
+ }
63
+
64
+ if (rval.has(p.typeName)) {
65
+ ErrorRatchet.throwFormattedErr('More than one processor defined for type %s', p.typeName);
66
+ }
67
+
68
+ rval.set(p.typeName, p);
69
+ });
70
+ return rval;
71
+ }
72
+
73
+ public static validateAwsConfig(cfg: BackgroundAwsConfig): string[] {
74
+ const rval: string[] = [];
75
+ if (!cfg) {
76
+ rval.push('Null config');
77
+ } else {
78
+ if (!cfg.notificationArn) {
79
+ rval.push('AWS config missing notificationArn');
80
+ }
81
+ if (!cfg.queueUrl) {
82
+ rval.push('AWS config missing queueUrl');
83
+ }
84
+ if (
85
+ (cfg.sendNotificationOnBackgroundError || cfg.sendNotificationOnBackgroundValidationFailure) &&
86
+ !cfg.backgroundProcessFailureSnsArn
87
+ ) {
88
+ rval.push('At least one send notification flag set to true but no sns arn set');
89
+ }
90
+ }
91
+ return rval;
92
+ }
93
+
94
+ public static validateConfig(cfg: BackgroundConfig): string[] {
95
+ const rval: string[] = [];
96
+ if (!cfg) {
97
+ rval.push('Null config');
98
+ } else {
99
+ if (!cfg.processors || cfg.processors.length === 0) {
100
+ rval.push('No processes specified');
101
+ }
102
+ }
103
+ return rval;
104
+ }
105
+ }
@@ -0,0 +1,110 @@
1
+ import { StringRatchet } from '@bitblit/ratchet-common/lang/string-ratchet';
2
+
3
+ export class EpsilonBackgroundProcessError<T = void> extends Error {
4
+ private static readonly EPSILON_BACKGROUND_PROCESS_ERROR_FLAG_KEY: string = '__epsilonBackgroundProcessErrorFlag';
5
+ private _errors: string[];
6
+ private _detailErrorCode: number;
7
+ private _details: T;
8
+ private _requestId: string;
9
+ private _wrappedError: Error;
10
+
11
+ constructor(...errors: string[]) {
12
+ super(EpsilonBackgroundProcessError.combineErrorStringsWithDefault(errors));
13
+ Object.setPrototypeOf(this, EpsilonBackgroundProcessError.prototype);
14
+ this._errors = errors;
15
+ this[EpsilonBackgroundProcessError.EPSILON_BACKGROUND_PROCESS_ERROR_FLAG_KEY] = true; // Just used to tell if one has been wrapped
16
+ }
17
+
18
+ public static combineErrorStringsWithDefault(errors: string[], defMessage: string = 'Internal Server Error'): string {
19
+ return errors && errors.length > 0 ? errors.join(',') : defMessage;
20
+ }
21
+
22
+ public setFormattedErrorMessage(format: string, ...input: any[]): void {
23
+ const msg: string = StringRatchet.format(format, ...input);
24
+ this.errors = [msg];
25
+ }
26
+
27
+ public withFormattedErrorMessage(format: string, ...input: any[]): EpsilonBackgroundProcessError<T> {
28
+ this.setFormattedErrorMessage(format, ...input);
29
+ return this;
30
+ }
31
+
32
+ public withErrors(errors: string[]): EpsilonBackgroundProcessError<T> {
33
+ this.errors = errors; // Call setter
34
+ return this;
35
+ }
36
+
37
+ public withDetailErrorCode(detailErrorCode: number): EpsilonBackgroundProcessError<T> {
38
+ this._detailErrorCode = detailErrorCode; // Call setter
39
+ return this;
40
+ }
41
+
42
+ public withDetails(details: T): EpsilonBackgroundProcessError<T> {
43
+ this._details = details; // Call setter
44
+ return this;
45
+ }
46
+
47
+ public withRequestId(requestId: string): EpsilonBackgroundProcessError<T> {
48
+ this._requestId = requestId; // Call setter
49
+ return this;
50
+ }
51
+
52
+ public withWrappedError(err: Error): EpsilonBackgroundProcessError<T> {
53
+ this._wrappedError = err; // Call setter
54
+ return this;
55
+ }
56
+
57
+ public isWrappedError(): boolean {
58
+ return !!this._wrappedError;
59
+ }
60
+
61
+ public static wrapError<T = void>(err: Error): EpsilonBackgroundProcessError<T> {
62
+ let rval: EpsilonBackgroundProcessError<T> = null;
63
+ if (EpsilonBackgroundProcessError.objectIsEpsilonBackgroundProcessError(err)) {
64
+ rval = err as EpsilonBackgroundProcessError<T>;
65
+ } else {
66
+ rval = new EpsilonBackgroundProcessError<T>(err.message).withWrappedError(err);
67
+ }
68
+ return rval;
69
+ }
70
+
71
+ public static objectIsEpsilonBackgroundProcessError(obj: any): boolean {
72
+ return obj && obj[EpsilonBackgroundProcessError.EPSILON_BACKGROUND_PROCESS_ERROR_FLAG_KEY] === true;
73
+ }
74
+
75
+ set errors(value: string[]) {
76
+ this._errors = value || ['Internal Server Error'];
77
+ this.message = EpsilonBackgroundProcessError.combineErrorStringsWithDefault(this._errors);
78
+ }
79
+ get errors(): string[] {
80
+ return this._errors;
81
+ }
82
+
83
+ set detailErrorCode(value: number) {
84
+ this._detailErrorCode = value;
85
+ }
86
+ get detailErrorCode(): number {
87
+ return this._detailErrorCode;
88
+ }
89
+
90
+ set details(value: T) {
91
+ this._details = value;
92
+ }
93
+ get details(): T {
94
+ return this._details;
95
+ }
96
+
97
+ set requestId(value: string) {
98
+ this._requestId = value || 'MISSING';
99
+ }
100
+ get requestId(): string {
101
+ return this._requestId;
102
+ }
103
+
104
+ set wrappedError(value: Error) {
105
+ this._wrappedError = value;
106
+ }
107
+ get wrappedError(): Error {
108
+ return this._wrappedError;
109
+ }
110
+ }
@@ -0,0 +1,10 @@
1
+ import { BackgroundEntry } from './background-entry.js';
2
+
3
+ export interface InternalBackgroundEntry<T> extends BackgroundEntry<T> {
4
+ guid: string;
5
+ createdEpochMS: number;
6
+ traceId: string;
7
+ traceDepth: number;
8
+
9
+ meta?: Record<string, string>;
10
+ }
@@ -0,0 +1,120 @@
1
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
2
+ import { RequireRatchet } from '@bitblit/ratchet-common/lang/require-ratchet';
3
+ import { ErrorRatchet } from '@bitblit/ratchet-common/lang/error-ratchet';
4
+ import { StringRatchet } from '@bitblit/ratchet-common/lang/string-ratchet';
5
+ import { BackgroundEntry } from '../background-entry.js';
6
+ import { InternalBackgroundEntry } from '../internal-background-entry.js';
7
+ import { DateTime } from 'luxon';
8
+ import { ContextUtil } from '../../util/context-util.js';
9
+ import { BackgroundManagerLike } from './background-manager-like.js';
10
+
11
+ /**
12
+ * Handles all submission of work to the background processing system.
13
+ *
14
+ * Note that this does NOT validate the input, it just passes it along. This is
15
+ * because it creates a circular reference to the processors if we try since they
16
+ * define the type and validation.
17
+ */
18
+ export abstract class AbstractBackgroundManager implements BackgroundManagerLike {
19
+ abstract addEntryToQueue<T>(entry: BackgroundEntry<T>, fireStartMessage?: boolean): Promise<string>;
20
+ abstract fireImmediateProcessRequest<T>(entry: BackgroundEntry<T>): Promise<string>;
21
+ abstract fireStartProcessingRequest(): Promise<string>;
22
+ abstract fetchApproximateNumberOfQueueEntries(): Promise<number>;
23
+ abstract backgroundManagerName: string;
24
+ abstract takeEntryFromBackgroundQueue(): Promise<InternalBackgroundEntry<any>[]>;
25
+
26
+ public createEntry<T>(type: string, data?: T): BackgroundEntry<T> {
27
+ const rval: BackgroundEntry<T> = {
28
+ type: type,
29
+ data: data,
30
+ };
31
+ return rval;
32
+ }
33
+
34
+ public async wrapEntryForInternal<T>(
35
+ entry: BackgroundEntry<T>,
36
+ overrideTraceId?: string,
37
+ overrideTraceDepth?: number,
38
+ ): Promise<InternalBackgroundEntry<T>> {
39
+ const rval: InternalBackgroundEntry<T> = Object.assign({}, entry, {
40
+ createdEpochMS: new Date().getTime(),
41
+ guid: AbstractBackgroundManager.generateBackgroundGuid(),
42
+ traceId: overrideTraceId || ContextUtil.currentTraceId(), // || cuz no empty strings allowed
43
+ traceDepth: overrideTraceDepth || ContextUtil.currentTraceDepth() + 1, // || no 0 allowed either
44
+ });
45
+ return rval;
46
+ }
47
+
48
+ public async addEntryToQueueByParts<T>(type: string, data?: T, fireStartMessage?: boolean): Promise<string> {
49
+ let rval: string = null;
50
+ const entry: BackgroundEntry<T> = this.createEntry(type, data);
51
+ if (entry) {
52
+ rval = await this.addEntryToQueue(entry, fireStartMessage);
53
+ }
54
+ return rval;
55
+ }
56
+
57
+ public async addEntriesToQueue(entries: BackgroundEntry<any>[], fireStartMessage?: boolean): Promise<string[]> {
58
+ const rval: string[] = [];
59
+ for (const eVal of entries) {
60
+ //for (let i = 0; i < entries.length; i++) {
61
+ try {
62
+ // Always defer the fire to after the last enqueue
63
+ const tmp: string = await this.addEntryToQueue(eVal, false);
64
+ rval.push(tmp);
65
+ } catch (err) {
66
+ Logger.error('Error processing %j : %s', eVal, err);
67
+ rval.push(err['message']);
68
+ }
69
+
70
+ if (fireStartMessage) {
71
+ const fireResult: string = await this.fireStartProcessingRequest();
72
+ Logger.silly('FireResult : %s', fireResult);
73
+ }
74
+ }
75
+ return rval;
76
+ }
77
+
78
+ public async fireImmediateProcessRequestByParts<T>(type: string, data?: T): Promise<string> {
79
+ let rval: string = null;
80
+ const entry: BackgroundEntry<T> = this.createEntry(type, data);
81
+ if (entry) {
82
+ rval = await this.fireImmediateProcessRequest(entry);
83
+ }
84
+ return rval;
85
+ }
86
+
87
+ public static generateBackgroundGuid(targetEpochMS: number = new Date().getTime()): string {
88
+ const dt: DateTime = DateTime.fromMillis(targetEpochMS);
89
+ return dt.toFormat('yyyy-MM-dd-HH-mm-ss-') + StringRatchet.createType4Guid();
90
+ }
91
+
92
+ public static backgroundGuidToPath(prefix: string, guid: string): string {
93
+ let path: string = StringRatchet.trimToEmpty(prefix);
94
+ if (path.length && !path.endsWith('/')) {
95
+ path += '/';
96
+ }
97
+ path += guid.substring(0, 4) + '/' + guid.substring(5, 7) + '/' + guid.substring(8, 10) + '/';
98
+ path += guid + '.json';
99
+ return path;
100
+ }
101
+
102
+ public static pathToBackgroundGuid(prefix: string, path: string): string {
103
+ RequireRatchet.notNullOrUndefined(path, 'path');
104
+ let start: number = 0;
105
+ if (!path.endsWith('.json')) {
106
+ ErrorRatchet.throwFormattedErr('Cannot extract guid, does not end with .json : %s : %s', path, prefix);
107
+ }
108
+ if (StringRatchet.trimToNull(prefix)) {
109
+ if (!path.startsWith(prefix)) {
110
+ ErrorRatchet.throwFormattedErr('Cannot extract guid, does not start with prefix : %s : %s', path, prefix);
111
+ }
112
+ start = prefix.length;
113
+ if (!prefix.endsWith('/')) {
114
+ start++;
115
+ }
116
+ }
117
+ start += 11;
118
+ return path.substring(start, path.length - 5); // strip prefix and .json at the end
119
+ }
120
+ }
@@ -0,0 +1,87 @@
1
+ import { SQSClient } from '@aws-sdk/client-sqs';
2
+ import { BackgroundEntry } from '../background-entry.js';
3
+ import { BackgroundAwsConfig } from '../../config/background/background-aws-config.js';
4
+ import { InternalBackgroundEntry } from '../internal-background-entry.js';
5
+ import { SNSClient } from '@aws-sdk/client-sns';
6
+ import { AwsSqsSnsBackgroundManager } from './aws-sqs-sns-background-manager.js';
7
+ import { S3CacheRatchetLike } from '@bitblit/ratchet-aws/s3/s3-cache-ratchet-like';
8
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
9
+ import { ErrorRatchet } from '@bitblit/ratchet-common/lang/error-ratchet';
10
+ import { StringRatchet } from '@bitblit/ratchet-common/lang/string-ratchet';
11
+
12
+ /**
13
+ * Handles all submission of work to the background processing system.
14
+ *
15
+ * Note that this does NOT validate the input, it just passes it along. This is
16
+ * because it creates a circular reference to the processors if we try since they
17
+ * define the type and validation.
18
+ */
19
+ export class AwsLargePayloadS3SqsSnsBackgroundManager extends AwsSqsSnsBackgroundManager {
20
+ // SQS and SNS payload limit is (256 * 1024). Set threshold lower to allow extra metadata
21
+ private static readonly LARGE_MESSAGE_SIZE_THRESHOLD: number = 250_000;
22
+ private static readonly LARGE_MESSAGE_S3_PATH_META_KEY: string = 'S3_STORAGE_KEY';
23
+
24
+ constructor(
25
+ _awsConfig: BackgroundAwsConfig,
26
+ _sqs: SQSClient,
27
+ _sns: SNSClient,
28
+ private _s3: S3CacheRatchetLike,
29
+ private pathPrefix: string = '',
30
+ ) {
31
+ super(_awsConfig, _sqs, _sns);
32
+ if (!_s3) {
33
+ ErrorRatchet.throwFormattedErr('Cannot start - no s3Client provided');
34
+ }
35
+ if (!StringRatchet.trimToNull(_s3.getDefaultBucket())) {
36
+ ErrorRatchet.throwFormattedErr('Cannot start - no default bucket provided');
37
+ }
38
+ if (!pathPrefix && pathPrefix.endsWith('/')) {
39
+ ErrorRatchet.throwFormattedErr('Path prefix may not end with /');
40
+ }
41
+ }
42
+
43
+ public readonly backgroundManagerName: string = 'AwsLargePayloadS3SqsSnsBackgroundManager';
44
+
45
+ public get s3(): S3CacheRatchetLike {
46
+ return this._s3;
47
+ }
48
+
49
+ public async wrapEntryForInternal<T>(
50
+ entry: BackgroundEntry<T>,
51
+ overrideTraceId?: string,
52
+ overrideTraceDepth?: number,
53
+ ): Promise<InternalBackgroundEntry<T>> {
54
+ const rval: InternalBackgroundEntry<T> = await super.wrapEntryForInternal(entry, overrideTraceId, overrideTraceDepth);
55
+
56
+ const payloadApproximateSize: number = Buffer.byteLength(JSON.stringify(rval), 'utf-8');
57
+ if (payloadApproximateSize > AwsLargePayloadS3SqsSnsBackgroundManager.LARGE_MESSAGE_SIZE_THRESHOLD) {
58
+ Logger.info('Message payload is above LARGE_MESSAGE_SIZE_THRESHOLD. Uploading to s3.');
59
+ rval.meta = rval.meta || {};
60
+ const pathToData: string = await this.writeMessageToS3(rval.guid, rval.data);
61
+ rval.meta[AwsLargePayloadS3SqsSnsBackgroundManager.LARGE_MESSAGE_S3_PATH_META_KEY] = pathToData;
62
+ rval.data = undefined;
63
+ }
64
+
65
+ return rval;
66
+ }
67
+
68
+ private async writeMessageToS3(guid: string, entry: unknown): Promise<string> {
69
+ let s3FilePath: string = StringRatchet.trimToNull(this.pathPrefix) ? this.pathPrefix + '/' : '';
70
+ s3FilePath += guid + '.json';
71
+ await this.s3.writeObjectToCacheFile(s3FilePath, entry);
72
+ return s3FilePath;
73
+ }
74
+
75
+ public async modifyPayloadPreProcess<T>(entry: InternalBackgroundEntry<T>): Promise<InternalBackgroundEntry<T>> {
76
+ if (entry?.meta?.[AwsLargePayloadS3SqsSnsBackgroundManager.LARGE_MESSAGE_S3_PATH_META_KEY]) {
77
+ Logger.silly('Restoring large data from %s', entry.meta[AwsLargePayloadS3SqsSnsBackgroundManager.LARGE_MESSAGE_S3_PATH_META_KEY]);
78
+ const parsed: T = await this._s3.fetchCacheFileAsObject<T>(
79
+ entry.meta[AwsLargePayloadS3SqsSnsBackgroundManager.LARGE_MESSAGE_S3_PATH_META_KEY],
80
+ );
81
+ entry.data = parsed;
82
+ //eslint-disable-next-line @typescript-eslint/no-dynamic-delete
83
+ delete entry.meta[AwsLargePayloadS3SqsSnsBackgroundManager.LARGE_MESSAGE_S3_PATH_META_KEY];
84
+ }
85
+ return entry;
86
+ }
87
+ }
@@ -0,0 +1,201 @@
1
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
2
+ import { ErrorRatchet } from '@bitblit/ratchet-common/lang/error-ratchet';
3
+ import {
4
+ DeleteMessageCommand,
5
+ DeleteMessageCommandOutput,
6
+ GetQueueAttributesCommand,
7
+ GetQueueAttributesCommandOutput,
8
+ GetQueueAttributesRequest,
9
+ GetQueueAttributesResult,
10
+ ReceiveMessageCommand,
11
+ ReceiveMessageCommandOutput,
12
+ SendMessageCommand,
13
+ SendMessageCommandOutput,
14
+ SQSClient,
15
+ } from '@aws-sdk/client-sqs';
16
+ import { BackgroundEntry } from '../background-entry.js';
17
+ import { BackgroundAwsConfig } from '../../config/background/background-aws-config.js';
18
+ import { EpsilonConstants } from '../../epsilon-constants.js';
19
+ import { InternalBackgroundEntry } from '../internal-background-entry.js';
20
+ import { AbstractBackgroundManager } from './abstract-background-manager.js';
21
+ import { BackgroundValidator } from '../background-validator.js';
22
+ import { PublishCommand, PublishCommandOutput, SNSClient } from '@aws-sdk/client-sns';
23
+ import { NumberRatchet } from '@bitblit/ratchet-common/lang/number-ratchet';
24
+
25
+ /**
26
+ * Handles all submission of work to the background processing system.
27
+ *
28
+ * Note that this does NOT validate the input, it just passes it along. This is
29
+ * because it creates a circular reference to the processors if we try since they
30
+ * define the type and validation.
31
+ */
32
+ export class AwsSqsSnsBackgroundManager extends AbstractBackgroundManager {
33
+ constructor(
34
+ private _awsConfig: BackgroundAwsConfig,
35
+ private _sqs: SQSClient,
36
+ private _sns: SNSClient,
37
+ ) {
38
+ super();
39
+ const cfgErrors: string[] = BackgroundValidator.validateAwsConfig(_awsConfig);
40
+ if (cfgErrors.length) {
41
+ ErrorRatchet.throwFormattedErr('Cannot start - invalid AWS config : %j', cfgErrors);
42
+ }
43
+ }
44
+
45
+ public readonly backgroundManagerName: string = 'AwsSqsSnsBackgroundManager';
46
+
47
+ public get awsConfig(): BackgroundAwsConfig {
48
+ return this._awsConfig;
49
+ }
50
+
51
+ public get sqs(): SQSClient {
52
+ return this._sqs;
53
+ }
54
+
55
+ public get sns(): SNSClient {
56
+ return this._sns;
57
+ }
58
+
59
+ public async addEntryToQueue<T>(entry: BackgroundEntry<T>, fireStartMessage?: boolean): Promise<string> {
60
+ try {
61
+ const wrapped: InternalBackgroundEntry<T> = await this.wrapEntryForInternal(entry);
62
+ const rval: string = wrapped.guid;
63
+ // Guard against bad entries up front
64
+ const params = {
65
+ DelaySeconds: 0,
66
+ MessageBody: JSON.stringify(wrapped),
67
+ MessageGroupId: entry.type,
68
+ QueueUrl: this.awsConfig.queueUrl,
69
+ };
70
+
71
+ Logger.info('Add entry to queue (remote) : %j : Start : %s', params, fireStartMessage);
72
+ const result: SendMessageCommandOutput = await this.sqs.send(new SendMessageCommand(params));
73
+
74
+ if (fireStartMessage) {
75
+ const fireResult: string = await this.fireStartProcessingRequest();
76
+ Logger.silly('FireResult : %s', fireResult);
77
+ }
78
+
79
+ Logger.info('Background process %s using message id %s', rval, result.MessageId);
80
+ return rval;
81
+ } catch (error) {
82
+ Logger.error('Error inserting background entry into SQS queue : %j', error);
83
+ throw new Error('Error inserting background entry into SQS queue : ' + error['code'] + ' : ' + error['name']);
84
+ }
85
+ }
86
+
87
+ public async fireImmediateProcessRequest<T>(entry: BackgroundEntry<T>): Promise<string> {
88
+ try {
89
+ const wrapped: InternalBackgroundEntry<T> = await this.wrapEntryForInternal(entry);
90
+ const rval: string = wrapped.guid;
91
+ // Guard against bad entries up front
92
+ Logger.info('Fire immediately (remote) : %j ', entry);
93
+ const toWrite: any = {
94
+ type: EpsilonConstants.BACKGROUND_SNS_IMMEDIATE_RUN_FLAG,
95
+ backgroundEntry: wrapped,
96
+ };
97
+ const msg: string = JSON.stringify(toWrite);
98
+ const snsId: string = await this.writeMessageToSnsTopic(msg);
99
+ Logger.debug('Background guid %s Wrote message : %s to SNS : %s', rval, msg, snsId);
100
+ return rval;
101
+ } catch (err) {
102
+ Logger.error(
103
+ 'Failed to fireImmediateProcessRequest : SQS: %s SNS: %s :',
104
+ this?._awsConfig?.queueUrl,
105
+ this?._awsConfig?.notificationArn,
106
+ err,
107
+ err,
108
+ );
109
+ throw new Error('Failed to fireImmediateProcessRequest : : ' + err['code'] + ' : ' + err['name']);
110
+ }
111
+ }
112
+
113
+ public async fireStartProcessingRequest(): Promise<string> {
114
+ let rval: string = null;
115
+ try {
116
+ Logger.info('Fire start processing request (remote)');
117
+ rval = await this.writeMessageToSnsTopic(EpsilonConstants.BACKGROUND_SNS_START_MARKER);
118
+ } catch (err) {
119
+ Logger.error(
120
+ 'Failed to fireStartProcessingRequest : SQS: %s SNS: %s :',
121
+ this?._awsConfig?.queueUrl,
122
+ this?._awsConfig?.notificationArn,
123
+ err,
124
+ err,
125
+ );
126
+ }
127
+ return rval;
128
+ }
129
+
130
+ public async fetchApproximateNumberOfQueueEntries(): Promise<number> {
131
+ let rval: number = null;
132
+ const all: GetQueueAttributesResult = await this.fetchCurrentQueueAttributes();
133
+ rval = NumberRatchet.safeNumber(all.Attributes['ApproximateNumberOfMessages']);
134
+ return rval;
135
+ }
136
+
137
+ public async fetchCurrentQueueAttributes(): Promise<GetQueueAttributesCommandOutput> {
138
+ let rval: GetQueueAttributesCommandOutput = null;
139
+ const req: GetQueueAttributesRequest = {
140
+ AttributeNames: ['All'],
141
+ QueueUrl: this.awsConfig.queueUrl,
142
+ };
143
+
144
+ rval = await this.sqs.send(new GetQueueAttributesCommand(req));
145
+ return rval;
146
+ }
147
+
148
+ public async writeMessageToSnsTopic(message: string): Promise<string> {
149
+ let rval: string = null;
150
+ const params = {
151
+ Message: message,
152
+ TopicArn: this.awsConfig.notificationArn,
153
+ };
154
+
155
+ Logger.debug('Writing message to SNS topic : j', params);
156
+ const result: PublishCommandOutput = await this.sns.send(new PublishCommand(params));
157
+ rval = result.MessageId;
158
+ return rval;
159
+ }
160
+
161
+ public async takeEntryFromBackgroundQueue(): Promise<InternalBackgroundEntry<any>[]> {
162
+ const rval: InternalBackgroundEntry<any>[] = [];
163
+
164
+ const params = {
165
+ MaxNumberOfMessages: 1,
166
+ QueueUrl: this.awsConfig.queueUrl,
167
+ VisibilityTimeout: 300,
168
+ WaitTimeSeconds: 0,
169
+ };
170
+
171
+ const message: ReceiveMessageCommandOutput = await this.sqs.send(new ReceiveMessageCommand(params));
172
+ if (message && message.Messages && message.Messages.length > 0) {
173
+ for (const m of message.Messages) {
174
+ //for (let i = 0; i < message.Messages.length; i++) {
175
+ //const m: Message = message.Messages[i];
176
+ try {
177
+ const parsedBody: InternalBackgroundEntry<any> = JSON.parse(m.Body);
178
+ if (!parsedBody.type) {
179
+ Logger.warn('Dropping invalid background entry : %j', parsedBody);
180
+ } else {
181
+ rval.push(parsedBody);
182
+ }
183
+
184
+ Logger.debug('Removing message from queue');
185
+ const delParams = {
186
+ QueueUrl: this.awsConfig.queueUrl,
187
+ ReceiptHandle: m.ReceiptHandle,
188
+ };
189
+ const delResult: DeleteMessageCommandOutput = await this.sqs.send(new DeleteMessageCommand(delParams));
190
+ Logger.silly('Delete result : %j', delResult);
191
+ } catch (err) {
192
+ Logger.warn('Error parsing message, dropping : %j', m, err);
193
+ }
194
+ }
195
+ } else {
196
+ Logger.debug('No messages found (likely end of recursion)');
197
+ }
198
+
199
+ return rval;
200
+ }
201
+ }