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

feat(multiple-flows): Enable experimental support for multiple flows #1749

Merged
merged 3 commits into from
May 8, 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
19 changes: 18 additions & 1 deletion src/components/SettingsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ import {
Modal,
ModalVariant,
Popover,
Switch,
TextArea,
TextInput,
} from '@patternfly/react-core';
import { HelpIcon } from '@patternfly/react-icons';
import { useAlert } from '@rhoas/app-services-ui-shared';
import { useEffect, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';

export interface ISettingsModal {
handleCloseModal: () => void;
Expand Down Expand Up @@ -176,6 +177,10 @@ export const SettingsModal = ({ handleCloseModal, isModalOpen }: ISettingsModal)
handleCloseModal();
};

const onToggleUseMultipleFlowsSupport = useCallback((useMultipleFlows: boolean) => {
setLocalSettings({ ...localSettings, useMultipleFlows });
}, [localSettings]);

return (
<Modal
actions={[
Expand Down Expand Up @@ -326,6 +331,18 @@ export const SettingsModal = ({ handleCloseModal, isModalOpen }: ISettingsModal)
})}
</FormSelect>
</FormGroup>

{/* TODO: Temporary toggle to enable experimental support for Multiple Flows */}
<FormGroup
fieldId="useMultipleFlows"
label="Enable experimental support for multiple flows"
>
<Switch
aria-label="enable multiple flows support"
isChecked={localSettings.useMultipleFlows}
onChange={onToggleUseMultipleFlowsSupport}
/>
</FormGroup>
</Form>
</Modal>
);
Expand Down
6 changes: 4 additions & 2 deletions src/components/Visualization.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
VisualizationStepViews,
} from '@kaoto/components';
import { StepsService, VisualizationService } from '@kaoto/services';
import { useIntegrationJsonStore, useVisualizationStore } from '@kaoto/store';
import { useIntegrationJsonStore, useSettingsStore, useVisualizationStore } from '@kaoto/store';
import { IStepProps, IVizStepNode } from '@kaoto/types';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import ReactFlow, { Background, Viewport } from 'reactflow';
Expand All @@ -33,6 +33,8 @@ const Visualization = () => {
UUID: '',
integrationId: '',
});

const useMultipleFlows = useSettingsStore((state) => state.settings.useMultipleFlows);
const visualizationStore = useVisualizationStore.getState();
const layout = useVisualizationStore((state) => state.layout);
const nodes = useVisualizationStore((state) => state.nodes);
Expand Down Expand Up @@ -70,7 +72,7 @@ const Visualization = () => {

useEffect(() => {
visualizationService.redrawDiagram(handleDeleteStep, true).catch((e) => console.error(e));
}, [integrationJson, layout]);
}, [integrationJson, layout, useMultipleFlows]);

