mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
CB-2344 refactor: dependency injection for execution context
This commit is contained in:
@@ -10,23 +10,39 @@ import type { IAsyncContextLoader, IContextLoader, IExecutionContext, ISyncConte
|
||||
|
||||
export class ExecutionContext<TData> implements IExecutionContext<TData> {
|
||||
readonly contexts: Map<IContextLoader<any, TData>, any>;
|
||||
readonly contextCreators: Map<IContextLoader<any, TData>, IContextLoader<any, TData>>;
|
||||
|
||||
constructor(private data: TData, context?: IExecutionContext<any>) {
|
||||
this.contexts = context?.contexts || new Map<IContextLoader<any, TData>, any>();
|
||||
constructor(private readonly data: TData, context?: IExecutionContext<any>) {
|
||||
this.contexts = context?.contexts || new Map();
|
||||
this.contextCreators = context?.contextCreators ?? new Map();
|
||||
}
|
||||
|
||||
hasContext(loader: IContextLoader<any>): boolean {
|
||||
return this.contexts.has(loader);
|
||||
addContextCreators(
|
||||
creators: [IContextLoader<any, TData>, IContextLoader<any, TData>][]
|
||||
): void {
|
||||
for (const [key, value] of creators) {
|
||||
this.contextCreators.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
getContext<T>(token: ISyncContextLoader<T, TData>): T
|
||||
getContext<T>(token: IAsyncContextLoader<T, TData>): Promise<T>
|
||||
hasContext(loader: IContextLoader): boolean {
|
||||
return this.contextCreators.has(loader) || this.contexts.has(loader);
|
||||
}
|
||||
|
||||
getContext<T>(token: ISyncContextLoader<T, TData>): T;
|
||||
getContext<T>(token: IAsyncContextLoader<T, TData>): Promise<T>;
|
||||
getContext<T>(token: IContextLoader<T, TData>): Promise<T> | T {
|
||||
if (this.contexts.has(token)) {
|
||||
return this.contexts.get(token);
|
||||
}
|
||||
|
||||
const value = token(this, this.data);
|
||||
let value: T | Promise<T>;
|
||||
|
||||
if (this.contextCreators.has(token)) {
|
||||
value = this.contextCreators.get(token)!(this, this.data);
|
||||
} else {
|
||||
value = token(this, this.data);
|
||||
}
|
||||
|
||||
if (value instanceof Promise) {
|
||||
return this.getAsyncContext(token, value);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import { flat } from '@cloudbeaver/core-utils';
|
||||
|
||||
import { ExecutionContext } from './ExecutionContext';
|
||||
import { executionExceptionContext } from './executionExceptionContext';
|
||||
import { ExecutorHandlersCollection } from './ExecutorHandlersCollection';
|
||||
import { ExecutorInterrupter, IExecutorInterrupter } from './ExecutorInterrupter';
|
||||
import type { IExecutionContext, IExecutionContextProvider } from './IExecutionContext';
|
||||
@@ -68,9 +69,16 @@ export class Executor<T = void> extends ExecutorHandlersCollection<T> implements
|
||||
context: IExecutionContext<T>,
|
||||
scoped: Array<IExecutorHandlersCollection<T>>
|
||||
): Promise<IExecutionContextProvider<T>> {
|
||||
const interrupter = context.getContext(ExecutorInterrupter.interruptContext);
|
||||
scoped = [...collection.collections, ...scoped];
|
||||
|
||||
context.addContextCreators(collection.contextCreators as any);
|
||||
|
||||
for (const scope of scoped) {
|
||||
context.addContextCreators(scope.contextCreators as any);
|
||||
}
|
||||
|
||||
const interrupter = context.getContext(ExecutorInterrupter.interruptContext);
|
||||
|
||||
await this.executeChain(collection, data, context, 'before');
|
||||
|
||||
for (const scope of scoped) {
|
||||
@@ -83,7 +91,11 @@ export class Executor<T = void> extends ExecutorHandlersCollection<T> implements
|
||||
for (const scope of scoped) {
|
||||
await this.executeHandlers(data, context, scope.handlers, interrupter);
|
||||
}
|
||||
} finally {
|
||||
} catch (exception: any) {
|
||||
const exceptionContext = context.getContext(executionExceptionContext);
|
||||
exceptionContext.setException(exception);
|
||||
throw exception;
|
||||
} finally {
|
||||
await this.executeHandlers(data, context, collection.postHandlers);
|
||||
|
||||
for (const scope of scoped) {
|
||||
@@ -158,6 +170,7 @@ export class Executor<T = void> extends ExecutorHandlersCollection<T> implements
|
||||
|
||||
this.scheduler.schedule(data, async () => {
|
||||
const context = new ExecutionContext(data);
|
||||
context.addContextCreators(this.contextCreators as any);
|
||||
|
||||
try {
|
||||
await handler(data, context);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
import { makeObservable, observable } from 'mobx';
|
||||
|
||||
import type { IContextLoader, ISyncContextLoader, IAsyncContextLoader } from './IExecutionContext';
|
||||
import type { IExecutorHandler } from './IExecutorHandler';
|
||||
import type { ExecutorDataFilter, ExecutorDataMap, IChainLink, IExecutorHandlersCollection } from './IExecutorHandlersCollection';
|
||||
|
||||
@@ -16,12 +17,14 @@ implements IExecutorHandlersCollection<T, TResult> {
|
||||
handlers: Array<IExecutorHandler<T, TResult>> = [];
|
||||
postHandlers: Array<IExecutorHandler<T, TResult>> = [];
|
||||
chain: Array<IChainLink<T, TResult>> = [];
|
||||
readonly contextCreators: Map<IContextLoader<any, T>, IContextLoader<any, T>>;
|
||||
readonly collections: Array<IExecutorHandlersCollection<T, TResult>>;
|
||||
protected initialDataGetter: (() => T) | null;
|
||||
private readonly links: Map<IExecutorHandlersCollection<any, TResult>, IExecutorHandlersCollection<T, TResult>>;
|
||||
|
||||
constructor() {
|
||||
this.links = new Map();
|
||||
this.contextCreators = new Map();
|
||||
this.collections = [];
|
||||
this.initialDataGetter = null;
|
||||
|
||||
@@ -34,6 +37,22 @@ implements IExecutorHandlersCollection<T, TResult> {
|
||||
});
|
||||
}
|
||||
|
||||
addContextCreator<TContext>(
|
||||
context: ISyncContextLoader<TContext, T>,
|
||||
creator: ISyncContextLoader<TContext, T>
|
||||
): this;
|
||||
addContextCreator<TContext>(
|
||||
context: IAsyncContextLoader<TContext, T>,
|
||||
creator: IAsyncContextLoader<TContext, T>
|
||||
): this;
|
||||
addContextCreator<TContext>(
|
||||
context: IContextLoader<TContext, T>,
|
||||
creator: IContextLoader<TContext, T>
|
||||
): this {
|
||||
this.contextCreators.set(context, creator);
|
||||
return this;
|
||||
}
|
||||
|
||||
setInitialDataGetter(getter: (() => T) | null): this {
|
||||
this.initialDataGetter = getter;
|
||||
return this;
|
||||
|
||||
@@ -17,7 +17,13 @@ export interface IExecutionContextProvider<TData> {
|
||||
}
|
||||
|
||||
export interface IExecutionContext<TData> extends IExecutionContextProvider<TData> {
|
||||
readonly parent?: IExecutionContext<any>;
|
||||
readonly contexts: Map<IContextLoader<any, TData>, any>;
|
||||
readonly contextCreators: Map<IContextLoader<any, TData>, IContextLoader<any, TData>>;
|
||||
|
||||
addContextCreators(
|
||||
creators: [IContextLoader<any, TData>, IContextLoader<any, TData>][]
|
||||
): void;
|
||||
}
|
||||
|
||||
export type IAsyncContextLoader<T = any, TData = any> = (
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import type { IExecutionContextProvider } from './IExecutionContext';
|
||||
import type { IAsyncContextLoader, IContextLoader, IExecutionContextProvider, ISyncContextLoader } from './IExecutionContext';
|
||||
import type { IExecutorHandler } from './IExecutorHandler';
|
||||
|
||||
export type ExecutorDataFilter<T> = (data: T, contexts: IExecutionContextProvider<T>) => boolean;
|
||||
@@ -25,9 +25,19 @@ export interface IExecutorHandlersCollection<T = unknown, TResult = any | Promis
|
||||
readonly postHandlers: Array<IExecutorHandler<T, TResult>>;
|
||||
readonly chain: Array<IChainLink<T, TResult>>;
|
||||
readonly collections: Array<IExecutorHandlersCollection<T, TResult>>;
|
||||
readonly contextCreators: Map<IContextLoader<any, T>, IContextLoader<any, T>>;
|
||||
|
||||
setInitialDataGetter(getter: () => T): this;
|
||||
|
||||
addContextCreator<TContext>(
|
||||
context: ISyncContextLoader<TContext, T>,
|
||||
creator: ISyncContextLoader<TContext, T>
|
||||
): this;
|
||||
addContextCreator<TContext>(
|
||||
context: IAsyncContextLoader<TContext, T>,
|
||||
creator: IAsyncContextLoader<TContext, T>
|
||||
): this;
|
||||
|
||||
before: <TNext>(executor: IExecutorHandlersCollection<TNext, TResult>, map?: ExecutorDataMap<T, TNext>) => this;
|
||||
next: <TNext>(executor: IExecutorHandlersCollection<TNext, TResult>, map?: ExecutorDataMap<T, TNext>) => this;
|
||||
addCollection: (collection: IExecutorHandlersCollection<T, TResult>) => this;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import { flat } from '@cloudbeaver/core-utils';
|
||||
|
||||
import { ExecutionContext } from './ExecutionContext';
|
||||
import { executionExceptionContext } from './executionExceptionContext';
|
||||
import { ExecutorHandlersCollection } from './ExecutorHandlersCollection';
|
||||
import { ExecutorInterrupter, IExecutorInterrupter } from './ExecutorInterrupter';
|
||||
import type { IExecutionContext, IExecutionContextProvider } from './IExecutionContext';
|
||||
@@ -55,9 +56,16 @@ export class SyncExecutor<T = void> extends ExecutorHandlersCollection<T> implem
|
||||
context: IExecutionContext<T>,
|
||||
scoped: Array<IExecutorHandlersCollection<T>>
|
||||
): IExecutionContextProvider<T> {
|
||||
const interrupter = context.getContext(ExecutorInterrupter.interruptContext);
|
||||
scoped = [...collection.collections, ...scoped];
|
||||
|
||||
context.addContextCreators(collection.contextCreators as any);
|
||||
|
||||
for (const scope of scoped) {
|
||||
context.addContextCreators(scope.contextCreators as any);
|
||||
}
|
||||
|
||||
const interrupter = context.getContext(ExecutorInterrupter.interruptContext);
|
||||
|
||||
this.executeChain(collection, data, context, 'before');
|
||||
|
||||
for (const scope of scoped) {
|
||||
@@ -70,6 +78,10 @@ export class SyncExecutor<T = void> extends ExecutorHandlersCollection<T> implem
|
||||
for (const scope of scoped) {
|
||||
this.executeHandlers(data, context, scope.handlers, interrupter);
|
||||
}
|
||||
} catch (exception: any) {
|
||||
const exceptionContext = context.getContext(executionExceptionContext);
|
||||
exceptionContext.setException(exception);
|
||||
throw exception;
|
||||
} finally {
|
||||
this.executeHandlers(data, context, collection.postHandlers);
|
||||
|
||||
@@ -144,6 +156,8 @@ export class SyncExecutor<T = void> extends ExecutorHandlersCollection<T> implem
|
||||
const data = this.initialDataGetter();
|
||||
const context = new ExecutionContext(data);
|
||||
|
||||
context.addContextCreators(this.contextCreators as any);
|
||||
|
||||
try {
|
||||
handler(data, context);
|
||||
} finally {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2022 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
export function executionExceptionContext() {
|
||||
return {
|
||||
exception: null as null | Error,
|
||||
setException(exception: Error) {
|
||||
this.exception = exception;
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -3,6 +3,7 @@ export * from './TaskScheduler/ITask';
|
||||
export * from './TaskScheduler/Task';
|
||||
export * from './TaskScheduler/TaskScheduler';
|
||||
export * from './ExecutionContext';
|
||||
export * from './executionExceptionContext';
|
||||
export * from './Executor';
|
||||
export * from './ExecutorHandlersCollection';
|
||||
export * from './ExecutorInterrupter';
|
||||
|
||||
Reference in New Issue
Block a user