quidproquo-actionprocessor-awslambda 0.1.2 → 0.1.3
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.
- package/lib/commonjs/awsNamingUtils.d.ts +2 -0
- package/lib/commonjs/awsNamingUtils.js +24 -1
- package/lib/commonjs/getActionProcessor/core/apiKeyValidation/getApiKeyValidationValidateActionProcessor.js +15 -11
- package/lib/commonjs/getActionProcessor/core/event/apiGatwayEvent/api/getEventGetRecordsActionProcessor.js +12 -1
- package/lib/commonjs/getActionProcessor/core/event/utils/parseMultipartFormData.d.ts +7 -1
- package/lib/commonjs/getActionProcessor/core/event/utils/parseMultipartFormData.js +77 -8
- package/lib/commonjs/getActionProcessor/core/index.d.ts +1 -0
- package/lib/commonjs/getActionProcessor/core/index.js +3 -1
- package/lib/commonjs/getActionProcessor/core/metric/getMetricPutActionProcessor.d.ts +2 -0
- package/lib/commonjs/getActionProcessor/core/metric/getMetricPutActionProcessor.js +51 -0
- package/lib/commonjs/getActionProcessor/core/metric/index.d.ts +2 -0
- package/lib/commonjs/getActionProcessor/core/metric/index.js +17 -0
- package/lib/commonjs/lambdas/dynamicModuleLoader/dynamicModuleLoader.js +8 -1
- package/lib/commonjs/logic/apiGateway/getApiKeyValue.d.ts +1 -0
- package/lib/commonjs/logic/apiGateway/{getApiKeys.js → getApiKeyValue.js} +10 -14
- package/lib/commonjs/logic/federatedModuleStore/FederatedModuleLoadError.d.ts +10 -0
- package/lib/commonjs/logic/federatedModuleStore/FederatedModuleLoadError.js +26 -0
- package/lib/commonjs/logic/federatedModuleStore/FederatedModuleStoreManifest.d.ts +8 -0
- package/lib/commonjs/logic/federatedModuleStore/FederatedModuleStoreManifest.js +2 -0
- package/lib/commonjs/logic/federatedModuleStore/index.d.ts +4 -0
- package/lib/commonjs/logic/federatedModuleStore/index.js +20 -0
- package/lib/commonjs/logic/federatedModuleStore/loadFederatedStory.d.ts +3 -0
- package/lib/commonjs/logic/federatedModuleStore/loadFederatedStory.js +295 -0
- package/lib/commonjs/logic/federatedModuleStore/sharedPackages.d.ts +1 -0
- package/lib/commonjs/logic/federatedModuleStore/sharedPackages.js +10 -0
- package/lib/commonjs/logic/index.d.ts +1 -0
- package/lib/commonjs/logic/index.js +1 -0
- package/lib/esm/awsNamingUtils.d.ts +2 -0
- package/lib/esm/awsNamingUtils.js +20 -0
- package/lib/esm/getActionProcessor/core/apiKeyValidation/getApiKeyValidationValidateActionProcessor.js +16 -12
- package/lib/esm/getActionProcessor/core/event/apiGatwayEvent/api/getEventGetRecordsActionProcessor.js +14 -3
- package/lib/esm/getActionProcessor/core/event/utils/parseMultipartFormData.d.ts +7 -1
- package/lib/esm/getActionProcessor/core/event/utils/parseMultipartFormData.js +75 -7
- package/lib/esm/getActionProcessor/core/index.d.ts +1 -0
- package/lib/esm/getActionProcessor/core/index.js +3 -0
- package/lib/esm/getActionProcessor/core/metric/getMetricPutActionProcessor.d.ts +2 -0
- package/lib/esm/getActionProcessor/core/metric/getMetricPutActionProcessor.js +43 -0
- package/lib/esm/getActionProcessor/core/metric/index.d.ts +2 -0
- package/lib/esm/getActionProcessor/core/metric/index.js +4 -0
- package/lib/esm/lambdas/dynamicModuleLoader/dynamicModuleLoader.js +9 -2
- package/lib/esm/logic/apiGateway/getApiKeyValue.d.ts +1 -0
- package/lib/esm/logic/apiGateway/getApiKeyValue.js +12 -0
- package/lib/esm/logic/federatedModuleStore/FederatedModuleLoadError.d.ts +10 -0
- package/lib/esm/logic/federatedModuleStore/FederatedModuleLoadError.js +23 -0
- package/lib/esm/logic/federatedModuleStore/FederatedModuleStoreManifest.d.ts +8 -0
- package/lib/esm/logic/federatedModuleStore/FederatedModuleStoreManifest.js +1 -0
- package/lib/esm/logic/federatedModuleStore/index.d.ts +4 -0
- package/lib/esm/logic/federatedModuleStore/index.js +4 -0
- package/lib/esm/logic/federatedModuleStore/loadFederatedStory.d.ts +3 -0
- package/lib/esm/logic/federatedModuleStore/loadFederatedStory.js +246 -0
- package/lib/esm/logic/federatedModuleStore/sharedPackages.d.ts +1 -0
- package/lib/esm/logic/federatedModuleStore/sharedPackages.js +7 -0
- package/lib/esm/logic/index.d.ts +1 -0
- package/lib/esm/logic/index.js +1 -0
- package/package.json +7 -6
- package/lib/commonjs/logic/apiGateway/getApiKeys.d.ts +0 -2
- package/lib/esm/logic/apiGateway/getApiKeys.d.ts +0 -2
- package/lib/esm/logic/apiGateway/getApiKeys.js +0 -17
|
@@ -17,7 +17,9 @@ export declare const getCFExportNameApiKeyIdFromConfig: (apiKeyName: string, qpq
|
|
|
17
17
|
export declare const getCFExportNameSnsTopicArnFromConfig: (eventBusName: string, qpqConfig: QPQConfig, applicationOverride?: string) => string;
|
|
18
18
|
export declare const getCFExportNameDistributionIdArnFromConfig: (webEntryName: string, qpqConfig: QPQConfig, serviceOverride?: string, applicationOverride?: string) => string;
|
|
19
19
|
export declare const getCFExportNameWebsocketApiIdFromConfig: (websocketApiName: string, qpqConfig: QPQConfig) => string;
|
|
20
|
+
export declare const getWebsocketApiIdSsmParameterName: (websocketApiName: string, qpqConfig: QPQConfig) => string;
|
|
20
21
|
export declare const getEventBusSnsTopicArn: (eventBusName: string, qpqConfig: QPQConfig, module: string, environment: string, application: string, feature?: string) => string;
|
|
22
|
+
export declare const getAccountStackName: () => string;
|
|
21
23
|
export declare const getBaseStackName: (qpqConfig: QPQConfig) => string;
|
|
22
24
|
export declare const getInfStackName: (qpqConfig: QPQConfig) => string;
|
|
23
25
|
export declare const getWebStackName: (qpqConfig: QPQConfig) => string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getDomainStackName = exports.getBootstrapStackName = exports.getApiStackName = exports.getWebStackName = exports.getInfStackName = exports.getBaseStackName = exports.getEventBusSnsTopicArn = exports.getCFExportNameWebsocketApiIdFromConfig = exports.getCFExportNameDistributionIdArnFromConfig = exports.getCFExportNameSnsTopicArnFromConfig = exports.getCFExportNameApiKeyIdFromConfig = exports.getCFExportNameUserPoolClientIdFromConfig = exports.getCFExportNameCachePolicyIdFromConfig = exports.getCFExportNameUserPoolIdFromConfig = exports.getKvsDynamoTableNameFromConfig = exports.getQpqRuntimeResourceNameFromConfig = exports.getQpqRuntimeResourceName = exports.getConfigRuntimeResourceNameFromConfigWithServiceOverride = exports.getConfigRuntimeBootstrapResourceNameFromConfig = exports.resolveConfigRuntimeResourceNameFromConfig = exports.getConfigRuntimeResourceNameFromConfig = exports.getConfigRuntimeBootstrapResourceName = exports.getConfigRuntimeResourceName = exports.getGlobalQpqRuntimeResourceName = exports.getGlobalConfigRuntimeResourceName = void 0;
|
|
3
|
+
exports.getDomainStackName = exports.getBootstrapStackName = exports.getApiStackName = exports.getWebStackName = exports.getInfStackName = exports.getBaseStackName = exports.getAccountStackName = exports.getEventBusSnsTopicArn = exports.getWebsocketApiIdSsmParameterName = exports.getCFExportNameWebsocketApiIdFromConfig = exports.getCFExportNameDistributionIdArnFromConfig = exports.getCFExportNameSnsTopicArnFromConfig = exports.getCFExportNameApiKeyIdFromConfig = exports.getCFExportNameUserPoolClientIdFromConfig = exports.getCFExportNameCachePolicyIdFromConfig = exports.getCFExportNameUserPoolIdFromConfig = exports.getKvsDynamoTableNameFromConfig = exports.getQpqRuntimeResourceNameFromConfig = exports.getQpqRuntimeResourceName = exports.getConfigRuntimeResourceNameFromConfigWithServiceOverride = exports.getConfigRuntimeBootstrapResourceNameFromConfig = exports.resolveConfigRuntimeResourceNameFromConfig = exports.getConfigRuntimeResourceNameFromConfig = exports.getConfigRuntimeBootstrapResourceName = exports.getConfigRuntimeResourceName = exports.getGlobalQpqRuntimeResourceName = exports.getGlobalConfigRuntimeResourceName = void 0;
|
|
4
4
|
const quidproquo_config_aws_1 = require("quidproquo-config-aws");
|
|
5
5
|
const quidproquo_core_1 = require("quidproquo-core");
|
|
6
6
|
const quidproquo_webserver_1 = require("quidproquo-webserver");
|
|
@@ -156,6 +156,21 @@ const getCFExportNameWebsocketApiIdFromConfig = (websocketApiName, qpqConfig) =>
|
|
|
156
156
|
return (0, exports.getQpqRuntimeResourceName)(resourceName, application, service, environment, feature, 'websocket-api-id-export');
|
|
157
157
|
};
|
|
158
158
|
exports.getCFExportNameWebsocketApiIdFromConfig = getCFExportNameWebsocketApiIdFromConfig;
|
|
159
|
+
// SSM parameter holding a websocket api's AWS-generated id, written by the owning service's
|
|
160
|
+
// inf stack. Referencing services resolve it at deploy time to build exact execute-api
|
|
161
|
+
// grants (deploy the owning inf stack first - same owner-publishes/others-read pattern as
|
|
162
|
+
// the domain certificate arn parameter).
|
|
163
|
+
const getWebsocketApiIdSsmParameterName = (websocketApiName, qpqConfig) => {
|
|
164
|
+
var _a, _b, _c, _d, _e;
|
|
165
|
+
const websocketApiConfig = quidproquo_webserver_1.qpqWebServerUtils.getWebsocketEntryByApiName(websocketApiName, qpqConfig);
|
|
166
|
+
const application = ((_a = websocketApiConfig.owner) === null || _a === void 0 ? void 0 : _a.application) || quidproquo_core_1.qpqCoreUtils.getApplicationName(qpqConfig);
|
|
167
|
+
const service = ((_b = websocketApiConfig.owner) === null || _b === void 0 ? void 0 : _b.module) || quidproquo_core_1.qpqCoreUtils.getApplicationModuleName(qpqConfig);
|
|
168
|
+
const environment = ((_c = websocketApiConfig.owner) === null || _c === void 0 ? void 0 : _c.environment) || quidproquo_core_1.qpqCoreUtils.getApplicationModuleEnvironment(qpqConfig);
|
|
169
|
+
const feature = ((_d = websocketApiConfig.owner) === null || _d === void 0 ? void 0 : _d.feature) || quidproquo_core_1.qpqCoreUtils.getApplicationModuleFeature(qpqConfig);
|
|
170
|
+
const resourceName = ((_e = websocketApiConfig.owner) === null || _e === void 0 ? void 0 : _e.resourceNameOverride) || websocketApiName;
|
|
171
|
+
return `/qpq/websocket/api-id/${(0, exports.getConfigRuntimeResourceName)(resourceName, application, service, environment, feature)}`;
|
|
172
|
+
};
|
|
173
|
+
exports.getWebsocketApiIdSsmParameterName = getWebsocketApiIdSsmParameterName;
|
|
159
174
|
const getEventBusSnsTopicArn = (eventBusName, qpqConfig, module, environment, application, feature) => {
|
|
160
175
|
const topicName = (0, exports.getConfigRuntimeResourceName)(eventBusName, application, module, environment, feature);
|
|
161
176
|
const accountInfo = (0, quidproquo_config_aws_1.getAwsServiceAccountInfoByDeploymentInfo)(qpqConfig, module, environment, feature, application);
|
|
@@ -164,6 +179,14 @@ const getEventBusSnsTopicArn = (eventBusName, qpqConfig, module, environment, ap
|
|
|
164
179
|
return `arn:aws:sns:${region}:${awsAccountId}:${topicName}`;
|
|
165
180
|
};
|
|
166
181
|
exports.getEventBusSnsTopicArn = getEventBusSnsTopicArn;
|
|
182
|
+
// The account stack owns account-level resources (cloud trail, budgets, security services)
|
|
183
|
+
// so they never share an app's lifecycle. The name is deliberately static: stack names are
|
|
184
|
+
// already namespaced per account+region, and the resources it holds meter/audit the whole
|
|
185
|
+
// account - an app/environment segment would only fake a separation that doesn't exist.
|
|
186
|
+
// Everything in one account converges on this one stack, so exactly ONE repo/config must
|
|
187
|
+
// own it, and actor (feature) deploys must not deploy it.
|
|
188
|
+
const getAccountStackName = () => 'qpq-account';
|
|
189
|
+
exports.getAccountStackName = getAccountStackName;
|
|
167
190
|
const getBaseStackName = (qpqConfig) => {
|
|
168
191
|
const appName = quidproquo_core_1.qpqCoreUtils.getApplicationName(qpqConfig);
|
|
169
192
|
const moduleName = quidproquo_core_1.qpqCoreUtils.getApplicationModuleName(qpqConfig);
|
|
@@ -15,7 +15,8 @@ const quidproquo_core_1 = require("quidproquo-core");
|
|
|
15
15
|
const quidproquo_webserver_1 = require("quidproquo-webserver");
|
|
16
16
|
const crypto_1 = require("crypto");
|
|
17
17
|
const awsNamingUtils_1 = require("../../../awsNamingUtils");
|
|
18
|
-
const
|
|
18
|
+
const getApiKeyValue_1 = require("../../../logic/apiGateway/getApiKeyValue");
|
|
19
|
+
const getExportedValue_1 = require("../../../logic/cloudformation/getExportedValue");
|
|
19
20
|
// Constant-time string equality. `timingSafeEqual` throws on length mismatch,
|
|
20
21
|
// so guard the length check — the length itself isn't secret for API keys.
|
|
21
22
|
const safeEqual = (a, b) => {
|
|
@@ -28,17 +29,20 @@ const safeEqual = (a, b) => {
|
|
|
28
29
|
};
|
|
29
30
|
const getProcessApiKeyValidationValidate = (qpqConfig) => {
|
|
30
31
|
const region = quidproquo_config_aws_1.qpqConfigAwsUtils.getApplicationModuleDeployRegion(qpqConfig);
|
|
31
|
-
const application = quidproquo_core_1.qpqCoreUtils.getApplicationName(qpqConfig);
|
|
32
|
-
const service = quidproquo_core_1.qpqCoreUtils.getApplicationModuleName(qpqConfig);
|
|
33
|
-
const environment = quidproquo_core_1.qpqCoreUtils.getApplicationModuleEnvironment(qpqConfig);
|
|
34
|
-
const feature = quidproquo_core_1.qpqCoreUtils.getApplicationModuleFeature(qpqConfig);
|
|
35
32
|
return (_a) => __awaiter(void 0, [_a], void 0, function* ({ apiKeyValue, apiKeyReferences }) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
// Fetch each referenced key individually (id via CFN export, then GetApiKey) rather than
|
|
34
|
+
// listing every key in the account - keeps the IAM grant per-key instead of /apikeys-wide.
|
|
35
|
+
const realApiKeyValues = yield Promise.all(apiKeyReferences.map((apiKeyReference) => __awaiter(void 0, void 0, void 0, function* () {
|
|
36
|
+
try {
|
|
37
|
+
const apiKeyId = yield (0, getExportedValue_1.getExportedValue)((0, awsNamingUtils_1.getCFExportNameApiKeyIdFromConfig)(apiKeyReference.name, qpqConfig, apiKeyReference.serviceName, apiKeyReference.applicationName), region);
|
|
38
|
+
return yield (0, getApiKeyValue_1.getApiKeyValue)(region, apiKeyId);
|
|
39
|
+
}
|
|
40
|
+
catch (_a) {
|
|
41
|
+
// An unresolvable reference behaves like the old list-and-filter miss - no match
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
})));
|
|
45
|
+
const matched = realApiKeyValues.some((realApiKeyValue) => safeEqual(realApiKeyValue, apiKeyValue));
|
|
42
46
|
return (0, quidproquo_core_1.actionResult)(matched);
|
|
43
47
|
});
|
|
44
48
|
};
|
|
@@ -15,6 +15,7 @@ const quidproquo_webserver_1 = require("quidproquo-webserver");
|
|
|
15
15
|
const parseMultipartFormData_1 = require("../../utils/parseMultipartFormData");
|
|
16
16
|
const getProcessGetRecords = (qpqConfig) => {
|
|
17
17
|
const serviceName = quidproquo_core_1.qpqCoreUtils.getApplicationModuleName(qpqConfig);
|
|
18
|
+
const fileUploadSettings = quidproquo_webserver_1.qpqWebServerUtils.getFileUploadSettings(qpqConfig);
|
|
18
19
|
return (_a) => __awaiter(void 0, [_a], void 0, function* ({ eventParams: [apiGatewayEvent, context] }) {
|
|
19
20
|
// Initialize `path` by removing the service name prefix from `apiGatewayEvent.path`.
|
|
20
21
|
// This adjustment is necessary because the API gateway routes requests to services based on
|
|
@@ -35,7 +36,17 @@ const getProcessGetRecords = (qpqConfig) => {
|
|
|
35
36
|
};
|
|
36
37
|
// Transform the body if its a multipart/form-data
|
|
37
38
|
if ((quidproquo_webserver_1.qpqWebServerUtils.getHeaderValue('Content-Type', apiGatewayEvent.headers) || '').startsWith('multipart/form-data') && apiGatewayEvent.body) {
|
|
38
|
-
|
|
39
|
+
try {
|
|
40
|
+
internalEventRecord.files = yield (0, parseMultipartFormData_1.parseMultipartFormData)(apiGatewayEvent, fileUploadSettings);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
// Stamp the failure on the record so the auto-respond step returns the matching
|
|
44
|
+
// 4xx before the route story runs - throwing here would fail the whole event as a 5xx
|
|
45
|
+
internalEventRecord.fileUploadError =
|
|
46
|
+
error instanceof parseMultipartFormData_1.FileUploadValidationError
|
|
47
|
+
? { errorType: error.errorType, message: error.message }
|
|
48
|
+
: { errorType: quidproquo_webserver_1.FileUploadErrorTypeEnum.malformed, message: 'Unable to parse multipart/form-data body' };
|
|
49
|
+
}
|
|
39
50
|
}
|
|
40
51
|
return (0, quidproquo_core_1.actionResult)([internalEventRecord]);
|
|
41
52
|
});
|
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
import { QPQBinaryData } from 'quidproquo-core';
|
|
2
|
+
import { FileUploadErrorTypeEnum, FileUploadSettings } from 'quidproquo-webserver';
|
|
2
3
|
import { APIGatewayEvent } from 'aws-lambda';
|
|
3
|
-
export declare
|
|
4
|
+
export declare class FileUploadValidationError extends Error {
|
|
5
|
+
readonly errorType: FileUploadErrorTypeEnum;
|
|
6
|
+
constructor(errorType: FileUploadErrorTypeEnum, message: string);
|
|
7
|
+
}
|
|
8
|
+
export declare const sanitizeFilename: (filename?: string) => string;
|
|
9
|
+
export declare const parseMultipartFormData: (event: APIGatewayEvent, fileUploadSettings: FileUploadSettings) => Promise<QPQBinaryData[]>;
|
|
@@ -3,42 +3,111 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.parseMultipartFormData = void 0;
|
|
6
|
+
exports.parseMultipartFormData = exports.sanitizeFilename = exports.FileUploadValidationError = void 0;
|
|
7
|
+
const quidproquo_webserver_1 = require("quidproquo-webserver");
|
|
7
8
|
const busboy_1 = __importDefault(require("busboy"));
|
|
8
|
-
|
|
9
|
+
class FileUploadValidationError extends Error {
|
|
10
|
+
constructor(errorType, message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.errorType = errorType;
|
|
13
|
+
this.name = 'FileUploadValidationError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.FileUploadValidationError = FileUploadValidationError;
|
|
17
|
+
// Reduce a client-supplied filename to a bare, path-traversal-safe name.
|
|
18
|
+
const sanitizeFilename = (filename) => {
|
|
19
|
+
const baseName = (filename || '')
|
|
20
|
+
.split(/[/\\]/)
|
|
21
|
+
.filter((segment) => segment.length > 0)
|
|
22
|
+
.pop();
|
|
23
|
+
// eslint-disable-next-line no-control-regex
|
|
24
|
+
const cleaned = (baseName || '').replace(/[\x00-\x1f]/g, '');
|
|
25
|
+
if (!cleaned || cleaned === '.' || cleaned === '..') {
|
|
26
|
+
return 'unnamed';
|
|
27
|
+
}
|
|
28
|
+
return cleaned;
|
|
29
|
+
};
|
|
30
|
+
exports.sanitizeFilename = sanitizeFilename;
|
|
31
|
+
const isMimeTypeAllowed = (mimeType, allowedMimeTypes) => {
|
|
32
|
+
if (!allowedMimeTypes) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
const normalizedMimeType = (mimeType || '').toLowerCase();
|
|
36
|
+
return allowedMimeTypes.some((allowed) => {
|
|
37
|
+
const normalizedAllowed = allowed.toLowerCase();
|
|
38
|
+
return normalizedAllowed.endsWith('/*')
|
|
39
|
+
? normalizedMimeType.startsWith(normalizedAllowed.slice(0, -1))
|
|
40
|
+
: normalizedMimeType === normalizedAllowed;
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
const parseMultipartFormData = (event, fileUploadSettings) => new Promise((resolve, reject) => {
|
|
9
44
|
const busboy = (0, busboy_1.default)({
|
|
10
45
|
headers: {
|
|
11
46
|
'content-type': event.headers['content-type'] || event.headers['Content-Type'],
|
|
12
47
|
},
|
|
48
|
+
limits: {
|
|
49
|
+
fileSize: fileUploadSettings.maxFileSizeBytes,
|
|
50
|
+
files: fileUploadSettings.maxFileCount,
|
|
51
|
+
fields: fileUploadSettings.maxFieldCount,
|
|
52
|
+
fieldSize: fileUploadSettings.maxFieldSizeBytes,
|
|
53
|
+
},
|
|
13
54
|
});
|
|
14
55
|
const result = {
|
|
15
56
|
files: [],
|
|
16
57
|
fields: {},
|
|
17
58
|
};
|
|
59
|
+
let settled = false;
|
|
60
|
+
const rejectOnce = (error) => {
|
|
61
|
+
if (!settled) {
|
|
62
|
+
settled = true;
|
|
63
|
+
reject(error);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
18
66
|
busboy.on('file', (name, file, info) => {
|
|
19
|
-
|
|
67
|
+
const filename = (0, exports.sanitizeFilename)(info.filename);
|
|
68
|
+
if (!isMimeTypeAllowed(info.mimeType, fileUploadSettings.allowedMimeTypes)) {
|
|
69
|
+
rejectOnce(new FileUploadValidationError(quidproquo_webserver_1.FileUploadErrorTypeEnum.disallowedMimeType, `File "${filename}" has disallowed content type "${info.mimeType}"`));
|
|
70
|
+
file.resume();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const chunks = [];
|
|
20
74
|
file.on('data', (data) => {
|
|
21
|
-
|
|
75
|
+
chunks.push(data);
|
|
76
|
+
});
|
|
77
|
+
// Emitted when the stream is truncated at limits.fileSize - a silently truncated upload must fail, not succeed
|
|
78
|
+
file.on('limit', () => {
|
|
79
|
+
rejectOnce(new FileUploadValidationError(quidproquo_webserver_1.FileUploadErrorTypeEnum.fileTooLarge, `File "${filename}" exceeds the maximum file size of ${fileUploadSettings.maxFileSizeBytes} bytes`));
|
|
22
80
|
});
|
|
23
81
|
file.on('end', () => {
|
|
24
|
-
|
|
82
|
+
const content = Buffer.concat(chunks);
|
|
83
|
+
if (!settled && content.length > 0) {
|
|
25
84
|
result.files.push({
|
|
26
|
-
filename
|
|
85
|
+
filename,
|
|
27
86
|
contentType: info.mimeType,
|
|
28
87
|
encoding: info.encoding,
|
|
29
88
|
name: name,
|
|
30
|
-
content
|
|
89
|
+
content,
|
|
31
90
|
});
|
|
32
91
|
}
|
|
33
92
|
});
|
|
34
93
|
});
|
|
94
|
+
busboy.on('filesLimit', () => {
|
|
95
|
+
rejectOnce(new FileUploadValidationError(quidproquo_webserver_1.FileUploadErrorTypeEnum.tooManyFiles, `Upload exceeds the maximum of ${fileUploadSettings.maxFileCount} files`));
|
|
96
|
+
});
|
|
97
|
+
busboy.on('fieldsLimit', () => {
|
|
98
|
+
rejectOnce(new FileUploadValidationError(quidproquo_webserver_1.FileUploadErrorTypeEnum.tooManyFields, `Upload exceeds the maximum of ${fileUploadSettings.maxFieldCount} fields`));
|
|
99
|
+
});
|
|
35
100
|
busboy.on('field', (name, value) => {
|
|
36
101
|
result.fields[name] = value;
|
|
37
102
|
});
|
|
38
103
|
busboy.on('error', (error) => {
|
|
39
|
-
|
|
104
|
+
rejectOnce(error);
|
|
40
105
|
});
|
|
41
106
|
busboy.on('finish', () => {
|
|
107
|
+
if (settled) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
settled = true;
|
|
42
111
|
const qpqBinaryFiles = result.files.map((f) => ({
|
|
43
112
|
filename: f.filename,
|
|
44
113
|
mimetype: f.contentType,
|
|
@@ -32,6 +32,7 @@ __exportStar(require("./eventBus"), exports);
|
|
|
32
32
|
__exportStar(require("./file"), exports);
|
|
33
33
|
__exportStar(require("./graphDatabase"), exports);
|
|
34
34
|
__exportStar(require("./keyValueStore"), exports);
|
|
35
|
+
__exportStar(require("./metric"), exports);
|
|
35
36
|
__exportStar(require("./queue"), exports);
|
|
36
37
|
__exportStar(require("./system"), exports);
|
|
37
38
|
__exportStar(require("./userDirectory"), exports);
|
|
@@ -44,10 +45,11 @@ const eventBus_1 = require("./eventBus");
|
|
|
44
45
|
const file_1 = require("./file");
|
|
45
46
|
const graphDatabase_1 = require("./graphDatabase");
|
|
46
47
|
const keyValueStore_1 = require("./keyValueStore");
|
|
48
|
+
const metric_1 = require("./metric");
|
|
47
49
|
const queue_1 = require("./queue");
|
|
48
50
|
const system_1 = require("./system");
|
|
49
51
|
const userDirectory_1 = require("./userDirectory");
|
|
50
52
|
const getCoreActionProcessor = (qpqConfig, dynamicModuleLoader) => __awaiter(void 0, void 0, void 0, function* () {
|
|
51
|
-
return (Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (yield (0, ai_1.getAiActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, apiKeyValidation_1.getApiKeyValidationActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, file_1.getFileActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, keyValueStore_1.getKeyValueStoreActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, queue_1.getQueueActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, userDirectory_1.getUserDirectoryActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, eventBus_1.getEventBusActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, system_1.getSystemActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, config_1.getConfigActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, event_1.getApiGatewayApiEventEventProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, graphDatabase_1.getGraphDatabaseActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, quidproquo_webserver_1.getRouteAuthValidationActionProcessor)(qpqConfig, dynamicModuleLoader))));
|
|
53
|
+
return (Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (yield (0, ai_1.getAiActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, apiKeyValidation_1.getApiKeyValidationActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, file_1.getFileActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, keyValueStore_1.getKeyValueStoreActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, metric_1.getMetricActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, queue_1.getQueueActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, userDirectory_1.getUserDirectoryActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, eventBus_1.getEventBusActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, system_1.getSystemActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, config_1.getConfigActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, event_1.getApiGatewayApiEventEventProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, graphDatabase_1.getGraphDatabaseActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, quidproquo_webserver_1.getRouteAuthValidationActionProcessor)(qpqConfig, dynamicModuleLoader))));
|
|
52
54
|
});
|
|
53
55
|
exports.getCoreActionProcessor = getCoreActionProcessor;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.getMetricPutActionProcessor = void 0;
|
|
13
|
+
const quidproquo_core_1 = require("quidproquo-core");
|
|
14
|
+
// Emits the metric as a CloudWatch Embedded Metric Format (EMF) log line: CloudWatch
|
|
15
|
+
// extracts metrics from the function's log stream - no api calls, no extra IAM, no
|
|
16
|
+
// latency on the story. Cost: each unique metricName x dimension-combination is one
|
|
17
|
+
// custom metric (~$0.30/mo) - the standard dimensions are service/environment(/feature),
|
|
18
|
+
// so keep payload dimensions low-cardinality (never per-user / per-request ids).
|
|
19
|
+
const getProcessMetricPut = (qpqConfig) => {
|
|
20
|
+
// The namespace is the deployed app instance - app/environment(/feature) - mirroring how
|
|
21
|
+
// every qpq resource name embeds the deployment identity. Apps sharing an account can't
|
|
22
|
+
// merge same-named metrics, per-developer feature sandboxes (e.g. qpq/docgen/development/jane)
|
|
23
|
+
// never pollute mainline, and dimensions are reserved for slices WITHIN the deployment
|
|
24
|
+
// (service + caller-supplied).
|
|
25
|
+
const application = quidproquo_core_1.qpqCoreUtils.getApplicationName(qpqConfig);
|
|
26
|
+
const environment = quidproquo_core_1.qpqCoreUtils.getApplicationModuleEnvironment(qpqConfig);
|
|
27
|
+
const feature = quidproquo_core_1.qpqCoreUtils.getApplicationModuleFeature(qpqConfig);
|
|
28
|
+
const namespace = `qpq/${application}/${environment}${feature ? `/${feature}` : ''}`;
|
|
29
|
+
const service = quidproquo_core_1.qpqCoreUtils.getApplicationModuleName(qpqConfig);
|
|
30
|
+
return (_a) => __awaiter(void 0, [_a], void 0, function* ({ metricName, value, unit, dimensions }) {
|
|
31
|
+
const allDimensions = Object.assign({ service }, dimensions);
|
|
32
|
+
const emf = Object.assign(Object.assign({ _aws: {
|
|
33
|
+
Timestamp: Date.now(),
|
|
34
|
+
CloudWatchMetrics: [
|
|
35
|
+
{
|
|
36
|
+
Namespace: namespace,
|
|
37
|
+
Dimensions: [Object.keys(allDimensions)],
|
|
38
|
+
Metrics: [{ Name: metricName, Unit: unit !== null && unit !== void 0 ? unit : quidproquo_core_1.MetricUnit.count }],
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
} }, allDimensions), { [metricName]: value });
|
|
42
|
+
console.log(JSON.stringify(emf));
|
|
43
|
+
return (0, quidproquo_core_1.actionResult)(void 0);
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
const getMetricPutActionProcessor = (qpqConfig) => __awaiter(void 0, void 0, void 0, function* () {
|
|
47
|
+
return ({
|
|
48
|
+
[quidproquo_core_1.MetricActionType.Put]: getProcessMetricPut(qpqConfig),
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
exports.getMetricPutActionProcessor = getMetricPutActionProcessor;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.getMetricActionProcessor = void 0;
|
|
13
|
+
const getMetricPutActionProcessor_1 = require("./getMetricPutActionProcessor");
|
|
14
|
+
const getMetricActionProcessor = (qpqConfig, dynamicModuleLoader) => __awaiter(void 0, void 0, void 0, function* () {
|
|
15
|
+
return (Object.assign({}, (yield (0, getMetricPutActionProcessor_1.getMetricPutActionProcessor)(qpqConfig, dynamicModuleLoader))));
|
|
16
|
+
});
|
|
17
|
+
exports.getMetricActionProcessor = getMetricActionProcessor;
|
|
@@ -10,9 +10,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.dynamicModuleLoader = void 0;
|
|
13
|
-
// @ts-expect-error -
|
|
13
|
+
// @ts-expect-error - injected at build time by QpqPlugin (webpack virtual module)
|
|
14
14
|
const quidproquo_dynamic_loader_1 = require("quidproquo-dynamic-loader");
|
|
15
|
+
const federatedModuleStore_1 = require("../../logic/federatedModuleStore");
|
|
15
16
|
const dynamicModuleLoader = (qpqFunctionRuntime) => __awaiter(void 0, void 0, void 0, function* () {
|
|
17
|
+
// Tier 1: federated. undefined means "nothing published for this runtime" -> fall through.
|
|
18
|
+
const federatedStory = yield (0, federatedModuleStore_1.loadFederatedStory)(quidproquo_dynamic_loader_1.qpqConfig, qpqFunctionRuntime);
|
|
19
|
+
if (federatedStory !== undefined) {
|
|
20
|
+
return federatedStory;
|
|
21
|
+
}
|
|
22
|
+
// Tier 2: the statically bundled story (or a fail-fast throw for a thin shell).
|
|
16
23
|
return (0, quidproquo_dynamic_loader_1.qpqDynamicModuleLoader)(qpqFunctionRuntime);
|
|
17
24
|
});
|
|
18
25
|
exports.dynamicModuleLoader = dynamicModuleLoader;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getApiKeyValue: (region: string, apiKeyId: string) => Promise<string | undefined>;
|
|
@@ -9,21 +9,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.
|
|
12
|
+
exports.getApiKeyValue = void 0;
|
|
13
13
|
const client_api_gateway_1 = require("@aws-sdk/client-api-gateway");
|
|
14
14
|
const createAwsClient_1 = require("../createAwsClient");
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
// Fetch a single api key's value by id - keys are read individually (not listed) so the
|
|
16
|
+
// IAM grant can be scoped per-key with tag conditions rather than the /apikeys collection.
|
|
17
|
+
const getApiKeyValue = (region, apiKeyId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
17
18
|
const apiGatewayClient = (0, createAwsClient_1.createAwsClient)(client_api_gateway_1.APIGatewayClient, { region });
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
return (((_a = res.items) === null || _a === void 0 ? void 0 : _a.filter((i) => keyNames.indexOf(i.name) >= 0).map((i) => ({
|
|
24
|
-
name: i.name,
|
|
25
|
-
value: i.value,
|
|
26
|
-
description: i.description,
|
|
27
|
-
}))) || []);
|
|
19
|
+
const res = yield apiGatewayClient.send(new client_api_gateway_1.GetApiKeyCommand({
|
|
20
|
+
apiKey: apiKeyId,
|
|
21
|
+
includeValue: true,
|
|
22
|
+
}));
|
|
23
|
+
return res.value;
|
|
28
24
|
});
|
|
29
|
-
exports.
|
|
25
|
+
exports.getApiKeyValue = getApiKeyValue;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare enum FederatedModuleLoadErrorCode {
|
|
2
|
+
unsupportedStoreUrl = "unsupportedStoreUrl",
|
|
3
|
+
manifestInvalid = "manifestInvalid",
|
|
4
|
+
containerLoadFailed = "containerLoadFailed",
|
|
5
|
+
storyNotExported = "storyNotExported"
|
|
6
|
+
}
|
|
7
|
+
export declare class FederatedModuleLoadError extends Error {
|
|
8
|
+
readonly code: FederatedModuleLoadErrorCode;
|
|
9
|
+
constructor(code: FederatedModuleLoadErrorCode, message: string);
|
|
10
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FederatedModuleLoadError = exports.FederatedModuleLoadErrorCode = void 0;
|
|
4
|
+
// Discriminable failures raised inside loadFederatedStory. All of them are ultimately
|
|
5
|
+
// caught there and turned into a fall-back-to-bundled (with a warning) - they exist so
|
|
6
|
+
// logs/backoff can tell WHY a store didn't load, not to crash the request.
|
|
7
|
+
var FederatedModuleLoadErrorCode;
|
|
8
|
+
(function (FederatedModuleLoadErrorCode) {
|
|
9
|
+
// Store URL wasn't s3:// or file:// (a config/env mistake).
|
|
10
|
+
FederatedModuleLoadErrorCode["unsupportedStoreUrl"] = "unsupportedStoreUrl";
|
|
11
|
+
// manifest.json parsed but is missing required fields (bad/partial publish).
|
|
12
|
+
FederatedModuleLoadErrorCode["manifestInvalid"] = "manifestInvalid";
|
|
13
|
+
// A version's files downloaded but the entry didn't expose a valid MF container.
|
|
14
|
+
FederatedModuleLoadErrorCode["containerLoadFailed"] = "containerLoadFailed";
|
|
15
|
+
// The manifest exposed the module, but it didn't export the requested story name
|
|
16
|
+
// (e.g. a story was renamed in the remote but the manifest still lists it).
|
|
17
|
+
FederatedModuleLoadErrorCode["storyNotExported"] = "storyNotExported";
|
|
18
|
+
})(FederatedModuleLoadErrorCode || (exports.FederatedModuleLoadErrorCode = FederatedModuleLoadErrorCode = {}));
|
|
19
|
+
class FederatedModuleLoadError extends Error {
|
|
20
|
+
constructor(code, message) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.code = code;
|
|
23
|
+
this.name = 'FederatedModuleLoadError';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.FederatedModuleLoadError = FederatedModuleLoadError;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./FederatedModuleLoadError"), exports);
|
|
18
|
+
__exportStar(require("./FederatedModuleStoreManifest"), exports);
|
|
19
|
+
__exportStar(require("./loadFederatedStory"), exports);
|
|
20
|
+
__exportStar(require("./sharedPackages"), exports);
|