UNPKG

@finos/legend-data-cube

Version:
151 lines 6.24 kB
/** * Copyright (c) 2020-present, Goldman Sachs * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { IllegalStateError, assertErrorThrown, at, deepDiff, } from '@finos/legend-shared'; import { DataCubeSettingKey } from '../../__lib__/DataCubeSetting.js'; import { action, computed, makeObservable, observable } from 'mobx'; export class DataCubeSnapshotController { _engine; _settingService; _snapshotService; _latestSnapshot; constructor(engine, settingService, snapshotService) { this._engine = engine; this._settingService = settingService; this._snapshotService = snapshotService; } getLatestSnapshot() { return this._latestSnapshot; } async receiveSnapshot(snapshot) { const previousSnapshot = this._latestSnapshot; this._latestSnapshot = snapshot; // NOTE: fully clone the snapshot before applying it to avoid any potential // mutation of the snapshot by the subscriber await this.applySnapshot(snapshot.INTERNAL__fullClone(), previousSnapshot); } publishSnapshot(snapshot) { const previousSnapshot = this._latestSnapshot; this._latestSnapshot = snapshot; if (this._settingService.getBooleanValue(DataCubeSettingKey.DEBUGGER__ENABLE_DEBUG_MODE)) { this._engine.debugProcess(`New Snapshot`, ['Publisher', this.getSnapshotSubscriberName()], ['Snapshot', snapshot.serialize()], ['Previous Snapshot', previousSnapshot?.serialize()], ['Diff', deepDiff(previousSnapshot ?? {}, snapshot)]); } this._snapshotService.broadcastSnapshot(snapshot); } } const MINIMUM_HISTORY_SIZE = 10; export class DataCubeSnapshotService { _logService; _settingService; _subscribers = []; _snapshots = []; // stack _pointer = -1; _historySize; constructor(logService, settingService) { makeObservable(this, { _snapshots: observable.struct, _pointer: observable, canUndo: computed, canRedo: computed, undo: action, redo: action, adjustHistorySize: action, broadcastSnapshot: action, }); this._logService = logService; this._settingService = settingService; this._historySize = Math.max(this._settingService.getNumericValue(DataCubeSettingKey.EDITOR__MAX_HISTORY_STACK_SIZE), MINIMUM_HISTORY_SIZE); } registerSubscriber(subscriber) { const existingSubscriber = this._subscribers.find((sub) => sub.getSnapshotSubscriberName() === subscriber.getSnapshotSubscriberName()); if (existingSubscriber) { // eslint-disable-next-line no-process-env if (process.env.NODE_ENV === 'development') { this._logService.logDebug(`Subscriber with name '${subscriber.getSnapshotSubscriberName()}' already exists`); } else { throw new IllegalStateError(`Subscriber with name '${subscriber.getSnapshotSubscriberName()}' already exists`); } } this._subscribers.push(subscriber); } propagateSnapshot(snapshot) { this._subscribers.forEach((subscriber) => { const currentSnapshot = subscriber.getLatestSnapshot(); if (currentSnapshot?.uuid !== snapshot.uuid) { subscriber.receiveSnapshot(snapshot).catch((error) => { assertErrorThrown(error); this._logService.logIllegalStateError(`Error occured while subscribers receiving and applying new snapshot should be handled gracefully`, error); }); } }); } getCurrentSnapshot() { return at(this._snapshots, this._pointer); } getHistory(options) { return options?.full ? [...this._snapshots] : this._snapshots.slice(0, this._pointer + 1); } get canUndo() { return this._pointer > 0; } get canRedo() { return this._pointer < this._snapshots.length - 1; } undo() { // Always leave one snapshot in the stack this._pointer = Math.max(this._pointer - 1, 0); this.propagateSnapshot(this.getCurrentSnapshot()); } redo() { this._pointer = Math.min(this._pointer + 1, this._snapshots.length - 1); this.propagateSnapshot(this.getCurrentSnapshot()); } adjustHistorySize(size) { let newSize = size; if (size <= MINIMUM_HISTORY_SIZE) { this._logService.logIllegalStateError(`Can't adjust history size to ${size}: value must be greator than ${MINIMUM_HISTORY_SIZE}`); newSize = Math.max(size, MINIMUM_HISTORY_SIZE); } const snapshots = this.getHistory(); if (snapshots.length > newSize) { this._snapshots = snapshots.slice(-newSize); this._pointer = Math.min(Math.max(this._pointer - (snapshots.length - newSize), 0), snapshots.length - 1); } else { this._snapshots = snapshots; this._pointer = snapshots.length - 1; } this._historySize = newSize; } broadcastSnapshot(snapshot) { if (!snapshot.isFinalized()) { this._logService.logIllegalStateError(`Snapshot must be finalized before broadcasting`); return; } this._snapshots = [ ...this._snapshots.slice(0, this._pointer + 1), snapshot, ]; this._pointer += 1; // always adjust to history size after adding a new snapshot this.adjustHistorySize(this._historySize); this.propagateSnapshot(snapshot); } } //# sourceMappingURL=DataCubeSnapshotService.js.map