lemon-core
Version:
Lemon Serverless Micro-Service Platform
801 lines • 32.4 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
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());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createSigV4Proxy = exports.my_sequence = exports.my_parrallel = exports.parseRange = exports.isUserAuthorized = exports.getIdentityId = exports.$event = exports.$slack = exports.$info = exports.$protocol = exports.$rand = exports.$T = void 0;
/**
* `helpers.ts`
* - helper functions used globally in project
*
*
* @author Steve Jung <steve@lemoncloud.io>
* @date 2020-12-22 initial version
* @date 2021-12-21 protocol support `//self` url.
* @date 2021-12-23 optimize types of $protocol.
* @date 2022-03-17 addition text processing. (S2, P)
*
* @copyright (C) 2021 LemonCloud Co Ltd. - All Rights Reserved.
*/
const cores_1 = __importDefault(require("../cores/"));
const engine_1 = __importStar(require("../engine/"));
const test_helper_1 = require("../common/test-helper");
const sig_v4_1 = require("../extended/libs/sig-v4");
const perf_hooks_1 = require("perf_hooks");
const tools_1 = require("../tools/tools");
const request_1 = __importDefault(require("request"));
const query_string_1 = __importDefault(require("query-string"));
/**
* Helpers to transform data-types.
*/
exports.$T = {
/**
* transform to string w/ trim()
*/
S: (val, def = '') => `${val !== null && val !== void 0 ? val : def}`.trim(),
/**
* as string w/o white-space.
*/
S2: (val, def = '', delim = '') => `${val !== null && val !== void 0 ? val : def}`.replace(/\s+/g, delim),
/**
* transform to string[]
*/
SS: (val, def = []) => {
if (val === null || val === undefined)
return def;
if (typeof val === 'string')
return val ? val.split(',').map(_ => exports.$T.S(_, '').trim()) : def;
if (Array.isArray(val))
return val.length > 0 ? val.map(_ => exports.$T.S(_, '').trim()) : def;
return [exports.$T.S(val)];
},
/**
* text to Plain text (remove html tag)
*/
P: (text, max = 0) => {
const msg = (typeof text === 'string' ? text : `${text || ''}`)
.replace(/<[^>]*>/g, ' ') //* remove html tag.
.replace(/[^a-zA-Z0-9가-힣ㅋ-ㅎㅏ-ㅣ\.\?]+/g, ' ') //* remove non-char.
.trim();
const len = msg.length;
return max && len > max ? msg.substring(0, max) + '...' : msg;
},
/**
* transform to number(integer).
*/
N: (val, def = 0) => {
const n = engine_1.$U.N(val, def);
return Number.isNaN(n) ? def : n;
},
/**
* number array
*/
NN: (val, def = []) => {
if (val === null || val === undefined)
return def;
if (typeof val === 'string')
return val ? val.split(',').map(_ => exports.$T.N(_, 0)) : def;
if (Array.isArray(val))
return val.length > 0 ? val.map(_ => exports.$T.N(_, 0)) : def;
return [exports.$T.N(val)];
},
/**
* transform to number(float)
*/
F: (val, def = 0) => engine_1.$U.F(val, def),
/**
* transform to number(float)[]
*/
FF: (val, def = []) => {
if (val === null || val === undefined)
return def;
if (typeof val === 'string')
return val ? val.split(',').map(_ => exports.$T.F(_, 0)) : def;
if (Array.isArray(val))
return val.length > 0 ? val.map(_ => exports.$T.F(_, 0)) : def;
return [exports.$T.F(val)];
},
/**
* float w/ fixed len=3
*/
F3: (n, e = 0.000001) => Number((n + e).toFixed(3)),
/**
* transform to boolean.
*/
B: (val, def = 0) => {
if (val === null || val === undefined)
return def;
if (typeof val === 'boolean')
return val ? 1 : 0;
if (typeof val === 'string' && ['y', 'yes', 't', 'true'].includes(val.toLowerCase()))
return 1;
return engine_1.$U.N(val, def) && 1;
},
/**
* transform to Time number via string | number.
*/
T: (val, def = 0) => {
const checkVal = `${val || ''}`.includes('-');
if (checkVal) {
if (engine_1.$U.dt(val))
return engine_1.$U.dt(val).getTime();
else
throw new Error(`@val[${val}] is invalid!`);
}
else {
return engine_1.$U.dt(engine_1.$U.N(val, def)).getTime();
}
},
/**
* transform to Date formatted string
*/
D: (val, def = '') => {
let s = exports.$T.S(val);
let y;
let m;
let d;
if (s.includes('-')) {
[y, m, d] = s.split('-');
}
else {
y = s.slice(0, 4);
m = s.slice(4, 6);
d = s.slice(6);
}
s = [y, m, d]
.filter(e => (e === null || e === void 0 ? void 0 : e.length) > 0)
.map(e => e.padStart(2, '0'))
.join('-');
if (y && y.length === 4 && !Number.isNaN(Date.parse(s)))
return s;
return def;
},
/**
* date-time format
*/
DT: (val, def = '2020-01-01') => {
const s = exports.$T.D(val, '').split('-'); // must be valid date-format like '2000-01-02'
const d = def.split('-');
return d
.map((d, i) => s[i] || d || '01')
.map(e => e.padStart(2, '0'))
.join('-');
},
/**
* Extract Text
*/
EX: (data, txt1, txt2) => {
data = `${data || ''}`;
const a = data.indexOf(txt1);
const b = a >= 0 ? data.indexOf(txt2, a + txt1.length) : a;
return b > a ? data.substring(a + txt1.length, b) : '';
},
/**
* transform to simple-set.
* @param val json object.
*/
simples: (val, throws = false) => {
//* validate if simple-type (string | number | null | undefined)
const t = typeof val;
if (val === undefined)
return undefined;
else if (val === null || val === '')
return { _: null };
else if (t === 'string' || t === 'number')
return { _: val };
else if (t === 'object' && !Array.isArray(val)) {
const keys = Object.keys(val);
const reName = /^[a-z_][a-zA-Z0-9_\-]*$/;
return keys.reduce((N, k) => {
const v = val[k];
if (v === undefined) {
//* NOP
}
else if (reName.test(k)) {
const t = typeof v;
if (v === null || v === '')
N[k] = null;
else if (t === 'string' || t === 'number')
N[k] = v;
else if (throws)
throw new Error(`.${k}[${v}] is invalid!`);
}
else if (throws)
throw new Error(`.${k} is invalid format!`);
return N;
}, {});
}
else if (throws)
throw new Error(`@val[${t}] is invalid!`);
return {};
},
/**
* catch string between txt1 and txt2
* @param data string
* @param txt1 head
* @param txt2 tail
*/
catch: (data, txt1, txt2) => {
data = typeof data == 'string' ? data : `${data}`;
const a = data.indexOf(txt1);
const b = a >= 0 ? data.indexOf(txt2, a + txt1.length) : a;
const c = b > a ? data.substring(a + txt1.length, b) : '';
return c;
},
/**
* merge simple-set from $org to $new
* @param $org the origin set
* @param $new the update set.
*/
merge: ($org, $new) => {
if (!$new)
return $org;
return Object.keys($new).reduce((N, k) => {
const val = $new[k];
if (val === null || val === undefined)
delete N[k];
else
N[k] = val;
return N;
}, Object.assign({}, $org));
},
/**
* replace message with template.
*/
template: (msg, set) => {
// const msg = $U.env('MSG_PHONE_CODE', '인증 번호는 [{code}] 입니다.') as string;
const tmp = Object.assign({}, set);
return msg.replace(/\{(\w+)\}/g, (a, b) => (tmp[b] !== undefined ? `${tmp[b]}` : `{${b}}`));
},
/**
* make random-code by length
* @param size length of code
* @param rand flag to use random (0 => 0, 1 => max)
*/
makeRandomCode: (size = 6, rand) => {
const flag = rand === undefined || rand === true || typeof rand == 'number' ? true : false;
const min = size >= 1 ? Math.pow(10, size - 1) : 1;
const max = 10 * min - 1;
const val = min + (flag ? Math.floor((max - min) * (typeof rand == 'number' ? rand : Math.random())) : max - min);
return { val, min, max };
},
/**
* 객체 정규화 시킴.
* - null 에 대해서는 특별히 처리.
*/
normal: (N) => Object.keys(N || {}).reduce((M, k) => {
if (k.startsWith('_') || k.startsWith('$'))
return M;
const v = N[k];
//* `null` 은 DynamoDB에서 비어있는 문자임.
M[k] = v === null ? '' : v;
return M;
}, {}),
/**
* transform list to map by `id`
*/
asMap: (list, id = 'id') => list.reduce((M, N) => {
const key = `${N[id] || ''}`;
M[key] = N;
return M;
}, {}),
/**
* compare object, and extract the only diff properties.
*/
diff: (A, B, onlyValid = false) => {
if (!A || !B)
return B;
else if (typeof A !== 'object' || typeof B !== 'object')
return B;
return engine_1.$U
.diff(A, B)
.map(s => `${s || ''}`)
.reduce((M, k) => {
const org = A[k];
const val = B[k];
if (onlyValid) {
if (val !== undefined && val !== null) {
//* dynamo 에서는 null 과 '' 이 같음.
if (org === null && val === '') {
// NOP - due to same value.
}
else {
M[k] = val;
}
}
}
else {
M[k] = val === undefined && org !== undefined ? null : val;
}
return M;
}, {});
},
/**
* get $perf instance.
* ```ts
* const p = $T.perf()
* const took = p.took();
*/
perf: () => {
return new (class MyPerfmance {
constructor(t0) {
this.took = () => {
const t1 = perf_hooks_1.performance.now(); // start of processing
const took = Math.round((t1 - this.t0) / 100) / 10; // in sec.
return took;
};
this.t0 = t0 || perf_hooks_1.performance.now(); // start of processing
}
})();
},
/**
* parse `.meta` property as object.
* @param meta any
*/
parseMeta: (meta) => {
if (typeof meta === 'string' && meta) {
try {
if (meta.startsWith('[') && meta.endsWith(']')) {
const list = JSON.parse(meta);
const $ret = { list };
return $ret;
}
else if (meta.startsWith('{') && meta.endsWith('}')) {
return JSON.parse(meta);
}
else {
const $ret = { type: 'string', value: meta };
return $ret;
}
}
catch (e) {
const $ret = { type: 'string', value: meta, error: (0, test_helper_1.GETERR)(e) };
return $ret;
}
}
else if (meta === null || meta === undefined) {
return null;
}
else if (typeof meta === 'object') {
return meta;
}
else {
const type = typeof meta;
const $ret = { type, value: meta };
return $ret;
}
},
/**
* clear the undefined properties from the cloned object.
* - applied only to 1st depth.
*
* @param N object
* @param $def default if not valid object.
* @returns cloned object
*/
onlyDefined: tools_1.onlyDefined,
};
/**
* random number generator
*/
exports.$rand = {
/**
* list of number[] in n-size.
*/
range: (n) => [...Array(n).keys()],
/**
* generate random number
*/
float: (from, to) => Math.random() * (to - from) + from,
/**
* generate multiple float numbers
*/
floats: (from, to, n) => new Array(n).fill(0).map(() => exports.$rand.float(from, to)),
/**
* generate an integer
*/
integer: (from, to) => Math.floor(exports.$rand.float(Math.ceil(from), Math.floor(to))),
/**
* generate multiple integers
*/
integers: (from, to, n) => new Array(n).fill(0).map(() => exports.$rand.integer(from, to)),
};
/**
* builder to support protocol-service.
* @param context the current context (or service name).
* @param service service name
* @param options additional options.
*/
const $protocol = (context = {}, service, options) => {
//* for backward compartibility. shift arguments if 1st context is string.
const ctx = typeof context === 'string' ? {} : context;
service = typeof context === 'string' ? context : service;
const param = typeof context === 'string' ? service : options === null || options === void 0 ? void 0 : options.param;
const body = typeof context === 'string' ? param : options === null || options === void 0 ? void 0 : options.body;
if (!ctx)
throw new Error(`@context (NextContext) is required!`);
if (!service)
throw new Error(`@service (string) is required!`);
const $proto = cores_1.default.protocol.service;
const isProd = (options === null || options === void 0 ? void 0 : options.isProd) !== undefined ? options === null || options === void 0 ? void 0 : options.isProd : engine_1.$U.env('NS') === 'SS' ? true : false;
//TODO - `STAGE` is not changed from env.yml file @211215.
// _inf(NS, 'NS =', $U.env('NS'), $engine.cores.config.config.get('NS'), process.env['NS']);
// _inf(NS, 'stage =', $U.env('STAGE'), $engine.cores.config.config.get('STAGE'), process.env['STAGE']); //NOTE - STAGE is not changed.
//* prod용 lambda접근을 위한 환경 구성!!!!!
const $param = (p, b, x) => {
const protoParam = Object.assign(Object.assign({}, $proto.fromURL(ctx, asTargetUrl(), p || param, b || body)), x);
if (isProd)
protoParam.stage = 'prod';
return protoParam;
};
const $callback = (callback) => {
if (callback) {
const [path, qs] = callback.split('?');
if (path) {
const [type, id, cmd] = path.split('/');
const param = query_string_1.default.parse(qs);
return { type, id, cmd, param };
}
}
};
//* find the target protocol-url from context.
const asTargetUrl = () => {
if (!service.startsWith('//'))
throw new Error(`@service[${service}] (string) is invalid!`);
if (service.startsWith('//self/')) {
const self = $proto.myProtocolURI(ctx);
const [a, b] = [self.indexOf('@'), self.indexOf('#')];
const target = self.substring(a < 0 ? 'api://'.length : a + 1, b > a ? b : self.length) +
service.substring('//self'.length);
return `api://${target}`;
}
else {
return `api:${service}`;
}
};
//* execute via protocol-service.
const execute = (param, body, mode = 'POST') => $proto.execute($param(param, body, { mode }));
// eslint-disable-next-line prettier/prettier
const enqueue = (param, body, mode = 'POST', callback, delaySeconds = 1) => $proto.enqueue($param(param, body, { mode }), $callback(callback), delaySeconds);
const notify = (param, body, mode = 'POST', callback) => $proto.notify($param(param, body, { mode }), $callback(callback));
//* returns instance.
return {
hello: () => `helper:protocol:${service || ''}`,
asTargetUrl,
execute,
enqueue,
notify,
};
};
exports.$protocol = $protocol;
/**
* get the current config info
*/
const $info = () => {
const $conf = cores_1.default.config.config;
const service = $conf.getService();
const version = $conf.getVersion();
const stage = $conf.getStage();
return { service, version, stage };
};
exports.$info = $info;
/**
* send message to slack/public
*
* @param title 헤터 타이틀
* @param text object or 텍스트 내용
* @param pretext (optional) 텍스트 미리보기용.
* @param params (optional) customize more options.
*/
const $slack = (title, text, pretext, params) => __awaiter(void 0, void 0, void 0, function* () {
var _a, _b;
//* about current service.................
const { service, version, stage } = (0, exports.$info)();
const name = `${service}#${version}` + (stage !== 'prod' ? `/${stage}` : '');
return (0, engine_1.doReportSlack)((params === null || params === void 0 ? void 0 : params.channel) ? `!${params === null || params === void 0 ? void 0 : params.channel}` : 'public', {
channel: (_a = params === null || params === void 0 ? void 0 : params.channel) !== null && _a !== void 0 ? _a : undefined,
attachments: [
exports.$T.onlyDefined({
color: `${(params === null || params === void 0 ? void 0 : params.color) || '#FFB71B' || 'good'}`,
title,
pretext: pretext === null
? undefined
: pretext !== null && pretext !== void 0 ? pretext : ((params === null || params === void 0 ? void 0 : params.scope) ? `#${name} [\`${params.scope}\`]` : undefined),
text: text === null || text === undefined
? undefined
: typeof text === 'string'
? text
: engine_1.$U.json(text),
fields: params === null || params === void 0 ? void 0 : params.fields,
footer: (params === null || params === void 0 ? void 0 : params.footer) === null ? undefined : (_b = params === null || params === void 0 ? void 0 : params.footer) !== null && _b !== void 0 ? _b : `${service}/${stage}#${version}`,
ts: (params === null || params === void 0 ? void 0 : params.ts) === null ? undefined : Math.floor(engine_1.$U.current_time_ms() / 1000),
}),
],
}, params === null || params === void 0 ? void 0 : params.context).catch(e => `#err:${(0, test_helper_1.GETERR)(e)}`);
});
exports.$slack = $slack;
/**
* event producer builder
* @param context current context
* @param defEndpoint (optional) the default endpoint.
*/
const $event = (context, defEndpoint = '') => {
const $protocol = cores_1.default.protocol;
const endpoint = engine_1.$U.env('EVENT_RELAY_SNS', defEndpoint);
if (!endpoint)
throw new Error(`env[EVENT_RELAY_SNS] is required - $event()`);
return {
publish: (body) => __awaiter(void 0, void 0, void 0, function* () { return $protocol.service.broadcast(context, endpoint, body); }),
};
};
exports.$event = $event;
/**
* authentication helper - get identity-id from context
* @param context the current context
*/
function getIdentityId(context) {
var _a;
const identityId = (_a = context === null || context === void 0 ? void 0 : context.identity) === null || _a === void 0 ? void 0 : _a.identityId;
if (!identityId && (context === null || context === void 0 ? void 0 : context.domain) === 'localhost') {
//* use `env[LOCAL_ACCOUNT]` only if runs in local server.
return engine_1.$U.env('LOCAL_ACCOUNT', '');
}
return identityId;
}
exports.getIdentityId = getIdentityId;
/**
* authentication helper - check user is authorized
* - 이 메서드는 AWS IAM 인증 여부만을 확인한다.
* - 따라서 true를 반환한다고 하여 회원 가입이 되어있다는 의미는 아니다.
*
* @param context the current context
* @params params (optional) to override `identity` when running local.
*/
function isUserAuthorized(context, params) {
const identityId = getIdentityId(context);
//WARN - in local server, override the identity w/ param
if ((context === null || context === void 0 ? void 0 : context.domain) === 'localhost') {
//!* override with optional parameter.
if (context) {
context.identity = Object.assign(Object.assign({}, (params !== undefined ? params : context.identity)), { identityId });
}
}
return !!identityId;
}
exports.isUserAuthorized = isUserAuthorized;
/**
* parse range expression
* @param exp range expression (e.g. '[63100 TO 224000]' or '[* TO 150000}')
*/
function parseRange(exp) {
const match = exp.match(/^([\[{])([0-9]+|\*) TO ([0-9]+|\*)([}\]])$/);
if (match && (match[2] !== '*' || match[3] !== '*')) {
const range = {};
if (match[2] !== '*') {
const n = exports.$T.N(match[2]);
if (match[1] === '[')
range.gte = n;
else if (match[1] === '{')
range.gt = n;
}
if (match[3] !== '*') {
const n = exports.$T.N(match[3]);
if (match[4] === ']')
range.lte = n;
else if (match[4] === '}')
range.lt = n;
}
return range;
}
}
exports.parseRange = parseRange;
/**
* customized of `do_parrallel` for safe error-handling.
* - use `.error` to report the internal error.
*
* @param list list of model.
* @param func callback to process of each
* @param size (optional) size of parrallel (default 10)
*/
const my_parrallel = (list, func, size) => __awaiter(void 0, void 0, void 0, function* () {
const results = yield (0, engine_1.do_parrallel)(list, (item, i) => {
const ret = (() => {
try {
return func(item, i);
}
catch (e) {
return Promise.reject(e);
}
})();
const res = ret instanceof Promise ? ret : Promise.resolve(ret);
return res.catch(e => ({ id: item.id, error: (0, test_helper_1.GETERR)(e) }));
}, size);
return results;
});
exports.my_parrallel = my_parrallel;
/**
* run in sequence order
* - same as `my_parrallel(list, func, 1)`;
*
* 주의) 내부 error를 throw 하지 않으니, list 를 전부 처리할때까지 안끝남.
*
* @param list list of model.
* @param func callback to process of each
*/
const my_sequence = (list, func) => (0, exports.my_parrallel)(list, func, 1);
exports.my_sequence = my_sequence;
/**
* create api-http-proxy with sig-v4 agent, which using endpoint as proxy server.
*
* # as cases.
* as proxy agent: GET <endpoint>/<host?>/<path?>
* as direct agent: GET <endpoint>/<id?>/<cmd?>
*
* @param name client-name
* @param endpoint service url (or backbone proxy-url)
* @param sigConfig sig-v4 client-config
* @param options optional parameters
*/
const createSigV4Proxy = (
/** name of client */
name,
/** endpoint of service */
endpoint,
/** sig-v4 client-config */
sigConfig,
/** (optional) parameters */
options) => {
var _a, _b, _c, _d;
const errScope = `createSigV4Proxy(${name !== null && name !== void 0 ? name : ''})`;
if (!endpoint)
throw new Error(`@endpoint (url) is required - ${errScope}`);
const NS = engine_1.$U.NS(`X${name}`, 'magenta'); // NAMESPACE TO BE PRINTED.
const encoder = (_a = options === null || options === void 0 ? void 0 : options.encoder) !== null && _a !== void 0 ? _a : ((name, path) => path);
const relayHeaderKey = (_b = options === null || options === void 0 ? void 0 : options.relayHeaderKey) !== null && _b !== void 0 ? _b : '';
const resultKey = (_c = options === null || options === void 0 ? void 0 : options.resultKey) !== null && _c !== void 0 ? _c : '';
const verbose = (_d = options === null || options === void 0 ? void 0 : options.verbose) !== null && _d !== void 0 ? _d : false;
const _log = verbose ? engine_1.default.log : () => { };
const _err = verbose ? engine_1.default.err : () => { };
const _inf = verbose ? engine_1.default.inf : () => { };
// initialize AWS SigV4 Client
const sigClient = sigConfig ? (0, sig_v4_1.sigV4Client)(Object.assign(Object.assign({}, sigConfig), { endpoint })) : null;
/**
* class: `ApiHttpProxy`
* - http proxy client via backbone's web.
*/
return new (class {
// eslint-disable-next-line @typescript-eslint/no-empty-function
constructor(headers) {
this.headers = headers;
this.hello = () => `http-web-proxy:${name}`;
}
doProxy(method, path1, path2, $param, $body, $ctx) {
if (!method)
throw new Error(`@method is required - ${errScope}`);
if (!(endpoint === null || endpoint === void 0 ? void 0 : endpoint.startsWith('http')) && !(endpoint === null || endpoint === void 0 ? void 0 : endpoint.startsWith('https')))
throw new Error(`@endpoint[${endpoint}] is invalid - ${errScope}`);
_inf(NS, `doProxy(${method})..`);
const _isNa = (a) => a === undefined || a === null;
_log(NS, '> endpoint =', endpoint);
_isNa(path1) && _log(NS, `> host(id) =`, typeof path1, path1);
_isNa(path2) && _log(NS, `> path(cmd) =`, typeof path2, path2);
//* prepare request parameters
// eslint-disable-next-line prettier/prettier
const query_string = _isNa($param) ? '' : (typeof $param == 'object' ? query_string_1.default.stringify($param) : `${$param}`);
const url = endpoint +
(_isNa(path1) ? '' : `/${encoder('host', path1)}`) +
(_isNa(path1) && _isNa(path2) ? '' : `/${encoder('path', path2)}`) +
(!query_string ? '' : '?' + query_string);
const request = request_1.default;
const options = {
method,
uri: url,
headers: Object.assign({}, this.headers),
body: $body === null ? undefined : $body,
json: typeof $body === 'string' ? false : true,
};
if (sigClient) {
const signedRequest = sigClient.signRequest({
method,
path: (_isNa(path1) ? '' : `/${encoder('host', path1)}`) +
(_isNa(path1) && _isNa(path2) ? '' : `/${encoder('path', path2)}`),
queryParams: $param,
headers: options.headers,
body: $body,
});
options.headers = Object.assign(Object.assign({}, options.headers), signedRequest.headers);
options.uri = signedRequest.url;
}
//* relay HEADERS to `WEB-API`
if (this.headers) {
const headers = this.headers;
options.headers = Object.keys(headers).reduce((H, key) => {
const val = headers[key];
const name = `${relayHeaderKey}${key}`;
const text = `${val}`;
H[name] = text;
return H;
}, options.headers);
}
_log(NS, ' url :=', options.method, url);
_log(NS, '*', options.method, url, options.json ? 'json' : 'plain');
_log(NS, '> options =', engine_1.$U.json(options));
//* returns promise
return new Promise((resolve, reject) => {
//* start request..
request(options, function (error, response, body) {
error && _err(NS, '>>>>> requested! err=', error);
if (error)
return reject(error instanceof Error ? error : new Error((0, test_helper_1.GETERR)(error)));
//* detect trouble.
const statusCode = response.statusCode;
const statusMessage = response.statusMessage;
//* if not in success
if (statusCode !== 200 && statusCode !== 201) {
const msg = body ? (0, test_helper_1.GETERR)(body) : `${statusMessage || ''}`;
if (statusCode === 400 || statusCode === 404) {
const title = `${(statusCode == 404 ? '' : statusMessage) || 'NOT FOUND'}`.toUpperCase();
const message = msg.startsWith('404 NOT FOUND') ? msg : `${statusCode} ${title} - ${msg}`;
return reject(new Error(message));
}
statusMessage && _log(NS, `> statusMessage[${statusCode}] =`, statusMessage);
body && _log(NS, `> body[${statusCode}] =`, engine_1.$U.json(body));
return reject(new Error(`${statusCode} ${statusMessage || 'FAILURE'} - ${msg}`));
}
//* try to parse body.
try {
if (body && typeof body == 'string' && body.startsWith('{') && body.endsWith('}')) {
body = JSON.parse(body);
}
else if (body && typeof body == 'string' && body.startsWith('[') && body.endsWith(']')) {
body = JSON.parse(body);
}
}
catch (e) {
_err(NS, '!WARN! parse(body) =', e instanceof Error ? e : engine_1.$U.json(e));
}
//* ok! succeeded.
resolve(body);
});
}).then((res) => {
if (resultKey && res && res[resultKey] !== undefined)
return res[resultKey];
return res;
});
}
})(options === null || options === void 0 ? void 0 : options.headers);
};
exports.createSigV4Proxy = createSigV4Proxy;
//# sourceMappingURL=helpers.js.map
;