@finos/legend-application-studio
Version:
Legend Studio application core
223 lines (206 loc) • 7.37 kB
text/typescript
/**
* 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 {
type GeneratorFn,
assertErrorThrown,
LogEvent,
} from '@finos/legend-shared';
import { observable, makeObservable, flow, flowResult } from 'mobx';
import {
type PackageableElement,
V1_DataProductArtifact,
DataProduct,
IngestDefinition,
type TEMPORARY_IngestContent,
type ArtifactGenerationExtensionResult,
TDSExecutionResult,
type RawLambda,
type ExecutionResultWithMetadata,
LakehouseRuntime,
GRAPH_MANAGER_EVENT,
} from '@finos/legend-graph';
import type { EditorStore } from './EditorStore.js';
import {
LegendSQLPlaygroundState,
DEFAULT_SQL_TEXT,
buildDefaultDataProductQuery,
buildDefaultIngestQuery,
QueryExecutionResult,
} from '@finos/legend-query-builder';
const DATA_PRODUCT_ARTIFACT_EXTENSION = 'dataProduct';
export class LegendSQLStudioPlaygroundState extends LegendSQLPlaygroundState {
readonly editorStore: EditorStore;
targetElement?: PackageableElement | undefined;
isOpen = false;
constructor(editorStore: EditorStore) {
super();
makeObservable(this, {
targetElement: observable,
isOpen: observable,
executeRawSQL: flow,
initializeAccessorExplorer: flow,
});
this.editorStore = editorStore;
}
open(element: PackageableElement): void {
this.isOpen = true;
this.targetElement = element;
this.accessorExplorerState = undefined;
flowResult(this.initializeAccessorExplorer()).catch(
this.editorStore.applicationStore.alertUnhandledError,
);
if (element instanceof DataProduct) {
const firstAccessPointId =
element.accessPointGroups[0]?.accessPoints[0]?.id;
this.setSQLQuery(
`${DEFAULT_SQL_TEXT}${buildDefaultDataProductQuery(element.path, firstAccessPointId)}`,
);
} else if (element instanceof IngestDefinition) {
const content = element.content as unknown as
| TEMPORARY_IngestContent
| undefined;
const firstDatasetName = content?.datasets?.[0]?.name;
this.setSQLQuery(
`${DEFAULT_SQL_TEXT}${buildDefaultIngestQuery(element.path, firstDatasetName)}`,
);
} else {
this.setSQLQuery(DEFAULT_SQL_TEXT);
}
}
close(): void {
this.isOpen = false;
this.targetElement = undefined;
this.accessorExplorerState = undefined;
this.setSQLQuery(DEFAULT_SQL_TEXT);
}
*initializeAccessorExplorer(): GeneratorFn<void> {
if (this.accessorExplorerState || !this.targetElement) {
return;
}
try {
const entities = this.editorStore.graphManagerState.graph.allElements
.filter(
(element) =>
element instanceof DataProduct ||
element instanceof IngestDefinition,
)
.map((element) =>
this.editorStore.graphManagerState.graphManager.elementToEntity(
element,
),
);
yield flowResult(
this.initializeExplorer(
entities,
this.editorStore.graphManagerState.graphManager.pluginManager.getPureProtocolProcessorPlugins(),
(path) => this.fetchDataProductArtifact(path),
),
);
} catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.logService.error(
LogEvent.create(GRAPH_MANAGER_EVENT.EXECUTION_FAILURE),
error,
);
this.editorStore.applicationStore.notificationService.notifyError(error);
}
}
private async fetchDataProductArtifact(
dataProductPath: string,
): Promise<V1_DataProductArtifact | undefined> {
try {
const generatedArtifacts =
(await this.editorStore.graphManagerState.graphManager.generateArtifacts(
this.editorStore.graphManagerState.graph,
this.editorStore.graphEditorMode.getGraphTextInputOption(),
[dataProductPath],
)) as unknown as ArtifactGenerationExtensionResult;
const dataProductArtifactResults = generatedArtifacts.values.filter(
(artifact) => artifact.extension === DATA_PRODUCT_ARTIFACT_EXTENSION,
);
for (const artifactResult of dataProductArtifactResults) {
for (const artifactByElement of artifactResult.artifactsByExtensionElements) {
const dataProductContent = artifactByElement.files[0]?.content;
if (dataProductContent) {
return V1_DataProductArtifact.serialization.fromJson(
JSON.parse(dataProductContent),
);
}
}
}
} catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.notificationService.notifyError(
`Error fetching artifact for ${dataProductPath}: ${error}`,
);
}
return undefined;
}
override *executeRawSQL(): GeneratorFn<void> {
if (this.executeRawSQLState.isInProgress || !this.targetElement) {
return;
}
try {
this.executeRawSQLState.inProgress();
const sql = this.getSelectedSQL();
const sqlQuery = `#SQL{${sql}}#`;
const runtimes = this.editorStore.graphManagerState.graph.ownRuntimes;
const packageableRuntime = runtimes[0];
if (!packageableRuntime) {
this.editorStore.applicationStore.notificationService.notifyError(
new Error('No runtime found in the graph'),
);
return;
}
if (!(packageableRuntime.runtimeValue instanceof LakehouseRuntime)) {
this.editorStore.applicationStore.notificationService.notifyError(
new Error('Runtime must be a LakehouseRuntime'),
);
return;
}
const runtime = packageableRuntime.runtimeValue;
const queryToExecute = `${sqlQuery}->from(${packageableRuntime.path})`;
const lambda =
(yield this.editorStore.graphManagerState.graphManager.pureCodeToLambda(
queryToExecute,
)) as RawLambda;
const executionResult =
(yield this.editorStore.graphManagerState.graphManager.runQuery(
lambda,
undefined,
runtime,
this.editorStore.graphManagerState.graph,
)) as ExecutionResultWithMetadata;
const result = executionResult.executionResult;
if (result instanceof TDSExecutionResult) {
this.setSqlExecutionResult(new QueryExecutionResult(result));
} else {
this.editorStore.applicationStore.notificationService.notifyError(
'Expected TDS execution result, got unsupported result type',
);
}
} catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.logService.error(
LogEvent.create(GRAPH_MANAGER_EVENT.EXECUTION_FAILURE),
error,
);
this.editorStore.applicationStore.notificationService.notifyError(error);
} finally {
this.executeRawSQLState.complete();
}
}
}