Skip to content
This repository has been archived by the owner on Nov 27, 2023. It is now read-only.

feat(viz): add ability to insert branch steps #1140

Merged
merged 1 commit into from
Jan 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 36 additions & 11 deletions src/components/PlusButtonEdge.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import './PlusButtonEdge.css';
import { MiniCatalog } from '@kaoto/components';
import { findStepIdxWithUUID, insertableStepTypes } from '@kaoto/services';
import { useIntegrationJsonStore } from '@kaoto/store';
import { IStepProps } from '@kaoto/types';
import { findStepIdxWithUUID, insertableStepTypes, insertStep } from '@kaoto/services';
import { useIntegrationJsonStore, useNestedStepsStore } from '@kaoto/store';
import { IStepProps, IVizStepNode } from '@kaoto/types';
import { Popover } from '@patternfly/react-core';
import { PlusIcon } from '@patternfly/react-icons';
import { getBezierPath, Node, Position, useNodes, useReactFlow } from 'reactflow';
import { getBezierPath, Position, useReactFlow } from 'reactflow';

const foreignObjectSize = 40;
const insertStep = useIntegrationJsonStore.getState().insertStep;
const insertStepInStore = useIntegrationJsonStore.getState().insertStep;
const replaceStep = useIntegrationJsonStore.getState().replaceStep;

