UNPKG

@chevre/domain

Version:

Chevre Domain Library for Node.js

214 lines (213 loc) 11.1 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.triggerWebhook = triggerWebhook; const crypto = require("crypto"); const http_status_1 = require("http-status"); const factory = require("../../factory"); const USERNAME_ARGUMENT_REQUIRED_MESSAGE = 'Missing required key \'Username\' in params'; function triggerWebhook(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { var _a, _b; const setting = yield repos.setting.findOne({ project: { id: { $eq: '*' } } }, ['triggerWebhook']); const retryCountMax = (_a = setting === null || setting === void 0 ? void 0 : setting.triggerWebhook) === null || _a === void 0 ? void 0 : _a.retryCountMax; const retryIntervalInMS = (_b = setting === null || setting === void 0 ? void 0 : setting.triggerWebhook) === null || _b === void 0 ? void 0 : _b.retryIntervalInMS; if (typeof retryCountMax !== 'number') { throw new factory.errors.NotFound('setting.triggerWebhook.retryCountMax'); } if (typeof retryIntervalInMS !== 'number') { throw new factory.errors.NotFound('setting.triggerWebhook.retryIntervalInMS'); } // retryableにする(2023-05-12~) let retry = true; let numberOfTry = 0; while (numberOfTry >= 0) { try { numberOfTry += 1; if (numberOfTry > retryCountMax) { retry = false; } // retryInterval if (numberOfTry > 1) { yield sleep(retryIntervalInMS * (numberOfTry - 1)); } yield processInformAction(params, setting)(repos); break; } catch (error) { if (retry) { continue; } else { throw error; } } } }); } function sleep(waitTime) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve) => { setTimeout(() => { resolve(); }, waitTime); }); }); } function createInformActionAttributes(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { var _a, _b; const { about, object, purpose, recipient, project, id, typeOf } = params; // redefine recipient(2025-02-15~) let informRecipient; // let informRecipient: factory.action.interact.inform.IRecipient | factory.action.interact.inform.IRecipientDeprecated = { // ...recipient // // url: target.urlTemplate // discontinue(2025-02-15~) // }; if ((recipient === null || recipient === void 0 ? void 0 : recipient.typeOf) === factory.organizationType.Corporation && typeof recipient.id === 'string') { informRecipient = { id: recipient.id, name: recipient.name, typeOf: recipient.typeOf }; } else if ((recipient === null || recipient === void 0 ? void 0 : recipient.typeOf) === factory.creativeWorkType.WebApplication) { informRecipient = { name: recipient.name, typeOf: recipient.typeOf }; } let target; if (typeOf === factory.actionType.InformAction && typeof id === 'string' && id !== '') { // find target from potentialAction(2025-02-06~) const potentialInformAction = (yield repos.potentialAction.projectFields({ limit: 1, page: 1, id: { $eq: id }, typeOf: { $eq: typeOf } }, ['target', 'recipient'])).shift(); if (potentialInformAction === undefined) { throw new factory.errors.NotFound(factory.actionType.InformAction); } if (typeof ((_a = potentialInformAction.recipient) === null || _a === void 0 ? void 0 : _a.name) === 'string') { informRecipient = { name: potentialInformAction.recipient.name, typeOf: potentialInformAction.recipient.typeOf }; } target = potentialInformAction.target; } else { if (((_b = params.target) === null || _b === void 0 ? void 0 : _b.typeOf) === 'EntryPoint') { target = params.target; } } if (informRecipient === undefined) { throw new factory.errors.ArgumentNull('recipient'); } if (target === undefined) { throw new factory.errors.ArgumentNull('target'); } return Object.assign(Object.assign(Object.assign({ agent: { id: project.id, typeOf: factory.organizationType.Project }, object, project: { id: project.id, typeOf: factory.organizationType.Project }, recipient: informRecipient, typeOf: factory.actionType.InformAction }, (typeof (purpose === null || purpose === void 0 ? void 0 : purpose.typeOf) === 'string') ? { purpose } : undefined), (typeof (about === null || about === void 0 ? void 0 : about.typeOf) === 'string') ? { about } : undefined), (typeof (target === null || target === void 0 ? void 0 : target.typeOf) === 'string') ? { target } : undefined // add(2025-02-06~) ); }); } function signRequest(params) { const { requestBody, timestamp, secretKey } = params; const payload = `${requestBody}${timestamp.toString()}`; return crypto.createHmac('sha256', secretKey) .update(payload) .digest('hex'); } // tslint:disable-next-line:max-func-body-length function processInformAction(params, options) { // tslint:disable-next-line:cyclomatic-complexity return (repos) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f; const timeout = (_a = options === null || options === void 0 ? void 0 : options.triggerWebhook) === null || _a === void 0 ? void 0 : _a.timeout; const useFetchAPI = ((_b = options === null || options === void 0 ? void 0 : options.triggerWebhook) === null || _b === void 0 ? void 0 : _b.useFetchAPI) === true; const secretKey = (_c = options === null || options === void 0 ? void 0 : options.triggerWebhook) === null || _c === void 0 ? void 0 : _c.secretKey; const headerIdentifier = (_d = options === null || options === void 0 ? void 0 : options.triggerWebhook) === null || _d === void 0 ? void 0 : _d.headerIdentifier; const { identifier } = params; const informActionAttributes = yield createInformActionAttributes(params)(repos); const action = yield repos.action.start(informActionAttributes); let result = {}; try { const urlTemplate = (_e = informActionAttributes.target) === null || _e === void 0 ? void 0 : _e.urlTemplate; if (typeof urlTemplate === 'string') { const requestBody = JSON.stringify(Object.assign({ data: informActionAttributes.object, id: action.id }, (typeof identifier === 'string' && identifier !== '') ? { identifier } : undefined)); const timestamp = action.startDate.valueOf(); // sign request(2024-10-28~) let signature; if (typeof secretKey === 'string' && secretKey !== '' && typeof headerIdentifier === 'string' && headerIdentifier !== '') { signature = signRequest({ requestBody, timestamp, secretKey }); } if (useFetchAPI) { try { const headers = new Headers({ 'Content-Type': 'application/json' }); if (typeof headerIdentifier === 'string' && headerIdentifier !== '') { headers.append(`X-${headerIdentifier}-Webhook-Timestamp`, String(timestamp)); if (typeof signature === 'string') { headers.append(`X-${headerIdentifier}-Webhook-Signature`, signature); } } const res = yield fetch(urlTemplate, Object.assign({ method: 'POST', headers, body: requestBody }, (typeof timeout === 'number') ? { signal: AbortSignal.timeout(timeout) } : undefined)); let body; try { body = yield res.text(); } catch (error) { // no op } switch (res.status) { case http_status_1.OK: case http_status_1.CREATED: case http_status_1.ACCEPTED: case http_status_1.NO_CONTENT: result = { statusCode: res.status, useFetchAPI }; break; default: throw new factory.errors.Internal(`statusCode: ${res.status} body: ${body}`); } } catch (err) { throw err; } } else { throw new factory.errors.NotImplemented('only useFetchAPI implemented'); } } } catch (error) { let throwsError = true; // プロジェクト固有の特別対応 if (error.statusCode === http_status_1.INTERNAL_SERVER_ERROR && ((_f = error.body) === null || _f === void 0 ? void 0 : _f.message) === USERNAME_ARGUMENT_REQUIRED_MESSAGE) { throwsError = false; } if (throwsError) { try { yield repos.action.giveUp({ typeOf: action.typeOf, id: action.id, error }); } catch (__) { // 失敗したら仕方ない } throw error; } } yield repos.action.completeWithVoid({ typeOf: action.typeOf, id: action.id, result: result }); }); }