Skip to content

Commit 3fb2b71

Browse files
authored
Feat(Data Mapper): Refactored connections (#6022)
* starting to convert connection types * build passes * improved types for connections and tests * fixed bugs * able to delete from fn menu * fixing deserialization tests * fixing tests * more tests pass * tests pass * fixed build * fixed flatteninput in test * removed unused test * refactor functionutils * fixed custom value issues * fixed output
1 parent bde7fbc commit 3fb2b71

26 files changed

+1339
-1809
lines changed

apps/Standalone/src/dataMapperV1/components/DevToolbox.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,18 @@ interface SchemaFileData {
5151
}
5252
const sourceSchemaFileOptions: SchemaFileData[] = [
5353
{ filename: 'PlaygroundSourceSchema.json', schemaFormat: SchemaFileFormat.XML },
54-
{ filename: 'ProjectRequest.json', schemaFormat: SchemaFileFormat.XML },
5554
{ filename: 'SourceSchema.json', schemaFormat: SchemaFileFormat.XML },
5655
{ filename: 'ComprehensiveSourceSchema.json', schemaFormat: SchemaFileFormat.XML },
5756
{ filename: 'SourceSchemaJson.json', schemaFormat: SchemaFileFormat.JSON },
57+
{ filename: 'ProjectRequest.json', schemaFormat: SchemaFileFormat.XML },
5858
];
5959
const targetSchemaFileOptions: SchemaFileData[] = [
6060
{ filename: 'PlaygroundTargetSchema.json', schemaFormat: SchemaFileFormat.XML },
6161
{ filename: 'OebsProjectRequest.json', schemaFormat: SchemaFileFormat.XML },
6262
{ filename: 'TargetSchema.json', schemaFormat: SchemaFileFormat.XML },
6363
{ filename: 'ComprehensiveTargetSchema.json', schemaFormat: SchemaFileFormat.XML },
6464
{ filename: 'TargetSchemaJson.json', schemaFormat: SchemaFileFormat.JSON },
65+
{ filename: 'OebsProjectRequest.json', schemaFormat: SchemaFileFormat.XML },
6566
];
6667
const mapSchemaFileOptionsToDropdownOptions = (schemaFileData: SchemaFileData[]) =>
6768
schemaFileData.map<IDropdownOption>((schemaOpt) => ({

libs/data-mapper-v2/src/components/common/reactflow/FunctionNode.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import type { StringIndexed } from '@microsoft/logic-apps-shared';
1212
import { setHoverState, setSelectedItem } from '../../../core/state/DataMapSlice';
1313
import { useHoverFunctionNode, useSelectedNode } from '../../../core/state/selectors/selectors';
1414
import { useCallback, useMemo } from 'react';
15-
import { isFunctionInputSlotAvailable } from '../../../utils/Connection.Utils';
15+
import { isEmptyConnection, isFunctionInputSlotAvailable } from '../../../utils/Connection.Utils';
1616
import { customTokens } from '../../../core/ThemeConect';
1717

1818
export interface FunctionCardProps extends CardProps {
@@ -47,9 +47,9 @@ export const FunctionNode = (props: NodeProps<Node<StringIndexed<FunctionCardPro
4747
);
4848

4949
const isLeftConnected =
50-
functionWithConnections?.inputs[0] &&
51-
functionWithConnections?.inputs[0].length > 0 &&
52-
functionWithConnections?.inputs[0][0] !== undefined;
50+
functionWithConnections?.inputs &&
51+
functionWithConnections?.inputs.length > 0 &&
52+
functionWithConnections?.inputs[0] !== undefined && !isEmptyConnection(functionWithConnections?.inputs[0]);
5353
const isRightConnected = functionWithConnections?.outputs.length > 0;
5454

5555
const getHandleStyle = useCallback(

libs/data-mapper-v2/src/components/functionConfigurationMenu/inputDropdown/InputDropdown.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,10 @@ export const InputDropdown = ({
178178
if (inputValue) {
179179
setSelectedOptions([inputValue]);
180180
}
181+
if (inputValue === undefined) {
182+
setSelectedOptions([]);
183+
setCustomValue(undefined);
184+
}
181185
}, [inputValue]);
182186

183187
const originalOptions = useMemo(() => {

libs/data-mapper-v2/src/components/functionConfigurationMenu/inputTab/InputList.tsx

+4-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { useDispatch, useSelector } from 'react-redux';
66
import type { RootState } from '../../../core/state/Store';
77
import { InputDropdown, type InputOptionProps } from '../inputDropdown/InputDropdown';
88
import { getInputTypeFromNode, validateAndCreateConnectionInput } from './inputTab';
9-
import { setConnectionInput } from '../../../core/state/DataMapSlice';
9+
import { deleteConnectionFromFunctionMenu, setConnectionInput } from '../../../core/state/DataMapSlice';
1010
import { getInputName, getInputValue } from '../../../utils/Function.Utils';
1111
import { useStyles } from './styles';
1212
import { ListItem } from '@fluentui/react-list-preview';
@@ -60,14 +60,12 @@ const InputList = (props: InputListProps) => {
6060
const removeUnboundedInput = useCallback(() => {
6161
const targetNodeReactFlowKey = functionKey;
6262
dispatch(
63-
setConnectionInput({
64-
targetNode: data,
65-
targetNodeReactFlowKey,
63+
deleteConnectionFromFunctionMenu({
64+
targetId: targetNodeReactFlowKey,
6665
inputIndex: index,
67-
input: null,
6866
})
6967
);
70-
}, [data, dispatch, functionKey, index]);
68+
}, [dispatch, functionKey, index]);
7169

7270
const updateInput = useCallback(
7371
(newValue: InputConnection) => {

libs/data-mapper-v2/src/components/functionConfigurationMenu/inputTab/inputTab.tsx

+22-16
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,27 @@ import { Badge, Button, Caption1, Caption2 } from '@fluentui/react-components';
22
import { LinkDismissRegular, AddRegular } from '@fluentui/react-icons';
33
import { useDispatch, useSelector } from 'react-redux';
44
import { UnboundedInput } from '../../../constants/FunctionConstants';
5-
import { createInputSlotForUnboundedInput, setConnectionInput, updateFunctionConnectionInputs } from '../../../core/state/DataMapSlice';
5+
import {
6+
createInputSlotForUnboundedInput,
7+
deleteConnectionFromFunctionMenu,
8+
setConnectionInput,
9+
updateFunctionConnectionInputs,
10+
} from '../../../core/state/DataMapSlice';
611
import type { RootState } from '../../../core/state/Store';
712
import type { FunctionData, FunctionDictionary } from '../../../models';
8-
import type { ConnectionDictionary, ConnectionUnit, InputConnection } from '../../../models/Connection';
13+
import type { ConnectionDictionary, NodeConnection, CustomValueConnection, InputConnection } from '../../../models/Connection';
914
import { getInputName, getInputValue } from '../../../utils/Function.Utils';
1015
import type { InputOptionProps } from '../inputDropdown/InputDropdown';
1116
import { InputDropdown } from '../inputDropdown/InputDropdown';
1217
import { useStyles } from './styles';
1318
import { mergeStyles } from '@fluentui/react';
1419
import { isSchemaNodeExtended } from '../../../utils';
15-
import { newConnectionWillHaveCircularLogic } from '../../../utils/Connection.Utils';
20+
import {
21+
connectionDoesExist,
22+
createCustomInputConnection,
23+
isNodeConnection,
24+
newConnectionWillHaveCircularLogic,
25+
} from '../../../utils/Connection.Utils';
1626
import { SchemaType, type SchemaNodeDictionary } from '@microsoft/logic-apps-shared';
1727
import DraggableList from 'react-draggable-list';
1828
import InputListWrapper, { type TemplateItemProps, type CommonProps } from './InputList';
@@ -36,11 +46,7 @@ export const InputTabContents = (props: {
3646

3747
if (props.func.maxNumberOfInputs !== UnboundedInput) {
3848
const tableContents = props.func.inputs.map((input, index) => {
39-
const inputConnection = functionConnection
40-
? Object.values(functionConnection.inputs).length > 1
41-
? functionConnection.inputs[index][0]
42-
: functionConnection.inputs[0][index]
43-
: undefined;
49+
const inputConnection = functionConnection && functionConnection.inputs[index] ? functionConnection.inputs[index] : undefined;
4450

4551
const inputType = getInputTypeFromNode(inputConnection);
4652

@@ -74,11 +80,9 @@ export const InputTabContents = (props: {
7480
const removeConnection = (inputIndex: number) => {
7581
const targetNodeReactFlowKey = props.functionKey;
7682
dispatch(
77-
setConnectionInput({
78-
targetNode: props.func,
79-
targetNodeReactFlowKey,
83+
deleteConnectionFromFunctionMenu({
8084
inputIndex,
81-
input: undefined,
85+
targetId: targetNodeReactFlowKey,
8286
})
8387
);
8488
};
@@ -187,7 +191,7 @@ const UnlimitedInputs = (props: {
187191
</span>
188192
</div>
189193
<DraggableList<TemplateItemProps, CommonProps, any>
190-
list={Object.entries(functionConnection.inputs[0]).map((input, index) => ({ input: input[1], index }))}
194+
list={Object.entries(functionConnection.inputs).map((input, index) => ({ input: input[1], index }))}
191195
commonProps={{
192196
functionKey: props.functionKey,
193197
data: props.func,
@@ -214,7 +218,7 @@ const UnlimitedInputs = (props: {
214218

215219
export const getInputTypeFromNode = (input: InputConnection | undefined) => {
216220
let inputType = '';
217-
if (typeof input !== 'string' && input !== undefined) {
221+
if (connectionDoesExist(input) && isNodeConnection(input)) {
218222
if (isSchemaNodeExtended(input.node)) {
219223
inputType = input?.node.type;
220224
} else {
@@ -244,15 +248,17 @@ export const validateAndCreateConnectionInput = (
244248

245249
// Create connection
246250
const source = isSelectedInputFunction ? functionNodeDictionary[selectedInputKey] : sourceSchemaDictionary[selectedInputKey];
247-
const srcConUnit: ConnectionUnit = {
251+
const srcConUnit: NodeConnection = {
248252
node: source,
249253
reactFlowKey: selectedInputKey,
254+
isCustom: false,
255+
isDefined: true,
250256
};
251257

252258
return srcConUnit;
253259
}
254260
// Create custom value connection
255-
const srcConUnit: InputConnection = optionValue;
261+
const srcConUnit: CustomValueConnection = createCustomInputConnection(optionValue);
256262

257263
return srcConUnit;
258264
}

libs/data-mapper-v2/src/components/functionConfigurationMenu/outputTab/outputTab.tsx

+31-17
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,19 @@ import { AddRegular } from '@fluentui/react-icons';
33
import { useDispatch, useSelector } from 'react-redux';
44
import type { RootState } from '../../../core/state/Store';
55
import type { FunctionData, FunctionDictionary } from '../../../models';
6-
import type { ConnectionDictionary, ConnectionUnit, InputConnection } from '../../../models/Connection';
6+
import type { ConnectionDictionary, NodeConnection, InputConnection, EmptyConnection } from '../../../models/Connection';
77
import type { InputOptionProps } from '../inputDropdown/InputDropdown';
88
import { useStyles } from '../styles';
99
import { List } from '@fluentui/react-list-preview';
1010
import type { SchemaNodeDictionary } from '@microsoft/logic-apps-shared';
1111
import { SchemaType } from '@microsoft/logic-apps-shared';
12-
import { flattenInputs, newConnectionWillHaveCircularLogic } from '../../../utils/Connection.Utils';
12+
import {
13+
createNewEmptyConnection,
14+
isNodeConnection,
15+
isEmptyConnection,
16+
newConnectionWillHaveCircularLogic,
17+
createCustomInputConnection,
18+
} from '../../../utils/Connection.Utils';
1319
import { makeConnectionFromMap, setConnectionInput } from '../../../core/state/DataMapSlice';
1420
import { useState } from 'react';
1521
import { isSchemaNodeExtended } from '../../../utils';
@@ -24,36 +30,38 @@ export const OutputTabContents = (props: {
2430
const functionNodeDictionary = useSelector((state: RootState) => state.dataMap.present.curDataMapOperation.functionNodes);
2531
const connections = useSelector((state: RootState) => state.dataMap.present.curDataMapOperation.dataMapConnections);
2632
const styles = useStyles();
27-
const outputs: (ConnectionUnit | undefined)[] = [...connections[props.functionId].outputs];
33+
const outputs: (NodeConnection | EmptyConnection | undefined)[] = [...connections[props.functionId].outputs];
2834
const dispatch = useDispatch();
29-
const [additionalOutput, setAdditionalOutput] = useState<(ConnectionUnit | undefined)[]>([]);
35+
const [additionalOutput, setAdditionalOutput] = useState<(NodeConnection | EmptyConnection | undefined)[]>([]);
3036

3137
if (outputs.length === 0) {
3238
outputs[0] = undefined;
3339
}
3440

3541
const addOutputClick = () => {
36-
setAdditionalOutput([...additionalOutput, undefined]);
42+
setAdditionalOutput([...additionalOutput, createNewEmptyConnection()]);
3743
};
3844

3945
const getIDForTargetConnection = (connection: InputConnection) => {
40-
if (connection === undefined) {
46+
if (connection === undefined || !isNodeConnection(connection)) {
4147
return '';
4248
}
43-
if (typeof connection === 'string') {
44-
return connection;
45-
}
4649
return connection.reactFlowKey;
4750
};
4851

49-
const removeConnection = (newOutput: InputConnection) => {
52+
const removeConnection = (newOutput?: InputConnection) => {
5053
if (newOutput === undefined) {
5154
return;
5255
}
56+
if (!isNodeConnection(newOutput)) {
57+
const shortenedOutput = additionalOutput.slice(0, additionalOutput.length - 2);
58+
setAdditionalOutput(shortenedOutput);
59+
return;
60+
}
5361
const dest = getIDForTargetConnection(newOutput);
5462
const destinationNode = connectionDictionary[dest];
55-
const flattened = flattenInputs(destinationNode.inputs);
56-
const index = flattened.findIndex((input) => getIDForTargetConnection(input) === props.functionId);
63+
const inputs = destinationNode.inputs;
64+
const index = inputs.findIndex((input) => getIDForTargetConnection(input) === props.functionId);
5765
dispatch(
5866
setConnectionInput({
5967
targetNode: destinationNode.self.node,
@@ -80,9 +88,11 @@ export const OutputTabContents = (props: {
8088
const validateAndCreateConnection = (
8189
optionValue: string | undefined,
8290
option: InputOptionProps | undefined,
83-
oldOutput: InputConnection
91+
oldOutput: InputConnection | undefined
8492
) => {
85-
removeConnection(oldOutput);
93+
if (oldOutput !== undefined) {
94+
removeConnection(oldOutput);
95+
}
8696
if (optionValue) {
8797
const newOutput = validateAndCreateConnectionOutput(
8898
optionValue,
@@ -104,7 +114,9 @@ export const OutputTabContents = (props: {
104114
<List>
105115
{outputs.concat(additionalOutput).map((output, index) => {
106116
let outputValue = undefined;
107-
if (output) {
117+
if (output && isEmptyConnection(output)) {
118+
outputValue = '';
119+
} else if (output) {
108120
outputValue = isSchemaNodeExtended(output?.node) ? output?.node.name : '';
109121
}
110122
const listItem = (
@@ -163,15 +175,17 @@ const validateAndCreateConnectionOutput = (
163175

164176
// Create connection
165177
const output = isSelectedOutputFunction ? functionNodeDictionary[selectedOutputKey] : sourceSchemaDictionary[selectedOutputKey];
166-
const srcConUnit: ConnectionUnit = {
178+
const srcConUnit: NodeConnection = {
167179
node: output,
168180
reactFlowKey: selectedOutputKey,
181+
isCustom: false,
182+
isDefined: true,
169183
};
170184

171185
return srcConUnit;
172186
}
173187
// Create custom value connection
174-
const srcConUnit: InputConnection = optionValue;
188+
const srcConUnit: InputConnection = createCustomInputConnection(optionValue);
175189

176190
return srcConUnit;
177191
}

0 commit comments

Comments
 (0)