const nodeTypes = useMemo(() => ({ step: VisualizationStep }), []);
const edgeTypes = useMemo(
Expand Down
40 changes: 37 additions & 3 deletions src/services/stepsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
IIntegrationJsonStore,
INestedStepStore,
RFState,
useFlowsStore,
useIntegrationJsonStore,
useNestedStepsStore,
useVisualizationStore,
Expand Down Expand Up @@ -301,6 +302,8 @@ export class StepsService {
async handleAppendStep(currentStep: IStepProps, selectedStep: IStepProps) {
// fetch parameters and other details
fetchStepDetails(selectedStep.id).then((newStep) => {
const integrationId = currentStep.integrationId;

newStep.UUID = selectedStep.UUID;
const currentStepNested = this.getStepNested(currentStep.UUID);
if (currentStepNested) {
Expand All @@ -312,8 +315,13 @@ export class StepsService {
useIntegrationJsonStore
.getState()
.replaceBranchParentStep(newParentStep, currentStepNested.pathToParentStep);

useFlowsStore
.getState()
.insertStep(integrationId, newParentStep, { mode: 'replace', path: currentStepNested.pathToParentStep });
} else {
useIntegrationJsonStore.getState().appendStep(newStep);
useFlowsStore.getState().insertStep(integrationId, newStep, { mode: 'append' });
}
});
}
Expand All @@ -332,6 +340,8 @@ export class StepsService {
) {
// fetch parameters and other details
return fetchStepDetails(proposedStep.id).then((newStep) => {
const integrationId = node.step.integrationId;

const validation = ValidationService.canStepBeReplaced(node, newStep);
// Replace step
if (validation.isValid) {
Expand All @@ -341,10 +351,18 @@ export class StepsService {
useIntegrationJsonStore
.getState()
.replaceBranchParentStep(newStep, currentStepNested.pathToStep);

useFlowsStore
.getState()
.insertStep(integrationId, newStep, { mode: 'replace', path: currentStepNested.pathToStep });
}
} else {
const currentIdx = this.findStepIdxWithUUID(currentStep.UUID);
let currentIdx = this.findStepIdxWithUUID(currentStep.UUID);
useIntegrationJsonStore.getState().replaceStep(newStep, currentIdx);

const integration = this.getIntegration(integrationId);
currentIdx = this.findStepIdxWithUUID(currentStep.UUID, integration?.steps);
useFlowsStore.getState().insertStep(integrationId, newStep, { mode: 'replace', index: currentIdx });
}
}
return validation;
Expand All @@ -353,12 +371,14 @@ export class StepsService {

/**
* Handler for inserting a step, where `targetNode` is the
* right-hand node. Ex: source -> {insert step here} -> target
* right-hand node. Ex: source -> {insert step here} -> targetNode
* @param targetNode
* @param step
*/
async handleInsertStep(targetNode: IVizStepNode | undefined, step: IStepProps) {
return fetchStepDetails(step.id).then((newStep) => {
const integrationId = targetNode?.data.step.integrationId;

if (targetNode?.data.branchInfo) {
const currentStepNested = this.getStepNested(targetNode.data.step.UUID);

Expand All @@ -376,14 +396,26 @@ export class StepsService {
useIntegrationJsonStore
.getState()
.replaceBranchParentStep(newParentStep, currentStepNested.pathToParentStep);

useFlowsStore
.getState()
.insertStep(integrationId, newParentStep, { mode: 'replace', path: currentStepNested.pathToParentStep });
}
} else {
const currentIdx = this.findStepIdxWithUUID(targetNode?.data.step.UUID);
let currentIdx = this.findStepIdxWithUUID(targetNode?.data.step.UUID);
useIntegrationJsonStore.getState().insertStep(newStep, currentIdx);

const integration = this.getIntegration(integrationId);
currentIdx = this.findStepIdxWithUUID(targetNode?.data.step.UUID, integration?.steps);
useFlowsStore.getState().insertStep(integrationId, newStep, { mode: 'insert', index: currentIdx });
}
});
}

getIntegration(integrationId: string): IIntegration | undefined {
return useFlowsStore.getState().flows.find((integration) => integration.id === integrationId);
}

/**
* Prepends a selected step to the current step.
* @param currentStep
Expand Down Expand Up @@ -503,9 +535,11 @@ export class StepsService {
);
const newPath = pathToBranch?.concat('steps', '0');
useIntegrationJsonStore.getState().replaceBranchParentStep(step, newPath);
useFlowsStore.getState().insertStep(node.step.integrationId, step, { mode: 'replace', path: newPath });
}
} else {
useIntegrationJsonStore.getState().replaceStep(step);
useFlowsStore.getState().insertStep(node.step.integrationId, step, { mode: 'append' });
}
}
return validation;
Expand Down
8 changes: 4 additions & 4 deletions src/services/visualizationService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ describe('visualizationService', () => {
expect(VisualizationService.buildEdges(nodes)).toHaveLength(1);

// let's test that it works for branching too
const stepNodes = VisualizationService.buildNodesFromSteps(branchSteps, 'RIGHT');
const stepNodes = VisualizationService.buildNodesFromSteps('Camel Route-1', branchSteps, 'RIGHT');

expect(VisualizationService.buildEdges(stepNodes)).toHaveLength(branchSteps.length - 1);
});
Expand Down Expand Up @@ -260,13 +260,13 @@ describe('visualizationService', () => {
});

it('buildNodesFromSteps(): should build visualization nodes from an array of steps', () => {
const stepNodes = VisualizationService.buildNodesFromSteps(steps, 'RIGHT');
const stepNodes = VisualizationService.buildNodesFromSteps('Camel Route-1', steps, 'RIGHT');
expect(stepNodes[0].data.step.UUID).toBeDefined();
expect(stepNodes[0].id).toContain(stepNodes[0].data.step.UUID);
});

it.skip('buildNodesFromSteps(): should build visualization nodes from an array of steps with branches', () => {
const stepNodes = VisualizationService.buildNodesFromSteps(branchSteps, 'RIGHT');
const stepNodes = VisualizationService.buildNodesFromSteps('Camel Route-1', branchSteps, 'RIGHT');
expect(stepNodes[0].data.step.UUID).toBeDefined();
expect(stepNodes).toHaveLength(branchSteps.length);
});
Expand Down Expand Up @@ -480,7 +480,7 @@ describe('visualizationService', () => {

it('insertAddStepPlaceholder(): should add an ADD STEP placeholder to the beginning of the array', () => {
const nodes: IVizStepNode[] = [];
VisualizationService.insertAddStepPlaceholder(nodes, '', 'START', { nextStepUuid: '' });
VisualizationService.insertAddStepPlaceholder('Camel Route-1', nodes, '', 'START', { nextStepUuid: '' });
expect(nodes).toHaveLength(1);
});

Expand Down
41 changes: 33 additions & 8 deletions src/services/visualizationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import {
IIntegrationJsonStore,
RFState,
useIntegrationJsonStore,
useFlowsStore,
useVisualizationStore,
useSettingsStore,
} from '@kaoto/store';
import {
IStepProps,
Expand Down Expand Up @@ -279,12 +281,32 @@ export class VisualizationService {
* @param handleDeleteStep
*/
buildNodesAndEdges(handleDeleteStep: (uuid: string) => void) {
const steps = useIntegrationJsonStore.getState().integrationJson.steps;
const { id: singleFlowId, steps: singleFlowSteps } = useIntegrationJsonStore.getState().integrationJson;
const layout = useVisualizationStore.getState().layout;

// build all nodes
const stepNodes = VisualizationService.buildNodesFromSteps(steps, layout, {
handleDeleteStep,
});
let stepNodes: IVizStepNode[] = [];

/**
* TODO: The following check is meant to be a temporary one
* while the Multiple Flows support is completed.
*
* This will be removed once all the existing functionality
* it's ported to the new "multi-flow" approach
*/
if (!useSettingsStore.getState().settings.useMultipleFlows) {
stepNodes = VisualizationService.buildNodesFromSteps(singleFlowId, singleFlowSteps, layout, { handleDeleteStep });
} else {
const integrations = useFlowsStore.getState().flows;
stepNodes = integrations.reduce((acc, currentIntegration) => acc.concat(
VisualizationService.buildNodesFromSteps(
currentIntegration.id,
currentIntegration.steps,
layout,
{ handleDeleteStep },
),
), [] as IVizStepNode[]);
}

// build edges only for main nodes
const filteredNodes = stepNodes.filter((node) => !node.data.branchInfo);
Expand All @@ -304,28 +326,29 @@ export class VisualizationService {
* Data is stored in the `nodes` hook.
*/
static buildNodesFromSteps(
integrationId: string,
steps: IStepProps[],
layout: string,
props?: { [prop: string]: any },
branchInfo?: IVizStepNodeDataBranch
): IVizStepNode[] {
let stepNodes: IVizStepNode[] = [];
let id = 0;
let getId = (uuid: string) => `node_${id++}-${uuid}-${getRandomArbitraryNumber()}`;
let getId = (UUID: string) => `node_${id++}-${UUID}-${getRandomArbitraryNumber()}`;

// if no steps or first step isn't START, create a dummy placeholder step
if (
(steps.length === 0 && !branchInfo) ||
(!StepsService.isFirstStepStart(steps) && !branchInfo)
) {
VisualizationService.insertAddStepPlaceholder(stepNodes, getId(''), 'START', {
VisualizationService.insertAddStepPlaceholder(integrationId, stepNodes, getId(''), 'START', {
nextStepUuid: steps[0]?.UUID,
});
}

// build EIP placeholder
if (branchInfo && steps.length === 0 && !StepsService.isFirstStepStart(steps)) {
VisualizationService.insertAddStepPlaceholder(stepNodes, getId(''), 'MIDDLE', {
VisualizationService.insertAddStepPlaceholder(integrationId, stepNodes, getId(''), 'MIDDLE', {
branchInfo,
nextStepUuid: steps[0]?.UUID,
});
Expand Down Expand Up @@ -360,7 +383,7 @@ export class VisualizationService {
if (step.branches && step.branches.length > 0 && step.maxBranches !== 0) {
step.branches.forEach((branch) => {
stepNodes = stepNodes.concat(
VisualizationService.buildNodesFromSteps(branch.steps, layout, props, {
VisualizationService.buildNodesFromSteps(integrationId, branch.steps, layout, props, {
branchIdentifier: branch.condition !== null ? branch.condition : branch.identifier,

// rootStepUuid is the parent for a first branch step,
Expand Down Expand Up @@ -487,6 +510,7 @@ export class VisualizationService {
}

static insertAddStepPlaceholder(
integrationId: string,
stepNodes: IVizStepNode[],
id: string,
type: string,
Expand All @@ -499,6 +523,7 @@ export class VisualizationService {
name: '',
type: type,
UUID: `placeholder-${getRandomArbitraryNumber()}`,
integrationId,
},
isPlaceholder: true,
...props,
Expand Down
Loading