/** * 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 { useState, useEffect, useCallback, forwardRef } from 'react'; import { observer } from 'mobx-react-lite'; import { type RuntimeEditorState, LakehouseBaseRuntimeEditorState, LakehouseRuntimeEditorState, PackageableRuntimeEditorState, RuntimeEditorRuntimeTabState, IdentifiedConnectionsEditorTabState, IdentifiedConnectionsPerClassEditorTabState, IdentifiedConnectionsPerStoreEditorTabState, LakehouseRuntimeType, } from '../../../stores/editor/editor-state/element-editor-state/RuntimeEditorState.js'; import type { EditorStore } from '../../../stores/editor/EditorStore.js'; import { clsx, Dialog, type TreeNodeContainerProps, ResizablePanel, ResizablePanelGroup, ResizablePanelSplitter, ResizablePanelSplitterLine, TreeView, ContextMenu, CustomSelectorInput, createFilter, BlankPanelContent, MenuContent, MenuContentItem, BlankPanelPlaceholder, PURE_RuntimeIcon, PURE_ConnectionIcon, PURE_ModelStoreIcon, PURE_ClassIcon, PURE_MappingIcon, ChevronDownIcon, ChevronRightIcon, PlusIcon, TimesIcon, LongArrowRightIcon, CogIcon, CaretRightIcon, PanelDropZone, Panel, PanelContent, PanelHeader, ModalHeader, ModalTitle, ModalHeaderActions, ModalBody, Modal, PanelHeaderActions, PanelHeaderActionItem, LockIcon, PanelFormTextField, PanelFormSection, } from '@finos/legend-art'; import { getElementIcon } from '../../ElementIconUtils.js'; import type { RuntimeExplorerTreeNodeData } from '../../../stores/editor/utils/TreeUtils.js'; import { ConnectionEditor } from './connection-editor/ConnectionEditor.js'; import { type UMLEditorElementDropTarget, CORE_DND_TYPE, ElementDragSource, } from '../../../stores/editor/utils/DnDUtils.js'; import { useDrop } from 'react-dnd'; import { assertErrorThrown, prettyCONSTName } from '@finos/legend-shared'; import type { ConnectionEditorState } from '../../../stores/editor/editor-state/element-editor-state/connection/ConnectionEditorState.js'; import { useEditorStore } from '../EditorStoreProvider.js'; import { type PackageableElementReference, type PackageableRuntime, Connection, ConnectionPointer, Runtime, IdentifiedConnection, RuntimePointer, ModelStore, Mapping, Class, Store, PackageableConnection, JsonModelConnection, XmlModelConnection, FlatDataConnection, RelationalDatabaseConnection, PackageableElementExplicitReference, ModelChainConnection, generateIdentifiedConnectionId, } from '@finos/legend-graph'; import { useApplicationNavigationContext, useApplicationStore, } from '@finos/legend-application'; import { buildElementOption, type PackageableElementOption, } from '@finos/legend-lego/graph-editor'; import type { DSL_Mapping_LegendStudioApplicationPlugin_Extension } from '../../../stores/extensions/DSL_Mapping_LegendStudioApplicationPlugin_Extension.js'; import { runtime_addIdentifiedConnection, runtime_deleteIdentifiedConnection, } from '../../../stores/graph-modifier/DSL_Mapping_GraphModifierHelper.js'; import { LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY } from '../../../__lib__/LegendStudioApplicationNavigationContext.js'; import { CUSTOM_LABEL } from '../../../stores/editor/NewElementState.js'; import { lakehouseRuntime_setWarehouse, lakehouseRuntime_setConnection, LakehouseComputeEngine, LAKEHOUSE_COMPUTE_ENGINE_LABEL, getLakehouseComputeEngine, lakehouseRuntime_setComputeEngine, } from '../../../stores/graph-modifier/DSL_LakehouseRuntime_GraphModifierHelper.js'; import { useAuth, type AuthContextProps } from 'react-oidc-context'; import { flowResult, runInAction } from 'mobx'; const getConnectionTooltipText = ( connection: Connection, editorStore: EditorStore, ): string => { const connectionValue = connection instanceof ConnectionPointer ? connection.packageableConnection.value.connectionValue : connection; if (connectionValue instanceof JsonModelConnection) { return `JSON model connection \u2022 Class ${connectionValue.class.value.path}`; } else if (connectionValue instanceof XmlModelConnection) { return `XML model connection \u2022 Class ${connectionValue.class.value.path}`; } else if (connectionValue instanceof FlatDataConnection) { return `Flat-data connection \u2022 Flat-data store ${connectionValue.store.value.path}`; } else if (connectionValue instanceof RelationalDatabaseConnection) { return connectionValue.store ? `Relational database connection \u2020 database store ${connectionValue.store.value.path}` : `Relational database connection`; } else if (connectionValue instanceof ModelChainConnection) { return `Model chain connection \u2022`; } const extraConnectionToolTipTexts = editorStore.pluginManager .getApplicationPlugins() .flatMap( (plugin) => ( plugin as DSL_Mapping_LegendStudioApplicationPlugin_Extension ).getExtraRuntimeConnectionTooltipTextBuilders?.() ?? [], ); for (const builder of extraConnectionToolTipTexts) { const tooltipText = builder(connectionValue); if (tooltipText) { return tooltipText; } } return `Unknown connection`; }; const IdentifiedConnectionsPerStoreExplorerContextMenu = observer( forwardRef< HTMLDivElement, { identifiedConnection?: IdentifiedConnection; deleteIdentifiedConnection?: () => void; createNewIdentifiedConnection: () => void; } >(function IdentifiedConnectionsPerStoreExplorerContextMenu(props, ref) { const { identifiedConnection, deleteIdentifiedConnection, createNewIdentifiedConnection, } = props; const remove = (): void => deleteIdentifiedConnection?.(); return ( {identifiedConnection && ( Remove )} {!identifiedConnection && ( Create a new connection for store )} ); }), ); export const IdentifiedConnectionsPerStoreExplorerItem = observer( (props: { identifiedConnection: IdentifiedConnection; currentRuntimeEditorTabState: IdentifiedConnectionsEditorTabState; isActive: boolean; isReadOnly: boolean; }) => { const { identifiedConnection, currentRuntimeEditorTabState, isActive, isReadOnly, } = props; const editorStore = useEditorStore(); const openConnection = (): void => currentRuntimeEditorTabState.openIdentifiedConnection( identifiedConnection, ); const deleteConnection = (): void => currentRuntimeEditorTabState.deleteIdentifiedConnection( identifiedConnection, ); const openNewConnectionModal = (): void => currentRuntimeEditorTabState.addIdentifiedConnection(); // context menu const [isSelectedFromContextMenu, setIsSelectedFromContextMenu] = useState(false); const onContextMenuOpen = (): void => setIsSelectedFromContextMenu(true); const onContextMenuClose = (): void => setIsSelectedFromContextMenu(false); return ( } menuProps={{ elevation: 7 }} onOpen={onContextMenuOpen} onClose={onContextMenuClose} > ); }, ); const getRuntimeExplorerTreeNodeIcon = ( editorStore: EditorStore, node: RuntimeExplorerTreeNodeData, ): React.ReactNode => { if (node.data instanceof Runtime) { return ; } else if (node.data instanceof ModelStore) { return ; } else if (node.data instanceof Connection) { return ; } return getElementIcon(node.data, editorStore); }; const getRuntimeExplorerTreeNodeTooltipText = ( node: RuntimeExplorerTreeNodeData, ): string | undefined => { if (node.data instanceof Runtime) { return ''; // number of mapping and stores } else if (node.data instanceof ModelStore) { return ''; } else if (node.data instanceof Store) { /* do nothing */ } else if (node.data instanceof Class) { // return `JSON model connection \u2022 Class ${connectionValue.class.path}`; } return undefined; }; const RuntimeExplorerTreeNodeContainer = observer( ( props: TreeNodeContainerProps< RuntimeExplorerTreeNodeData, { isReadOnly: boolean; runtimeEditorState: RuntimeEditorState; onNodeExpand: (node: RuntimeExplorerTreeNodeData) => void; } >, ) => { const { node, level, stepPaddingInRem, onNodeSelect, innerProps } = props; const { runtimeEditorState, onNodeExpand } = innerProps; const engineEditorState = runtimeEditorState.runtimeValueEditorState; const editorStore = useEditorStore(); const isExpandable = Boolean(node.childrenIds?.length); const nodeExpandIcon = isExpandable ? ( node.isOpen ? ( ) : ( ) ) : (
); const selectNode = (): void => onNodeSelect?.(node); const onExpandIconClick = (): void => onNodeExpand(node); // Selection const isActive = engineEditorState.isTreeNodeSelected(node); return (
); }, ); const RuntimeExplorer = observer( (props: { runtimeEditorState: RuntimeEditorState; isReadOnly: boolean }) => { const { runtimeEditorState, isReadOnly } = props; const engineEditorState = runtimeEditorState.runtimeValueEditorState; const runtime = runtimeEditorState.runtime; const runtimeValue = engineEditorState.runtimeValue; const runtimeName = runtime instanceof RuntimePointer ? runtime.packageableRuntime.value.name : CUSTOM_LABEL; // explorer tree data const treeData = engineEditorState.explorerTreeData; const onNodeSelect = (node: RuntimeExplorerTreeNodeData): void => engineEditorState.onExplorerTreeNodeSelect(node); const onNodeExpand = (node: RuntimeExplorerTreeNodeData): void => engineEditorState.onExplorerTreeNodeExpand(node); const getTreeChildNodes = ( node: RuntimeExplorerTreeNodeData, ): RuntimeExplorerTreeNodeData[] => engineEditorState.getExplorerTreeChildNodes(node); // DnD const handleDropRuntimeSubElement = useCallback( (item: UMLEditorElementDropTarget): void => { const element = item.data.packageableElement; if (!isReadOnly) { if (element instanceof PackageableConnection) { engineEditorState.addIdentifiedConnection( new IdentifiedConnection( generateIdentifiedConnectionId(runtimeValue), new ConnectionPointer( PackageableElementExplicitReference.create(element), ), ), ); } else if (element instanceof Mapping) { engineEditorState.addMapping(element); } } }, [engineEditorState, isReadOnly, runtimeValue], ); const [{ isRuntimeSubElementDragOver }, dropConnector] = useDrop< ElementDragSource, void, { isRuntimeSubElementDragOver: boolean } >( () => ({ accept: [ CORE_DND_TYPE.PROJECT_EXPLORER_CONNECTION, CORE_DND_TYPE.PROJECT_EXPLORER_MAPPING, ], drop: (item) => handleDropRuntimeSubElement(item), collect: (monitor) => ({ isRuntimeSubElementDragOver: monitor.isOver({ shallow: true }), }), }), [handleDropRuntimeSubElement], ); useEffect(() => { engineEditorState.decorateRuntimeConnections(); engineEditorState.reprocessCurrentTabState(); engineEditorState.reprocessRuntimeExplorerTree(); return (): void => engineEditorState.cleanUpDecoration(); }, [engineEditorState, runtimeEditorState]); return (
{runtimeName}
); }, ); const IdentifiedConnectionEditor = observer( (props: { runtimeEditorState: RuntimeEditorState; currentRuntimeEditorTabState: IdentifiedConnectionsEditorTabState; connectionEditorState: ConnectionEditorState; identifiedConnection: IdentifiedConnection; isReadOnly: boolean; }) => { const { runtimeEditorState, currentRuntimeEditorTabState, connectionEditorState, identifiedConnection, isReadOnly, } = props; const applicationStore = useApplicationStore(); const engineEditorState = runtimeEditorState.runtimeValueEditorState; const runtimeValue = engineEditorState.runtimeValue; // TODO: add runtime connection ID // connection pointer const isEmbeddedConnection = !( identifiedConnection.connection instanceof ConnectionPointer ); const embeddedConnectionLabel = (
{CUSTOM_LABEL}
); // only show custom connnection option when a connnection pointer is currently selected let connectionOptions = isEmbeddedConnection ? [] : ([{ label: embeddedConnectionLabel }] as { label: string | React.ReactNode; value: PackageableConnection; }[]); connectionOptions = connectionOptions.concat( currentRuntimeEditorTabState.packageableConnections.map( (packageableConnection) => ({ label: packageableConnection.path, value: packageableConnection, }), ), ); const selectedConnectionOption = identifiedConnection.connection instanceof ConnectionPointer ? { value: identifiedConnection.connection.packageableConnection.value, label: isEmbeddedConnection ? embeddedConnectionLabel : identifiedConnection.connection.packageableConnection.value .path, } : null; const editorStore = useEditorStore(); const onConnectionSelectionChange = (val: { label: string | React.ReactNode; value?: PackageableConnection; }): void => { if (val.value === undefined) { let customConnection: Connection; try { customConnection = currentRuntimeEditorTabState.createDefaultConnection(); } catch (error) { assertErrorThrown(error); applicationStore.notificationService.notifyWarning(error.message); return; } const newIdentifiedConnection = new IdentifiedConnection( generateIdentifiedConnectionId(runtimeValue), customConnection, ); runtime_addIdentifiedConnection( runtimeValue, newIdentifiedConnection, editorStore.changeDetectionState.observerContext, ); runtime_deleteIdentifiedConnection(runtimeValue, identifiedConnection); currentRuntimeEditorTabState.openIdentifiedConnection( newIdentifiedConnection, ); } else if ( val.value instanceof PackageableConnection && (!(identifiedConnection.connection instanceof ConnectionPointer) || val.value !== identifiedConnection.connection.packageableConnection.value) ) { const connectionPointer = new ConnectionPointer( PackageableElementExplicitReference.create(val.value), ); const newIdentifiedConnection = new IdentifiedConnection( generateIdentifiedConnectionId(runtimeValue), connectionPointer, ); runtime_addIdentifiedConnection( runtimeValue, newIdentifiedConnection, editorStore.changeDetectionState.observerContext, ); runtime_deleteIdentifiedConnection(runtimeValue, identifiedConnection); currentRuntimeEditorTabState.openIdentifiedConnection( newIdentifiedConnection, ); } }; const visitConnection = (): void => { if (identifiedConnection.connection instanceof ConnectionPointer) { editorStore.graphEditorMode.openElement( identifiedConnection.connection.packageableConnection.value, ); } }; return (
{!isEmbeddedConnection && ( )}
); }, ); const IdentifiedConnectionsPerStoreEditor = observer( (props: { runtimeEditorState: RuntimeEditorState; currentRuntimeEditorTabState: IdentifiedConnectionsEditorTabState; isReadOnly: boolean; }) => { const { isReadOnly, currentRuntimeEditorTabState, runtimeEditorState } = props; const isEmbeddedRuntime = runtimeEditorState.isEmbeddedRuntime; const connectionEditorState = currentRuntimeEditorTabState.getConnectionEditorState(); const identifiedConnection = currentRuntimeEditorTabState.identifiedConnectionEditorState ?.idenfitiedConnection; // explorer const addNewConnection = (): void => currentRuntimeEditorTabState.addIdentifiedConnection(); // DnD const handleDropConnection = useCallback( (item: UMLEditorElementDropTarget): void => { const element = item.data.packageableElement; if ( !isReadOnly && element instanceof PackageableConnection && currentRuntimeEditorTabState.packageableConnections.includes(element) ) { currentRuntimeEditorTabState.addIdentifiedConnection(element); } }, [currentRuntimeEditorTabState, isReadOnly], ); const [{ isConnectionDragOver, dragItem }, dropConnector] = useDrop< ElementDragSource, void, { isConnectionDragOver: boolean; dragItem: ElementDragSource | null } >( () => ({ accept: [CORE_DND_TYPE.PROJECT_EXPLORER_CONNECTION], drop: (item) => handleDropConnection(item), collect: ( monitor, ): { isConnectionDragOver: boolean; dragItem: ElementDragSource | null; } => ({ isConnectionDragOver: monitor.isOver({ shallow: true }), dragItem: monitor.getItem(), }), }), [handleDropConnection], ); const canDropConnection = !isReadOnly && dragItem instanceof ElementDragSource && dragItem.data.packageableElement instanceof PackageableConnection && currentRuntimeEditorTabState.packageableConnections.includes( dragItem.data.packageableElement, ); return (
{currentRuntimeEditorTabState instanceof IdentifiedConnectionsPerStoreEditorTabState && (
store
{currentRuntimeEditorTabState.store.name}
)} {currentRuntimeEditorTabState instanceof IdentifiedConnectionsPerClassEditorTabState && (
store
ModelStore
{currentRuntimeEditorTabState.class.name}
)}
} menuProps={{ elevation: 7 }} > {Boolean( currentRuntimeEditorTabState.identifiedConnections.length, ) && (
{currentRuntimeEditorTabState.identifiedConnections.map( (rtConnection) => ( ), )}
)} {!currentRuntimeEditorTabState.identifiedConnections .length && ( )}
{connectionEditorState && identifiedConnection && ( )} {(!connectionEditorState || !identifiedConnection) && ( No connection selected )}
); }, ); const RuntimeMappingEditor = observer( (props: { runtimeEditorState: RuntimeEditorState; mappingRef: PackageableElementReference; isReadOnly: boolean; }) => { const { runtimeEditorState, mappingRef, isReadOnly } = props; const engineEditorState = runtimeEditorState.runtimeValueEditorState; const editorStore = useEditorStore(); const applicationStore = editorStore.applicationStore; const runtimeValue = engineEditorState.runtimeValue; const mappingOptions = editorStore.graphManagerState.graph.ownMappings .filter((m) => !runtimeValue.mappings.map((_m) => _m.value).includes(m)) .map(buildElementOption); const filterOption = createFilter({ ignoreCase: true, ignoreAccents: false, stringify: (option: { data: PackageableElementOption; }): string => option.data.value.path, }); const selectedMappingOption = { value: mappingRef.value, label: mappingRef.value.name, }; const changeMapping = (val: PackageableElementOption): void => engineEditorState.changeMapping(mappingRef, val.value); const deleteMapping = (): void => engineEditorState.deleteMapping(mappingRef); const visitMapping = (): void => editorStore.graphEditorMode.openElement(mappingRef.value); return (
{!isReadOnly && ( )}
); }, ); const RuntimeGeneralEditor = observer( (props: { runtimeEditorState: RuntimeEditorState; runtimeEditorTabState: RuntimeEditorRuntimeTabState; isReadOnly: boolean; }) => { const { runtimeEditorState, isReadOnly } = props; const engineEditorState = runtimeEditorState.runtimeValueEditorState; const editorStore = useEditorStore(); const runtime = runtimeEditorState.runtime; const runtimeValue = engineEditorState.runtimeValue; const isRuntimeEmbedded = !(runtime instanceof RuntimePointer); // mappings const mappings = editorStore.graphManagerState.graph.ownMappings.filter( (mapping) => !runtimeValue.mappings.map((m) => m.value).includes(mapping), ); const allowAddingMapping = !isReadOnly && Boolean(mappings.length); const addMapping = (): void => { if (allowAddingMapping) { engineEditorState.addMapping(mappings[0] as Mapping); } }; const handleDropMapping = useCallback( (item: UMLEditorElementDropTarget) => { const element = item.data.packageableElement; if ( !isReadOnly && // Have to be a mapping element instanceof Mapping && // Must not be an already specified mapping !runtimeValue.mappings.map((m) => m.value).includes(element) ) { engineEditorState.addMapping(element); } }, [engineEditorState, isReadOnly, runtimeValue.mappings], ); const [{ isMappingDragOver }, dropConnector] = useDrop< ElementDragSource, void, { isMappingDragOver: boolean } >( () => ({ accept: [CORE_DND_TYPE.PROJECT_EXPLORER_MAPPING], drop: (item) => handleDropMapping(item), collect: (monitor) => ({ isMappingDragOver: monitor.isOver({ shallow: true }), }), }), [handleDropMapping], ); return (
Mappings
Specifies the list of mappings covered by this runtime
{runtimeValue.mappings.map((mappingRef) => ( ))} {!isRuntimeEmbedded && (
)}
); }, ); const RuntimeEditorPanel = observer( (props: { runtimeEditorState: RuntimeEditorState; isReadOnly: boolean }) => { const { runtimeEditorState, isReadOnly } = props; const engineEditorState = runtimeEditorState.runtimeValueEditorState; const currentRuntimeEditorTabState = engineEditorState.currentTabState; if ( currentRuntimeEditorTabState instanceof IdentifiedConnectionsEditorTabState ) { return ( ); } else if ( currentRuntimeEditorTabState instanceof RuntimeEditorRuntimeTabState ) { return ( ); } return
; // no need for splash screen since will always show at least a tab }, ); export const RuntimeEditor = observer( (props: { runtimeEditorState: RuntimeEditorState; isReadOnly: boolean }) => { const { runtimeEditorState, isReadOnly } = props; return (
); }, ); /** * NOTE: `useAuth()` is typed as always returning `AuthContextProps`, but its * implementation just reads `useContext(AuthContext)` and only warns (does * not throw) when there is no ancestor `AuthProvider`, so it can genuinely * be `undefined` at runtime, e.g. when this editor renders outside one. */ const useLakehouseSummariesEffect = ( editorStore: EditorStore, lakehouseRuntimeEditorState: LakehouseBaseRuntimeEditorState, ): void => { const auth = useAuth() as AuthContextProps | undefined; const applicationStore = editorStore.applicationStore; useEffect(() => { flowResult( lakehouseRuntimeEditorState.fetchLakehouseSummaries( auth?.user?.access_token, ), ).catch(applicationStore.alertUnhandledError); }, [ applicationStore.alertUnhandledError, auth?.user?.access_token, lakehouseRuntimeEditorState, ]); }; const LakehouseBaseRuntimeEditor = (props: { isReadOnly: boolean; children: React.ReactNode; }): React.ReactElement => { const { isReadOnly, children } = props; return (
{isReadOnly && (
)}
lakehouse runtime
{children}
); }; const LakehouseEnvironmentSelector = observer( (props: { lakehouseRuntimeEditorState: LakehouseBaseRuntimeEditorState; darkMode: boolean; }) => { const { lakehouseRuntimeEditorState, darkMode } = props; const lakehouseRuntime = lakehouseRuntimeEditorState.runtimeValue; const environmentOptions = lakehouseRuntimeEditorState.envOptions; const onEnvironmentSelectionChange = ( val: { value: string; label: string; } | null, ): void => { runInAction(() => { if (!val) { lakehouseRuntime.environment = undefined; return; } if (val.value !== lakehouseRuntime.environment) { lakehouseRuntime.environment = val.value; } }); }; const selectedEnvironmentOption = lakehouseRuntime.environment ? { label: lakehouseRuntime.environment, value: lakehouseRuntime.environment, } : null; return (
Environment
); }, ); const LakehouseComputeEngineSelector = observer( (props: { runtimeEditorState: RuntimeEditorState; packageableRuntime: PackageableRuntime; lakehouseRuntimeEditorState: LakehouseBaseRuntimeEditorState; darkMode: boolean; }) => { const { runtimeEditorState, packageableRuntime, lakehouseRuntimeEditorState, darkMode, } = props; const computeEngine = getLakehouseComputeEngine( lakehouseRuntimeEditorState.runtimeValue, ); const computeEngineOptions = Object.values(LakehouseComputeEngine).map( (engine) => ({ label: LAKEHOUSE_COMPUTE_ENGINE_LABEL[engine], value: engine, }), ); const selectedComputeEngineOption = { label: LAKEHOUSE_COMPUTE_ENGINE_LABEL[computeEngine], value: computeEngine, }; const onComputeEngineChange = (val: { label: string; value: LakehouseComputeEngine; }): void => { if (val.value === computeEngine) { return; } const nextRuntimeValue = lakehouseRuntime_setComputeEngine( packageableRuntime, val.value, runtimeEditorState.editorStore.changeDetectionState.observerContext, ); runtimeEditorState.setRuntimeValueEditorState(nextRuntimeValue); }; return (
Compute Engine
); }, ); const SnowflakeRuntimeSourceFields = observer( (props: { runtimeEditorState: RuntimeEditorState; lakehouseRuntimeEditorState: LakehouseRuntimeEditorState; darkMode: boolean; }) => { const { runtimeEditorState, lakehouseRuntimeEditorState, darkMode } = props; const editorStore = runtimeEditorState.editorStore; const lakehouseRuntime = lakehouseRuntimeEditorState.runtimeValue; // type const typeOptions = Object.values(LakehouseRuntimeType).map((type) => ({ label: prettyCONSTName(type), value: type, })); const selectedType = { label: prettyCONSTName(lakehouseRuntimeEditorState.lakehouseRuntimeType), value: lakehouseRuntimeEditorState.lakehouseRuntimeType, }; const onTypeChange = (val: { label: string; value: LakehouseRuntimeType; }): void => { if (val.value !== lakehouseRuntimeEditorState.lakehouseRuntimeType) { lakehouseRuntimeEditorState.setLakehouseRuntimeType(val.value); } }; const handleWarehouseChange = (val: string | undefined): void => { lakehouseRuntime_setWarehouse(lakehouseRuntime, val); }; const connection = lakehouseRuntime.connectionPointer?.packageableConnection.value; const connectionOptions = editorStore.graphManagerState.usableConnections.map(buildElementOption); const selectedConnectionOption = connection ? { label: connection.path, value: connection } : null; const onConnectionSelectionChange = ( val: PackageableElementOption, ): void => { if (val.value !== connection) { lakehouseRuntime_setConnection(lakehouseRuntime, val.value); } }; return ( <>
Lakehouse Runtime Source
{lakehouseRuntimeEditorState.lakehouseRuntimeType === LakehouseRuntimeType.ENVIRONMENT ? ( ) : (
Connection
)} ); }, ); export const LakehouseRuntimeEditor = observer( (props: { runtimeEditorState: RuntimeEditorState; packageableRuntime: PackageableRuntime; lakehouseRuntimeEditorState: LakehouseBaseRuntimeEditorState; isReadOnly: boolean; }) => { const { runtimeEditorState, packageableRuntime, lakehouseRuntimeEditorState, isReadOnly, } = props; const editorStore = runtimeEditorState.editorStore; const applicationStore = editorStore.applicationStore; const darkMode = !applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled; const computeEngine = getLakehouseComputeEngine( lakehouseRuntimeEditorState.runtimeValue, ); // always true exactly when computeEngine === SNOWFLAKE (the two track the // same underlying runtime class); kept as a separate check only so TS can // narrow the type SnowflakeRuntimeSourceFields requires below const isSnowflakeRuntimeEditorState = lakehouseRuntimeEditorState instanceof LakehouseRuntimeEditorState; useLakehouseSummariesEffect(editorStore, lakehouseRuntimeEditorState); return ( {lakehouseRuntimeEditorState.lakehouseRuntimeType === LakehouseRuntimeType.ENVIRONMENT && ( )} {computeEngine === LakehouseComputeEngine.SNOWFLAKE && isSnowflakeRuntimeEditorState && ( )} ); }, ); export const PackageableRuntimeEditor = observer(() => { const editorStore = useEditorStore(); const editorState = editorStore.tabManagerState.getCurrentEditorState( PackageableRuntimeEditorState, ); const isReadOnly = editorState.isReadOnly; const runtimeEditorState = editorState.runtimeEditorState; const engineState = runtimeEditorState.runtimeValueEditorState; useApplicationNavigationContext( LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY.RUNTIME_EDITOR, ); if (engineState instanceof LakehouseBaseRuntimeEditorState) { return ( ); } return ( ); }); export const EmbeddedRuntimeEditor = observer( (props: { runtimeEditorState?: RuntimeEditorState | undefined; isReadOnly: boolean; onClose: () => void; }) => { const { runtimeEditorState, onClose, isReadOnly } = props; const applicationStore = useApplicationStore(); const closeEditor = (): void => onClose(); if (!runtimeEditorState) { return null; } return ( } title="custom runtime" /> ); }, );