UNPKG

lemon-core

Version:
382 lines 15.6 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.convDateToTS = exports.convDateToTime = exports.convDate = exports.DEFAULT_TIME_ZONE = exports.do_parrallel = exports.doReportMetric = exports.doReportSlack = exports.doReportCallback = exports.doReportError = exports.getHelloArn = void 0; /** * `core/engine.ts` * - shared core engine's export. * * **NOTE** * - override `process.env` before use(or import) this. * * ```js * //! import core engine like this. * import { $engine, _log, _inf, _err, $U } from '../core/engine'; * const NS = $U.NS(name, 'yellow'); * _inf(NS, `! model[${name}] is ready..`); * ``` * * @author Steve Jung <steve@lemoncloud.io> * @date 2019-05-24 initial version in `lemon-todaq-api`. * @date 2019-08-01 support `loadJsonSync()` + move common functions + export core services + '$web' * @date 2019-08-02 improved type helper with `lemon-engine#2.2.0` + fix $client() error. * @date 2019-08-06 improved type helper with `lemon-engine#2.2.3` * @date 2019-08-08 improved `$api().do(event, context, callback)`. * @date 2019-11-26 cleanup and optimized for `lemon-core#v2` * @date 2022-02-21 remove `$_` the lodash libs. * * @copyright (C) lemoncloud.io 2019 - All Rights Reserved. */ const index_1 = require("./index"); const shared_1 = require("../tools/shared"); const aws_sns_service_1 = require("../cores/aws/aws-sns-service"); //! create SNS Service const $sns = (arn) => new aws_sns_service_1.AWSSNSService(arn); /** * find ARN('lemon-hello-sns') via context information or environment. * * @param context the current running context * @param NS namespace to log */ const getHelloArn = (context, NS) => { NS = NS || index_1.$U.NS('HELO'); //! use pre-defined env via `serverless.yml` const arn = index_1.$engine.environ('REPORT_ERROR_ARN', ''); if (arn.startsWith('arn:aws:sns:')) return arn; if (!context) throw new Error(`@context (RequestContext) is required!`); if (true) { const target = 'lemon-hello-sns'; const $ctx = context; const $req = context; const $ncx = context; //! build arn via context information. const invokedFunctionArn = `${$ctx.invokedFunctionArn || ''}`; // if called via lambda call ex: 'arn:aws:lambda:ap-northeast-2:085403634746:function:lemon-messages-api-prod-user' const accountId = `${$ncx.accountId || invokedFunctionArn.split(':')[4] || $req.accountId || ''}`; const region = invokedFunctionArn.split(':')[3] || `ap-northeast-2`; (0, index_1._inf)(NS, '! accountId =', accountId); if (!accountId) { (0, index_1._err)(NS, 'ERROR! missing accountId. context =', index_1.$U.json(context)); throw new Error('.accountId is missing'); } return `arn:aws:sns:${region}:${accountId}:${target}`; } }; exports.getHelloArn = getHelloArn; /** * report error via `lemon-hello-sns`. * * @param e Error * @param context Lambda Context * @param event Origin Event Object. * @param data Optinal Data(body). */ const doReportError = (e, context, event, data) => __awaiter(void 0, void 0, void 0, function* () { //! ignore only if local express-run. if (context && context.source === 'express') return '!ignore'; const NS = index_1.$U.NS('RPTE'); //TODO - optimize message extractor. const $message = (e) => { const m = (e && (e.message || e.statusMessage)) || e; return typeof m == 'object' ? index_1.$U.json(m) : `${m}`; }; (0, index_1._log)(NS, `doReportError(${$message(e)})...`); //! dispatch invoke conditins. try { const message = $message(e); const $pack = (shared_1.loadJsonSync && (0, shared_1.loadJsonSync)('package.json')) || {}; const name = (context && context.name) || process.env.NAME || ''; const stage = (context && context.stage) || process.env.STAGE || ''; const apiId = (context && context.apiId) || ''; const domainPrefix = (context && context.domainPrefix) || ''; const resourcePath = (context && context.resourcePath) || ''; const identity = (context && context.identity) || {}; const service = `api://${$pack.name || 'lemon-core'}/${name}-${stage}#${$pack.version || '0.0.0'}`; //! prepare payload to publish. const payload = { service, message, context: Object.assign(Object.assign({}, context), { stage, apiId, resourcePath, identity, domainPrefix, event }), data, }; //! find target arn. const arn = (0, exports.getHelloArn)(context, NS); (0, index_1._log)(NS, `> report-error.arn =`, arn); return $sns(arn) .reportError(e, payload, arn) .then((mid) => { (0, index_1._inf)(NS, '> err.message-id =', mid); return `${mid}`; }) .catch((e) => { (0, index_1._err)(NS, '! err.report =', e); return ''; }); } catch (e2) { (0, index_1._err)(NS, '! err-ignored =', e2); return `!err - ${e2.message || e2}`; } }); exports.doReportError = doReportError; /** * send callback data via web-hook endpoint. * * TODO - improve function identity.!! @191212. * * @param data payload */ const doReportCallback = (data, service, context) => __awaiter(void 0, void 0, void 0, function* () { const NS = index_1.$U.NS('callback', 'cyan'); try { const $pack = (shared_1.loadJsonSync && (0, shared_1.loadJsonSync)('package.json')) || {}; const stage = `${index_1.$U.env('STAGE', 'local')}`.toLowerCase(); const name = `${index_1.$U.env('NAME', '')}`.toLowerCase(); service = service || `api://${$pack.name || 'lemon-core'}#${$pack.version || '0.0.0'}/${name}-${stage}`; const payload = { service, data }; const arn = (0, exports.getHelloArn)(context, NS); return $sns(arn) .publish('', 'callback', payload) // subject should be 'callback' .then((mid) => { (0, index_1._inf)(NS, '> callback.res =', mid); return `${mid}`; }) .catch((e) => { (0, index_1._err)(NS, '! callback.err =', e); return ''; }); } catch (e) { (0, index_1._err)(NS, '> reportCallback.err =', e); return (0, exports.doReportError)(e, context, null, data); } }); exports.doReportCallback = doReportCallback; /** * report slack message via `lemon-hello-sns`. * * @param channel channel of slack * @param body slack body * @param context current running context. */ const doReportSlack = (channel, body, context) => __awaiter(void 0, void 0, void 0, function* () { const NS = index_1.$U.NS('RPTS'); (0, index_1._log)(NS, `doReportSlack()...`); //! dispatch invoke conditins. try { const $pack = (shared_1.loadJsonSync && (0, shared_1.loadJsonSync)('package.json')) || {}; const service = `api://${$pack.name || 'lemon-core'}#${$pack.version || '0.0.0'}`; const stage = (context && context.stage) || ''; const apiId = (context && context.apiId) || ''; const domainPrefix = (context && context.domainPrefix) || ''; const resourcePath = (context && context.resourcePath) || ''; const identity = (context && context.identity) || {}; const param = {}; //! prepare payload to publish. const payload = { channel, service, param, body, context: { stage, apiId, resourcePath, identity, domainPrefix }, }; //! find target arn. const arn = (0, exports.getHelloArn)(context, NS); (0, index_1._log)(NS, `> report-slack.arn =`, arn); return $sns(arn) .publish(arn, 'slack', payload) .then((mid) => { (0, index_1._inf)(NS, '> sns.message-id =', mid); return `${mid}`; }) .catch((e) => { (0, index_1._err)(NS, '! err.slack =', e); return ''; }); } catch (e2) { (0, index_1._err)(NS, '! err-ignored =', e2); return `!err - ${e2.message || e2}`; } }); exports.doReportSlack = doReportSlack; /** * report metric-data like chart/graph to record via `lemon-metrics-sns`. * * @param ns namespace like `[a-zA-Z][a-zA-Z0-9]+` * @param id id value like `[a-zA-Z0-9][a-zA-Z0-9_:\-]+` * @param body any body data * @param context current running context. */ const doReportMetric = (ns, id, body, context) => __awaiter(void 0, void 0, void 0, function* () { const NS = index_1.$U.NS('RPTM'); //! validate parameters. (see `lemon-metrics-api`) const reNs = /^[a-zA-Z][a-zA-Z0-9]+$/; const reId = /^[a-zA-Z0-9][a-zA-Z0-9_:\-]+$/; if (!reNs.test(ns)) throw new Error('Invalid text-format @ns:' + ns); if (!reId.test(id)) throw new Error('Invalid text-format @id:' + id); (0, index_1._log)(NS, `doReportMetric(${ns},${id})...`); //! dispatch invoke conditins. try { const $pack = (shared_1.loadJsonSync && (0, shared_1.loadJsonSync)('package.json')) || {}; const service = `api://${$pack.name || 'lemon-core'}#${$pack.version || '0.0.0'}`; const stage = (context && context.stage) || ''; const apiId = (context && context.apiId) || ''; const domainPrefix = (context && context.domainPrefix) || ''; const resourcePath = (context && context.resourcePath) || ''; const identity = (context && context.identity) || {}; const param = { ns, id }; //! prepare payload: `POST /metrics/!/report` const payload = { service, type: 'metrics', method: 'post', id: '!', cmd: 'report', param, body, context: { stage, apiId, resourcePath, identity, domainPrefix }, }; //! find metric-arn via error-arn. const target = 'lemon-metrics-sns'; const arn0 = (0, exports.getHelloArn)(context, NS); // eslint-disable-next-line prettier/prettier const arn = arn0.startsWith('arn:aws:sns:') && arn0.split(':').length == 6 ? arn0.split(':').map((v, i) => i == 5 ? target : v).join(':') : arn0; (0, index_1._log)(NS, `> report-metric.arn =`, arn); return $sns(arn) .publish(arn || target, 'metric', payload) .then((mid) => { (0, index_1._inf)(NS, '> sns.message-id =', mid); return `${mid}`; }) .catch((e) => { (0, index_1._err)(NS, '! err.metric =', e); return ''; }); } catch (e2) { (0, index_1._err)(NS, '! err-ignored =', e2); return `!err - ${e2.message || e2}`; } }); exports.doReportMetric = doReportMetric; /** * parrallel actions in list (in batch-size = 10) * * **TODO** - improve return types by refering callback. * * @param param any list * @param callback (item)=>any | Promise<any> * @param size (optional) size of parrallel (default 10) * @param pos (optional) current pos (default 0) * @param result (optional) result set in stacked. */ const do_parrallel = (param, callback, size = 10, pos = 0, result = []) => { size = size === undefined ? 10 : size; pos = pos === undefined ? 0 : pos; result = result === undefined ? [] : result; //! annonymous method of callback const safeCall = (n, i) => { try { return callback(n, i); } catch (e) { return Promise.reject(e); } }; // _log(NS, `! parrallel(${pos}/${size})`) const list = Array.isArray(param) ? param : param.list; const list2 = list.slice(pos, pos + size); const actions = list2.map((node, i) => { const index = pos + i; try { //! error proof. const R = safeCall(node, index); if (R && typeof R == 'object' && R instanceof Promise) { const R2 = R; // avoid compile error. return R2.catch(e => { (0, index_1._err)(`!ERR@1 node[${index}] =`, e); //! make sure error instance. return e instanceof Error ? e : new Error(typeof e == 'string' ? e : JSON.stringify(e)); }); } return R; } catch (e) { (0, index_1._err)(`!ERR@2 node[${index}] =`, e); //! make sure error instance. return e instanceof Error ? e : new Error(typeof e == 'string' ? e : JSON.stringify(e)); } }); //! do parrallel. return Promise.all(actions) .then(res => { if (Array.isArray(param)) return res; const { ignoreError, reportError, event, context, message } = param; const errors = res.filter(i => i instanceof Error); if (!errors.length) return res; const data = { message, pos, size }; data.errors = res.map((_, i) => { if (!(_ instanceof Error)) return ''; return { error: _.message, node: list2[i] }; }); return (reportError ? (0, exports.doReportError)(errors[0], context, event, data) : Promise.resolve('')).then(() => { if (ignoreError) { res = res.map((_, i) => (_ instanceof Error ? list2[i] : _)); } return res; }); }) .then(_ => { result = result.concat(_); if (!_.length) return Promise.resolve(result); return (0, exports.do_parrallel)(param, callback, size, pos + size, result); }); }; exports.do_parrallel = do_parrallel; //! default time-zone for this api. (Asia/Seoul - 9 hours) exports.DEFAULT_TIME_ZONE = 9; //! convert to date of input. const convDate = (dt) => index_1.$U.dt(dt, exports.DEFAULT_TIME_ZONE); exports.convDate = convDate; /** * Convert input to time value (in number) * * @param {*} dt see `conv_date()` * @param {*} name name of property */ const convDateToTime = (dt) => { if (dt === '' || dt === '0' || dt === 0) return 0; // 0 means null (not-set) const t = (0, exports.convDate)(dt); return t.getTime(); }; exports.convDateToTime = convDateToTime; /** * Convert input (Date) to time-stamp (YYYY-MM-DD hh:mm:ss) * - consider with current time-zone. * * @param {*} dt */ const convDateToTS = (dt) => { const t = (0, exports.convDate)(dt); return index_1.$U.ts(t, exports.DEFAULT_TIME_ZONE); }; exports.convDateToTS = convDateToTS; //# sourceMappingURL=engine.js.map