/** * 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, makeObservable, observable } from 'mobx'; import { ActionState, assertErrorThrown, IllegalStateError, UnsupportedOperationError, type PlainObject, } from '@finos/legend-shared'; import { LegendQueryDataCubeSourceBuilderState } from './source/LegendQueryDataCubeSourceBuilderState.js'; import type { LegendDataCubeApplicationStore } from '../LegendDataCubeBaseStore.js'; import { LegendDataCubeSourceBuilderType, type LegendDataCubeSourceBuilderState, } from './source/LegendDataCubeSourceBuilderState.js'; import { type DataCubeAlertService, DEFAULT_TOOL_PANEL_WINDOW_CONFIG, type DisplayState, } from '@finos/legend-data-cube'; import type { LegendDataCubeDataCubeEngine } from '../LegendDataCubeDataCubeEngine.js'; import { LegendDataCubeCreator } from '../../components/builder/LegendDataCubeCreator.js'; import { FreeformTDSExpressionDataCubeSourceBuilderState } from './source/FreeformTDSExpressionDataCubeSourceBuilderState.js'; import { LegendDataCubeBuilderState, type LegendDataCubeBuilderStore, } from './LegendDataCubeBuilderStore.js'; import { generateBuilderRoute } from '../../__lib__/LegendDataCubeNavigation.js'; import { LocalFileDataCubeSourceBuilderState } from './source/LocalFileDataCubeSourceBuilderState.js'; import { UserDefinedFunctionDataCubeSourceBuilderState } from './source/UserDefinedFunctionDataCubeSourceBuilderState.js'; import { LEGEND_DATACUBE_APP_EVENT } from '../../__lib__/LegendDataCubeEvent.js'; import { LakehouseProducerDataCubeSourceBuilderState } from './source/LakehouseProducerDataCubeSourceBuilderState.js'; import { LakehouseConsumerDataCubeSourceBuilderState } from './source/LakehouseConsumerDataCubeSourceBuilderState.js'; const DEFAULT_SOURCE_TYPE = LegendDataCubeSourceBuilderType.LEGEND_QUERY; export class LegendDataCubeCreatorState { private readonly _application: LegendDataCubeApplicationStore; private readonly _store: LegendDataCubeBuilderStore; private readonly _engine: LegendDataCubeDataCubeEngine; private readonly _alertService: DataCubeAlertService; readonly finalizeState = ActionState.create(); readonly display: DisplayState; sourceBuilder: LegendDataCubeSourceBuilderState; constructor(store: LegendDataCubeBuilderStore) { makeObservable(this, { sourceBuilder: observable, changeSourceBuilder: action, }); this._application = store.application; this._store = store; this._engine = store.engine; this._alertService = store.alertService; this.display = store.layoutService.newDisplay( 'New DataCube', () => , { ...DEFAULT_TOOL_PANEL_WINDOW_CONFIG, width: 500, minWidth: 500, }, ); this.sourceBuilder = this.createSourceBuilder(DEFAULT_SOURCE_TYPE); } changeSourceBuilder( type: LegendDataCubeSourceBuilderType, skipCheck?: boolean | undefined, ): void { if (this.sourceBuilder.label !== type || skipCheck) { this.sourceBuilder = this.createSourceBuilder(type); } } private createSourceBuilder( type: LegendDataCubeSourceBuilderType, ): LegendDataCubeSourceBuilderState { switch (type) { case LegendDataCubeSourceBuilderType.LEGEND_QUERY: return new LegendQueryDataCubeSourceBuilderState( this._application, this._engine, this._store.engineServerClient, this._store.depotServerClient, this._store.graphManager, this._alertService, ); case LegendDataCubeSourceBuilderType.FREEFORM_TDS_EXPRESSION: return new FreeformTDSExpressionDataCubeSourceBuilderState( this._application, this._engine, this._alertService, this._store, ); case LegendDataCubeSourceBuilderType.LOCAL_FILE: return new LocalFileDataCubeSourceBuilderState( this._application, this._engine, this._alertService, ); case LegendDataCubeSourceBuilderType.USER_DEFINED_FUNCTION: return new UserDefinedFunctionDataCubeSourceBuilderState( this._application, this._engine, this._alertService, this._store, ); case LegendDataCubeSourceBuilderType.LAKEHOUSE_PRODUCER: return new LakehouseProducerDataCubeSourceBuilderState( this._application, this._engine, this._store.platformServerClient, this._store.ingestServerClient, this._store.contractServerClient, this._alertService, ); case LegendDataCubeSourceBuilderType.LAKEHOUSE_CONSUMER: return new LakehouseConsumerDataCubeSourceBuilderState( this._application, this._engine, this._store.depotServerClient, this._store.contractServerClient, this._alertService, ); default: throw new UnsupportedOperationError( `Can't create source builder for unsupported type '${type}'`, ); } } async finalize(sourceData?: PlainObject) { if (!sourceData && !this.sourceBuilder.isValid) { throw new IllegalStateError( `Can't generate DataCube: source is not valid`, ); } this.finalizeState.inProgress(); try { sourceData = sourceData ?? (await this.sourceBuilder.generateSourceData()); try { const source = await this._engine.processSource(sourceData); const specification = await this._engine.generateBaseSpecification( sourceData, source, ); if (specification.configuration) { this.sourceBuilder.finalizeConfiguration(specification.configuration); } // reset this._store.setBuilder( new LegendDataCubeBuilderState(this._store, specification), ); // only update the route instead of reloading in case we are creating // a new DataCube when we are editing another DataCube this._application.navigationService.navigator.updateCurrentLocation( generateBuilderRoute(null), ); this.changeSourceBuilder(DEFAULT_SOURCE_TYPE, true); this.display.close(); this.finalizeState.pass(); this._engine.sendTelemetry( LEGEND_DATACUBE_APP_EVENT.NEW_DATACUBE__SUCCESS, this._engine.getDataFromSource(source), ); } catch (error) { assertErrorThrown(error); const message = `DataCube Creation Failure: ${error.message}`; this._engine.sendTelemetry( LEGEND_DATACUBE_APP_EVENT.NEW_DATACUBE__FAILURE, { ...this._engine.getDataFromRawSource(sourceData), error: message }, ); this._alertService.alertError(error, { message: message, }); this.finalizeState.fail(); } } catch (error) { assertErrorThrown(error); const message = `DataCube Creation Failure: ${error.message}`; this._engine.sendTelemetry( LEGEND_DATACUBE_APP_EVENT.NEW_DATACUBE__FAILURE, { error: message }, ); this._alertService.alertError(error, { message: message, }); this.finalizeState.fail(); } } }