UNPKG

resolve-runtime

Version:
256 lines (213 loc) 9.03 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; require("source-map-support/register"); var _iotdata = _interopRequireDefault(require("aws-sdk/clients/iotdata")); var _dynamodb = require("aws-sdk/clients/dynamodb"); var _awsSignatureV = _interopRequireDefault(require("aws-signature-v4")); var _sts = _interopRequireDefault(require("aws-sdk/clients/sts")); var _lambda = _interopRequireDefault(require("aws-sdk/clients/lambda")); var _resolveApiHandlerAwslambda = _interopRequireDefault(require("resolve-api-handler-awslambda")); var _resolveCommand = _interopRequireDefault(require("resolve-command")); var _resolveEs = _interopRequireDefault(require("resolve-es")); var _resolveQuery = _interopRequireWildcard(require("resolve-query")); var _main_handler = _interopRequireDefault(require("./handlers/main_handler")); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } const invokeLambdaSelf = async event => { const lambda = new _lambda.default({ apiVersion: '2015-03-31' }); const invokeParams = { FunctionName: process.env.AWS_LAMBDA_FUNCTION_NAME, InvocationType: 'Event', Payload: JSON.stringify(event), LogType: 'None' }; return await new Promise((resolve, reject) => lambda.invoke(invokeParams, (err, data) => !err ? resolve(data) : reject(err))); }; const initResolve = async ({ snapshotAdapter: createSnapshotAdapter, storageAdapter: createStorageAdapter, readModelAdapters: readModelAdaptersCreators }, resolve) => { const storageAdapter = createStorageAdapter(); const eventStore = (0, _resolveEs.default)({ storage: storageAdapter }); const { aggregates, readModels, viewModels } = resolve; const snapshotAdapter = createSnapshotAdapter(); const readModelAdapters = {}; for (const _ref of readModelAdaptersCreators) { const { name, factory } = _ref; readModelAdapters[name] = factory(); } const executeCommand = (0, _resolveCommand.default)({ eventStore, aggregates, snapshotAdapter }); if (resolve.activeDemandSet == null) { resolve.__proto__.activeDemandSet = new Set(); } const doUpdateRequest = async (pool, readModelName, readOptions) => { const executor = pool.getExecutor(pool, readModelName); Promise.resolve().then(() => executor.read(readOptions)).catch(error => error).then(() => invokeLambdaSelf({ Records: [] })).catch(error => { resolveLog('error', 'Update lambda invocation error', error); }); if (!resolve.activeDemandSet.has(readModelName)) { // Delay initial read-model on-demand request to enforce awaiting tables creation in common // cases for better usability, but will be evenntually consistent anyway, even no timeout await new Promise(resolve => setTimeout(resolve, 2000)); resolve.activeDemandSet.add(readModelName); } }; const executeQuery = (0, _resolveQuery.default)({ eventStore, viewModels, readModels, readModelAdapters, snapshotAdapter, doUpdateRequest }); Object.assign(resolve, { executeCommand, executeQuery, eventStore }); Object.defineProperties(resolve, { readModelAdapters: { value: readModelAdapters }, snapshotAdapter: { value: snapshotAdapter }, storageAdapter: { value: storageAdapter } }); }; const disposeResolve = async resolve => { await resolve.storageAdapter.dispose(); await resolve.snapshotAdapter.dispose(); for (const name of Object.keys(resolve.readModelAdapters)) { await resolve.readModelAdapters[name].dispose(); } }; const getSubscribeAdapterOptions = async ({ sts }) => { const { DEPLOYMENT_ID, IOT_ENDPOINT_HOST, IOT_ROLE_ARN } = process.env; const data = await sts.assumeRole({ RoleArn: IOT_ROLE_ARN, RoleSessionName: `role-session-${DEPLOYMENT_ID}`, DurationSeconds: 3600 }).promise(); const url = _awsSignatureV.default.createPresignedURL('GET', IOT_ENDPOINT_HOST, '/mqtt', 'iotdevicegateway', '', { key: data.Credentials.AccessKeyId, secret: data.Credentials.SecretAccessKey, sessionToken: data.Credentials.SessionToken, protocol: 'wss' }); return { appId: DEPLOYMENT_ID, url }; }; const lambdaWorker = async (assemblies, resolveBase, lambdaEvent, lambdaContext) => { resolveLog('debug', 'Lambda handler has received event', lambdaEvent); lambdaContext.callbackWaitsForEmptyEventLoop = false; let executorResult = null; const resolve = Object.create(resolveBase); try { await initResolve(assemblies, resolve); resolveLog('debug', 'Lambda handler has initialized resolve instance', resolve); // API gateway event if (lambdaEvent.headers != null && lambdaEvent.httpMethod != null) { resolveLog('debug', 'Lambda handler classified event as API gateway', lambdaEvent.httpMethod, lambdaEvent.headers); const getCustomParameters = async () => ({ resolve }); const executor = (0, _resolveApiHandlerAwslambda.default)(_main_handler.default, getCustomParameters); executorResult = await executor(lambdaEvent, lambdaContext); } // DynamoDB trigger event // AWS DynamoDB streams guarantees that changesets from one table partition will // be delivered strictly into one lambda instance, i.e. following code works in // single-thread mode for one event storage - see https://amzn.to/2LkKXAV else if (lambdaEvent.Records != null) { resolveLog('debug', 'Lambda handler classified event as Dynamo stream', lambdaEvent.Records); const applicationPromises = []; const events = lambdaEvent.Records.map(record => _dynamodb.Converter.unmarshall(record.dynamodb.NewImage)); // TODO. Refactoring MQTT publish event for (const event of events) { const eventDescriptor = { topic: `${process.env.DEPLOYMENT_ID}/${event.type}/${event.aggregateId}`, payload: JSON.stringify(event), qos: 1 }; applicationPromises.push(resolve.mqtt.publish(eventDescriptor).promise().then(() => { resolveLog('info', 'Lambda pushed event into MQTT successfully', eventDescriptor); }).catch(error => { resolveLog('warn', 'Lambda can not publish event into MQTT', eventDescriptor, error); })); } const executors = resolve.executeQuery.getExecutors(_resolveQuery.constants.modelTypes.readModel); for (const executor of executors) { applicationPromises.push(executor.updateByEvents(events)); } await Promise.all(applicationPromises); executorResult = true; } } finally { await disposeResolve(resolve); resolveLog('debug', 'Lambda handler has disposed resolve instance'); } if (executorResult == null) { throw new Error(`Lambda cannot be invoked with event: ${lambdaEvent}`); } return executorResult; }; const cloudEntry = async ({ assemblies, constants, domain, redux, routes }) => { try { const resolve = _objectSpread({ aggregateActions: assemblies.aggregateActions, seedClientEnvs: assemblies.seedClientEnvs, sts: new _sts.default(), mqtt: new _iotdata.default({ endpoint: process.env.IOT_ENDPOINT_HOST }) }, constants, domain, { redux, routes }); resolve.getSubscribeAdapterOptions = getSubscribeAdapterOptions.bind(null, resolve); resolveLog('debug', 'Cloud entry point cold start success', resolve); return lambdaWorker.bind(null, assemblies, resolve); } catch (error) { resolveLog('error', 'Cloud entry point cold start failure', error); } }; var _default = cloudEntry; exports.default = _default; //# sourceMappingURL=cloud_entry.js.map