UNPKG

@bitblit/epsilon

Version:

Tiny adapter to simplify building API gateway Lambda APIS

153 lines 8.87 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BackgroundHttpAdapterHandler = void 0; const common_1 = require("@bitblit/ratchet/common"); const background_process_handling_1 = require("./background-process-handling"); const bad_request_error_1 = require("../http/error/bad-request-error"); const not_found_error_1 = require("../http/error/not-found-error"); /** * We use a FIFO queue so that 2 different Lambdas don't both work on the same * thing at the same time. */ class BackgroundHttpAdapterHandler { constructor(backgroundConfig, modelValidator, backgroundManager, maxWaitInMsForBackgroundJobToStart = 10000) { this.backgroundConfig = backgroundConfig; this.modelValidator = modelValidator; this.backgroundManager = backgroundManager; this.maxWaitInMsForBackgroundJobToStart = maxWaitInMsForBackgroundJobToStart; } get httpMetaEndpoint() { return this.backgroundConfig.httpMetaEndpoint; } get httpSubmissionPath() { return this.backgroundConfig.httpSubmissionPath; } get httpStatusPath() { return this.backgroundConfig.httpStatusEndpoint; } get implyTypeFromPathSuffix() { return this.backgroundConfig.implyTypeFromPathSuffix; } handleBackgroundStatusRequest(evt, context) { return __awaiter(this, void 0, void 0, function* () { common_1.Logger.info('handleBackgroundStatusRequest called'); if (!this.backgroundConfig.transactionLogger) { throw new bad_request_error_1.BadRequestError('Process logging not enabled'); } else { const guid = common_1.StringRatchet.trimToNull(evt.pathParameters['guid']) || common_1.StringRatchet.trimToNull(evt.queryStringParameters['guid']); if (guid) { const sw = new common_1.StopWatch(); let log = null; while (!log && sw.elapsedMS() < this.maxWaitInMsForBackgroundJobToStart) { log = yield this.backgroundConfig.transactionLogger.readTransactionLog(guid); if (!log) { common_1.Logger.debug('No log found yet, waiting 500 ms and retrying (%s of %d waited so far)', sw.dump(), this.maxWaitInMsForBackgroundJobToStart); yield common_1.PromiseRatchet.wait(500); } } if (!log) { throw new not_found_error_1.NotFoundError().withFormattedErrorMessage('No background result found for guid %s', guid); } return log; } else { throw new bad_request_error_1.BadRequestError('No guid specified'); } } }); } handleBackgroundMetaRequest(evt, context) { return __awaiter(this, void 0, void 0, function* () { common_1.Logger.info('handleBackgroundMetaRequest called'); const currentCount = yield this.backgroundManager.fetchApproximateNumberOfQueueEntries(); const valid = this.backgroundConfig.processors.map((b) => b.typeName).filter((a) => !!a); valid.sort((a, b) => a.localeCompare(b)); const rval = { currentQueueLength: currentCount, validTypes: valid, backgroundManagerName: this.backgroundManager.backgroundManagerName, }; return rval; }); } handleBackgroundSubmission(evt, context) { return __awaiter(this, void 0, void 0, function* () { common_1.Logger.info('handleBackgroundSubmission : %j (mgr:%s)', evt.parsedBody, this.backgroundManager.backgroundManagerName); let rval = null; const startIdx = evt.path.indexOf(this.httpSubmissionPath) + this.httpSubmissionPath.length; let pathSuppliedBackgroundType = this.backgroundConfig.implyTypeFromPathSuffix ? evt.path.substring(startIdx).split('-').join('').toLowerCase() : ''; // Strip any query params or fragments if (pathSuppliedBackgroundType.includes('?')) { pathSuppliedBackgroundType = pathSuppliedBackgroundType.substring(0, pathSuppliedBackgroundType.indexOf('?')); } if (pathSuppliedBackgroundType.includes('#')) { pathSuppliedBackgroundType = pathSuppliedBackgroundType.substring(0, pathSuppliedBackgroundType.indexOf('#')); } const entry = evt.parsedBody || {}; // Many background submissions contain no body (pure triggers) // So, either this was configured pathed (like xxx/background/{typename} or non-pathed // like /xxx/background. If non-pathed, you must supply the type field in the body. If // pathed, you must either NOT supply the type field in the body, since it'll be determined // by the path, or the types must match if (common_1.StringRatchet.trimToNull(pathSuppliedBackgroundType)) { if (common_1.StringRatchet.trimToNull(entry === null || entry === void 0 ? void 0 : entry.type) && entry.type.toLocaleLowerCase() !== pathSuppliedBackgroundType.toLocaleLowerCase()) { throw new bad_request_error_1.BadRequestError('Background submission has type but does not match path supplied type'); } else { entry.type = pathSuppliedBackgroundType; } } else { // No path, must be in here if (!common_1.StringRatchet.trimToNull(entry === null || entry === void 0 ? void 0 : entry.type)) { throw new bad_request_error_1.BadRequestError('Background submission missing type and not configured in pathed mode'); } } const foundProc = this.backgroundConfig.processors.find((s) => s.typeName.toLowerCase() === entry.type.toLowerCase()); const immediate = common_1.BooleanRatchet.parseBool(evt.queryStringParameters['immediate']); const startProcessor = common_1.BooleanRatchet.parseBool(evt.queryStringParameters['startProcessor']); if (foundProc) { // Perform a validation (if this is a path-supplied type it probably already happened, but don't hurt to do it again) // If you're worried about that, and you do path-typing, just don't have your processors set the dataModelName if (common_1.StringRatchet.trimToNull(foundProc.dataSchemaName)) { // I'm not allowing empty and extra properties here since this is a fully internally defined object const errors = this.modelValidator.validate(foundProc.dataSchemaName, entry.data, false, false); if (errors.length > 0) { throw new bad_request_error_1.BadRequestError().withErrors(errors); } } let result = null; if (immediate) { result = yield this.backgroundManager.fireImmediateProcessRequest(entry); } else { result = yield this.backgroundManager.addEntryToQueue(entry, startProcessor); } rval = { processHandling: immediate ? background_process_handling_1.BackgroundProcessHandling.Immediate : background_process_handling_1.BackgroundProcessHandling.Queued, startProcessorRequested: startProcessor, success: true, resultId: result, error: null, }; } else { throw new bad_request_error_1.BadRequestError().withFormattedErrorMessage('Could not find target background processor : %s', entry.type); } return rval; }); } } exports.BackgroundHttpAdapterHandler = BackgroundHttpAdapterHandler; //# sourceMappingURL=background-http-adapter-handler.js.map