@finos/legend-data-cube
Version:
303 lines • 14.6 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 { action, computed, makeObservable, observable } from 'mobx';
import {} from '../../core/DataCubeSnapshot.js';
import { _findCol, _toCol, } from '../../core/model/DataCubeColumn.js';
import { assertErrorThrown, deleteEntry, guaranteeNonNullable, noop, uniqBy, } from '@finos/legend-shared';
import { DataCubeSnapshotController } from '../../services/DataCubeSnapshotService.js';
import { DataCubeConfiguration, } from '../../core/model/DataCubeConfiguration.js';
import { DataCubeExistingColumnEditorState, DataCubeNewColumnState, } from './DataCubeColumnEditorState.js';
import { DataCubeColumnDataType, DataCubeColumnKind, getDataType, } from '../../core/DataCubeQueryEngine.js';
import { newColumnConfiguration } from '../../core/DataCubeConfigurationBuilder.js';
import { _lambda } from '../../core/DataCubeQueryBuilderUtils.js';
import { EngineError } from '@finos/legend-graph';
class DataCubeQueryExtendedColumnState {
name;
type;
data;
constructor(data) {
this.name = data.name;
this.type = data.type;
this.data = data;
}
}
/**
* This query editor state backs the form editor for extend columns, i.e. creating new columns.
*/
export class DataCubeExtendManagerState extends DataCubeSnapshotController {
view;
columnConfigurations = [];
selectColumns = [];
sourceColumns = [];
horizontalPivotCastColumns = [];
leafExtendedColumns = [];
groupExtendedColumns = [];
newColumnEditors = [];
existingColumnEditors = [];
constructor(view) {
super(view.engine, view.settingService, view.snapshotService);
makeObservable(this, {
sourceColumns: observable.ref,
horizontalPivotCastColumns: observable.ref,
leafExtendedColumns: observable,
setLeafExtendedColumns: action,
groupExtendedColumns: observable,
setGroupExtendedColumns: action,
allColumnNames: computed,
addNewColumn: action,
updateColumn: action,
deleteColumn: action,
applySnapshot: action,
});
this.view = view;
}
setLeafExtendedColumns(val) {
this.leafExtendedColumns = val;
}
setGroupExtendedColumns(val) {
this.groupExtendedColumns = val;
}
get allColumnNames() {
return uniqBy([
...this.sourceColumns,
...this.leafExtendedColumns,
...this.groupExtendedColumns,
...this.horizontalPivotCastColumns,
], (col) => col.name).map((col) => col.name);
}
async openNewColumnEditor(referenceColumn) {
const editor = new DataCubeNewColumnState(this, referenceColumn);
await editor.initialize();
this.newColumnEditors.push(editor);
editor.display.open();
}
async openExistingColumnEditor(columnName) {
const existingEditor = this.existingColumnEditors.find((editor) => editor.initialData.name === columnName);
if (existingEditor) {
existingEditor.display.open();
return;
}
if (!_findCol(this.leafExtendedColumns, columnName) &&
!_findCol(this.groupExtendedColumns, columnName)) {
return;
}
const editor = new DataCubeExistingColumnEditorState(this, guaranteeNonNullable(_findCol(this.leafExtendedColumns, columnName) ??
_findCol(this.groupExtendedColumns, columnName)).data, guaranteeNonNullable(_findCol(this.columnConfigurations, columnName)).kind, Boolean(_findCol(this.groupExtendedColumns, columnName)));
await editor.initialize();
this.existingColumnEditors.push(editor);
editor.display.open();
}
addNewColumn(column, isGroupLevel, columnKind, editor) {
const columnConfiguration = newColumnConfiguration(column);
if (columnKind) {
columnConfiguration.kind = columnKind;
columnConfiguration.excludedFromPivot =
columnKind === DataCubeColumnKind.DIMENSION;
}
this.columnConfigurations.push(columnConfiguration);
deleteEntry(this.newColumnEditors, editor);
if (isGroupLevel) {
this.groupExtendedColumns.push(new DataCubeQueryExtendedColumnState(column));
}
else {
this.leafExtendedColumns.push(new DataCubeQueryExtendedColumnState(column));
this.selectColumns.push(_toCol(column));
}
this.applyChanges();
}
async updateColumn(columnName, updatedColumn, isGroupLevel, columnKind) {
if (!_findCol(this.leafExtendedColumns, columnName) &&
!_findCol(this.groupExtendedColumns, columnName)) {
return;
}
const columnConfiguration = guaranteeNonNullable(_findCol(this.columnConfigurations, columnName));
const task = this.view.taskService.newTask('Checking column update...');
const currentSnapshot = guaranteeNonNullable(this.getLatestSnapshot());
const tempSnapshot = currentSnapshot.clone();
tempSnapshot.data.leafExtendedColumns =
tempSnapshot.data.leafExtendedColumns.filter((col) => col.name !== columnName);
tempSnapshot.data.groupExtendedColumns =
tempSnapshot.data.groupExtendedColumns.filter((col) => col.name !== columnName);
tempSnapshot.data.selectColumns = tempSnapshot.data.selectColumns.filter((col) => col.name !== columnName);
if (isGroupLevel) {
tempSnapshot.data.groupExtendedColumns.push(updatedColumn);
}
else {
tempSnapshot.data.leafExtendedColumns.push(updatedColumn);
if (columnConfiguration.isSelected) {
tempSnapshot.data.selectColumns.push(_toCol(updatedColumn));
}
}
tempSnapshot.data.configuration = {
...tempSnapshot.data.configuration,
columns: this.columnConfigurations.map((col) => {
if (col.name === columnName) {
return {
...col.serialize(),
..._toCol(updatedColumn),
kind: columnKind,
};
}
return col.serialize();
}),
};
const codePrefix = `->`;
const code = await this._engine.getPartialQueryCode(tempSnapshot, true);
try {
await this._engine.getQueryCodeRelationReturnType(codePrefix + code, _lambda([], [this.view.source.query]), this.view.source);
}
catch (error) {
assertErrorThrown(error);
if (error instanceof EngineError) {
this.view.dataCube.alertService.alertCodeCheckError(error, code, codePrefix, {
message: `Column Update Check Failure: Can't safely update column '${columnName}'. Check the query code below for more details.`,
text: `Error: ${error.message}`,
});
}
else {
this.view.dataCube.alertService.alertError(error, {
message: `Column Update Check Failure: Can't safely update column '${columnName}'.`,
text: `Error: ${error.message}`,
});
}
return;
}
finally {
this.view.taskService.endTask(task);
}
this.setLeafExtendedColumns(this.leafExtendedColumns.filter((col) => col.name !== columnName));
this.setGroupExtendedColumns(this.groupExtendedColumns.filter((col) => col.name !== columnName));
this.selectColumns = this.selectColumns.filter((col) => col.name !== columnName);
if (isGroupLevel) {
this.setGroupExtendedColumns([
...this.groupExtendedColumns,
new DataCubeQueryExtendedColumnState(updatedColumn),
]);
}
else {
this.setLeafExtendedColumns([
...this.leafExtendedColumns,
new DataCubeQueryExtendedColumnState(updatedColumn),
]);
if (columnConfiguration.isSelected) {
this.selectColumns.push(_toCol(updatedColumn));
}
}
this.columnConfigurations = this.columnConfigurations.map((col) => {
if (col.name === columnName) {
col.kind =
(columnKind ??
getDataType(updatedColumn.type) === DataCubeColumnDataType.NUMBER)
? DataCubeColumnKind.MEASURE
: DataCubeColumnKind.DIMENSION;
col.name = updatedColumn.name;
col.type = updatedColumn.type;
col.excludedFromPivot = col.kind === DataCubeColumnKind.DIMENSION;
}
return col;
});
// close and remove editors for the updated column
const matchingEditors = this.existingColumnEditors.filter((editor) => editor.initialData.name === columnName);
matchingEditors.forEach((editor) => editor.display.close());
this.existingColumnEditors = this.existingColumnEditors.filter((editor) => editor.initialData.name !== columnName);
this.applyChanges();
}
async deleteColumn(columnName) {
if (!_findCol(this.leafExtendedColumns, columnName) &&
!_findCol(this.groupExtendedColumns, columnName)) {
return;
}
const task = this.view.taskService.newTask('Checking column deletion...');
const currentSnapshot = guaranteeNonNullable(this.getLatestSnapshot());
const tempSnapshot = currentSnapshot.clone();
tempSnapshot.data.leafExtendedColumns =
tempSnapshot.data.leafExtendedColumns.filter((col) => col.name !== columnName);
tempSnapshot.data.groupExtendedColumns =
tempSnapshot.data.groupExtendedColumns.filter((col) => col.name !== columnName);
tempSnapshot.data.selectColumns = tempSnapshot.data.selectColumns.filter((col) => col.name !== columnName);
tempSnapshot.data.configuration = {
...tempSnapshot.data.configuration,
columns: this.columnConfigurations
.filter((col) => col.name !== columnName)
.map((col) => col.serialize()),
};
const codePrefix = `->`;
const code = await this._engine.getPartialQueryCode(tempSnapshot, true);
try {
await this._engine.getQueryCodeRelationReturnType(codePrefix + code, _lambda([], [this.view.source.query]), this.view.source);
}
catch (error) {
assertErrorThrown(error);
if (error instanceof EngineError) {
this.view.dataCube.alertService.alertCodeCheckError(error, code, codePrefix, {
message: `Column Delete Check Failure: Can't safely delete column '${columnName}'. Check the query code below for more details.`,
text: `Error: ${error.message}`,
});
}
else {
this.view.dataCube.alertService.alertError(error, {
message: `Column Delete Check Failure: Can't safely delete column '${columnName}'.`,
text: `Error: ${error.message}`,
});
}
return;
}
finally {
this.view.taskService.endTask(task);
}
this.setLeafExtendedColumns(this.leafExtendedColumns.filter((col) => col.name !== columnName));
this.setGroupExtendedColumns(this.groupExtendedColumns.filter((col) => col.name !== columnName));
this.columnConfigurations = this.columnConfigurations.filter((col) => col.name !== columnName);
this.selectColumns = this.selectColumns.filter((col) => col.name !== columnName);
// close and remove editors for the deleted column
const matchingEditors = this.existingColumnEditors.filter((editor) => editor.initialData.name === columnName);
matchingEditors.forEach((editor) => editor.display.close());
this.existingColumnEditors = this.existingColumnEditors.filter((editor) => editor.initialData.name !== columnName);
this.applyChanges();
}
getSnapshotSubscriberName() {
return 'extend-manager';
}
async applySnapshot(snapshot, previousSnapshot) {
const configuration = DataCubeConfiguration.serialization.fromJson(snapshot.data.configuration);
this.columnConfigurations = configuration.columns;
this.sourceColumns = snapshot.data.sourceColumns;
this.leafExtendedColumns = snapshot.data.leafExtendedColumns.map((col) => new DataCubeQueryExtendedColumnState(col));
this.groupExtendedColumns = snapshot.data.groupExtendedColumns.map((col) => new DataCubeQueryExtendedColumnState(col));
this.horizontalPivotCastColumns = snapshot.data.pivot?.castColumns ?? [];
this.selectColumns = snapshot.data.selectColumns.map(_toCol);
// trigger re-compile in each existing column editor as the base query has changed
[...this.newColumnEditors, ...this.existingColumnEditors].forEach((editor) => {
editor.getReturnType().catch(noop());
});
}
applyChanges() {
const baseSnapshot = guaranteeNonNullable(this.getLatestSnapshot());
const newSnapshot = baseSnapshot.clone();
newSnapshot.data.configuration = {
...baseSnapshot.data.configuration,
columns: this.columnConfigurations.map((col) => col.serialize()),
};
newSnapshot.data.leafExtendedColumns = this.leafExtendedColumns.map((col) => col.data);
newSnapshot.data.groupExtendedColumns = this.groupExtendedColumns.map((col) => col.data);
newSnapshot.data.selectColumns = [...this.selectColumns];
newSnapshot.finalize();
if (newSnapshot.hashCode !== baseSnapshot.hashCode) {
this.publishSnapshot(newSnapshot);
}
}
}
//# sourceMappingURL=DataCubeExtendManagerState.js.map