tachybase_todo/packages/core/resourcer/src/action.ts

329 lines
6.6 KiB
TypeScript
Raw Normal View History

import { requireModule } from '@nocobase/utils';
import compose from 'koa-compose';
import _ from 'lodash';
import { assign, MergeStrategies } from './assign';
import Middleware, { MiddlewareType } from './middleware';
2020-10-24 15:34:43 +08:00
import Resource from './resource';
import { HandlerType } from './resourcer';
export type ActionType = string | HandlerType | ActionOptions;
export type DefaultActionType = 'list' | 'create' | 'get' | 'update' | 'destroy' | 'set' | 'add' | 'remove';
export type ActionName = DefaultActionType | Omit<String, DefaultActionType>;
export interface ActionContext {
action?: Action;
[key: string]: any;
}
2021-12-06 21:23:34 +08:00
export type FieldsOptions =
| string[]
| {
only?: string[];
appends?: string[];
}
| {
except?: string[];
appends?: string[];
};
2020-10-24 15:34:43 +08:00
export type FieldsOptionsFn = (ctx: ActionContext) => FieldsOptions | Promise<FieldsOptions>;
/**
*
2021-12-06 21:23:34 +08:00
*
2020-10-24 15:34:43 +08:00
* TODO
*/
export interface FilterOptions {
[key: string]: any;
}
export type FilterOptionsFn = (ctx: ActionContext) => FilterOptions | Promise<FieldsOptions>;
export type ParamsCallback = (ctx: ActionContext) => ActionParams | Promise<ActionParams>;
export interface ActionOptions {
/**
*
*/
values?: any;
2020-10-24 15:34:43 +08:00
/**
*
2021-12-06 21:23:34 +08:00
*
2020-10-24 15:34:43 +08:00
*
* ['col1', 'col2', 'relation.col1'];
2021-12-06 21:23:34 +08:00
*
2020-10-24 15:34:43 +08:00
*
* {
* only: ['col1'],
* }
2021-12-06 21:23:34 +08:00
*
2020-10-24 15:34:43 +08:00
*
* {
* except: ['col1'],
* }
*/
2021-12-06 21:23:34 +08:00
fields?: string[];
appends?: string[];
except?: string[];
whitelist?: string[];
blacklist?: string[];
2020-10-24 15:34:43 +08:00
/**
*
*/
filter?: FilterOptions;
/**
*
*/
2021-12-06 21:23:34 +08:00
sort?: string[];
2020-10-24 15:34:43 +08:00
/**
*
*/
page?: number;
/**
*
*/
2022-02-18 09:46:10 +08:00
pageSize?: number;
/**
*
*/
2022-02-18 09:46:10 +08:00
maxPageSize?: number;
2020-10-24 15:34:43 +08:00
/**
*
*/
middleware?: MiddlewareType;
/**
*
2021-12-06 21:23:34 +08:00
*
2020-10-24 15:34:43 +08:00
* middleware
*/
middlewares?: MiddlewareType;
/**
* Action
2021-12-06 21:23:34 +08:00
*
2020-10-24 15:34:43 +08:00
* Function require
*/
handler?: HandlerType;
/**
*
*/
[key: string]: any;
}
/**
* action params action options
* - options
* - params +
*/
export interface ActionParams {
filterByTk?: any;
2020-10-24 15:34:43 +08:00
/**
*
2021-12-06 21:23:34 +08:00
*
2020-10-24 15:34:43 +08:00
* ActionOptions fields object onlyexceptappends
*/
2021-12-06 21:23:34 +08:00
fields?: string[];
appends?: string[];
except?: string[];
whitelist?: string[];
blacklist?: string[];
2020-10-24 15:34:43 +08:00
/**
*
*/
filter?: FilterOptions;
/**
*
2021-12-06 21:23:34 +08:00
*
2020-10-24 15:34:43 +08:00
* ActionOptions sort array
*/
sort?: string[];
/**
*
*/
page?: number;
/**
*
*/
2022-02-18 09:46:10 +08:00
pageSize?: number;
2020-10-24 15:34:43 +08:00
/**
* options.defaultValues + request.body
*/
values?: any;
/**
* Model
*/
resourceName?: string;
/**
*
*/
resourceIndex?: string;
2020-10-24 15:34:43 +08:00
/**
*
*/
associatedName?: string;
/**
*
*/
associatedIndex?: string;
2020-10-24 15:34:43 +08:00
/**
*
*/
2021-03-28 13:34:51 +08:00
associated?: any;
2020-10-24 15:34:43 +08:00
/**
*
*/
actionName?: string;
/**
*
*/
[key: string]: any;
}
export class Action {
protected handler: any;
protected resource: Resource;
protected name: ActionName;
protected options: ActionOptions;
protected context: ActionContext = {};
2021-12-06 21:23:34 +08:00
public params: ActionParams = {};
public actionName: string;
public resourceName: string;
public resourceOf: any;
2020-10-24 15:34:43 +08:00
public readonly middlewares: Array<Middleware> = [];
constructor(options: ActionOptions) {
options = requireModule(options);
if (typeof options === 'function') {
options = { handler: options };
}
2021-12-06 21:23:34 +08:00
const { middleware, middlewares = [], handler, ...params } = options;
2020-10-24 15:34:43 +08:00
this.middlewares = Middleware.toInstanceArray(middleware || middlewares);
this.handler = handler;
this.options = options;
2021-12-06 21:23:34 +08:00
this.mergeParams(params);
2020-10-24 15:34:43 +08:00
}
toJSON() {
return {
actionName: this.actionName,
resourceName: this.resourceName,
resourceOf: this.resourceOf,
params: this.params,
};
}
2020-10-24 15:34:43 +08:00
clone() {
const options = _.cloneDeep(this.options);
delete options.middleware;
delete options.middlewares;
const action = new Action(options);
action.setName(this.name);
action.setResource(this.resource);
action.middlewares.push(...this.middlewares);
return action;
}
setContext(context: any) {
this.context = context;
}
2021-12-06 21:23:34 +08:00
mergeParams(params: ActionParams, strategies: MergeStrategies = {}) {
assign(this.params, params, {
filter: 'andMerge',
fields: 'intersect',
appends: 'union',
except: 'union',
whitelist: 'intersect',
blacklist: 'intersect',
sort: 'overwrite',
...strategies,
});
2020-10-24 15:34:43 +08:00
}
setResource(resource: Resource) {
this.resource = resource;
return this;
}
getResource() {
return this.resource;
}
getOptions(): ActionOptions {
return this.options;
}
setName(name: ActionName) {
this.name = name;
return this;
}
getName() {
return this.name;
}
getMiddlewareHandlers() {
return this.middlewares
2021-12-06 21:23:34 +08:00
.filter((middleware) => middleware.canAccess(this.name))
.map((middleware) => middleware.getHandler());
2020-10-24 15:34:43 +08:00
}
getHandler() {
const handler = requireModule(this.handler || this.resource.resourcer.getRegisteredHandler(this.name));
if (typeof handler !== 'function') {
throw new Error('Handler must be a function!');
}
return handler;
}
getHandlers() {
const handers = [
...this.resource.resourcer.getMiddlewares(),
...this.getMiddlewareHandlers(),
this.getHandler(),
].filter(Boolean);
return handers;
2020-10-24 15:34:43 +08:00
}
async execute(context: any, next?: any) {
return await compose(this.getHandlers())(context, next);
}
static toInstanceMap(actions: object, resource?: Resource) {
2021-12-06 21:23:34 +08:00
return new Map(
Object.entries(actions).map(([key, options]) => {
let action: Action;
if (options instanceof Action) {
action = options;
} else {
action = new Action(options);
}
action.setName(key);
action.setResource(resource);
resource && action.middlewares.unshift(...resource.middlewares);
return [key, action];
}),
);
2020-10-24 15:34:43 +08:00
}
}
export default Action;