tachybase_todo/packages/server/src/middleware.ts
Junyi b2fe087fc2
Feature: custom operators for querying (#48)
* feat: add some custom operators for querying

* feat: add some custom operators for querying

* test: fix cases

* improve custom operator function

* improve filter field component

Co-authored-by: chenos <chenlinxh@gmail.com>
2020-12-28 21:08:13 +08:00

129 lines
4.0 KiB
TypeScript

import qs from 'qs';
import compose from 'koa-compose';
import { pathToRegexp } from 'path-to-regexp';
import Resourcer, { getNameByParams, KoaMiddlewareOptions, parseRequest, parseQuery, ResourcerContext } from '@nocobase/resourcer';
import Database, { BELONGSTO, BELONGSTOMANY, HASMANY, HASONE } from '@nocobase/database';
interface MiddlewareOptions extends KoaMiddlewareOptions {
resourcer?: Resourcer;
database?: Database;
}
/**
* database + resourcer 结合的中间件(暂时不知道起什么名好)
*
* @param options
*/
export function middleware(options: MiddlewareOptions = {}) {
const {
prefix,
database,
resourcer,
accessors,
paramsKey = 'params',
nameRule = getNameByParams,
} = options;
return async (ctx: ResourcerContext, next: () => Promise<any>) => {
ctx.resourcer = resourcer;
let params = parseRequest({
path: ctx.request.path,
method: ctx.request.method,
}, {
prefix,
accessors,
});
if (!params) {
return next();
}
try {
const resourceName = nameRule(params);
if (!resourcer.isDefined(resourceName)) {
const names = resourceName.split('.');
const tableName = names.shift();
const Collection = database.getModel('collections');
if (!database.isDefined(tableName) && Collection) {
await Collection.load({
where: {
name: tableName,
},
});
}
if (database.isDefined(tableName)) {
const table = database.getTable(tableName);
const field = table.getField(names[0]) as BELONGSTO | HASMANY | BELONGSTOMANY | HASONE;
if (names.length == 0 || field) {
let resourceType = 'single';
let actions = {};
if (field) {
if (field instanceof HASONE) {
resourceType = 'hasOne';
} else if (field instanceof HASMANY) {
resourceType = 'hasMany';
} else if (field instanceof BELONGSTO) {
resourceType = 'belongsTo';
} else if (field instanceof BELONGSTOMANY) {
resourceType = 'belongsToMany';
}
if (field.options.actions) {
actions = field.options.actions;
}
}
resourcer.define({
type: resourceType as any,
name: resourceName,
actions,
});
}
}
}
const resource = resourcer.getResource(resourceName);
// 为关系资源时,暂时需要再执行一遍 parseRequest
if (resource.options.type !== 'single') {
params = parseRequest({
path: ctx.request.path,
method: ctx.request.method,
type: resource.options.type,
}, {
prefix,
accessors,
});
if (!params) {
return next();
}
}
// console.log(resource);
// action 需要 clone 之后再赋给 ctx
ctx.action = resourcer.getAction(resourceName, params.actionName).clone();
ctx.action.setContext(ctx);
// 自带 query 处理的不太给力,需要用 qs 转一下
const query = parseQuery(ctx.request.querystring);
// 兼容 ctx.params 的处理,之后的版本里会去掉
ctx[paramsKey] = {
table: params.resourceName,
tableKey: params.resourceKey,
relatedTable: params.associatedName,
relatedKey: params.resourceKey,
action: params.actionName,
};
if (pathToRegexp('/resourcer/{:associatedName.}?:resourceName{\\::actionName}').test(ctx.request.path)) {
await ctx.action.mergeParams({
...query,
...params,
...ctx.request.body,
});
} else {
await ctx.action.mergeParams({
...query,
...params,
values: ctx.request.body,
});
}
return compose(ctx.action.getHandlers())(ctx, next);
} catch (error) {
console.log(error);
return next();
}
}
}
export default middleware;