fix(plugin-custom-request): variables not work in form block (#2873)

* fix: custom-request not work in form

* feat: setup logger

* test: add test case and fix crush error

* feat: improve logger

* feat: update log option

* fix: logger

---------

Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
Dunqing 2023-10-19 09:49:33 -05:00 committed by GitHub
parent 07c5b7b0eb
commit 010c286f7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 111 additions and 50 deletions

View File

@ -19,7 +19,7 @@ const Transports = {
...options, ...options,
}); });
}, },
dailyRotateFile(options: any) { dailyRotateFile(options: any = {}) {
let dirname = getLoggerFilePath(); let dirname = getLoggerFilePath();
if (!path.isAbsolute(dirname)) { if (!path.isAbsolute(dirname)) {
dirname = path.resolve(process.cwd(), dirname); dirname = path.resolve(process.cwd(), dirname);

View File

@ -47,7 +47,7 @@ export const useCustomizeRequestActionProps = () => {
await form.submit(); await form.submit();
} }
const requestConfig = {}; let formValues = {};
const methods = ['POST', 'PUT', 'PATCH']; const methods = ['POST', 'PUT', 'PATCH'];
if (xAction === 'customize:form:request' && methods.includes(options['method'])) { if (xAction === 'customize:form:request' && methods.includes(options['method'])) {
const fieldNames = fields.map((field) => field.name); const fieldNames = fields.map((field) => field.name);
@ -60,7 +60,7 @@ export const useCustomizeRequestActionProps = () => {
resource, resource,
actionFields: getActiveFieldsName?.('form') || [], actionFields: getActiveFieldsName?.('form') || [],
}); });
requestConfig['data'] = values; formValues = values;
} }
actionField.data ??= {}; actionField.data ??= {};
@ -70,10 +70,10 @@ export const useCustomizeRequestActionProps = () => {
url: `/customRequests:send/${fieldSchema['x-uid']}`, url: `/customRequests:send/${fieldSchema['x-uid']}`,
method: 'POST', method: 'POST',
data: { data: {
requestConfig,
currentRecord: { currentRecord: {
id: record[getPrimaryKey()], id: record[getPrimaryKey()],
appends: service.params[0].appends, appends: service.params[0].appends,
data: formValues,
}, },
}, },
}); });

View File

@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`actions send basic 1`] = `undefined`;
exports[`actions send currentRecord.data 1`] = `
{
"username": "testname",
}
`;

View File

@ -9,10 +9,10 @@ describe('actions', () => {
let agent: ReturnType<MockServer['agent']>; let agent: ReturnType<MockServer['agent']>;
let resource: ReturnType<ReturnType<MockServer['agent']>['resource']>; let resource: ReturnType<ReturnType<MockServer['agent']>['resource']>;
beforeEach(async () => { beforeAll(async () => {
app = mockServer({ app = mockServer({
registerActions: true, registerActions: true,
acl: true, acl: false,
plugins: ['users', 'auth', 'acl', 'custom-request'], plugins: ['users', 'auth', 'acl', 'custom-request'],
}); });
@ -24,35 +24,47 @@ describe('actions', () => {
}); });
describe('send', () => { describe('send', () => {
let params; let params = null;
beforeEach(async () => { beforeAll(async () => {
app.resource({ app.resourcer.getResource('customRequests').addAction('test', (ctx: Context) => {
name: 'custom-request-test', params = ctx.action.params.values;
actions: { return ctx.action.params.values;
test(ctx: Context) {
params = ctx.action.params;
console.log('🚀 ~ file: actions.test.ts:34 ~ test ~ params:', params);
return 'test ok';
},
},
}); });
});
beforeEach(async () => {
await repo.create({ await repo.create({
values: { values: {
key: 'test', key: 'test',
options: { options: {
url: 'http://localhost:13000/api/custom-request-test:test', url: '/customRequests:test',
method: 'GET', method: 'GET',
data: {
username: '{{ currentRecord.username }}',
},
}, },
}, },
}); });
}); });
test('basic', async () => { test('basic', async () => {
const res = await resource.send({ const res = await resource.send({
filterByTk: 'test', filterByTk: 'test',
}); });
console.log(res.status); expect(res.status).toBe(200);
expect(params).toMatchSnapshot();
});
test('currentRecord.data', async () => {
const res = await resource.send({
filterByTk: 'test',
values: {
currentRecord: {
data: {
username: 'testname',
},
},
},
});
expect(res.status).toBe(200);
expect(params).toMatchSnapshot();
}); });
}); });
}); });

View File

