/** * 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 { clsx, CustomSelectorInput, ErrorIcon, ExclamationTriangleIcon, LongArrowRightIcon, PanelDropZone, PanelFormSection, PencilIcon, PURE_MappingIcon, PURE_RuntimeIcon, XIcon, } from '@finos/legend-art'; import type { DataSpaceExecutionContext } from '@finos/legend-extension-dsl-data-space/graph'; import { observer } from 'mobx-react-lite'; import { dataSpace_setDefaultExecutionContext, dataSpace_setExecutionContextDefaultRuntime, dataSpace_setExecutionContextMapping, } from '../../stores/studio/DSL_DataSpace_GraphModifierHelper.js'; import { CORE_DND_TYPE, type ElementDragSource, useEditorStore, type UMLEditorElementDropTarget, } from '@finos/legend-application-studio'; import { DataSpaceEditorState } from '../../stores/DataSpaceEditorState.js'; import { validate_PureExecutionMapping, Mapping, PackageableElementExplicitReference, PackageableRuntime, } from '@finos/legend-graph'; import { useCallback } from 'react'; import { useDrop } from 'react-dnd'; import { buildElementOption, type PackageableElementOption, } from '@finos/legend-lego/graph-editor'; import { guaranteeNonNullable } from '@finos/legend-shared'; import { RenameModal, NewExecutionContextModal, } from '../DataSpaceExecutionContextEditor.js'; import type { PropsValue } from 'react-select'; export const DataSpaceDefaultExecutionContextSection = observer(() => { const editorStore = useEditorStore(); const applicationStore = editorStore.applicationStore; const dataSpaceState = editorStore.tabManagerState.getCurrentEditorState(DataSpaceEditorState); const dataSpace = dataSpaceState.dataSpace; const executionContext = dataSpace.defaultExecutionContext; const executionContextState = dataSpaceState.executionContextState; // Mapping // TODO: this is not generic error handling, as there could be other problems // with mapping, we need to genericize this const isMappingEmpty = validate_PureExecutionMapping( executionContext.mapping.value, ); const mapping = executionContext.mapping.value; const mappingOptions = editorStore.graphManagerState.usableMappings.map(buildElementOption); const noMappingLabel = (
(none)
); const selectedMappingOption = { value: mapping, label: isMappingEmpty ? noMappingLabel : mapping.path, } as PackageableElementOption; const onMappingSelectionChange = ( val: PackageableElementOption, ): void => { if (val.value !== mapping) { dataSpace_setExecutionContextMapping( executionContext, PackageableElementExplicitReference.create(val.value), ); executionContextState.autoSelectRuntimeOnMappingChange(val.value); } }; const visitMapping = (): void => editorStore.graphEditorMode.openElement(mapping); // Runtime const defaultRuntime = executionContext.defaultRuntime; // NOTE: for now, only include runtime associated with the mapping // TODO?: Should we bring the runtime compatibility check from query to here? const runtimes = editorStore.graphManagerState.graph.runtimes.filter((rt) => rt.runtimeValue.mappings.map((m) => m.value).includes(mapping), ); const runtimeOptions = runtimes.map((rt) => ({ label: rt.path, value: PackageableElementExplicitReference.create(rt), })); const runtimePointerWarning = !runtimes.includes(defaultRuntime.value) // if the runtime does not belong to the chosen mapping ? `runtime is not associated with specified mapping '${mapping.path}'` : undefined; const selectedRuntimeOption = { value: defaultRuntime, label: (
{defaultRuntime.value.path}
{runtimePointerWarning && (
)}
), } as unknown as PropsValue<{ label: string; value: PackageableElementExplicitReference; }>; const onRuntimeSelectionChange = (val: { label: string | React.ReactNode; value: PackageableElementExplicitReference | undefined; }): void => { if (val.value?.value !== defaultRuntime.value && val.value !== undefined) { dataSpace_setExecutionContextDefaultRuntime(executionContext, val.value); } }; const visitRuntime = (): void => { editorStore.graphEditorMode.openElement(defaultRuntime.value); }; // DnD const handleMappingOrRuntimeDrop = useCallback( (item: UMLEditorElementDropTarget): void => { const element = item.data.packageableElement; if (!dataSpaceState.isReadOnly) { if (element instanceof Mapping) { dataSpace_setExecutionContextMapping( executionContext, PackageableElementExplicitReference.create(element), ); executionContextState.autoSelectRuntimeOnMappingChange(element); } else if ( element instanceof PackageableRuntime && element.runtimeValue.mappings.map((m) => m.value).includes(mapping) ) { dataSpace_setExecutionContextDefaultRuntime( executionContext, PackageableElementExplicitReference.create(element), ); } } }, [ dataSpaceState.isReadOnly, mapping, executionContextState, executionContext, ], ); const [{ isMappingOrRuntimeDragOver }, dropConnector] = useDrop< ElementDragSource, void, { isMappingOrRuntimeDragOver: boolean } >( () => ({ accept: [ CORE_DND_TYPE.PROJECT_EXPLORER_MAPPING, CORE_DND_TYPE.PROJECT_EXPLORER_RUNTIME, ], drop: (item) => handleMappingOrRuntimeDrop(item), collect: (monitor) => ({ isMappingOrRuntimeDragOver: monitor.isOver({ shallow: true }), }), }), [handleMappingOrRuntimeDrop], ); //multiple execution contexts const addExecutionKey = (): void => { executionContextState.setNewExecutionContextModal(true); }; type ExecutionContextOption = { label: string; value: DataSpaceExecutionContext; }; const buildExecutionContextOption = ( context: DataSpaceExecutionContext, ): ExecutionContextOption => ({ label: context.name, value: context, }); const executionContextOptions = dataSpace.executionContexts.map( buildExecutionContextOption, ); const onExecutionContextChange = (option: ExecutionContextOption) => { if (option.value !== executionContext) { dataSpace_setDefaultExecutionContext(dataSpace, option.value); executionContextState.setSelectedExecutionContext(option.value); } }; const activeExecutionContext = buildExecutionContextOption(executionContext); const deleteExecutionContext = (): void => { executionContextState.removeExecutionContext(executionContext); if ( dataSpace.executionContexts.length > 0 && dataSpace.executionContexts[0] ) { dataSpace_setDefaultExecutionContext( dataSpace, dataSpace.executionContexts[0], ); } }; return (
Execution Context
Define the mapping and runtime for this Data Product.
{dataSpace.executionContexts.length > 1 && (
)} {executionContextState.newExecutionContextModal && ( )} {executionContextState.executionContextToRename && ( executionContextState.setExecutionContextToRename(undefined) } setValue={(val: string): void => executionContextState.renameExecutionContext( guaranteeNonNullable( executionContextState.executionContextToRename, ), val, ) } executionContext={executionContextState.executionContextToRename} /> )}
); });