refactor: decorator

This commit is contained in:
xiaojunnuo
2022-12-27 12:32:09 +08:00
parent c23f6172b5
commit 717d203622
30 changed files with 639 additions and 309 deletions
@@ -1,4 +0,0 @@
import { AbstractRegistrable } from "../registry";
import { AccessDefine } from "./api";
export abstract class AbstractAccess extends AbstractRegistrable<AccessDefine> {}
+3 -10
View File
@@ -3,22 +3,15 @@ import { accessRegistry } from "./registry";
import { FormItemProps } from "../d.ts";
import { AbstractAccess } from "./abstract-access";
export type AccessInput = FormItemProps & {
export type AccessInputDefine = FormItemProps & {
title: string;
required?: boolean;
};
export type AccessDefine = Registrable & {
input: {
[key: string]: AccessInput;
inputs?: {
[key: string]: AccessInputDefine;
};
};
export function IsAccess(define: AccessDefine) {
return function (target: any) {
target.prototype.define = define;
accessRegistry.install(target);
};
}
export interface IAccessService {
getById(id: any): Promise<AbstractAccess>;
}
@@ -0,0 +1,71 @@
// src/decorator/memoryCache.decorator.ts
import {
attachClassMetadata,
attachPropertyDataToClass,
getClassMetadata,
listModule,
listPropertyDataFromClass,
saveClassMetadata,
saveModule,
} from "@midwayjs/decorator";
import { AccessDefine, AccessInputDefine } from "./api";
import _ from "lodash-es";
import { accessRegistry } from "./registry";
// 提供一个唯一 key
export const ACCESS_CLASS_KEY = "decorator:access";
export function IsAccess(define: AccessDefine): ClassDecorator {
console.log("is access define:", define);
return (target: any) => {
console.log("is access load:", target);
// 将装饰的类,绑定到该装饰器,用于后续能获取到 class
saveModule(ACCESS_CLASS_KEY, target);
// 保存一些元数据信息,任意你希望存的东西
saveClassMetadata(
ACCESS_CLASS_KEY,
{
define,
},
target
);
};
}
export const ACCESS_INPUT_KEY = "decorator:access:input";
export function IsAccessInput(input?: AccessInputDefine): PropertyDecorator {
return (target, propertyKey) => {
attachPropertyDataToClass(ACCESS_INPUT_KEY, { input }, target, propertyKey, propertyKey as string);
attachClassMetadata(
ACCESS_CLASS_KEY,
{
inputs: {
[propertyKey]: input,
},
},
target
);
};
}
export function registerAccess() {
const modules = listModule(ACCESS_CLASS_KEY);
for (const mod of modules) {
console.log("mod", mod);
const define = getClassMetadata(ACCESS_CLASS_KEY, mod);
console.log("define", define);
const inputs = listPropertyDataFromClass(ACCESS_INPUT_KEY, mod);
console.log("inputs", inputs);
for (const input of inputs) {
define.inputs = {};
_.merge(define.inputs, input.inputs);
}
accessRegistry.register(define.name, {
define,
target: mod,
});
}
}
+1 -1
View File
@@ -1,3 +1,3 @@
export * from "./api";
export * from "./abstract-access";
export * from "./registry";
export * from "./decorator";
@@ -1,5 +1,4 @@
import { Registry } from "../registry";
import { AbstractAccess } from "./abstract-access";
// @ts-ignore
export const accessRegistry = new Registry<typeof AbstractAccess>();
export const accessRegistry = new Registry();
@@ -0,0 +1,6 @@
import { AxiosInstance } from "axios";
import { IContext } from "../core";
export type HttpClient = AxiosInstance;
export type UserContext = IContext;
export type PipelineContext = IContext;
+20 -21
View File
@@ -1,13 +1,14 @@
import { ConcurrencyStrategy, Pipeline, ResultType, Runnable, RunStrategy, Stage, Step, Task } from "../d.ts";
import _ from "lodash";
import { RunHistory } from "./run-history";
import { pluginRegistry, TaskPlugin } from "../plugin";
import { pluginRegistry, ITaskPlugin, PluginDefine } from "../plugin";
import { ContextFactory, IContext } from "./context";
import { IStorage } from "./storage";
import { logger } from "../utils/util.log";
import { Logger } from "log4js";
import { request } from "../utils/util.request";
import { IAccessService } from "../access";
import { Registrable, RegistryItem } from "../registry";
export class Executor {
userId: any;
pipeline: Pipeline;
@@ -134,40 +135,38 @@ export class Executor {
private async runStep(step: Step) {
//执行任务
const taskPlugin: TaskPlugin = await this.getPlugin(step.type, this.runtime.loggers[step.id]);
const plugin: RegistryItem = pluginRegistry.get(step.type);
const context = {
logger: this.runtime.loggers[step.id],
accessService: this.accessService,
pipelineContext: this.pipelineContext,
userContext: this.contextFactory.getContext("user", this.userId),
http: request,
};
// @ts-ignore
delete taskPlugin.define;
const define = taskPlugin.getDefine();
const instance = new plugin.target();
// @ts-ignore
const define: PluginDefine = plugin.define;
//从outputContext读取输入参数
_.forEach(define.input, (item, key) => {
_.forEach(define.inputs, (item, key) => {
if (item.component?.name === "pi-output-selector") {
const contextKey = step.input[key];
if (contextKey != null) {
step.input[key] = this.runtime.context[contextKey];
}
} else {
instance[key] = step.input[key];
}
});
const res = await taskPlugin.execute(step.input);
const res = await instance.execute();
//输出到output context
_.forEach(define.output, (item, key) => {
_.forEach(define.outputs, (item, key) => {
const contextKey = `step.${step.id}.${key}`;
this.runtime.context[contextKey] = res[key];
});
}
private async getPlugin(type: string, logger: Logger): Promise<TaskPlugin> {
const pluginClass = pluginRegistry.get(type);
// @ts-ignore
const plugin = new pluginClass();
await plugin.doInit({
accessService: this.accessService,
pipelineContext: this.pipelineContext,
userContext: this.contextFactory.getContext("user", this.userId),
logger,
http: request,
});
return plugin;
}
}
@@ -1,6 +1,4 @@
import { Registrable } from "../registry";
import { dnsProviderRegistry } from "./registry";
import { AbstractDnsProvider } from "./abstract-dns-provider";
export type DnsProviderDefine = Registrable & {
accessType: string;
@@ -16,19 +14,6 @@ export type RemoveRecordOptions = CreateRecordOptions & {
};
export interface IDnsProvider {
getDefine(): DnsProviderDefine;
createRecord(options: CreateRecordOptions): Promise<any>;
removeRecord(options: RemoveRecordOptions): Promise<any>;
}
export function IsDnsProvider(define: (() => DnsProviderDefine) | DnsProviderDefine) {
return function (target: typeof AbstractDnsProvider) {
if (define instanceof Function) {
target.prototype.define = define();
} else {
target.prototype.define = define;
}
dnsProviderRegistry.install(target);
};
}
@@ -0,0 +1,55 @@
// src/decorator/memoryCache.decorator.ts
import {
attachClassMetadata,
attachPropertyDataToClass,
createCustomPropertyDecorator,
getClassMetadata,
listModule,
listPropertyDataFromClass,
Provide,
saveClassMetadata,
saveModule,
Scope,
ScopeEnum,
} from "@midwayjs/decorator";
import _ from "lodash-es";
import { dnsProviderRegistry } from "./registry";
import { DnsProviderDefine } from "./api";
// 提供一个唯一 key
export const DNS_PROVIDER_CLASS_KEY = "decorator:dnsProvider";
export function IsDnsProvider(define: DnsProviderDefine): ClassDecorator {
console.log("is task plugin define:", define);
return (target: any) => {
console.log("is task plugin load:", target);
// 将装饰的类,绑定到该装饰器,用于后续能获取到 class
saveModule(DNS_PROVIDER_CLASS_KEY, target);
// 保存一些元数据信息,任意你希望存的东西
saveClassMetadata(
DNS_PROVIDER_CLASS_KEY,
{
define,
},
target
);
// 指定 IoC 容器创建实例的作用域,这里注册为请求作用域,这样能取到 ctx
Scope(ScopeEnum.Prototype)(target);
// 调用一下 Provide 装饰器,这样用户的 class 可以省略写 @Provide() 装饰器了
Provide()(target);
};
}
export function registerDnsProviders() {
const modules = listModule(DNS_PROVIDER_CLASS_KEY);
for (const mod of modules) {
console.log("mod", mod);
const define = getClassMetadata(DNS_PROVIDER_CLASS_KEY, mod);
console.log("define", define);
dnsProviderRegistry.register(define.name, {
define,
target: mod,
});
}
}
@@ -1,3 +1,3 @@
export * from "./api";
export * from "./registry";
export * from "./abstract-dns-provider";
export * from "./decorator";
@@ -1,5 +1,4 @@
import { Registry } from "../registry";
import { AbstractDnsProvider } from "./abstract-dns-provider";
// @ts-ignore
export const dnsProviderRegistry = new Registry<typeof AbstractDnsProvider>();
export const dnsProviderRegistry = new Registry();
+3
View File
@@ -5,3 +5,6 @@ export * from "./registry";
export * from "./dns-provider";
export * from "./plugin";
export * from "./utils";
export * from "./decorator";
export * from "./midway";
export * from "./context";
@@ -0,0 +1,60 @@
import { Config, Configuration, getClassMetadata, Inject, listModule, listPropertyDataFromClass, Logger } from "@midwayjs/decorator";
import _ from "lodash-es";
// @ts-ignore
import { ILogger } from "@midwayjs/logger";
import { IMidwayContainer, MidwayDecoratorService } from "@midwayjs/core";
import { pluginRegistry } from "../plugin";
import { registerPlugins } from "../plugin/decorator";
import { registerAccess } from "../access/decorator";
import { registerDnsProviders } from "../dns-provider";
// ... (see below) ...
@Configuration({
namespace: "pipeline",
//importConfigs: [join(__dirname, './config')],
})
export class PipelineConfiguration {
@Config()
// @ts-ignore
config;
@Logger()
// @ts-ignore
logger: ILogger;
@Inject()
// @ts-ignore
decoratorService: MidwayDecoratorService;
async onReady(container: IMidwayContainer) {
this.logger.info("pipeline install");
registerPlugins();
registerAccess();
registerDnsProviders();
//this.implPropertyDecorator(container);
this.logger.info("pipeline installed");
}
// implPropertyDecorator(container: IMidwayContainer) {
// this.logger.info("初始化 property decorator");
// // 实现装饰器
// this.decoratorService.registerPropertyHandler(CLASS_INPUTS_KEY, (propertyName, meta) => {
// return undefined;
// });
//
// const autowireWhiteList: any = {
// logger: true,
// };
// this.decoratorService.registerPropertyHandler(CLASS_AUTOWIRE_KEY, (propertyName, meta) => {
// // eslint-disable-next-line no-debugger
// debugger;
// const className = meta.name;
// if (autowireWhiteList[className]) {
// //在白名单里面,注入
// return container.get(className);
// }
// this.logger.warn(`autowire failed:${className} class is not in white list`);
// return undefined;
// });
// }
}
@@ -0,0 +1,5 @@
// src/index.ts
export { PipelineConfiguration } from "./configuration";
// export * from './controller/user';
// export * from './controller/api';
// export * from './service/user';
+7 -24
View File
@@ -1,13 +1,6 @@
import { Registrable } from "../registry";
import { pluginRegistry } from "./registry";
import { FormItemProps } from "../d.ts";
export type TaskInput = {
[key: string]: any;
};
export type TaskOutput = {
[key: string]: any;
};
export enum ContextScope {
global,
@@ -28,17 +21,19 @@ export type TaskOutputDefine = {
export type TaskInputDefine = FormItemProps;
export type PluginDefine = Registrable & {
input: {
default?: any;
inputs?: {
[key: string]: TaskInputDefine;
};
output: {
outputs?: {
[key: string]: TaskOutputDefine;
};
autowire?: any;
};
export interface TaskPlugin {
getDefine(): PluginDefine;
execute(input: TaskInput): Promise<TaskOutput>;
export interface ITaskPlugin {
execute(): Promise<void>;
}
export type OutputVO = {
@@ -46,15 +41,3 @@ export type OutputVO = {
title: string;
value: any;
};
export function IsTask(define: (() => PluginDefine) | PluginDefine) {
return function (target: any) {
if (define instanceof Function) {
target.prototype.define = define();
} else {
target.prototype.define = define;
}
pluginRegistry.install(target);
};
}
@@ -0,0 +1,141 @@
// src/decorator/memoryCache.decorator.ts
import {
attachClassMetadata,
attachPropertyDataToClass,
createCustomPropertyDecorator,
getClassMetadata,
listModule,
listPropertyDataFromClass,
Provide,
saveClassMetadata,
saveModule,
Scope,
ScopeEnum,
} from "@midwayjs/decorator";
import _ from "lodash-es";
import { pluginRegistry } from "./registry";
import { PluginDefine, TaskInputDefine, TaskOutputDefine } from "./api";
// 提供一个唯一 key
export const PLUGIN_CLASS_KEY = "decorator:plugin";
export function IsTaskPlugin(define: PluginDefine): ClassDecorator {
console.log("is task plugin define:", define);
return (target: any) => {
console.log("is task plugin load:", target);
// 将装饰的类,绑定到该装饰器,用于后续能获取到 class
saveModule(PLUGIN_CLASS_KEY, target);
// 保存一些元数据信息,任意你希望存的东西
saveClassMetadata(
PLUGIN_CLASS_KEY,
{
define,
},
target
);
// 指定 IoC 容器创建实例的作用域,这里注册为请求作用域,这样能取到 ctx
Scope(ScopeEnum.Prototype)(target);
// 调用一下 Provide 装饰器,这样用户的 class 可以省略写 @Provide() 装饰器了
Provide()(target);
};
}
export const PLUGIN_INPUT_KEY = "decorator:plugin:input";
export function TaskInput(input?: TaskInputDefine): PropertyDecorator {
return (target, propertyKey) => {
attachPropertyDataToClass(PLUGIN_INPUT_KEY, { input }, target, propertyKey, propertyKey as string);
attachClassMetadata(
PLUGIN_CLASS_KEY,
{
inputs: {
[propertyKey]: input,
},
},
target
);
};
//
// return createCustomPropertyDecorator(CLASS_PROPS_KEY, {
// input,
// });
}
// 装饰器内部的唯一 id
export const PLUGIN_OUTPUT_KEY = "decorator:plugin:output";
export function TaskOutput(output?: TaskOutputDefine): PropertyDecorator {
return (target, propertyKey) => {
attachPropertyDataToClass(PLUGIN_OUTPUT_KEY, { output }, target, propertyKey, propertyKey as string);
attachClassMetadata(
PLUGIN_CLASS_KEY,
{
outputs: {
[propertyKey]: output,
},
},
target
);
};
//
// return createCustomPropertyDecorator(CLASS_PROPS_KEY, {
// input,
// });
}
export type AutowireProp = {
name?: string;
};
export const PLUGIN_AUTOWIRE_KEY = "decorator:plugin:autowire";
export function Autowire(props?: AutowireProp): PropertyDecorator {
return (target, propertyKey) => {
attachPropertyDataToClass(
PLUGIN_AUTOWIRE_KEY,
{
autowire: {
[propertyKey]: props,
},
},
target,
propertyKey,
propertyKey as string
);
};
}
export function registerPlugins() {
const modules = listModule(PLUGIN_CLASS_KEY);
for (const mod of modules) {
console.log("mod", mod);
const define: PluginDefine = getClassMetadata(PLUGIN_CLASS_KEY, mod);
console.log("define", define);
const inputs = listPropertyDataFromClass(PLUGIN_INPUT_KEY, mod);
console.log("inputs", inputs);
for (const input of inputs) {
define.inputs = {};
_.merge(define.inputs, input.inputs);
}
const outputs = listPropertyDataFromClass(PLUGIN_OUTPUT_KEY, mod);
console.log("outputs", outputs);
for (const output of outputs) {
define.outputs = {};
_.merge(define.outputs, output.outputs);
}
const autowire = listPropertyDataFromClass(PLUGIN_AUTOWIRE_KEY, mod);
console.log("autowire", autowire);
for (const auto of autowire) {
define.autowire = {};
_.merge(define.autowire, auto.autowire);
}
pluginRegistry.register(define.name, {
define,
target: mod,
});
}
}
+1 -1
View File
@@ -1,3 +1,3 @@
export * from "./api";
export * from "./registry";
export * from "./abstract-plugin";
export * from "./decorator";
@@ -1,5 +1,4 @@
import { Registry } from "../registry";
import { AbstractPlugin } from "./abstract-plugin";
// @ts-ignore
export const pluginRegistry = new Registry<typeof AbstractPlugin>();
export const pluginRegistry = new Registry();
@@ -0,0 +1,26 @@
import { ILogger } from "@midwayjs/logger";
import { ITaskPlugin } from "../api";
import { Autowire, IsTaskPlugin, TaskInput } from "../decorator";
@IsTaskPlugin({
name: "EchoPlugin",
title: "测试插件",
desc: "test",
})
export class EchoPlugin implements ITaskPlugin {
@TaskInput({
title: "测试属性",
component: {
name: "text",
},
})
test?: string;
@Autowire()
// @ts-ignore
logger: ILogger;
async execute(): Promise<void> {
return Promise.resolve(undefined);
}
}
+10 -32
View File
@@ -4,36 +4,16 @@ export type Registrable = {
desc?: string;
};
export abstract class AbstractRegistrable<T extends Registrable> {
// @ts-ignore
define: T;
getDefine(): T {
return this.define;
}
}
export class Registry<T extends typeof AbstractRegistrable> {
export type RegistryItem = {
define: Registrable;
target: any;
};
export class Registry {
storage: {
[key: string]: T;
[key: string]: RegistryItem;
} = {};
install(pluginClass: T) {
if (pluginClass == null) {
return;
}
// @ts-ignore
const plugin = new pluginClass();
delete plugin.define;
const define = plugin.getDefine();
let defineName = define.name;
if (defineName == null) {
defineName = plugin.name;
}
this.register(defineName, pluginClass);
}
register(key: string, value: T) {
register(key: string, value: RegistryItem) {
if (!key || value == null) {
return;
}
@@ -68,12 +48,10 @@ export class Registry<T extends typeof AbstractRegistrable> {
}
getDefine(key: string) {
const PluginClass = this.storage[key];
if (!PluginClass) {
const item = this.storage[key];
if (!item) {
return;
}
// @ts-ignore
const plugin = new PluginClass();
return plugin.define;
return item.define;
}
}