@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,314 @@
1
+ import { EpsilonRouter } from './epsilon-router.js';
2
+ import { MisconfiguredError } from '../error/misconfigured-error.js';
3
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
4
+ import { RouteMapping } from './route-mapping.js';
5
+ import { RouteValidatorConfig } from './route-validator-config.js';
6
+ import { JwtTokenBase } from '@bitblit/ratchet-common/jwt/jwt-token-base';
7
+ import { BooleanRatchet } from '@bitblit/ratchet-common/lang/boolean-ratchet';
8
+ import { OpenApiDocument } from '../../config/open-api/open-api-document.js';
9
+ import { ModelValidator } from '@bitblit/ratchet-misc/model-validator/model-validator';
10
+ import { BackgroundHttpAdapterHandler } from '../../background/background-http-adapter-handler.js';
11
+ import { HandlerFunction } from '../../config/http/handler-function.js';
12
+ import { HttpConfig } from '../../config/http/http-config.js';
13
+ import { AuthorizerFunction } from '../../config/http/authorizer-function.js';
14
+ import { HttpProcessingConfig } from '../../config/http/http-processing-config.js';
15
+ import { NullReturnedObjectHandling } from '../../config/http/null-returned-object-handling.js';
16
+ import { MappedHttpProcessingConfig } from '../../config/http/mapped-http-processing-config.js';
17
+ import { BuiltInFilters } from '../../built-in/http/built-in-filters.js';
18
+ import { WebTokenManipulator } from '../auth/web-token-manipulator.js';
19
+ import { BuiltInHandlers } from '../../built-in/http/built-in-handlers.js';
20
+ import { FilterFunction } from '../../config/http/filter-function.js';
21
+ import { BuiltInAuthFilters } from '../../built-in/http/built-in-auth-filters.js';
22
+ import { LogLevelManipulationFilter } from '../../built-in/http/log-level-manipulation-filter.js';
23
+
24
+ /**
25
+ * Endpoints about the api itself
26
+ */
27
+ export class RouterUtil {
28
+ // Prevent instantiation
29
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
30
+ private constructor() {}
31
+
32
+ public static defaultAuthenticationHeaderParsingEpsilonPreFilters(
33
+ webTokenManipulator: WebTokenManipulator<JwtTokenBase>,
34
+ ): FilterFunction[] {
35
+ return [
36
+ (fCtx) => BuiltInAuthFilters.parseAuthorizationHeader(fCtx, webTokenManipulator),
37
+ (fCtx) => BuiltInAuthFilters.applyOpenApiAuthorization(fCtx),
38
+ ].concat(RouterUtil.defaultEpsilonPreFilters());
39
+ }
40
+
41
+ public static defaultEpsilonPreFilters(): FilterFunction[] {
42
+ return [
43
+ (fCtx) => BuiltInFilters.autoRespondToOptionsRequestWithCors(fCtx),
44
+ (fCtx) => BuiltInFilters.ensureEventMaps(fCtx),
45
+ (fCtx) => LogLevelManipulationFilter.setLogLevelForTransaction(fCtx),
46
+ (fCtx) => BuiltInFilters.parseJsonBodyToObject(fCtx),
47
+ (fCtx) => BuiltInFilters.fixStillEncodedQueryParams(fCtx),
48
+ (fCtx) => BuiltInFilters.uriDecodeQueryParams(fCtx),
49
+ (fCtx) => BuiltInFilters.disallowStringNullAsPathParameter(fCtx),
50
+ (fCtx) => BuiltInFilters.disallowStringNullAsQueryStringParameter(fCtx),
51
+ (fCtx) => BuiltInFilters.validateInboundBody(fCtx),
52
+ (fCtx) => BuiltInFilters.validateInboundQueryParams(fCtx),
53
+ (fCtx) => BuiltInFilters.validateInboundQueryParams(fCtx),
54
+ ];
55
+ }
56
+
57
+ public static defaultEpsilonPostFilters(): FilterFunction[] {
58
+ return [
59
+ (fCtx) => BuiltInFilters.validateOutboundResponse(fCtx),
60
+ (fCtx) => BuiltInFilters.addAWSRequestIdHeader(fCtx),
61
+ (fCtx) => BuiltInFilters.addAllowReflectionCORSHeaders(fCtx),
62
+ (fCtx) => BuiltInFilters.applyGzipIfPossible(fCtx),
63
+ (fCtx) => BuiltInFilters.checkMaximumLambdaBodySize(fCtx),
64
+ (fCtx) => LogLevelManipulationFilter.clearLogLevelForTransaction(fCtx),
65
+ ];
66
+ }
67
+
68
+ public static defaultEpsilonErrorFilters(): FilterFunction[] {
69
+ return [
70
+ (fCtx) => BuiltInFilters.addAWSRequestIdHeader(fCtx),
71
+ (fCtx) => BuiltInFilters.addAllowReflectionCORSHeaders(fCtx),
72
+ (fCtx) => LogLevelManipulationFilter.clearLogLevelForTransaction(fCtx),
73
+ ];
74
+ }
75
+
76
+ public static defaultHttpMetaProcessingConfigWithAuthenticationHeaderParsing(
77
+ webTokenManipulator: WebTokenManipulator<JwtTokenBase>,
78
+ ): HttpProcessingConfig {
79
+ const defaults: HttpProcessingConfig = {
80
+ configName: 'EpsilonDefaultHttpMetaProcessingConfig',
81
+ timeoutMS: 30_000,
82
+ overrideAuthorizerName: null,
83
+ preFilters: RouterUtil.defaultAuthenticationHeaderParsingEpsilonPreFilters(webTokenManipulator),
84
+ postFilters: RouterUtil.defaultEpsilonPostFilters(),
85
+ errorFilters: RouterUtil.defaultEpsilonErrorFilters(),
86
+ nullReturnedObjectHandling: NullReturnedObjectHandling.Return404NotFoundResponse,
87
+ };
88
+ return defaults;
89
+ }
90
+
91
+ public static defaultHttpMetaProcessingConfig(): HttpProcessingConfig {
92
+ const defaults: HttpProcessingConfig = {
93
+ configName: 'EpsilonDefaultHttpMetaProcessingConfig',
94
+ timeoutMS: 30_000,
95
+ overrideAuthorizerName: null,
96
+ preFilters: RouterUtil.defaultEpsilonPreFilters(),
97
+ postFilters: RouterUtil.defaultEpsilonPostFilters(),
98
+ errorFilters: RouterUtil.defaultEpsilonErrorFilters(),
99
+ nullReturnedObjectHandling: NullReturnedObjectHandling.Return404NotFoundResponse,
100
+ };
101
+ return defaults;
102
+ }
103
+
104
+ public static assignDefaultsOnHttpConfig(cfg: HttpConfig): HttpConfig {
105
+ const defaults: HttpConfig = {
106
+ handlers: new Map<string, HandlerFunction<any>>(),
107
+ authorizers: new Map<string, AuthorizerFunction>(),
108
+ defaultMetaHandling: this.defaultHttpMetaProcessingConfig(),
109
+ staticContentRoutes: {},
110
+ prefixesToStripBeforeRouteMatch: [],
111
+ filterHandledRouteMatches: ['options .*'], // Ignore all Options since they are handled by the default prefilter
112
+ };
113
+ const rval: HttpConfig = Object.assign({}, defaults, cfg || {});
114
+ return rval;
115
+ }
116
+
117
+ // Search the overrides in order to find a match, otherwise return default
118
+ public static findApplicableMeta(httpConfig: HttpConfig, method: string, path: string): HttpProcessingConfig {
119
+ let rval: HttpProcessingConfig = null;
120
+ if (httpConfig?.overrideMetaHandling) {
121
+ for (let i = 0; i < httpConfig.overrideMetaHandling.length && !rval; i++) {
122
+ const test: MappedHttpProcessingConfig = httpConfig.overrideMetaHandling[i];
123
+ if (
124
+ !test.methods ||
125
+ test.methods.length === 0 ||
126
+ test.methods.map((s) => s.toLocaleLowerCase()).includes(method.toLocaleLowerCase())
127
+ ) {
128
+ const matches: boolean = !!path.match(test.pathRegex); // .match(path);
129
+ if ((matches && !test.invertPathMatching) || (!matches && test.invertPathMatching)) {
130
+ rval = test.config;
131
+ }
132
+ }
133
+ }
134
+ }
135
+ if (!rval) {
136
+ rval = httpConfig.defaultMetaHandling || RouterUtil.defaultHttpMetaProcessingConfig(); // If nothing found, use epsilon defaults
137
+ }
138
+
139
+ return rval;
140
+ }
141
+
142
+ // Parses an open api file to create a router config
143
+ public static openApiYamlToRouterConfig(
144
+ httpConfig: HttpConfig,
145
+ openApiDoc: OpenApiDocument,
146
+ modelValidator: ModelValidator,
147
+ backgroundHttpAdapterHandler: BackgroundHttpAdapterHandler,
148
+ ): EpsilonRouter {
149
+ if (!openApiDoc || !httpConfig) {
150
+ throw new MisconfiguredError('Cannot configure, missing either yaml or cfg');
151
+ }
152
+ const rval: EpsilonRouter = {
153
+ routes: [],
154
+ openApiModelValidator: modelValidator,
155
+ config: RouterUtil.assignDefaultsOnHttpConfig(httpConfig),
156
+ };
157
+
158
+ if (openApiDoc?.components?.securitySchemes) {
159
+ // Just validation, nothing to wire here
160
+ Object.keys(openApiDoc.components.securitySchemes).forEach((sk) => {
161
+ if (!rval.config.authorizers || !rval.config.authorizers.get(sk)) {
162
+ throw new MisconfiguredError().withFormattedErrorMessage('Doc requires authorizer %s but not found in map', sk);
163
+ }
164
+ });
165
+ }
166
+
167
+ const missingPaths: string[] = [];
168
+ const filterHandledPathMatches: string[] = httpConfig.filterHandledRouteMatches || [];
169
+
170
+ if (openApiDoc?.paths) {
171
+ Object.keys(openApiDoc.paths).forEach((path) => {
172
+ Object.keys(openApiDoc.paths[path]).forEach((method) => {
173
+ const convertedPath: string = RouterUtil.openApiPathToRouteParserPath(path);
174
+ const finder: string = method + ' ' + path;
175
+ const applicableMeta: HttpProcessingConfig = RouterUtil.findApplicableMeta(httpConfig, method, path);
176
+
177
+ const entry: any = openApiDoc.paths[path][method];
178
+ const isBackgroundEndpoint: boolean = path.startsWith(backgroundHttpAdapterHandler.httpSubmissionPath);
179
+ const isBackgroundMetaEndpoint: boolean = path === backgroundHttpAdapterHandler.httpMetaEndpoint;
180
+ const isBackgroundStatusEndpoint: boolean = path === backgroundHttpAdapterHandler.httpStatusPath;
181
+ // Auto-assign the background handler endpoints
182
+ if (isBackgroundEndpoint) {
183
+ rval.config.handlers.set(finder, (evt, ctx) => backgroundHttpAdapterHandler.handleBackgroundSubmission(evt, ctx));
184
+ }
185
+ if (isBackgroundMetaEndpoint) {
186
+ rval.config.handlers.set(finder, (evt, ctx) => backgroundHttpAdapterHandler.handleBackgroundMetaRequest(evt, ctx));
187
+ }
188
+ if (isBackgroundStatusEndpoint) {
189
+ rval.config.handlers.set(finder, (evt, ctx) => backgroundHttpAdapterHandler.handleBackgroundStatusRequest(evt, ctx));
190
+ }
191
+
192
+ if (!rval.config.handlers || !rval.config.handlers.get(finder)) {
193
+ const match: string = filterHandledPathMatches.find((reg) => finder.match(reg));
194
+ if (match) {
195
+ Logger.debug('Adding filter-handled handler for %s', finder);
196
+ // Insert a placeholder for these, which still handles them in runtime if the filter is misconfigured
197
+ rval.config.handlers.set(finder, (evt) => BuiltInHandlers.expectedHandledByFilter(evt));
198
+ } else {
199
+ missingPaths.push(finder);
200
+ }
201
+ }
202
+
203
+ if (entry && entry['security'] && entry['security'].length > 1) {
204
+ throw new MisconfiguredError('Epsilon does not currently support multiple security (path was ' + finder + ')');
205
+ }
206
+ const authorizerName: string = entry['security'] && entry['security'].length == 1 ? Object.keys(entry['security'][0])[0] : null;
207
+
208
+ const newRoute: RouteMapping = {
209
+ path: convertedPath,
210
+ method: method,
211
+ function: rval.config.handlers.get(finder),
212
+ authorizerName: applicableMeta.overrideAuthorizerName || authorizerName,
213
+ metaProcessingConfig: applicableMeta,
214
+ validation: null,
215
+ outboundValidation: null,
216
+ };
217
+
218
+ // Add inbound validation, if available
219
+ if (
220
+ entry['requestBody'] &&
221
+ entry['requestBody']['content'] &&
222
+ entry['requestBody']['content']['application/json'] &&
223
+ entry['requestBody']['content']['application/json']['schema']
224
+ ) {
225
+ // TODO: this is brittle as hell, need to firm up
226
+ const schema: any = entry['requestBody']['content'];
227
+ Logger.silly('Applying schema %j to %s', schema, finder);
228
+
229
+ const modelName = this.findAndValidateModelName(
230
+ method,
231
+ path,
232
+ schema,
233
+ rval.config.overrideModelValidator || rval.openApiModelValidator,
234
+ );
235
+ const required: boolean = BooleanRatchet.parseBool(entry['requestBody']['required']);
236
+ const validation: RouteValidatorConfig = {
237
+ extraPropertiesAllowed: true,
238
+ emptyAllowed: !required,
239
+ modelName: modelName,
240
+ } as RouteValidatorConfig;
241
+
242
+ newRoute.validation = validation;
243
+ }
244
+
245
+ // Add outbound validation, if available
246
+ if (
247
+ entry['responses'] &&
248
+ entry['responses']['200'] &&
249
+ entry['responses']['200']['content'] &&
250
+ entry['responses']['200']['content']['application/json'] &&
251
+ entry['responses']['200']['content']['application/json']['schema']
252
+ ) {
253
+ // TODO: this is brittle as hell, need to firm up
254
+ const schema: any = entry['responses']['200']['content'];
255
+ Logger.silly('Applying schema %j to %s', schema, finder);
256
+ const modelName = this.findAndValidateModelName(
257
+ method,
258
+ path,
259
+ schema,
260
+ rval.config.overrideModelValidator || rval.openApiModelValidator,
261
+ );
262
+ const validation: RouteValidatorConfig = {
263
+ extraPropertiesAllowed: false,
264
+ emptyAllowed: false, // Its a 200 response, must be non-null
265
+ modelName: modelName,
266
+ } as RouteValidatorConfig;
267
+
268
+ newRoute.outboundValidation = validation;
269
+ }
270
+
271
+ rval.routes.push(newRoute);
272
+ });
273
+ });
274
+ }
275
+
276
+ if (missingPaths.length > 0) {
277
+ throw new MisconfiguredError().withFormattedErrorMessage('Missing expected handlers : %j', missingPaths);
278
+ }
279
+
280
+ return rval;
281
+ }
282
+
283
+ private static findAndValidateModelName(method: string, path: string, schema: any, modelValidator: ModelValidator): string | undefined {
284
+ let rval: string | undefined = undefined;
285
+ const schemaPath: string = schema['application/json']['schema']['$ref'];
286
+ const inlinePath: string = schema['application/json']['schema']['type'];
287
+ if (schemaPath) {
288
+ rval = schemaPath.substring(schemaPath.lastIndexOf('/') + 1);
289
+ if (!modelValidator.fetchModel(rval)) {
290
+ throw new MisconfiguredError(`Path ${method} ${path} refers to schema ${rval} but its not in the schema section`);
291
+ }
292
+ } else if (inlinePath) {
293
+ rval = `${method}-${path}-requestBodyModel`;
294
+ const model = schema['application/json']['schema'];
295
+ modelValidator.addModel(rval, model);
296
+ }
297
+ return rval;
298
+ }
299
+
300
+ public static openApiPathToRouteParserPath(input: string): string {
301
+ let rval: string = input;
302
+
303
+ if (rval) {
304
+ let sIdx: number = rval.indexOf('{');
305
+ while (sIdx > -1) {
306
+ const eIdx: number = rval.indexOf('}');
307
+ rval = rval.substring(0, sIdx) + ':' + rval.substring(sIdx + 1, eIdx) + rval.substring(eIdx + 1);
308
+ sIdx = rval.indexOf('{');
309
+ }
310
+ }
311
+
312
+ return rval;
313
+ }
314
+ }
@@ -0,0 +1,99 @@
1
+ import { APIGatewayEvent, APIGatewayEventRequestContext, Context, ProxyResult } from 'aws-lambda';
2
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
3
+ import { EpsilonGlobalHandler } from '../epsilon-global-handler.js';
4
+ import { SampleServerComponents } from '../sample/sample-server-components.js';
5
+ import { LoggerLevelName } from '@bitblit/ratchet-common/logger/logger-level-name';
6
+ import { describe, expect, test } from 'vitest';
7
+
8
+ describe('#errorToProxyResult', function () {
9
+ /*
10
+ test('should set the default status code to 500', function() {
11
+
12
+ let err:Error = new BadRequestError('this is a test','a1','a2');
13
+ let res:ProxyResult = ResponseUtil.errorToProxyResult(err);
14
+
15
+ expect(res.statusCode).toEqual(400);
16
+ });
17
+
18
+ test('should parse a request body correctly', function() {
19
+
20
+ let evt:APIGatewayEvent = {
21
+ httpMethod:'post',
22
+ path:'/test',
23
+ pathParameters: null,
24
+ queryStringParameters: null,
25
+ stageVariables:null,
26
+ requestContext:{} as APIGatewayEventRequestContext,
27
+ resource:'/test',
28
+ headers: {
29
+ 'content-type':'application/json'
30
+ },
31
+ isBase64Encoded: true,
32
+ body: 'ew0KICJtZXNzYWdlIiA6ICJ0aGlzIGlzIGEgdGVzdCIsDQogIm51bWJlciIgOiAxDQp9'
33
+
34
+ } as APIGatewayEvent;
35
+
36
+ let body = EventUtil.bodyObject(evt);
37
+ expect(body).toBeTruthy();
38
+ expect(body.message).toEqual('this is a test');
39
+ expect(body.number).toEqual(1);
40
+
41
+ });
42
+
43
+ test('should parse a request body correctly part 2', function() {
44
+
45
+ let evt:APIGatewayEvent = {
46
+ httpMethod:'post',
47
+ path:'/test',
48
+ pathParameters: null,
49
+ queryStringParameters: null,
50
+ stageVariables:null,
51
+ requestContext:{} as APIGatewayEventRequestContext,
52
+ resource:'/test',
53
+ headers: {
54
+ 'content-type':'application/json; charset=UTF-8'
55
+ },
56
+ isBase64Encoded: true,
57
+ body: 'ew0KICJtZXNzYWdlIiA6ICJ0aGlzIGlzIGEgdGVzdCIsDQogIm51bWJlciIgOiAxDQp9'
58
+
59
+ } as APIGatewayEvent;
60
+
61
+ let body = EventUtil.bodyObject(evt);
62
+ expect(body).toBeTruthy();
63
+ expect(body.message).toEqual('this is a test');
64
+ expect(body.number).toEqual(1);
65
+ });
66
+ */
67
+
68
+ test('should gzip responses correctly', async () => {
69
+ const inst: EpsilonGlobalHandler = await SampleServerComponents.createSampleEpsilonGlobalHandler('jest-gzip');
70
+
71
+ expect(inst.epsilon.modelValidator).toBeTruthy();
72
+
73
+ const evt: APIGatewayEvent = {
74
+ httpMethod: 'get',
75
+ multiValueHeaders: {},
76
+ multiValueQueryStringParameters: {},
77
+ path: '/meta/server',
78
+ pathParameters: null,
79
+ queryStringParameters: null,
80
+ stageVariables: null,
81
+ requestContext: {} as APIGatewayEventRequestContext,
82
+ resource: '/meta/server',
83
+ headers: {
84
+ 'content-type': 'application/json; charset=UTF-8',
85
+ 'accept-encoding': 'gzip, deflate, br',
86
+ },
87
+ isBase64Encoded: true,
88
+ body: null,
89
+ } as APIGatewayEvent;
90
+
91
+ Logger.setLevel(LoggerLevelName.silly);
92
+ const result: ProxyResult = await inst.epsilon.webHandler.processEvent(evt, {} as Context);
93
+
94
+ expect(result).toBeTruthy();
95
+ expect(result.isBase64Encoded).toEqual(true);
96
+ expect(result.headers).toBeTruthy();
97
+ expect(result.headers['Content-Encoding']).toEqual('gzip');
98
+ });
99
+ });
@@ -0,0 +1,157 @@
1
+ import { EpsilonRouter } from './route/epsilon-router.js';
2
+ import { APIGatewayEvent, Context, ProxyResult } from 'aws-lambda';
3
+
4
+ import { RequireRatchet } from '@bitblit/ratchet-common/lang/require-ratchet';
5
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
6
+ import { StringRatchet } from '@bitblit/ratchet-common/lang/string-ratchet';
7
+ import { RestfulApiHttpError } from '@bitblit/ratchet-common/network/restful-api-http-error';
8
+ import { ResponseUtil } from './response-util.js';
9
+ import { ExtendedAPIGatewayEvent } from '../config/http/extended-api-gateway-event.js';
10
+ import { BuiltInFilters } from '../built-in/http/built-in-filters.js';
11
+ import { HttpProcessingConfig } from '../config/http/http-processing-config.js';
12
+ import { FilterFunction } from '../config/http/filter-function.js';
13
+ import { RunHandlerAsFilter } from '../built-in/http/run-handler-as-filter.js';
14
+ import { FilterChainContext } from '../config/http/filter-chain-context.js';
15
+ import { ContextUtil } from '../util/context-util.js';
16
+ import { EpsilonLambdaEventHandler } from '../config/epsilon-lambda-event-handler.js';
17
+ import { LambdaEventDetector } from '@bitblit/ratchet-aws/lambda/lambda-event-detector';
18
+ import { RouteAndParse } from './route/route-and-parse.js';
19
+ import Route from 'route-parser';
20
+
21
+ /**
22
+ * This class functions as the adapter from a default lambda function to the handlers exposed via Epsilon
23
+ */
24
+ export class WebHandler implements EpsilonLambdaEventHandler<APIGatewayEvent> {
25
+ public static readonly MAXIMUM_LAMBDA_BODY_SIZE_BYTES: number = 1024 * 1024 * 5 - 1024 * 100; // 5Mb - 100k buffer
26
+
27
+ constructor(private routerConfig: EpsilonRouter) {
28
+ RequireRatchet.notNullOrUndefined(routerConfig);
29
+ }
30
+
31
+ public get router(): EpsilonRouter {
32
+ return this.routerConfig;
33
+ }
34
+
35
+ public extractLabel(evt: APIGatewayEvent, _context: Context): string {
36
+ return 'WEB:' + StringRatchet.trimToEmpty(evt.httpMethod).toUpperCase() + ':' + evt.path;
37
+ }
38
+
39
+ public handlesEvent(evt: any): boolean {
40
+ return LambdaEventDetector.isValidApiGatewayEvent(evt);
41
+ }
42
+
43
+ public async processEvent(event: APIGatewayEvent, context: Context): Promise<ProxyResult> {
44
+ if (!this.routerConfig) {
45
+ throw new Error('Router config not found');
46
+ }
47
+ const asExtended: ExtendedAPIGatewayEvent = Object.assign(
48
+ {},
49
+ { parsedBody: null, authorization: null, convertedFromV2Event: false },
50
+ event,
51
+ );
52
+ const rval: ProxyResult = await this.openApiLambdaHandler(asExtended, context);
53
+ ContextUtil.addTraceToProxyResult(rval);
54
+ Logger.updateTracePrefix(null); // Just in case it was set
55
+ return rval;
56
+ }
57
+
58
+ public async openApiLambdaHandler(evt: ExtendedAPIGatewayEvent, context: Context): Promise<ProxyResult> {
59
+ const rm: RouteAndParse = this.findBestMatchingRoute(evt);
60
+ const procConfig: HttpProcessingConfig = rm?.mapping?.metaProcessingConfig
61
+ ? rm.mapping.metaProcessingConfig
62
+ : this.routerConfig.config.defaultMetaHandling;
63
+ const fCtx: FilterChainContext = {
64
+ event: evt,
65
+ context: context,
66
+ result: null,
67
+ rawResult: null,
68
+ routeAndParse: rm,
69
+ modelValidator: this.routerConfig.config.overrideModelValidator || this.routerConfig.openApiModelValidator,
70
+ authenticators: this.routerConfig.config.authorizers,
71
+ };
72
+
73
+ try {
74
+ let filterChain: FilterFunction[] = Object.assign([], procConfig.preFilters || []);
75
+ RunHandlerAsFilter.addRunHandlerAsFilterToList(filterChain, rm);
76
+ filterChain = filterChain.concat(procConfig.postFilters || []);
77
+ await BuiltInFilters.combineFilters(fCtx, filterChain);
78
+ } catch (err) {
79
+ // Convert to an epsilon error
80
+ const wrapper: RestfulApiHttpError = RestfulApiHttpError.wrapError(err as Error);
81
+ fCtx.result = ResponseUtil.errorResponse(wrapper);
82
+ try {
83
+ await BuiltInFilters.combineFilters(fCtx, procConfig.errorFilters);
84
+ } catch (convErr) {
85
+ Logger.error('REALLY BAD - FAILED WHILE PROCESSING ERROR FILTERS : %s', convErr);
86
+ }
87
+ }
88
+ return fCtx.result;
89
+ }
90
+
91
+ public findBestMatchingRoute(event: APIGatewayEvent): RouteAndParse {
92
+ let rval: RouteAndParse = null;
93
+ // See: https://www.npmjs.com/package/route-parser
94
+ const cleanPath: string = this.cleanPath(event);
95
+
96
+ // Filter routes to only matches
97
+ const methodLower: string = event.httpMethod.toLowerCase();
98
+ const matchRoutes: RouteAndParse[] = this.routerConfig.routes
99
+ .map((r) => {
100
+ let rval: RouteAndParse = null;
101
+ if (r.method && r.method.toLowerCase() === methodLower) {
102
+ const routeParser: Route = new Route(r.path);
103
+ const parsed: any = routeParser.match(cleanPath);
104
+ if (parsed !== false) {
105
+ rval = {
106
+ mapping: r,
107
+ route: routeParser,
108
+ parsed: parsed,
109
+ };
110
+ }
111
+ }
112
+ return rval;
113
+ })
114
+ .filter((r) => r != null);
115
+ // Pick the 'best' match
116
+ matchRoutes.sort((a, b) => {
117
+ return Object.keys(a.parsed).length - Object.keys(b.parsed).length;
118
+ });
119
+
120
+ rval = matchRoutes && matchRoutes.length > 0 ? matchRoutes[0] : null;
121
+
122
+ if (!rval) {
123
+ Logger.debug(
124
+ 'Failed to find handler for %s (cleaned path was %s, strip prefixes were %j)',
125
+ event.path,
126
+ cleanPath,
127
+ this.routerConfig.config.prefixesToStripBeforeRouteMatch,
128
+ );
129
+ }
130
+ return rval;
131
+ }
132
+
133
+ private cleanPath(event: APIGatewayEvent): string {
134
+ let rval: string = event.path;
135
+ // First, strip any leading /
136
+ while (rval.startsWith('/')) {
137
+ rval = rval.substring(1);
138
+ }
139
+ // If there are any listed prefixes, strip them
140
+ if (this.routerConfig.config.prefixesToStripBeforeRouteMatch) {
141
+ this.routerConfig.config.prefixesToStripBeforeRouteMatch.forEach((prefix) => {
142
+ if (rval.toLowerCase().startsWith(prefix.toLowerCase() + '/')) {
143
+ rval = rval.substring(prefix.length);
144
+ }
145
+ });
146
+ }
147
+
148
+ // Strip any more leading /
149
+ while (rval.startsWith('/')) {
150
+ rval = rval.substring(1);
151
+ }
152
+ // Finally, put back exactly 1 leading / to match what comes out of open api
153
+ rval = '/' + rval;
154
+
155
+ return rval;
156
+ }
157
+ }
@@ -0,0 +1,34 @@
1
+ import { APIGatewayEvent, APIGatewayProxyEventV2, Context, ProxyResult } from 'aws-lambda';
2
+ import { ExtendedAPIGatewayEvent } from '../config/http/extended-api-gateway-event.js';
3
+ import { AwsUtil } from '../util/aws-util.js';
4
+ import { EpsilonLambdaEventHandler } from '../config/epsilon-lambda-event-handler.js';
5
+ import { LambdaEventDetector } from '@bitblit/ratchet-aws/lambda/lambda-event-detector';
6
+ import { WebHandler } from './web-handler.js';
7
+
8
+ /**
9
+ * This class functions as the adapter from a default lambda function to the handlers exposed via Epsilon
10
+ */
11
+ export class WebV2Handler implements EpsilonLambdaEventHandler<APIGatewayProxyEventV2> {
12
+ constructor(private webHandler: WebHandler) {}
13
+
14
+ public extractLabel(evt: APIGatewayProxyEventV2, context: Context): string {
15
+ let rval: string = this.webHandler.extractLabel(AwsUtil.apiGatewayV2ToApiGatewayV1(evt), context);
16
+ rval = rval.replace('WEB:', 'WEB2:');
17
+ return rval;
18
+ }
19
+
20
+ public handlesEvent(evt: any): boolean {
21
+ return LambdaEventDetector.isValidApiGatewayV2WithRequestContextEvent(evt);
22
+ }
23
+
24
+ public async processEvent(evt: APIGatewayProxyEventV2, context: Context): Promise<ProxyResult> {
25
+ const conv: APIGatewayEvent = AwsUtil.apiGatewayV2ToApiGatewayV1(evt);
26
+ const asExtended: ExtendedAPIGatewayEvent = Object.assign(
27
+ {},
28
+ { parsedBody: null, authorization: null, convertedFromV2Event: true },
29
+ conv,
30
+ );
31
+ const rval: ProxyResult = await this.webHandler.openApiLambdaHandler(asExtended, context);
32
+ return rval;
33
+ }
34
+ }
@@ -0,0 +1,8 @@
1
+ export interface InterApiEntry<T> {
2
+ source: string;
3
+ type: string;
4
+ data: T;
5
+
6
+ traceId?: string;
7
+ traceDepth?: number;
8
+ }