@ -1,8 +1,8 @@
import { Context, Next } from '@nocobase/actions'; import { Context, Next } from '@nocobase/actions';
import actions from '@nocobase/actions';
import { parse } from '@nocobase/utils'; import { parse } from '@nocobase/utils';
import axios from 'axios'; import axios from 'axios';
import CustomRequestPlugin from '../plugin';
const getHeaders = (headers: Record<string, any>) => { const getHeaders = (headers: Record<string, any>) => {
return Object.keys(headers).reduce((hds, key) => { return Object.keys(headers).reduce((hds, key) => {
@ -29,14 +29,14 @@ const omitNullAndUndefined = (obj: any) => {
}, {}); }, {});
}; };
export async function send(ctx: Context, next: Next) { export async function send(this: CustomRequestPlugin, ctx: Context, next: Next) {
const { filterByTk, resourceName, values = {} } = ctx.action.params; const { filterByTk, resourceName, values = {} } = ctx.action.params;
const { const {
currentRecord: { id: currentRecordId, appends: currentRecordAppends } = { currentRecord = {
id: 0, id: 0,
appends: [], appends: [],
data: {},
}, },
requestConfig: requestConfigFirst = {},
} = values; } = values;
// root role has all permissions // root role has all permissions
@ -68,24 +68,26 @@ export async function send(ctx: Context, next: Next) {
ctx.withoutDataWrapping = true; ctx.withoutDataWrapping = true;
const { collectionName, url, headers = {}, params = {}, data = {}, ...options } = requestConfig.options; const { collectionName, url, headers = [], params = [], data = {}, ...options } = requestConfig.options;
let currentRecord = {}; let currentRecordVariables = {};
if (collectionName && typeof currentRecordId !== 'undefined') { if (collectionName && typeof currentRecord.id !== 'undefined') {
const recordRepo = ctx.db.getRepository(collectionName); const recordRepo = ctx.db.getRepository(collectionName);
currentRecord = await recordRepo.findOne({ currentRecordVariables = await recordRepo.findOne({
filterByTk: currentRecordId, filterByTk: currentRecord.id,
appends: currentRecordAppends, appends: currentRecord.appends,
}); });
} }
const variables = { const variables = {
currentRecord, currentRecord: {
...currentRecordVariables,
...currentRecord.data,
},
currentUser: ctx.auth.user, currentUser: ctx.auth.user,
currentTime: new Date().toISOString(), currentTime: new Date().toISOString(),
}; };
try { const axiosRequestConfig = {
ctx.body = await axios({
baseURL: ctx.origin, baseURL: ctx.origin,
...options, ...options,
url: parse(url)(variables), url: parse(url)(variables),
@ -95,18 +97,37 @@ export async function send(ctx: Context, next: Next) {
...omitNullAndUndefined(parse(arrayToObject(headers))(variables)), ...omitNullAndUndefined(parse(arrayToObject(headers))(variables)),
}, },
params: parse(arrayToObject(params))(variables), params: parse(arrayToObject(params))(variables),
data: parse({ data: parse(data)(variables),
...data, };
...requestConfigFirst?.data,
})(variables), const requestUrl = axios.getUri(axiosRequestConfig);
}).then((res) => { 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 {
ctx.body = await axios(axiosRequestConfig).then((res) => {
this.logger.info(`custom-request:send:${filterByTk} success`);
return res.data; return res.data;
}); });
} catch (err: any) { } catch (err) {
if (axios.isAxiosError(err)) { if (axios.isAxiosError(err)) {
ctx.status = err.response?.status || 500; ctx.status = err.response?.status || 500;
ctx.body = err.response?.data || { message: err.message }; ctx.body = err.response?.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 { } else {
this.logger.error(`custom-request:send:${filterByTk} error. status: ${ctx.status}, message: ${err.message}`);
ctx.throw(500, err?.message); ctx.throw(500, err?.message);
} }
} }

View File

@ -1,12 +1,31 @@
import { Logger, LoggerOptions, Transports, createLogger, getLoggerFilePath } from '@nocobase/logger';
import { InstallOptions, Plugin } from '@nocobase/server'; import { InstallOptions, Plugin } from '@nocobase/server';
import { resolve } from 'path'; import { resolve } from 'path';
import { send } from './actions/send';
import { listByCurrentRole } from './actions/listByCurrentRole'; import { listByCurrentRole } from './actions/listByCurrentRole';
import { send } from './actions/send';
export class CustomRequestPlugin extends Plugin { export class CustomRequestPlugin extends Plugin {
logger: Logger;
afterAdd() {} afterAdd() {}
beforeLoad() {} beforeLoad() {
this.logger = this.getLogger();
}
getLogger(): Logger {
const logger = createLogger({
transports: [
'console',
Transports.dailyRotateFile({
dirname: getLoggerFilePath('custom-request'),
filename: this.app.name + '-%DATE%.log',
}),
],
} as LoggerOptions);
return logger;
}
async load() { async load() {
await this.db.import({ await this.db.import({
@ -16,7 +35,7 @@ export class CustomRequestPlugin extends Plugin {
this.app.resource({ this.app.resource({
name: 'customRequests', name: 'customRequests',
actions: { actions: {
send, send: send.bind(this),
listByCurrentRole, listByCurrentRole,
}, },
}); });