@finos/legend-application-studio
Version:
Legend Studio application core
208 lines • 18.5 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
/**
* 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 React, { useEffect } from 'react';
import { observer } from 'mobx-react-lite';
import { ServicePureExecutionState } from '../../../../stores/editor/editor-state/element-editor-state/service/ServiceExecutionState.js';
import { Dialog, PanelLoadingIndicator, PlayIcon, ControlledDropdownMenu, MenuContent, CaretDownIcon, MenuContentItem, PauseCircleIcon, PencilIcon, Modal, ModalBody, ModalFooter, ModalFooterButton, ModalHeader, } from '@finos/legend-art';
import { assertErrorThrown, guaranteeNonNullable, returnUndefOnError, } from '@finos/legend-shared';
import { flowResult } from 'mobx';
import { useEditorStore } from '../../EditorStoreProvider.js';
import { KeyedExecutionParameter, MultiExecutionParameters, PureExecution, } from '@finos/legend-graph';
import { ServiceQueryBuilderState, LambdaParameterValuesEditor, QueryBuilderTextEditorMode, QueryLoaderDialog, ExecutionPlanViewer, QueryBuilderAdvancedWorkflowState, QueryBuilderActionConfig, LineageViewer, } from '@finos/legend-query-builder';
import { ProjectViewerEditorMode } from '../../../../stores/project-view/ProjectViewerEditorMode.js';
import { useLegendStudioApplicationStore } from '../../../LegendStudioFrameworkProvider.js';
import { SNAPSHOT_ALIAS, SNAPSHOT_VERSION_ALIAS, } from '@finos/legend-server-depot';
import { CODE_EDITOR_LANGUAGE } from '@finos/legend-code-editor';
import { CodeEditor } from '@finos/legend-lego/code-editor';
import { EXTERNAL_APPLICATION_NAVIGATION__generateServiceQueryCreatorUrl } from '../../../../__lib__/LegendStudioNavigation.js';
import { pureExecution_setFunction } from '../../../../stores/graph-modifier/DSL_Service_GraphModifierHelper.js';
import { ServiceEditorState } from '../../../../stores/editor/editor-state/element-editor-state/service/ServiceEditorState.js';
const ServiceExecutionResultViewer = observer((props) => {
const { executionState } = props;
const applicationStore = executionState.editorStore.applicationStore;
// execution
const executionResultText = executionState.executionResultText;
const closeExecutionResultViewer = () => executionState.setExecutionResultText(undefined);
return (_jsx(Dialog, { open: Boolean(executionResultText), onClose: closeExecutionResultViewer, classes: {
root: 'editor-modal__root-container',
container: 'editor-modal__container',
paper: 'editor-modal__content',
}, children: _jsxs(Modal, { darkMode: !applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled, className: "editor-modal", children: [_jsx(ModalHeader, { title: "Execution Result" }), _jsx(ModalBody, { children: _jsx(CodeEditor, { inputValue: executionResultText ?? '', isReadOnly: true, language: CODE_EDITOR_LANGUAGE.JSON }) }), _jsx(ModalFooter, { children: _jsx(ModalFooterButton, { className: "modal__footer__close-btn", onClick: closeExecutionResultViewer, text: "Close", type: "secondary" }) })] }) }));
});
export const ServiceExecutionQueryEditor = observer((props) => {
const { executionState, isReadOnly } = props;
const applicationStore = useLegendStudioApplicationStore();
const editorStore = useEditorStore();
const queryState = executionState.queryState;
const embeddedQueryBuilderState = editorStore.embeddedQueryBuilderState;
const service = executionState.serviceEditorState.service;
// actions
const editWithQueryBuilder = (openInTextMode = false) => applicationStore.guardUnhandledError(async () => {
const selectedExecutionState = executionState.selectedExecutionContextState;
await flowResult(embeddedQueryBuilderState.setEmbeddedQueryBuilderConfiguration({
setupQueryBuilderState: async () => {
const sourceInfo = {
service: service.path,
...editorStore.editorMode.getSourceInfo(),
};
const queryBuilderState = new ServiceQueryBuilderState(embeddedQueryBuilderState.editorStore.applicationStore, embeddedQueryBuilderState.editorStore.graphManagerState, QueryBuilderAdvancedWorkflowState.INSTANCE, QueryBuilderActionConfig.INSTANCE, service, undefined, selectedExecutionState?.executionContext instanceof
KeyedExecutionParameter
? selectedExecutionState.executionContext.key
: undefined, undefined, undefined, embeddedQueryBuilderState.editorStore.applicationStore.config.options.queryBuilderConfig, sourceInfo);
queryBuilderState.initializeWithQuery(executionState.execution.func);
if (openInTextMode) {
queryBuilderState.textEditorState.openModal(QueryBuilderTextEditorMode.TEXT);
}
return queryBuilderState;
},
actionConfigs: [
{
key: 'save-query-btn',
renderer: (queryBuilderState) => {
const save = applicationStore.guardUnhandledError(async () => {
try {
const rawLambda = queryBuilderState.buildQuery();
await flowResult(executionState.queryState.updateLamba(rawLambda));
applicationStore.notificationService.notifySuccess(`Service query is updated`);
embeddedQueryBuilderState.setEmbeddedQueryBuilderConfiguration(undefined);
}
catch (error) {
assertErrorThrown(error);
applicationStore.notificationService.notifyError(`Can't save query: ${error.message}`);
}
});
return (_jsx("button", { className: "query-builder__dialog__header__custom-action", tabIndex: -1, disabled: isReadOnly, onClick: save, children: "Save Query" }));
},
},
],
disableCompile: true,
}));
executionState.setOpeningQueryEditor(false);
});
const importQuery = () => queryState.queryLoaderState.setQueryLoaderDialogOpen(true);
const runQuery = applicationStore.guardUnhandledError(() => flowResult(executionState.handleRunQuery()));
const executionIsRunning = executionState.isRunningQuery || executionState.isGeneratingPlan;
const cancelQuery = applicationStore.guardUnhandledError(() => flowResult(executionState.cancelQuery()));
const generatePlan = applicationStore.guardUnhandledError(() => flowResult(executionState.generatePlan(false)));
const generateLineage = applicationStore.guardUnhandledError(() => flowResult(executionState.generateLineage()));
const debugPlanGeneration = applicationStore.guardUnhandledError(() => flowResult(executionState.generatePlan(true)));
const openServiceCubeViewer = editorStore.applicationStore.guardUnhandledError(async () => {
await executionState.handleOpeningDataCube(service, editorStore);
});
const openQueryInLegendQuery = () => {
if (!applicationStore.config.queryApplicationUrl) {
return;
}
let projectGAV;
if (editorStore.editorMode instanceof ProjectViewerEditorMode) {
const viewerEditorMode = editorStore.editorMode;
// for Achive mode
if (viewerEditorMode.viewerStore.projectGAVCoordinates) {
projectGAV = viewerEditorMode.viewerStore.projectGAVCoordinates;
}
else {
// for other viewer modes, if no version we use project `HEAD`
projectGAV = {
groupId: editorStore.projectConfigurationEditorState
.currentProjectConfiguration.groupId,
artifactId: editorStore.projectConfigurationEditorState
.currentProjectConfiguration.artifactId,
versionId: viewerEditorMode.viewerStore.version?.id.id ??
SNAPSHOT_VERSION_ALIAS,
};
}
}
else {
const currentWorkSpaceId = editorStore.sdlcState.currentWorkspace?.workspaceId;
projectGAV = {
groupId: editorStore.projectConfigurationEditorState
.currentProjectConfiguration.groupId,
artifactId: editorStore.projectConfigurationEditorState
.currentProjectConfiguration.artifactId,
versionId: editorStore.sdlcState.projectPublishedVersions.includes(`${currentWorkSpaceId}-${SNAPSHOT_ALIAS}`)
? `${currentWorkSpaceId}-${SNAPSHOT_ALIAS}`
: SNAPSHOT_VERSION_ALIAS,
};
}
applicationStore.navigationService.navigator.visitAddress(EXTERNAL_APPLICATION_NAVIGATION__generateServiceQueryCreatorUrl(applicationStore.config.queryApplicationUrl, projectGAV.groupId, projectGAV.artifactId, projectGAV.versionId, service.path));
};
// convert to string
useEffect(() => {
flowResult(queryState.convertLambdaObjectToGrammarString({ pretty: true })).catch(applicationStore.alertUnhandledError);
}, [applicationStore, queryState]);
return (_jsxs("div", { className: "panel service-execution-query-editor", children: [_jsxs("div", { className: "panel__header", children: [_jsx("div", { className: "panel__header__title", children: _jsx("div", { className: "panel__header__title__label service-editor__execution__label--query", children: "query" }) }), _jsxs("div", { className: "panel__header__actions", children: [_jsxs("div", { className: "btn__dropdown-combo btn__dropdown-combo--primary", children: [_jsxs("button", { className: "btn__dropdown-combo__label", onClick: editWithQueryBuilder(), title: "Edit Query", tabIndex: -1, children: [_jsx(PencilIcon, { className: "btn__dropdown-combo__label__icon" }), _jsx("div", { className: "btn__dropdown-combo__label__title", children: "Edit Query" })] }), _jsx(ControlledDropdownMenu, { className: "btn__dropdown-combo__dropdown-btn", content: _jsx(MenuContent, { children: _jsx(MenuContentItem, { className: "btn__dropdown-combo__option", onClick: editWithQueryBuilder(true), children: "Text Mode" }) }), menuProps: {
anchorOrigin: { vertical: 'bottom', horizontal: 'right' },
transformOrigin: { vertical: 'top', horizontal: 'right' },
}, children: _jsx(CaretDownIcon, {}) })] }), _jsx("div", { className: "btn__dropdown-combo btn__dropdown-combo--primary", children: executionState.isRunningQuery ? (_jsx("button", { className: "btn__dropdown-combo__canceler", onClick: cancelQuery, tabIndex: -1, children: _jsxs("div", { className: "btn--dark btn--caution btn__dropdown-combo__canceler__label", children: [_jsx(PauseCircleIcon, { className: "btn__dropdown-combo__canceler__label__icon" }), _jsx("div", { className: "btn__dropdown-combo__canceler__label__title", children: "Stop" })] }) })) : (_jsxs(_Fragment, { children: [_jsxs("button", { className: "btn__dropdown-combo__label", onClick: runQuery, title: "Run Query", disabled: executionIsRunning, tabIndex: -1, children: [_jsx(PlayIcon, { className: "btn__dropdown-combo__label__icon" }), _jsx("div", { className: "btn__dropdown-combo__label__title", children: "Run Query" })] }), _jsx(ControlledDropdownMenu, { className: "btn__dropdown-combo__dropdown-btn", disabled: executionIsRunning, content: _jsxs(MenuContent, { children: [_jsx(MenuContentItem, { className: "btn__dropdown-combo__option", onClick: generatePlan, children: "Generate Plan" }), _jsx(MenuContentItem, { className: "btn__dropdown-combo__option", onClick: generateLineage, children: "View Lineage" }), _jsx(MenuContentItem, { className: "btn__dropdown-combo__option", onClick: debugPlanGeneration, children: "Debug" })] }), menuProps: {
anchorOrigin: { vertical: 'bottom', horizontal: 'right' },
transformOrigin: { vertical: 'top', horizontal: 'right' },
}, children: _jsx(CaretDownIcon, {}) })] })) }), _jsx("div", { className: "btn__dropdown-combo btn__dropdown-combo--primary", children: _jsx("button", { className: "btn__dropdown-combo__label", onClick: openServiceCubeViewer, title: "Data Cube (BETA)", disabled: executionIsRunning, tabIndex: -1, children: _jsx("div", { className: "btn__dropdown-combo__label__title", children: "Data Cube" }) }) }), _jsxs(ControlledDropdownMenu, { className: "btn__dropdown-combo", disabled: executionIsRunning, content: _jsxs(MenuContent, { children: [_jsx(MenuContentItem, { className: "btn__dropdown-combo__option", onClick: importQuery, children: "Import Query" }), _jsx(MenuContentItem, { className: "btn__dropdown-combo__option", onClick: openQueryInLegendQuery, disabled: !applicationStore.config.queryApplicationUrl, children: "Create an Ad-hoc Query" })] }), menuProps: {
anchorOrigin: { vertical: 'bottom', horizontal: 'right' },
transformOrigin: { vertical: 'top', horizontal: 'right' },
}, children: [_jsx("div", { className: "btn__dropdown-combo__label", children: _jsx("div", { className: "btn__dropdown-combo__label__title", children: "Advanced" }) }), _jsx("div", { className: "btn__dropdown-combo__dropdown-btn", children: _jsx(CaretDownIcon, {}) })] })] })] }), _jsxs("div", { className: "panel__content property-mapping-editor__entry__container", children: [_jsx(PanelLoadingIndicator, { isLoading: executionState.isOpeningQueryEditor || executionIsRunning }), _jsx("div", { className: "service-execution-query-editor__content", children: _jsx(CodeEditor, { inputValue: queryState.lambdaString, isReadOnly: true, language: CODE_EDITOR_LANGUAGE.PURE }) }), queryState.queryLoaderState.isQueryLoaderDialogOpen && (_jsx(QueryLoaderDialog, { queryLoaderState: queryState.queryLoaderState, title: "import query" })), _jsx(ExecutionPlanViewer, { executionPlanState: executionState.executionPlanState }), _jsx(LineageViewer, { lineageState: executionState.lineageState }), _jsx(ServiceExecutionResultViewer, { executionState: executionState }), executionState.parametersState.parameterValuesEditorState
.showModal && (_jsx(LambdaParameterValuesEditor, { graph: executionState.editorStore.graphManagerState.graph, observerContext: executionState.editorStore.changeDetectionState.observerContext, lambdaParametersState: executionState.parametersState }))] })] }));
});
export const queryService = async (service, editorStore) => {
const embeddedQueryBuilderState = editorStore.embeddedQueryBuilderState;
const applicationStore = editorStore.applicationStore;
const execution = service.execution instanceof PureExecution ? service.execution : undefined;
const selectedExec = execution instanceof MultiExecutionParameters
? execution.singleExecutionParameters[0]?.key
: undefined;
const sourceInfo = {
service: service.path,
...editorStore.editorMode.getSourceInfo(),
};
await flowResult(embeddedQueryBuilderState.setEmbeddedQueryBuilderConfiguration({
setupQueryBuilderState: async () => {
const queryBuilderState = new ServiceQueryBuilderState(embeddedQueryBuilderState.editorStore.applicationStore, embeddedQueryBuilderState.editorStore.graphManagerState, QueryBuilderAdvancedWorkflowState.INSTANCE, QueryBuilderActionConfig.INSTANCE, service, undefined, selectedExec, undefined, undefined, embeddedQueryBuilderState.editorStore.applicationStore.config.options.queryBuilderConfig, sourceInfo);
if (execution) {
queryBuilderState.initializeWithQuery(execution.func);
}
return queryBuilderState;
},
actionConfigs: [
{
key: 'save-query-btn',
renderer: (queryBuilderState) => {
const save = applicationStore.guardUnhandledError(async () => {
try {
const rawLambda = queryBuilderState.buildQuery();
const serviceState = returnUndefOnError(() => editorStore.tabManagerState.getCurrentEditorState(ServiceEditorState));
if (serviceState?.service === service &&
serviceState.executionState instanceof
ServicePureExecutionState) {
await flowResult(serviceState.executionState.queryState.updateLamba(rawLambda));
}
else {
pureExecution_setFunction(guaranteeNonNullable(execution, 'Service execution expected to be a pure execution'), rawLambda);
}
applicationStore.notificationService.notifySuccess(`Service query is updated`);
embeddedQueryBuilderState.setEmbeddedQueryBuilderConfiguration(undefined);
}
catch (error) {
assertErrorThrown(error);
applicationStore.notificationService.notifyError(`Can't save query: ${error.message}`);
}
});
return (_jsx("button", { className: "query-builder__dialog__header__custom-action", tabIndex: -1, disabled: editorStore.disableGraphEditing, onClick: save, children: "Save Query" }));
},
},
],
}));
};
//# sourceMappingURL=ServiceExecutionQueryEditor.js.map