/**
* 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 = (
);
// 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 (
);
},
);