@finos/legend-application-studio
Version:
Legend Studio application core
174 lines • 14.4 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 { observer } from 'mobx-react-lite';
import { PurePropertyMappingEditor } from './PurePropertyMappingEditor.js';
import { getElementIcon } from '../../../ElementIconUtils.js';
import { MappingEditorState, } from '../../../../stores/editor/editor-state/element-editor-state/mapping/MappingEditorState.js';
import { PurePropertyMappingState, PureInstanceSetImplementationState, } from '../../../../stores/editor/editor-state/element-editor-state/mapping/PureInstanceSetImplementationState.js';
import { clsx, ArrowCircleRightIcon, TimesCircleIcon, TimesIcon, AsteriskIcon, } from '@finos/legend-art';
import { guaranteeType } from '@finos/legend-shared';
import { FlatDataInstanceSetImplementationState, } from '../../../../stores/editor/editor-state/element-editor-state/mapping/FlatDataInstanceSetImplementationState.js';
import { FlatDataPropertyMappingEditor } from './FlatDataPropertyMappingEditor.js';
import { RelationalPropertyMappingEditor } from './relational/RelationalPropertyMappingEditor.js';
import { useEditorStore } from '../../EditorStoreProvider.js';
import { getRootSetImplementation, Class, SetImplementation, PrimitiveType, PureInstanceSetImplementation, EmbeddedFlatDataPropertyMapping, OperationSetImplementation, FlatDataInstanceSetImplementation, RootRelationalInstanceSetImplementation, MULTIPLICITY_INFINITE, } from '@finos/legend-graph';
import { useApplicationStore } from '@finos/legend-application';
import { instanceSetImplementation_deletePropertyMapping, setImpl_nominateRoot, setImplementation_setRoot, } from '../../../../stores/graph-modifier/DSL_Mapping_GraphModifierHelper.js';
import { CLASS_PROPERTY_TYPE, getClassPropertyType, } from '../../../../stores/editor/utils/ModelClassifierUtils.js';
import { Fragment } from 'react';
const MultiplicityBadge = (props) => {
const { multiplicity } = props;
const isRequired = multiplicity.lowerBound && multiplicity.lowerBound > 0;
const tooltipText = `${isRequired ? '[required]' : '[optional]'}${isRequired ? ` minimum: ${multiplicity.lowerBound}` : ''} maximum: ${multiplicity.upperBound ?? MULTIPLICITY_INFINITE}`;
return (_jsx("div", { className: `multiplicity-badge ${isRequired ? 'multiplicity-badge--required' : ''}`, title: tooltipText, children: multiplicity.upperBound ? null : _jsx(AsteriskIcon, {}) }));
};
export const getExpectedReturnType = (targetSetImplementation) => {
if (targetSetImplementation instanceof PureInstanceSetImplementation) {
return targetSetImplementation.srcClass?.value;
}
else if (targetSetImplementation instanceof OperationSetImplementation) {
return targetSetImplementation.class.value;
}
else {
return undefined;
}
};
const GenericPropertyMappingEditorEntry = observer((props) => {
const { children, instanceSetImplementationState, propertyMappingStates, propertyMappingState, propertyBasicType, } = props;
const removePropertyMapping = (pm) => {
instanceSetImplementation_deletePropertyMapping(instanceSetImplementationState.mappingElement, pm);
instanceSetImplementationState.decorate();
};
return (_jsxs("div", { className: "property-mapping-editor__generic-entry", children: [children, propertyMappingStates.length > 1 &&
propertyBasicType !== CLASS_PROPERTY_TYPE.CLASS && (_jsx("button", { className: "property-mapping-editor__generic-entry__remove-btn", onClick: () => removePropertyMapping(propertyMappingState.propertyMapping), tabIndex: -1, title: "Remove", children: _jsx(TimesIcon, {}) }))] }));
});
export const PropertyMappingEditor = observer((props) => {
const { instanceSetImplementationState, property, isReadOnly } = props;
const instanceSetImplementation = instanceSetImplementationState.mappingElement;
const validationErrorMessage = instanceSetImplementation.propertyMappings.filter((pm) => pm.property.value === property).length > 1 && !(property.genericType.value.rawType instanceof Class)
? 'Only one property mapping should exist per property of type other than class (e.g. primitive, measure, enumeration)'
: undefined;
const editorStore = useEditorStore();
const applicationStore = useApplicationStore();
const mappingEditorState = editorStore.tabManagerState.getCurrentEditorState(MappingEditorState);
const propertyRawType = property.genericType.value.rawType;
const propertyBasicType = getClassPropertyType(propertyRawType);
const isEmbedded = instanceSetImplementation._isEmbedded;
// Parser Error
const propertyMappingStates = instanceSetImplementationState.propertyMappingStates.filter((pm) => pm.propertyMapping.property.value === property);
const propertyHasParserError = Boolean(propertyMappingStates.find((pm) => pm.parserError));
const setImplementationHasParserError = Boolean(instanceSetImplementationState.propertyMappingStates.find((pm) => pm.parserError));
/**
* TODO: revisit this behavior now that we have more types of property mapping to support
* e.g. embedded, target set implementation, etc.
* See https://github.com/finos/legend-studio/issues/310
*
* @modularize
* TODO: modularize this for other types
* See https://github.com/finos/legend-studio/issues/65
*/
const visitOrCreateMappingElement = () => {
if (propertyRawType instanceof Class) {
if (instanceSetImplementation instanceof PureInstanceSetImplementation) {
const rootMappingElement = getRootSetImplementation(mappingEditorState.mapping, propertyRawType);
if (rootMappingElement) {
if (!rootMappingElement.root.value) {
setImplementation_setRoot(rootMappingElement, true);
}
const parent = rootMappingElement._PARENT;
if (parent !== mappingEditorState.element) {
// TODO: think more about this flow. Right now we open the mapping element in the parent mapping
editorStore.graphEditorMode.openElement(parent);
editorStore.tabManagerState
.getCurrentEditorState(MappingEditorState)
.openMappingElement(rootMappingElement, false);
}
{
mappingEditorState.openMappingElement(rootMappingElement, true);
}
}
else {
if (!isReadOnly) {
mappingEditorState.createMappingElement({
target: property.genericType.value.rawType,
showTarget: false,
openInAdjacentTab: true,
postSubmitAction: (newSetImpl) => {
// Make this set implementation the new root
if (newSetImpl instanceof SetImplementation) {
setImpl_nominateRoot(newSetImpl);
}
},
});
}
}
}
else if (instanceSetImplementationState instanceof
FlatDataInstanceSetImplementationState) {
if (propertyMappingStates.length === 1 &&
propertyMappingStates[0]?.propertyMapping instanceof
EmbeddedFlatDataPropertyMapping) {
mappingEditorState.openMappingElement(propertyMappingStates[0].propertyMapping, true);
}
else if (!propertyMappingStates.length) {
const embedded = instanceSetImplementationState.addEmbeddedPropertyMapping(property);
mappingEditorState.openMappingElement(embedded, true);
}
}
else {
applicationStore.notificationService.notifyWarning(`Can't visit mapping element for type '${propertyRawType.name}'`);
}
}
};
const renderPropertyMappingEntry = (propertyMappingState) => {
if (instanceSetImplementation instanceof PureInstanceSetImplementation) {
return (_jsx(PurePropertyMappingEditor, { isReadOnly: isReadOnly, pureInstanceSetImplementationState: guaranteeType(instanceSetImplementationState, PureInstanceSetImplementationState), purePropertyMappingState: guaranteeType(propertyMappingState, PurePropertyMappingState), setImplementationHasParserError: setImplementationHasParserError }));
}
else if (instanceSetImplementation instanceof
FlatDataInstanceSetImplementation ||
instanceSetImplementation instanceof EmbeddedFlatDataPropertyMapping) {
return (_jsx(FlatDataPropertyMappingEditor, { isReadOnly: isReadOnly, flatDataInstanceSetImplementationState: instanceSetImplementationState, flatDataPropertyMappingState: propertyMappingState, setImplementationHasParserError: setImplementationHasParserError }));
}
else if (instanceSetImplementation instanceof
RootRelationalInstanceSetImplementation) {
return (_jsx(RelationalPropertyMappingEditor, { isReadOnly: isReadOnly, relationalInstanceSetImplementationState: instanceSetImplementationState, relationalPropertyMappingState: propertyMappingState, setImplementationHasParserError: setImplementationHasParserError }));
}
else {
const extraPropertyMappingEditorRenderers = editorStore.pluginManager
.getApplicationPlugins()
.flatMap((plugin) => plugin.getExtraPropertyMappingEditorRenderers?.() ?? []);
for (const renderer of extraPropertyMappingEditorRenderers) {
const renderedPropertyMappingEditor = renderer(instanceSetImplementationState, propertyMappingState);
if (renderedPropertyMappingEditor) {
return (_jsx(Fragment, { children: renderedPropertyMappingEditor }, propertyMappingState.uuid));
}
}
return (_jsx("div", { className: "property-mapping-editor__entry--unsupported", children: "Unsupported property mapping" }, propertyMappingState.uuid));
}
};
return (_jsxs("div", { className: clsx('property-mapping-editor', {
backdrop__element: propertyHasParserError,
}), children: [_jsxs("div", { className: "property-mapping-editor__metadata", children: [_jsxs("div", { className: clsx('property-mapping-editor__name', {
'property-mapping-editor__name--with-validation': Boolean(validationErrorMessage),
}), children: [_jsx("div", { className: "property-mapping-editor__name__label", children: property.name }), validationErrorMessage && (_jsx("div", { className: "property-mapping-editor__name--with-validation__indicator", title: validationErrorMessage, children: _jsx(TimesCircleIcon, {}) }))] }), _jsxs("div", { className: "property-mapping-editor__info", children: [_jsxs("div", { className: clsx('property-mapping-editor__type', `background--${propertyBasicType.toLowerCase()}`, {
'property-mapping-editor__type--has-visit-btn': propertyBasicType === CLASS_PROPERTY_TYPE.CLASS,
}), title: propertyRawType instanceof PrimitiveType
? undefined
: propertyRawType.path, children: [propertyBasicType !== CLASS_PROPERTY_TYPE.PRIMITIVE && (_jsx("div", { className: "property-mapping-editor__type__abbr", children: getElementIcon(propertyRawType, editorStore) })), _jsx("div", { className: "property-mapping-editor__type__label", children: propertyRawType.name }), propertyBasicType === CLASS_PROPERTY_TYPE.CLASS && (_jsx("button", { className: "property-mapping-editor__type__visit-btn", onClick: visitOrCreateMappingElement, tabIndex: -1, title: "Visit mapping element", children: _jsx(ArrowCircleRightIcon, {}) }))] }), _jsx("div", { className: "property-mapping-editor__multiplicity", children: _jsx(MultiplicityBadge, { multiplicity: property.multiplicity }) })] })] }), _jsxs("div", { className: "property-mapping-editor__content", children: [propertyMappingStates.map((propertyMappingState) => (_jsx(GenericPropertyMappingEditorEntry, { instanceSetImplementationState: instanceSetImplementationState, propertyBasicType: propertyBasicType, propertyMappingStates: propertyMappingStates, propertyMappingState: propertyMappingState, children: renderPropertyMappingEntry(propertyMappingState) }, propertyMappingState.uuid))), propertyBasicType === CLASS_PROPERTY_TYPE.CLASS &&
!propertyMappingStates.length && (_jsxs(_Fragment, { children: [isEmbedded && (_jsxs("div", { className: "property-mapping-editor__entry--empty", children: ["Click", _jsx("button", { className: "property-mapping-editor__entry--empty__visit-btn", onClick: visitOrCreateMappingElement, tabIndex: -1, title: "Create mapping element", children: _jsx(ArrowCircleRightIcon, {}) }), `to create an embedded class mapping for property '${property.name}'.`] })), !isEmbedded && (_jsxs("div", { className: "property-mapping-editor__entry--empty", children: ["No set implementation found. Click", _jsx("button", { className: "property-mapping-editor__entry--empty__visit-btn", onClick: visitOrCreateMappingElement, tabIndex: -1, title: "Create mapping element", children: _jsx(ArrowCircleRightIcon, {}) }), `to create a root class mapping for '${propertyRawType.name}'.`] }))] }))] })] }));
});
//# sourceMappingURL=PropertyMappingsEditor.js.map