/* * 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. */ import { action, makeObservable, observable, runInAction } from 'mobx'; import { getDependingDataActions } from './Actions/DatabaseDataActionDecorator'; import { isDatabaseDataAction } from './DatabaseDataAction'; import type { IDatabaseDataAction, IDatabaseDataActionClass, IDatabaseDataActionInterface } from './IDatabaseDataAction'; import type { IDatabaseDataActions } from './IDatabaseDataActions'; import type { IDatabaseDataResult } from './IDatabaseDataResult'; import type { IDatabaseDataSource } from './IDatabaseDataSource'; type ActionsList = Array>; export class DatabaseDataActions implements IDatabaseDataActions { private readonly actions: Map>; private readonly source: IDatabaseDataSource; constructor(source: IDatabaseDataSource) { this.actions = new Map(); this.source = source; makeObservable(this, { actions: observable.shallow, updateResults: action, createActionsList: action, }); } tryGet>( result: TResult, Action: IDatabaseDataActionClass ): T | undefined { if (Action.dataFormat && !Action.dataFormat.includes(result.dataFormat)) { return undefined; } return this.get(result, Action); } get>( result: TResult, Action: IDatabaseDataActionClass ): T { if (Action.dataFormat && !Action.dataFormat.includes(result.dataFormat)) { throw new Error('DataFormat unsupported'); } const actions = this.getOrCreateActionsList(result.uniqueResultId); return runInAction(() => { let action = actions.find(action => action instanceof Action); if (!action) { const allDeps = getDependingDataActions(Action) .slice(2); // skip source and result arguments const depends = allDeps .filter(isDatabaseDataAction) .map(action => this.get>(result, action)); if (allDeps.length !== depends.length) { throw new Error('Unsupported inject in: ' + Action.name); } action = new Action(this.source, result, ...depends); this.addActionToList(result.uniqueResultId, actions, action); } return action as T; }); } getImplementation>( result: TResult, Action: IDatabaseDataActionInterface ): T | undefined { const actions = this.getActionsList(result.uniqueResultId); const action = actions?.find(action => action instanceof Action); return action as T | undefined; } updateResults(results: TResult[]): void { let actionsMap = Array.from(this.actions.entries()); for (const [key, actions] of actionsMap) { const result = results.find(result => result.uniqueResultId === key); for (const action of actions) { action.updateResults(results); if (!result) { action.dispose(); } else { action.updateResult(result); } } if (!result) { this.actions.delete(key); } } actionsMap = Array.from(this.actions.entries()); for (const [, actions] of actionsMap) { for (const action of actions) { action.afterResultUpdate(); } } } private addActionToList( resultId: string, actions: ActionsList, action: IDatabaseDataAction ) { actions.push(action); } private getOrCreateActionsList(resultId: string): ActionsList { let actions = this.getActionsList(resultId); if (!actions) { actions = this.createActionsList(resultId); } return actions; } private createActionsList(resultId: string): ActionsList { const actions: ActionsList = observable.array([], { deep: false }); this.actions.set(resultId, actions); return actions; } private getActionsList(resultId: string): ActionsList | undefined { return this.actions.get(resultId); } }