export interface IPlusButtonEdge {
data?: any;
Expand All @@ -35,11 +36,13 @@ const PlusButtonEdge = ({
style = {},
markerEnd,
}: IPlusButtonEdge) => {
const nodes: Node[] = useNodes();
// substring is used to remove the 'e-' from the id (i.e. e-{nodeId}>{nodeId})
const nodeIds = id.substring(2).split('>');
const targetNode = useReactFlow().getNode(nodeIds[1]);
const currentIdx = findStepIdxWithUUID(targetNode?.data.step.UUID!);
const sourceNode: IVizStepNode | undefined = useReactFlow().getNode(nodeIds[0]);
const targetNode: IVizStepNode | undefined = useReactFlow().getNode(nodeIds[1]);
const currentIdx = findStepIdxWithUUID(targetNode?.data.step.UUID);
const { integrationJson } = useIntegrationJsonStore();
const { nestedSteps } = useNestedStepsStore();

const [edgePath, edgeCenterX, edgeCenterY] = getBezierPath({
sourceX,
Expand All @@ -50,8 +53,30 @@ const PlusButtonEdge = ({
targetPosition,
});

const onMiniCatalogClickInsert = (selectedStep: IStepProps) =>
insertStep(selectedStep, currentIdx);
const onMiniCatalogClickInsert = (selectedStep: IStepProps) => {
if (targetNode?.data.branchInfo) {
const rootStepIdx = findStepIdxWithUUID(targetNode?.data.branchInfo.parentUuid);
const currentStepNested = nestedSteps.map((ns) => ns.stepUuid === targetNode?.data.step.UUID);

if (currentStepNested) {
// 1. make a copy of the steps, get the root step
const newStep = integrationJson.steps.slice()[rootStepIdx];
// 2. find the correct branch, insert new step there
newStep.branches?.forEach((b, bIdx) => {
b.steps.map((bs, bsIdx) => {
if (bs.UUID === targetNode?.data.step.UUID) {
// 3. assign the new steps back to the branch
newStep.branches![bIdx].steps = insertStep(b.steps, bsIdx, selectedStep);
}
});
});

replaceStep(newStep, rootStepIdx);
}
} else {
insertStepInStore(selectedStep, currentIdx);
}
};

return (
<>
Expand All @@ -78,7 +103,7 @@ const PlusButtonEdge = ({
<MiniCatalog
handleSelectStep={onMiniCatalogClickInsert}
queryParams={{
type: insertableStepTypes(nodes[currentIdx - 1]?.data, nodes[currentIdx]?.data),
type: insertableStepTypes(sourceNode?.data.step, targetNode?.data.step),
}}
/>
}
Expand Down
2 changes: 1 addition & 1 deletion src/services/validationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export function canStepBeReplaced(
* @param _prevStep
* @param _nextStep
*/
export function insertableStepTypes(_prevStep?: any, _nextStep?: any): string {
export function insertableStepTypes(_prevStep?: IStepProps, _nextStep?: IStepProps): string {
let possibleSteps: string[] = ['START', 'MIDDLE', 'END'];
if (_prevStep) {
// inserted step can be MIDDLE or END
Expand Down
17 changes: 17 additions & 0 deletions src/services/visualizationService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
getRandomArbitraryNumber,
insertAddStepPlaceholder,
insertBranchGroupNode,
insertStep,
isEndStep,
isFirstStepEip,
isFirstStepStart,
Expand Down Expand Up @@ -321,6 +322,22 @@ describe('visualizationService', () => {
expect(nodes).toHaveLength(1);
});

/**
* insertStep
*/
it('insertStep(): should insert the provided step at the index specified, in a given array of steps', () => {
const steps = [
{
name: 'strawberry',
},
{
name: 'blueberry',
},
] as IStepProps[];

expect(insertStep(steps, 2, { name: 'peach' } as IStepProps)).toHaveLength(3);
});

/**
* isFirstStepEip
*/
Expand Down
20 changes: 19 additions & 1 deletion src/services/visualizationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export function buildBranchSpecialEdges(stepNodes: IVizStepNode[]): IVizStepProp
) {
const branchStepNextIdx = findNodeIdxWithUUID(node.data.nextStepUuid, stepNodes);
if (stepNodes[branchStepNextIdx]) {
specialEdges.push(buildEdgeParams(node, stepNodes[branchStepNextIdx], 'default'));
specialEdges.push(buildEdgeParams(node, stepNodes[branchStepNextIdx], 'insert'));
}
}

Expand Down Expand Up @@ -516,6 +516,24 @@ export function insertBranchGroupNode(
});
}

/**
* Insert the given step at the specified index of
* an array of provided steps
* @param steps
* @param insertIndex
* @param newStep
*/
export function insertStep(steps: IStepProps[], insertIndex: number, newStep: IStepProps) {
return [
// part of array before the index
...steps.slice(0, insertIndex),
// inserted item
newStep,
// part of array after the index
...steps.slice(insertIndex),
];
}

export function isEndStep(step: IStepProps): boolean {
return step.type === 'END';
}
Expand Down
19 changes: 4 additions & 15 deletions src/store/integrationJsonStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useIntegrationSourceStore } from './integrationSourceStore';
import { useNestedStepsStore } from './nestedStepsStore';
import { useSettingsStore } from './settingsStore';
import { useVisualizationStore } from './visualizationStore';
import { extractNestedSteps, regenerateUuids } from '@kaoto/services';
import { extractNestedSteps, insertStep, regenerateUuids } from '@kaoto/services';
import { IIntegration, IStepProps, IViewProps } from '@kaoto/types';
import { setDeepValue } from '@kaoto/utils';
import isEqual from 'lodash.isequal';
Expand All @@ -17,7 +17,7 @@ interface IIntegrationJsonStore {
deleteIntegration: () => void;
deleteStep: (index: number) => void;
deleteSteps: () => void;
insertStep: (newStep: IStepProps, index: number) => void;
insertStep: (newStep: IStepProps, insertIndex: number) => void;
integrationJson: IIntegration;
replaceStep: (newStep: IStepProps, oldStepIndex?: number, path?: string[]) => void;
setViews: (views: IViewProps[]) => void;
Expand Down Expand Up @@ -91,20 +91,9 @@ export const useIntegrationJsonStore = create<IIntegrationJsonStore>()(
},
}));
},
insertStep: (newStep, currentStepIdx) => {
insertStep: (newStep, insertIndex) => {
let steps = get().integrationJson.steps.slice();

// unlike appendStep, we need to also regenerate all UUIDs
// because positions are changing
const stepsWithNewUuids = regenerateUuids([
// part of array before the index
...steps.slice(0, currentStepIdx),
// inserted item
newStep,
// part of array after the index
...steps.slice(currentStepIdx),
]);

const stepsWithNewUuids = regenerateUuids(insertStep(steps, insertIndex, newStep));
const updateSteps = useNestedStepsStore.getState().updateSteps;
updateSteps(extractNestedSteps(stepsWithNewUuids));

Expand Down
9 changes: 5 additions & 4 deletions src/utils/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,18 @@ describe('utils', () => {
*/
it('filterNestedSteps(): should filter an array of steps given a conditional function', () => {
const nestedBranchCopy = nestedBranch.slice();
// @ts-ignore
expect(nestedBranchCopy[1].branches[0].steps[0].branches[0].steps).toHaveLength(1);
expect(nestedBranchCopy[1].branches![0].steps[0].branches![0].steps).toHaveLength(1);

const filteredNestedBranch = filterNestedSteps(
nestedBranchCopy,
(step) => step.UUID !== 'log-340230'
);
// @ts-ignore
expect(filteredNestedBranch[1].branches[0].steps[0].branches[0].steps).toHaveLength(0);
expect(filteredNestedBranch![1].branches![0].steps[0].branches![0].steps).toHaveLength(0);
});

/**
* setDeepValue
*/
it('setDeepValue(): given a path, should modify only a deeply nested value within a complex object', () => {
const object = { a: [{ bar: { c: 3 }, baz: { d: 2 } }] };

Expand Down