@nocobase/plugin-action-custom-request
Version:
Sending a request to any HTTP service supports sending context data to the target service.
226 lines (224 loc) • 7.69 kB
JavaScript
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var send_exports = {};
__export(send_exports, {
getParsedValue: () => getParsedValue,
send: () => send
});
module.exports = __toCommonJS(send_exports);
var import_utils = require("@nocobase/utils");
var import_evaluators = require("@nocobase/evaluators");
var import_axios = __toESM(require("axios"));
function toJSON(value) {
if (typeof value === "string") {
try {
return JSON.parse(value);
} catch (error) {
return value;
}
}
return value;
}
const getHeaders = (headers) => {
return Object.keys(headers).reduce((hds, key) => {
if (key.toLocaleLowerCase().startsWith("x-")) {
hds[key] = headers[key];
}
return hds;
}, {});
};
const arrayToObject = (arr) => {
return arr.reduce((acc, cur) => {
acc[cur.name] = cur.value;
return acc;
}, {});
};
const omitNullAndUndefined = (obj) => {
return Object.keys(obj).reduce((acc, cur) => {
if (obj[cur] !== null && typeof obj[cur] !== "undefined") {
acc[cur] = obj[cur];
}
return acc;
}, {});
};
const CurrentUserVariableRegExp = /{{\s*(currentUser[^}]+)\s*}}/g;
const getCurrentUserAppends = (str, user) => {
const matched = str.matchAll(CurrentUserVariableRegExp);
return Array.from(matched).map((item) => {
const keys = (item == null ? void 0 : item[1].split(".")) || [];
const appendKey = keys[1];
if (keys.length > 2 && !Reflect.has(user || {}, appendKey)) {
return appendKey;
}
}).filter(Boolean);
};
const getParsedValue = (value, variables) => {
const template = (0, import_utils.parse)(value);
template.parameters.forEach(({ key }) => {
(0, import_evaluators.appendArrayColumn)(variables, key);
});
return template(variables);
};
async function send(ctx, next) {
var _a, _b, _c, _d;
const resourceName = ctx.action.resourceName;
const { filterByTk, values = {} } = ctx.action.params;
const {
currentRecord = {
id: 0,
appends: [],
data: {}
},
$nForm,
$nSelectedRecord
} = values;
if (ctx.state.currentRole !== "root") {
const crRepo = ctx.db.getRepository("uiButtonSchemasRoles");
const hasRoles = await crRepo.find({
filter: {
uid: filterByTk
}
});
if (hasRoles.length) {
if (!hasRoles.some((item) => ctx.state.currentRoles.includes(item.roleName))) {
return ctx.throw(403, "custom request no permission");
}
}
}
const repo = ctx.db.getRepository(resourceName);
const requestConfig = await repo.findOne({
filter: {
key: filterByTk
}
});
if (!requestConfig) {
ctx.throw(404, "request config not found");
}
ctx.withoutDataWrapping = true;
const {
dataSourceKey,
collectionName,
url,
headers = [],
params = [],
data = {},
...options
} = requestConfig.options || {};
if (!url) {
return ctx.throw(400, ctx.t("Please configure the request settings first", { ns: "action-custom-request" }));
}
let currentRecordValues = {};
if (collectionName && typeof currentRecord.id !== "undefined") {
const app = ctx.app;
const dataSource = app.dataSourceManager.get(dataSourceKey || currentRecord.dataSourceKey || "main");
const recordRepo = dataSource.collectionManager.getRepository(collectionName);
currentRecordValues = ((_a = await recordRepo.findOne({
filterByTk: currentRecord.id,
appends: currentRecord.appends
})) == null ? void 0 : _a.toJSON()) || {};
}
let currentUser = ctx.auth.user;
const userAppends = getCurrentUserAppends(
JSON.stringify(url) + JSON.stringify(headers) + JSON.stringify(params) + JSON.stringify(data),
ctx.auth.user
);
if (userAppends.length) {
currentUser = ((_b = await ctx.db.getRepository("users").findOne({
filterByTk: ctx.auth.user.id,
appends: userAppends
})) == null ? void 0 : _b.toJSON()) || {};
}
const variables = {
currentRecord: {
...currentRecordValues,
...currentRecord.data
},
currentUser,
currentTime: (/* @__PURE__ */ new Date()).toISOString(),
$nToken: ctx.getBearerToken(),
$nForm,
$env: ctx.app.environment.getVariables(),
$nSelectedRecord
};
const axiosRequestConfig = {
baseURL: ctx.origin,
...options,
url: getParsedValue(url, variables),
headers: {
Authorization: "Bearer " + ctx.getBearerToken(),
...getHeaders(ctx.headers),
...omitNullAndUndefined(getParsedValue(arrayToObject(headers), variables))
},
params: getParsedValue(arrayToObject(params), variables),
data: getParsedValue(toJSON(data), variables)
};
const requestUrl = import_axios.default.getUri(axiosRequestConfig);
this.logger.info(`custom-request:send:${filterByTk} request url ${requestUrl}`);
this.logger.info(
`custom-request:send:${filterByTk} request config ${JSON.stringify({
...axiosRequestConfig,
headers: {
...axiosRequestConfig.headers,
Authorization: null
}
})}`
);
try {
const res = await (0, import_axios.default)(axiosRequestConfig);
this.logger.info(`custom-request:send:${filterByTk} success`);
ctx.body = res.data;
if (res.headers["content-disposition"]) {
ctx.set("Content-Disposition", res.headers["content-disposition"]);
}
} catch (err) {
if (import_axios.default.isAxiosError(err)) {
ctx.status = ((_c = err.response) == null ? void 0 : _c.status) || 500;
ctx.body = ((_d = err.response) == null ? void 0 : _d.data) || { message: err.message };
this.logger.error(
`custom-request:send:${filterByTk} error. status: ${ctx.status}, body: ${typeof ctx.body === "string" ? ctx.body : JSON.stringify(ctx.body)}`
);
} else {
this.logger.error(`custom-request:send:${filterByTk} error. status: ${ctx.status}, message: ${err.message}`);
ctx.throw(500, err == null ? void 0 : err.message);
}
}
return next();
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
getParsedValue,
send
});