@finos/legend-data-cube
Version:
151 lines • 6.24 kB
JavaScript
/**
* 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