@finos/legend-application-studio 28.19.8 → 28.19.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/lib/components/editor/editor-group/connection-editor/DatabaseEditorHelper.d.ts +1 -0
  2. package/lib/components/editor/editor-group/connection-editor/DatabaseEditorHelper.d.ts.map +1 -1
  3. package/lib/components/editor/editor-group/connection-editor/DatabaseEditorHelper.js +29 -1
  4. package/lib/components/editor/editor-group/connection-editor/DatabaseEditorHelper.js.map +1 -1
  5. package/lib/components/editor/editor-group/function-activator/SnowflakeM2MUdfFunctionActivatorEditor.js +1 -1
  6. package/lib/components/editor/editor-group/function-activator/SnowflakeM2MUdfFunctionActivatorEditor.js.map +1 -1
  7. package/lib/components/editor/editor-group/mapping-editor/ClassMappingEditor.d.ts +2 -1
  8. package/lib/components/editor/editor-group/mapping-editor/ClassMappingEditor.d.ts.map +1 -1
  9. package/lib/components/editor/editor-group/mapping-editor/ClassMappingEditor.js +6 -1
  10. package/lib/components/editor/editor-group/mapping-editor/ClassMappingEditor.js.map +1 -1
  11. package/lib/components/editor/editor-group/mapping-editor/InstanceSetImplementationEditor.d.ts.map +1 -1
  12. package/lib/components/editor/editor-group/mapping-editor/InstanceSetImplementationEditor.js +7 -2
  13. package/lib/components/editor/editor-group/mapping-editor/InstanceSetImplementationEditor.js.map +1 -1
  14. package/lib/components/editor/editor-group/mapping-editor/InstanceSetImplementationSourceSelectorModal.d.ts.map +1 -1
  15. package/lib/components/editor/editor-group/mapping-editor/InstanceSetImplementationSourceSelectorModal.js +14 -1
  16. package/lib/components/editor/editor-group/mapping-editor/InstanceSetImplementationSourceSelectorModal.js.map +1 -1
  17. package/lib/components/editor/editor-group/mapping-editor/PropertyMappingsEditor.d.ts.map +1 -1
  18. package/lib/components/editor/editor-group/mapping-editor/PropertyMappingsEditor.js +6 -1
  19. package/lib/components/editor/editor-group/mapping-editor/PropertyMappingsEditor.js.map +1 -1
  20. package/lib/components/editor/editor-group/mapping-editor/RelationFunctionPropertyMappingEditor.d.ts +25 -0
  21. package/lib/components/editor/editor-group/mapping-editor/RelationFunctionPropertyMappingEditor.d.ts.map +1 -0
  22. package/lib/components/editor/editor-group/mapping-editor/RelationFunctionPropertyMappingEditor.js +64 -0
  23. package/lib/components/editor/editor-group/mapping-editor/RelationFunctionPropertyMappingEditor.js.map +1 -0
  24. package/lib/components/editor/editor-group/mapping-editor/RelationTypeTree.d.ts +38 -0
  25. package/lib/components/editor/editor-group/mapping-editor/RelationTypeTree.d.ts.map +1 -0
  26. package/lib/components/editor/editor-group/mapping-editor/RelationTypeTree.js +140 -0
  27. package/lib/components/editor/editor-group/mapping-editor/RelationTypeTree.js.map +1 -0
  28. package/lib/index.css +1 -1
  29. package/lib/package.json +1 -1
  30. package/lib/stores/editor/editor-state/element-editor-state/mapping/MappingEditorState.d.ts.map +1 -1
  31. package/lib/stores/editor/editor-state/element-editor-state/mapping/MappingEditorState.js +19 -7
  32. package/lib/stores/editor/editor-state/element-editor-state/mapping/MappingEditorState.js.map +1 -1
  33. package/lib/stores/editor/editor-state/element-editor-state/mapping/MappingElementDecorator.d.ts.map +1 -1
  34. package/lib/stores/editor/editor-state/element-editor-state/mapping/MappingElementDecorator.js +37 -3
  35. package/lib/stores/editor/editor-state/element-editor-state/mapping/MappingElementDecorator.js.map +1 -1
  36. package/lib/stores/editor/editor-state/element-editor-state/mapping/RelationFunctionInstanceSetImplementationState.d.ts +47 -0
  37. package/lib/stores/editor/editor-state/element-editor-state/mapping/RelationFunctionInstanceSetImplementationState.d.ts.map +1 -0
  38. package/lib/stores/editor/editor-state/element-editor-state/mapping/RelationFunctionInstanceSetImplementationState.js +112 -0
  39. package/lib/stores/editor/editor-state/element-editor-state/mapping/RelationFunctionInstanceSetImplementationState.js.map +1 -0
  40. package/lib/stores/editor/editor-state/element-editor-state/mapping/testable/MappingTestingHelper.d.ts.map +1 -1
  41. package/lib/stores/editor/editor-state/element-editor-state/mapping/testable/MappingTestingHelper.js +1 -0
  42. package/lib/stores/editor/editor-state/element-editor-state/mapping/testable/MappingTestingHelper.js.map +1 -1
  43. package/lib/stores/graph-modifier/STO_RelationFunction_GraphModifierHelper.d.ts +19 -0
  44. package/lib/stores/graph-modifier/STO_RelationFunction_GraphModifierHelper.d.ts.map +1 -0
  45. package/lib/stores/graph-modifier/STO_RelationFunction_GraphModifierHelper.js +22 -0
  46. package/lib/stores/graph-modifier/STO_RelationFunction_GraphModifierHelper.js.map +1 -0
  47. package/package.json +14 -14
  48. package/src/components/editor/editor-group/connection-editor/DatabaseEditorHelper.tsx +43 -0
  49. package/src/components/editor/editor-group/function-activator/SnowflakeM2MUdfFunctionActivatorEditor.tsx +1 -1
  50. package/src/components/editor/editor-group/mapping-editor/ClassMappingEditor.tsx +7 -0
  51. package/src/components/editor/editor-group/mapping-editor/InstanceSetImplementationEditor.tsx +21 -0
  52. package/src/components/editor/editor-group/mapping-editor/InstanceSetImplementationSourceSelectorModal.tsx +17 -0
  53. package/src/components/editor/editor-group/mapping-editor/PropertyMappingsEditor.tsx +22 -0
  54. package/src/components/editor/editor-group/mapping-editor/RelationFunctionPropertyMappingEditor.tsx +127 -0
  55. package/src/components/editor/editor-group/mapping-editor/RelationTypeTree.tsx +257 -0
  56. package/src/stores/editor/editor-state/element-editor-state/mapping/MappingEditorState.ts +37 -10
  57. package/src/stores/editor/editor-state/element-editor-state/mapping/MappingElementDecorator.ts +70 -2
  58. package/src/stores/editor/editor-state/element-editor-state/mapping/RelationFunctionInstanceSetImplementationState.ts +172 -0
  59. package/src/stores/editor/editor-state/element-editor-state/mapping/testable/MappingTestingHelper.ts +1 -0
  60. package/src/stores/graph-modifier/STO_RelationFunction_GraphModifierHelper.ts +39 -0
  61. package/tsconfig.json +4 -0
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Copyright (c) 2025-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { useCallback, useRef } from 'react';
18
+ import { observer } from 'mobx-react-lite';
19
+ import { InlineLambdaEditor } from '@finos/legend-query-builder';
20
+ import type {
21
+ RelationFunctionInstanceSetImplementationState,
22
+ RelationFunctionPropertyMappingState,
23
+ } from '../../../../stores/editor/editor-state/element-editor-state/mapping/RelationFunctionInstanceSetImplementationState.js';
24
+ import { type ConnectDropTarget, useDrop } from 'react-dnd';
25
+ import {
26
+ getClassPropertyType,
27
+ CLASS_PROPERTY_TYPE,
28
+ } from '../../../../stores/editor/utils/ModelClassifierUtils.js';
29
+ import { RelationTypeDragSource } from './RelationTypeTree.js';
30
+ import { CORE_DND_TYPE } from '../../../../stores/editor/utils/DnDUtils.js';
31
+ import { guaranteeNonNullable } from '@finos/legend-shared';
32
+
33
+ const SimplePropertyMappingEditor = observer(
34
+ (props: {
35
+ propertyMappingState: RelationFunctionPropertyMappingState;
36
+ dropConnector?: ConnectDropTarget;
37
+ transformProps: {
38
+ disableTransform: boolean;
39
+ forceBackdrop: boolean;
40
+ };
41
+ isReadOnly: boolean;
42
+ }) => {
43
+ const { propertyMappingState, dropConnector, transformProps } = props;
44
+ const ref = useRef<HTMLDivElement>(null);
45
+ dropConnector?.(ref);
46
+ return (
47
+ <div className="property-mapping-editor__entry__container">
48
+ <div ref={ref} className="property-mapping-editor__entry">
49
+ <InlineLambdaEditor
50
+ disabled={transformProps.disableTransform}
51
+ lambdaEditorState={propertyMappingState}
52
+ forceBackdrop={transformProps.forceBackdrop}
53
+ />
54
+ </div>
55
+ </div>
56
+ );
57
+ },
58
+ );
59
+
60
+ export const RelationFunctionPropertyMappingEditor = observer(
61
+ (props: {
62
+ relationPropertyMappingState: RelationFunctionPropertyMappingState;
63
+ relationInstanceSetImplementationState: RelationFunctionInstanceSetImplementationState;
64
+ setImplementationHasParserError: boolean;
65
+ isReadOnly: boolean;
66
+ }) => {
67
+ const {
68
+ relationPropertyMappingState: relationPropertyMappingState,
69
+ relationInstanceSetImplementationState:
70
+ relationInstanceSetImplementationState,
71
+ setImplementationHasParserError,
72
+ isReadOnly,
73
+ } = props;
74
+ const disableEditingTransform =
75
+ relationInstanceSetImplementationState.isConvertingTransformLambdaObjects ||
76
+ isReadOnly;
77
+ // Drag and Drop
78
+ const handleDrop = useCallback(
79
+ (droppedItem: RelationTypeDragSource): void => {
80
+ if (!disableEditingTransform) {
81
+ if (droppedItem instanceof RelationTypeDragSource) {
82
+ const toAppend = guaranteeNonNullable(droppedItem.data).id;
83
+ if (toAppend) {
84
+ relationPropertyMappingState.setLambdaString(
85
+ relationPropertyMappingState.lambdaString + toAppend,
86
+ );
87
+ }
88
+ }
89
+ }
90
+ },
91
+ [disableEditingTransform, relationPropertyMappingState],
92
+ );
93
+ const [, dropConnector] = useDrop<RelationTypeDragSource>(
94
+ () => ({
95
+ accept: [CORE_DND_TYPE.PROJECT_EXPLORER_FUNCTION],
96
+ drop: (item) => handleDrop(item),
97
+ }),
98
+ [handleDrop],
99
+ );
100
+ const transformProps = {
101
+ disableTransform:
102
+ relationInstanceSetImplementationState.isConvertingTransformLambdaObjects,
103
+ forceBackdrop: setImplementationHasParserError,
104
+ };
105
+
106
+ switch (
107
+ getClassPropertyType(
108
+ relationPropertyMappingState.propertyMapping.property.value.genericType
109
+ .value.rawType,
110
+ )
111
+ ) {
112
+ case CLASS_PROPERTY_TYPE.UNIT:
113
+ case CLASS_PROPERTY_TYPE.MEASURE:
114
+ case CLASS_PROPERTY_TYPE.PRIMITIVE:
115
+ return (
116
+ <SimplePropertyMappingEditor
117
+ propertyMappingState={relationPropertyMappingState}
118
+ dropConnector={dropConnector}
119
+ transformProps={transformProps}
120
+ isReadOnly={isReadOnly}
121
+ />
122
+ );
123
+ default:
124
+ return null;
125
+ }
126
+ },
127
+ );
@@ -0,0 +1,257 @@
1
+ /**
2
+ * Copyright (c) 2025-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { useState, useEffect, useRef } from 'react';
18
+ import { useDrag } from 'react-dnd';
19
+ import {
20
+ type TreeNodeContainerProps,
21
+ type TreeData,
22
+ type TreeNodeData,
23
+ TreeView,
24
+ ChevronDownIcon,
25
+ ChevronRightIcon,
26
+ } from '@finos/legend-art';
27
+ import {
28
+ addUniqueEntry,
29
+ assertErrorThrown,
30
+ isNonNullable,
31
+ } from '@finos/legend-shared';
32
+ import {
33
+ type Type,
34
+ RawLambda,
35
+ type ConcreteFunctionDefinition,
36
+ } from '@finos/legend-graph';
37
+ import { renderColumnTypeIconFromType } from '../connection-editor/DatabaseEditorHelper.js';
38
+ import { CORE_DND_TYPE } from '../../../../stores/editor/utils/DnDUtils.js';
39
+ import type { EditorStore } from '../../../../stores/editor/EditorStore.js';
40
+
41
+ export class RelationColumnTypeTreeNodeData implements TreeNodeData {
42
+ id: string;
43
+ label: string;
44
+ isSelected?: boolean;
45
+ isOpen?: boolean;
46
+ childrenIds?: string[];
47
+ columnName: string;
48
+ columnType: string;
49
+
50
+ constructor(
51
+ id: string,
52
+ label: string,
53
+ columnName: string,
54
+ columnType: string,
55
+ ) {
56
+ this.id = id;
57
+ this.label = label;
58
+ this.columnName = columnName;
59
+ this.columnType = columnType;
60
+ }
61
+ }
62
+
63
+ const getColumnTreeNodeData = (
64
+ columnName: string,
65
+ columnType: string,
66
+ ): RelationColumnTypeTreeNodeData => {
67
+ const columnNode = new RelationColumnTypeTreeNodeData(
68
+ columnName,
69
+ columnName,
70
+ columnName,
71
+ columnType,
72
+ );
73
+ return columnNode;
74
+ };
75
+
76
+ const getRelationTypeTreeData = async (
77
+ relation: ConcreteFunctionDefinition,
78
+ editorStore: EditorStore,
79
+ ) => {
80
+ const rootIds: string[] = [];
81
+ const nodes = new Map<string, RelationColumnTypeTreeNodeData>();
82
+ // columns
83
+ // throw an error if more than one in concrete function definition
84
+ const lambda = new RawLambda(
85
+ relation.parameters.map((parameter) =>
86
+ editorStore.graphManagerState.graphManager.serializeRawValueSpecification(
87
+ parameter,
88
+ ),
89
+ ),
90
+ relation.expressionSequence,
91
+ );
92
+ try {
93
+ const relationType =
94
+ await editorStore.graphManagerState.graphManager.getLambdaRelationType(
95
+ lambda,
96
+ editorStore.graphManagerState.graph,
97
+ );
98
+ relationType.columns.forEach((column) => {
99
+ const columnNode = getColumnTreeNodeData(column.name, column.type);
100
+ addUniqueEntry(rootIds, columnNode.id);
101
+ nodes.set(columnNode.id, columnNode);
102
+ });
103
+ } catch (error) {
104
+ assertErrorThrown(error);
105
+ editorStore.applicationStore.alertUnhandledError(error);
106
+ }
107
+
108
+ return { rootIds, nodes };
109
+ };
110
+
111
+ export class RelationTypeDragSource {
112
+ data: RelationColumnTypeTreeNodeData | undefined;
113
+
114
+ constructor(data: RelationColumnTypeTreeNodeData) {
115
+ this.data = data;
116
+ }
117
+ }
118
+
119
+ const RelationTypeTreeNodeContainer: React.FC<
120
+ TreeNodeContainerProps<
121
+ RelationColumnTypeTreeNodeData,
122
+ { selectedType?: Type | undefined }
123
+ >
124
+ > = (props) => {
125
+ const { node, level, stepPaddingInRem, onNodeSelect } = props;
126
+ const [, dragConnector] = useDrag(
127
+ () => ({
128
+ type: CORE_DND_TYPE.PROJECT_EXPLORER_FUNCTION,
129
+ item: new RelationTypeDragSource(node),
130
+ }),
131
+ [node],
132
+ );
133
+ const ref = useRef<HTMLDivElement>(null);
134
+ dragConnector(ref);
135
+
136
+ const isExpandable = Boolean(node.childrenIds?.length);
137
+ const selectNode = (): void => onNodeSelect?.(node);
138
+ const nodeTypeIcon = renderColumnTypeIconFromType(node.columnType);
139
+ const nodeExpandIcon = isExpandable ? (
140
+ node.isOpen ? (
141
+ <ChevronDownIcon />
142
+ ) : (
143
+ <ChevronRightIcon />
144
+ )
145
+ ) : (
146
+ <div />
147
+ );
148
+
149
+ return (
150
+ <div
151
+ className="tree-view__node__container"
152
+ onClick={selectNode}
153
+ ref={ref}
154
+ style={{
155
+ paddingLeft: `${(level - 1) * (stepPaddingInRem ?? 1)}rem`,
156
+ display: 'flex',
157
+ }}
158
+ >
159
+ <div className="tree-view__node__icon">
160
+ <div className="tree-view__node__expand-icon">{nodeExpandIcon}</div>
161
+ <div className="type-tree__type-icon">{nodeTypeIcon}</div>
162
+ </div>
163
+ <div className="tree-view__node__label type-tree__node__label">
164
+ <button tabIndex={-1} title={`${node.id}`}>
165
+ {node.label}
166
+ </button>
167
+ {node instanceof RelationColumnTypeTreeNodeData && (
168
+ <div className="type-tree__node__type">
169
+ <button
170
+ className="type-tree__node__type__label"
171
+ // TODO: match type
172
+ // className={clsx('type-tree__node__type__label', {
173
+ // 'type-tree__node__type__label--highlighted':
174
+ // primitiveType && primitiveType === selectedType,
175
+ // })}
176
+ tabIndex={-1}
177
+ title="Column Type"
178
+ >
179
+ {node.columnType.split('::').pop()}
180
+ </button>
181
+ </div>
182
+ )}
183
+ </div>
184
+ </div>
185
+ );
186
+ };
187
+
188
+ export const RelationTypeTree: React.FC<{
189
+ relation: ConcreteFunctionDefinition;
190
+ editorStore: EditorStore;
191
+ selectedType?: Type | undefined;
192
+ }> = (props) => {
193
+ const { relation, selectedType, editorStore } = props;
194
+ // NOTE: We only need to compute this once so we use lazy initial state syntax
195
+ // See https://reactjs.org/docs/hooks-reference.html#lazy-initial-
196
+ const [treeData, setTreeData] = useState(
197
+ {} as TreeData<RelationColumnTypeTreeNodeData>,
198
+ );
199
+ const [loading, setLoading] = useState(false);
200
+
201
+ const onNodeSelect = (node: RelationColumnTypeTreeNodeData): void => {
202
+ //TODO: check for child nodes in this case
203
+ setTreeData({ ...treeData });
204
+ };
205
+
206
+ const getChildNodes = (
207
+ node: RelationColumnTypeTreeNodeData,
208
+ ): RelationColumnTypeTreeNodeData[] => {
209
+ if (!node.childrenIds) {
210
+ return [];
211
+ }
212
+ const childrenNodes = node.childrenIds
213
+ .map((id) => treeData.nodes.get(id))
214
+ .filter(isNonNullable)
215
+ // sort so that column nodes come before join nodes
216
+ .sort((a, b) => a.label.localeCompare(b.label))
217
+ .sort(
218
+ (a, b) =>
219
+ (b instanceof RelationColumnTypeTreeNodeData ? 1 : 0) -
220
+ (a instanceof RelationColumnTypeTreeNodeData ? 1 : 0),
221
+ );
222
+ return childrenNodes;
223
+ };
224
+
225
+ useEffect(() => {
226
+ const fetchTypeTreeData = async () => {
227
+ try {
228
+ const response = await getRelationTypeTreeData(relation, editorStore);
229
+ setTreeData(response);
230
+ setLoading(true);
231
+ } catch (error) {
232
+ assertErrorThrown(error);
233
+ setLoading(false);
234
+ }
235
+ };
236
+ fetchTypeTreeData()
237
+ .then()
238
+ .catch((error) => assertErrorThrown(error));
239
+ }, [relation, editorStore]);
240
+
241
+ if (loading) {
242
+ return (
243
+ <TreeView
244
+ components={{
245
+ TreeNodeContainer: RelationTypeTreeNodeContainer,
246
+ }}
247
+ treeData={treeData}
248
+ getChildNodes={getChildNodes}
249
+ onNodeSelect={onNodeSelect}
250
+ innerProps={{
251
+ selectedType,
252
+ }}
253
+ />
254
+ );
255
+ }
256
+ return <div></div>;
257
+ };
@@ -103,6 +103,7 @@ import {
103
103
  ModelStore,
104
104
  INTERNAL__UnknownSetImplementation,
105
105
  RelationFunctionInstanceSetImplementation,
106
+ ConcreteFunctionDefinition,
106
107
  } from '@finos/legend-graph';
107
108
  import type {
108
109
  DSL_Mapping_LegendStudioApplicationPlugin_Extension,
@@ -127,6 +128,8 @@ import { LambdaEditorState } from '@finos/legend-query-builder';
127
128
  import type { MappingEditorTabState } from './MappingTabManagerState.js';
128
129
  import { MappingTestableState } from './testable/MappingTestableState.js';
129
130
  import { MappingTestMigrationState } from './legacy/MappingTestMigrationState.js';
131
+ import { relationFunction_setRelationFunction } from '../../../../graph-modifier/STO_RelationFunction_GraphModifierHelper.js';
132
+ import { RelationFunctionInstanceSetImplementationState } from './RelationFunctionInstanceSetImplementationState.js';
130
133
 
131
134
  export interface MappingExplorerTreeNodeData extends TreeNodeData {
132
135
  mappingElement: MappingElement;
@@ -312,12 +315,10 @@ export const getMappingElementSource = (
312
315
  mappingElement.mainSetImplementation,
313
316
  plugins,
314
317
  );
315
- }
316
- // TODO: We could probably return the relation function used for the mapping here once we implement the form mode support for it
317
- else if (
318
+ } else if (
318
319
  mappingElement instanceof RelationFunctionInstanceSetImplementation
319
320
  ) {
320
- return undefined;
321
+ return mappingElement.relationFunction;
321
322
  }
322
323
  const extraMappingElementSourceExtractors = plugins.flatMap(
323
324
  (plugin) =>
@@ -907,12 +908,7 @@ export class MappingEditorState extends ElementEditorState {
907
908
  );
908
909
  return;
909
910
  }
910
- if (mappingElement instanceof RelationFunctionInstanceSetImplementation) {
911
- this.editorStore.applicationStore.notificationService.notifyUnsupportedFeature(
912
- 'Relation Function mapping editor',
913
- );
914
- return;
915
- }
911
+
916
912
  // Open mapping element from included mapping in another mapping editor tab
917
913
  if (mappingElement._PARENT !== this.element) {
918
914
  this.editorStore.graphEditorMode.openElement(mappingElement._PARENT);
@@ -1005,6 +1001,20 @@ export class MappingEditorState extends ElementEditorState {
1005
1001
  rootRelationalSetImp_setMainTableAlias(setImplementation, newSource);
1006
1002
  sourceUpdated = true;
1007
1003
  }
1004
+ } else if (
1005
+ setImplementation instanceof RelationFunctionInstanceSetImplementation
1006
+ ) {
1007
+ if (
1008
+ newSource instanceof ConcreteFunctionDefinition &&
1009
+ !getEmbeddedSetImplementations(setImplementation).length
1010
+ ) {
1011
+ relationFunction_setRelationFunction(
1012
+ setImplementation,
1013
+ newSource,
1014
+ this.editorStore.changeDetectionState.observerContext,
1015
+ );
1016
+ sourceUpdated = true;
1017
+ }
1008
1018
  } else {
1009
1019
  const extraInstanceSetImplementationSourceUpdaters =
1010
1020
  this.editorStore.pluginManager
@@ -1056,6 +1066,16 @@ export class MappingEditorState extends ElementEditorState {
1056
1066
  );
1057
1067
  newRootRelationalInstanceSetImplementation.mainTableAlias = newSource;
1058
1068
  newSetImp = newRootRelationalInstanceSetImplementation;
1069
+ } else if (newSource instanceof ConcreteFunctionDefinition) {
1070
+ const relationFunctionSetImpl =
1071
+ new RelationFunctionInstanceSetImplementation(
1072
+ setImplementation.id,
1073
+ this.mapping,
1074
+ setImplementation.class,
1075
+ setImplementation.root,
1076
+ );
1077
+ relationFunctionSetImpl.relationFunction = newSource;
1078
+ newSetImp = relationFunctionSetImpl;
1059
1079
  } else {
1060
1080
  throw new UnsupportedOperationError(
1061
1081
  `Can't use the specified class mapping source`,
@@ -1221,6 +1241,13 @@ export class MappingEditorState extends ElementEditorState {
1221
1241
  this.editorStore,
1222
1242
  mappingElement,
1223
1243
  );
1244
+ } else if (
1245
+ mappingElement instanceof RelationFunctionInstanceSetImplementation
1246
+ ) {
1247
+ return new RelationFunctionInstanceSetImplementationState(
1248
+ this.editorStore,
1249
+ mappingElement,
1250
+ );
1224
1251
  }
1225
1252
  const extraMappingElementStateCreators = this.editorStore.pluginManager
1226
1253
  .getApplicationPlugins()
@@ -37,6 +37,7 @@ import {
37
37
  type InstanceSetImplementation,
38
38
  type INTERNAL__UnresolvedSetImplementation,
39
39
  type Mapping,
40
+ type RelationColumn,
40
41
  getAllClassMappings,
41
42
  PurePropertyMapping,
42
43
  EmbeddedFlatDataPropertyMapping,
@@ -66,6 +67,7 @@ import {
66
67
  SetImplementationExplicitReference,
67
68
  type INTERNAL__UnknownSetImplementation,
68
69
  type RelationFunctionInstanceSetImplementation,
70
+ RelationFunctionPropertyMapping,
69
71
  } from '@finos/legend-graph';
70
72
  import type { EditorStore } from '../../../EditorStore.js';
71
73
  import {
@@ -79,6 +81,7 @@ import {
79
81
  } from '../../../../graph-modifier/DSL_Mapping_GraphModifierHelper.js';
80
82
  import { rootRelationalSetImp_setPropertyMappings } from '../../../../graph-modifier/STO_Relational_GraphModifierHelper.js';
81
83
  import type { DSL_Mapping_LegendStudioApplicationPlugin_Extension } from '../../../../extensions/DSL_Mapping_LegendStudioApplicationPlugin_Extension.js';
84
+ import { isStubbed_RelationColumn } from '../../../../graph-modifier/STO_RelationFunction_GraphModifierHelper.js';
82
85
 
83
86
  /**
84
87
  * Iterate through all properties (including supertypes' properties) of the set implementation
@@ -662,7 +665,65 @@ export class MappingElementDecorator implements SetImplementationVisitor<void> {
662
665
  visit_RelationFunctionInstanceSetImplementation(
663
666
  setImplementation: RelationFunctionInstanceSetImplementation,
664
667
  ): void {
665
- return;
668
+ const decoratePropertyMapping = (
669
+ propertyMappings: RelationFunctionPropertyMapping[] | undefined,
670
+ property: Property,
671
+ ): RelationFunctionPropertyMapping[] => {
672
+ const existingPropertyMappings = (propertyMappings ?? []).filter((pm) => {
673
+ if (pm instanceof RelationFunctionPropertyMapping) {
674
+ return !isStubbed_RelationColumn(pm.column);
675
+ }
676
+ return false;
677
+ });
678
+ const propertyType = property.genericType.value.rawType;
679
+ if (
680
+ propertyType instanceof PrimitiveType ||
681
+ propertyType instanceof Unit ||
682
+ propertyType instanceof Measure
683
+ ) {
684
+ if (existingPropertyMappings.length) {
685
+ // TODO?: do we want to check the type of the property mapping here?
686
+ return existingPropertyMappings;
687
+ }
688
+ const newPropertyMapping = new RelationFunctionPropertyMapping(
689
+ setImplementation,
690
+ PropertyExplicitReference.create(property),
691
+ SetImplementationExplicitReference.create(setImplementation),
692
+ undefined,
693
+ {} as RelationColumn,
694
+ );
695
+ return [newPropertyMapping];
696
+ } else if (propertyType instanceof Enumeration) {
697
+ // TODO: add changes
698
+ } else if (propertyType instanceof Class) {
699
+ // TODO: add change
700
+ }
701
+ return [];
702
+ };
703
+ const propertyMappingsBeforeDecoration =
704
+ setImplementation.propertyMappings as RelationFunctionPropertyMapping[];
705
+ const decoratedPropertyMappings =
706
+ getDecoratedSetImplementationPropertyMappings<RelationFunctionPropertyMapping>(
707
+ setImplementation,
708
+ decoratePropertyMapping,
709
+ );
710
+ instanceSetImplementation_setPropertyMappings(
711
+ setImplementation,
712
+ decoratedPropertyMappings.concat(
713
+ // NOTE: here, we remove some low-quality property mappings
714
+ // i.e. stubbed property mappings from before adding new decorated property mappings
715
+ propertyMappingsBeforeDecoration
716
+ .filter(
717
+ (propertyMapping) =>
718
+ !isStubbed_RelationColumn(propertyMapping.column),
719
+ )
720
+ .filter(
721
+ (propertyMapping) =>
722
+ !decoratedPropertyMappings.includes(propertyMapping),
723
+ ),
724
+ ),
725
+ this.editorStore.changeDetectionState.observerContext,
726
+ );
666
727
  }
667
728
  }
668
729
 
@@ -814,6 +875,13 @@ export class MappingElementDecorationCleaner
814
875
  visit_RelationFunctionInstanceSetImplementation(
815
876
  setImplementation: RelationFunctionInstanceSetImplementation,
816
877
  ): void {
817
- return;
878
+ instanceSetImplementation_setPropertyMappings(
879
+ setImplementation,
880
+ setImplementation.propertyMappings.filter(
881
+ (propertyMapping) =>
882
+ propertyMapping instanceof RelationFunctionPropertyMapping,
883
+ ),
884
+ this.editorStore.changeDetectionState.observerContext,
885
+ );
818
886
  }
819
887
  }