diff --git a/packages/core/client/src/schema-component/antd/variable/RawTextArea.tsx b/packages/core/client/src/schema-component/antd/variable/RawTextArea.tsx index 9e6386535..866a24dde 100644 --- a/packages/core/client/src/schema-component/antd/variable/RawTextArea.tsx +++ b/packages/core/client/src/schema-component/antd/variable/RawTextArea.tsx @@ -64,7 +64,6 @@ export function RawTextArea(props): JSX.Element { &:not(:hover) { border-right-color: transparent; border-top-color: transparent; - border-bottom-color: transparent; } background-color: transparent; ` diff --git a/packages/plugins/@nocobase/plugin-custom-request/src/server/__tests__/__snapshots__/actions.test.ts.snap b/packages/plugins/@nocobase/plugin-custom-request/src/server/__tests__/__snapshots__/actions.test.ts.snap index 81af83be9..bd9980513 100644 --- a/packages/plugins/@nocobase/plugin-custom-request/src/server/__tests__/__snapshots__/actions.test.ts.snap +++ b/packages/plugins/@nocobase/plugin-custom-request/src/server/__tests__/__snapshots__/actions.test.ts.snap @@ -7,3 +7,23 @@ exports[`actions send currentRecord.data 1`] = ` "username": "testname", } `; + +exports[`actions send currentUser with association data 1`] = ` +{ + "a": [ + "member", + "root", + "admin", + ], + "b": [ + "{{t("Member")}}", + "{{t("Root")}}", + "{{t("Admin")}}", + ], + "c": [ + 1, + 1, + 1, + ], +} +`; diff --git a/packages/plugins/@nocobase/plugin-custom-request/src/server/__tests__/actions.test.ts b/packages/plugins/@nocobase/plugin-custom-request/src/server/__tests__/actions.test.ts index 098ad74fd..4f0ef1d0b 100644 --- a/packages/plugins/@nocobase/plugin-custom-request/src/server/__tests__/actions.test.ts +++ b/packages/plugins/@nocobase/plugin-custom-request/src/server/__tests__/actions.test.ts @@ -12,7 +12,7 @@ describe('actions', () => { beforeAll(async () => { app = mockServer({ registerActions: true, - acl: false, + acl: true, plugins: ['users', 'auth', 'acl', 'custom-request'], }); @@ -20,7 +20,8 @@ describe('actions', () => { db = app.db; repo = db.getRepository('customRequests'); agent = app.agent(); - resource = agent.resource('customRequests'); + resource = (agent.set('X-Role', 'admin') as any).resource('customRequests'); + await agent.login(1); }); describe('send', () => { @@ -66,5 +67,94 @@ describe('actions', () => { expect(res.status).toBe(200); expect(params).toMatchSnapshot(); }); + + test('parse o2m variables correctly', async () => { + await repo.create({ + values: { + key: 'o2m', + options: { + url: '/customRequests:test', + method: 'GET', + data: { + o2m: '{{ currentRecord.o2m.id }}', + }, + }, + }, + }); + + const res = await resource.send({ + filterByTk: 'o2m', + values: { + currentRecord: { + data: { + o2m: [ + { + id: 1, + }, + { + id: 2, + }, + ], + }, + }, + }, + }); + expect(res.status).toBe(200); + expect(params).toMatchObject({ + o2m: [1, 2], + }); + }); + + test('currentRecord.id with collectionName works fine', async () => { + await repo.create({ + values: { + key: 'test2', + options: { + method: 'GET', + headers: [], + params: [{ name: 'userId', value: '{{currentRecord.id}}' }], + url: '/users:get', + collectionName: 'users', + data: null, + }, + }, + }); + + const userId = 1; + const res = await resource.send({ + filterByTk: 'test2', + values: { + currentRecord: { + id: userId, + }, + }, + }); + expect(res.status).toBe(200); + expect(res.body.data.id).toBe(userId); + }); + + test('currentUser with association data', async () => { + await repo.create({ + values: { + key: 'currentUser-with-association-data', + options: { + method: 'POST', + headers: [], + data: { + a: '{{currentUser.roles.name}}', + b: '{{currentUser.roles.title}}', + c: '{{currentUser.roles.rolesUsers.userId}}', + }, + url: '/customRequests:test', + }, + }, + }); + + const res = await resource.send({ + filterByTk: 'currentUser-with-association-data', + }); + expect(res.status).toBe(200); + expect(params).toMatchSnapshot(); + }); }); }); diff --git a/packages/plugins/@nocobase/plugin-custom-request/src/server/actions/send.ts b/packages/plugins/@nocobase/plugin-custom-request/src/server/actions/send.ts index f4fa214b6..ec27bab10 100644 --- a/packages/plugins/@nocobase/plugin-custom-request/src/server/actions/send.ts +++ b/packages/plugins/@nocobase/plugin-custom-request/src/server/actions/send.ts @@ -3,6 +3,7 @@ import { parse } from '@nocobase/utils'; import axios from 'axios'; import CustomRequestPlugin from '../plugin'; +import { appendArrayColumn } from '@nocobase/evaluators'; const getHeaders = (headers: Record) => { return Object.keys(headers).reduce((hds, key) => { @@ -29,6 +30,21 @@ const omitNullAndUndefined = (obj: any) => { }, {}); }; +const CurrentUserVariableRegExp = /{{\s*(currentUser[^}]+)\s*}}/g; + +const getCurrentUserAppends = (str: string, user) => { + const matched = str.matchAll(CurrentUserVariableRegExp); + return Array.from(matched) + .map((item) => { + const keys = item?.[1].split('.') || []; + const appendKey = keys[1]; + if (keys.length > 2 && !Reflect.has(user || {}, appendKey)) { + return appendKey; + } + }) + .filter(Boolean); +}; + export async function send(this: CustomRequestPlugin, ctx: Context, next: Next) { const { filterByTk, resourceName, values = {} } = ctx.action.params; const { @@ -69,35 +85,62 @@ export async function send(this: CustomRequestPlugin, ctx: Context, next: Next) ctx.withoutDataWrapping = true; const { collectionName, url, headers = [], params = [], data = {}, ...options } = requestConfig.options; - let currentRecordVariables = {}; + let currentRecordValues = {}; if (collectionName && typeof currentRecord.id !== 'undefined') { const recordRepo = ctx.db.getRepository(collectionName); - currentRecordVariables = await recordRepo.findOne({ - filterByTk: currentRecord.id, - appends: currentRecord.appends, - }); + currentRecordValues = + ( + await recordRepo.findOne({ + filterByTk: currentRecord.id, + appends: currentRecord.appends, + }) + )?.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 = + ( + await ctx.db.getRepository('users').findOne({ + filterByTk: ctx.auth.user.id, + appends: userAppends, + }) + )?.toJSON() || {}; } const variables = { currentRecord: { - ...currentRecordVariables, + ...currentRecordValues, ...currentRecord.data, }, - currentUser: ctx.auth.user, + currentUser, currentTime: new Date().toISOString(), }; + const getParsedValue = (value) => { + const template = parse(value); + template.parameters.forEach(({ key }) => { + appendArrayColumn(variables, key); + }); + return template(variables); + }; + const axiosRequestConfig = { baseURL: ctx.origin, ...options, - url: parse(url)(variables), + url: getParsedValue(url), headers: { Authorization: 'Bearer ' + ctx.getBearerToken(), ...getHeaders(ctx.headers), - ...omitNullAndUndefined(parse(arrayToObject(headers))(variables)), + ...omitNullAndUndefined(getParsedValue(arrayToObject(headers))), }, - params: parse(arrayToObject(params))(variables), - data: parse(data)(variables), + params: getParsedValue(arrayToObject(params)), + data: getParsedValue(data), }; const requestUrl = axios.getUri(axiosRequestConfig);