@chevre/domain
Version:
Chevre Domain Library for Node.js
214 lines (213 loc) • 11.1 kB
JavaScript
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 });
});
}
;