Skip to content

Commit

Permalink
fix(902): Route and Kamelet container name issue
Browse files Browse the repository at this point in the history
  • Loading branch information
Shivam Gupta committed Apr 4, 2024
1 parent 6e4d06d commit 7e9b444
Show file tree
Hide file tree
Showing 9 changed files with 642 additions and 134 deletions.
330 changes: 219 additions & 111 deletions packages/ui/src/components/Visualization/Canvas/CanvasForm.test.tsx

Large diffs are not rendered by default.

20 changes: 18 additions & 2 deletions packages/ui/src/components/Visualization/Canvas/CanvasForm.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Card, CardBody, CardHeader } from '@patternfly/react-core';
import { FunctionComponent, useCallback, useContext, useEffect, useMemo, useRef } from 'react';
import { VisibleFlowsContext } from '../../../providers';
import { EntitiesContext } from '../../../providers/entities.provider';
import { SchemaBridgeProvider } from '../../../providers/schema-bridge.provider';
import { isDefined, setValue } from '../../../utils';
Expand All @@ -20,9 +21,11 @@ interface CanvasFormProps {
}

export const CanvasForm: FunctionComponent<CanvasFormProps> = (props) => {
const { visualFlowsApi } = useContext(VisibleFlowsContext)!;
const entitiesContext = useContext(EntitiesContext);
const formRef = useRef<CustomAutoFormRef>(null);
const divRef = useRef<HTMLDivElement>(null);
const flowIdRef = useRef<string | undefined>(undefined);

const visualComponentSchema = useMemo(() => {
const answer = props.selectedNode.data?.vizNode?.getComponentSchema();
Expand All @@ -35,6 +38,11 @@ export const CanvasForm: FunctionComponent<CanvasFormProps> = (props) => {
const model = visualComponentSchema?.definition;
const title = visualComponentSchema?.title;

/** Store the flow's initial Id */
useEffect(() => {
flowIdRef.current = props.selectedNode.data?.vizNode?.getBaseEntity()?.getId();
}, []);

useEffect(() => {
formRef.current?.form.reset();
}, [props.selectedNode.data?.vizNode]);
Expand All @@ -45,7 +53,7 @@ export const CanvasForm: FunctionComponent<CanvasFormProps> = (props) => {
return;
}

const newModel = props.selectedNode.data?.vizNode?.getComponentSchema()?.definition || {};
const newModel = props.selectedNode.data.vizNode.getComponentSchema()?.definition || {};
setValue(newModel, path, value);
props.selectedNode.data.vizNode.updateModel(newModel);
entitiesContext?.updateSourceCodeFromEntities();
Expand All @@ -65,14 +73,22 @@ export const CanvasForm: FunctionComponent<CanvasFormProps> = (props) => {
return { isExpressionAwareStep, isDataFormatAwareStep, isLoadBalanceAwareStep, isUnknownComponent };
}, [visualComponentSchema]);

const onClose = useCallback(() => {
props.onClose?.();
const newId = props.selectedNode.data?.vizNode?.getBaseEntity()?.getId();
if (typeof flowIdRef.current === 'string' && typeof newId === 'string' && flowIdRef.current !== newId) {
visualFlowsApi.renameFlow(flowIdRef.current, newId);
}
}, [props, visualFlowsApi]);

return (
<ErrorBoundary key={props.selectedNode.id} fallback={<p>This node cannot be configured yet</p>}>
<Card className="canvas-form">
<CardHeader>
<CanvasFormHeader
nodeId={props.selectedNode.id}
title={title}
onClose={props.onClose}
onClose={onClose}
nodeIcon={props.selectedNode.data?.vizNode?.data?.icon}
/>
</CardHeader>
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@ import { IVisibleFlows, VisualFlowsApi } from '../../../../models/visualization/
import { EntitiesContext } from '../../../../providers/entities.provider';
import { VisibleFLowsContextResult, VisibleFlowsContext } from '../../../../providers/visible-flows.provider';
import { FlowsList } from './FlowsList';
import { camelRouteJson } from '../../../../stubs';

const getContextValue = () => {
const visualEntity1 = new CamelRouteVisualEntity({ ...camelRouteJson.route, id: 'entity1' });
const visualEntity2 = new CamelRouteVisualEntity({ ...camelRouteJson.route, id: 'entity2' });

return {
currentSchemaType: SourceSchemaType.Integration,
visualEntities: [{ id: 'entity1' } as CamelRouteVisualEntity, { id: 'entity2' } as CamelRouteVisualEntity],
visualEntities: [visualEntity1, visualEntity2],
updateEntitiesFromCamelResource: jest.fn(),
} as unknown as EntitiesContextResult;
};
const getVisibleFlowsContextValue = () => {
Expand Down Expand Up @@ -42,14 +47,14 @@ describe('FlowsList.tsx', () => {
visibleFlowsValue = getVisibleFlowsContextValue();
});

test('should render the existing flows', async () => {
it('should render the existing flows', async () => {
const wrapper = render(<FlowsListWithContexts contextValue={contextValue} visibleFlowsValue={visibleFlowsValue} />);
const flows = await wrapper.findAllByTestId(/flows-list-row-*/);

expect(flows).toHaveLength(2);
});

test('should display an empty state when there is no routes available', async () => {
it('should display an empty state when there is no routes available', async () => {
contextValue = { ...contextValue, visualEntities: [] };
visibleFlowsValue = { ...visibleFlowsValue, visibleFlows: {} };
const wrapper = render(<FlowsListWithContexts contextValue={contextValue} visibleFlowsValue={visibleFlowsValue} />);
Expand All @@ -59,7 +64,7 @@ describe('FlowsList.tsx', () => {
expect(emptyState).toBeInTheDocument();
});

test('should render the flows ids', async () => {
it('should render the flows ids', async () => {
const wrapper = render(<FlowsListWithContexts contextValue={contextValue} visibleFlowsValue={visibleFlowsValue} />);
const flow1 = await wrapper.findByText('entity1');
const flow2 = await wrapper.findByText('entity2');
Expand All @@ -68,7 +73,7 @@ describe('FlowsList.tsx', () => {
expect(flow2).toBeInTheDocument();
});

test('should make the selected flow visible by clicking on its ID', async () => {
it('should make the selected flow visible by clicking on its ID', async () => {
let resId = '';
const visFlowApi = new VisualFlowsApi(jest.fn);
jest.spyOn(visFlowApi, 'toggleFlowVisible').mockImplementation((id: string) => {
Expand All @@ -85,7 +90,7 @@ describe('FlowsList.tsx', () => {
expect(resId).toBe('entity1');
});

test('should call onClose when clicking on a flow ID', async () => {
it('should call onClose when clicking on a flow ID', async () => {
const onCloseSpy = jest.fn();

const wrapper = render(
Expand All @@ -101,7 +106,7 @@ describe('FlowsList.tsx', () => {
expect(onCloseSpy).toHaveBeenCalledTimes(1);
});

test('should toggle the visibility of a flow clicking on the Eye icon', async () => {
it('should toggle the visibility of a flow clicking on the Eye icon', async () => {
let resId = '';
const visFlowApi = new VisualFlowsApi(jest.fn);
jest.spyOn(visFlowApi, 'toggleFlowVisible').mockImplementation((id: string) => {
Expand All @@ -119,7 +124,7 @@ describe('FlowsList.tsx', () => {
expect(resId).toEqual('entity1');
});

test('should render the appropriate Eye icon', async () => {
it('should render the appropriate Eye icon', async () => {
const wrapper = render(<FlowsListWithContexts contextValue={contextValue} visibleFlowsValue={visibleFlowsValue} />);
const flow1 = await wrapper.findByTestId('toggle-btn-entity1-visible');
expect(flow1).toBeInTheDocument();
Expand All @@ -128,4 +133,32 @@ describe('FlowsList.tsx', () => {
const flow2 = await wrapper.findByTestId('toggle-btn-entity2-hidden');
expect(flow2).toBeInTheDocument();
});

it('should rename a flow', async () => {
const visualFlowsApi = new VisualFlowsApi(jest.fn);
const renameSpy = jest.spyOn(visualFlowsApi, 'renameFlow');

visibleFlowsValue = { ...getVisibleFlowsContextValue(), visualFlowsApi };
const wrapper = render(<FlowsListWithContexts contextValue={contextValue} visibleFlowsValue={visibleFlowsValue} />);

await act(async () => {
const entityOnePencilIcon = await wrapper.findByTestId('goto-btn-entity1--edit');
fireEvent.click(entityOnePencilIcon);
});

await act(async () => {
const input = await wrapper.findByDisplayValue('entity1');
fireEvent.change(input, { target: { value: 'new-name' } });
fireEvent.blur(input);
});

await act(async () => {
const entityOnePencilIcon = await wrapper.findByTestId('goto-btn-entity1--save');
fireEvent.click(entityOnePencilIcon);
});

expect(renameSpy).toHaveBeenCalledWith('entity1', 'new-name');
expect(contextValue.visualEntities[0].id).toEqual('new-name');
expect(contextValue.updateEntitiesFromCamelResource).toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export const FlowsList: FunctionComponent<IFlowsList> = (props) => {
onSelectFlow(flow.id);
}}
onChange={(name) => {
visualFlowsApi.renameFlow(flow.id, name);
flow.setId(name);
updateEntitiesFromCamelResource();
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ export abstract class AbstractCamelVisualEntity implements BaseVisualCamelEntity
const updatedValue = CamelComponentSchemaService.getUriSerializedDefinition(path, value);

setValue(this.route, path, updatedValue);

if (isDefined(this.route.id)) this.id = this.route.id;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export class KameletVisualEntity extends AbstractCamelVisualEntity {
updateModel(path: string | undefined, value: Record<string, unknown>): void {
if (path === ROOT_PATH) {
updateKameletFromCustomSchema(this.kamelet, value);
this.id = this.kamelet.metadata.name;
this.route.id = this.kamelet.metadata.name;
return;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { VisualFlowsApi } from './flows-visibility';

describe('VisualFlowsApi', () => {
let dispatch: jest.Mock;
let visualFlowsApi: VisualFlowsApi;

beforeEach(() => {
dispatch = jest.fn();
visualFlowsApi = new VisualFlowsApi(dispatch);
});

it('should toggle flow visibility', () => {
visualFlowsApi.toggleFlowVisible('flowId');

expect(dispatch).toHaveBeenCalledWith({ type: 'toggleFlowVisible', flowId: 'flowId', isVisible: undefined });
});

it('should toggle flow visibility with a given flag', () => {
visualFlowsApi.toggleFlowVisible('flowId', true);

expect(dispatch).toHaveBeenCalledWith({ type: 'toggleFlowVisible', flowId: 'flowId', isVisible: true });
});

it('should show all flows', () => {
visualFlowsApi.showAllFlows();

expect(dispatch).toHaveBeenCalledWith({ type: 'showAllFlows' });
});

it('should hide all flows', () => {
visualFlowsApi.hideAllFlows();

expect(dispatch).toHaveBeenCalledWith({ type: 'hideAllFlows' });
});

it('should set visible flows', () => {
visualFlowsApi.setVisibleFlows(['flowId']);

expect(dispatch).toHaveBeenCalledWith({ type: 'setVisibleFlows', flows: ['flowId'] });
});

it('should clear flows', () => {
visualFlowsApi.clearFlows();

expect(dispatch).toHaveBeenCalledWith({ type: 'clearFlows' });
});

it('should init visible flows', () => {
visualFlowsApi.initVisibleFlows({ flowId: true });

expect(dispatch).toHaveBeenCalledWith({ type: 'initVisibleFlows', visibleFlows: { flowId: true } });
});

it('should rename flow', () => {
visualFlowsApi.renameFlow('flowId', 'newName');

expect(dispatch).toHaveBeenCalledWith({ type: 'renameFlow', flowId: 'flowId', newName: 'newName' });
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ type VisibleFlowAction =
| { type: 'hideAllFlows' }
| { type: 'clearFlows' }
| { type: 'setVisibleFlows'; flows: string[] }
| { type: 'initVisibleFlows'; visibleFlows: IVisibleFlows };
| { type: 'initVisibleFlows'; visibleFlows: IVisibleFlows }
| { type: 'renameFlow'; flowId: string; newName: string };

export function VisibleFlowsReducer(state: IVisibleFlows, action: VisibleFlowAction) {
let visibleFlows;
Expand Down Expand Up @@ -84,6 +85,16 @@ export function VisibleFlowsReducer(state: IVisibleFlows, action: VisibleFlowAct
return {};
case 'initVisibleFlows':
return { ...action.visibleFlows };

case 'renameFlow':
// eslint-disable-next-line no-case-declarations
const newState = {
...state,
[action.newName]: state[action.flowId],
};
delete newState[action.flowId];

return newState;
}
}

Expand Down Expand Up @@ -117,4 +128,8 @@ export class VisualFlowsApi {
initVisibleFlows(visibleFlows: IVisibleFlows) {
this.dispatch({ type: 'initVisibleFlows', visibleFlows: visibleFlows });
}

renameFlow(flowId: string, newName: string) {
this.dispatch({ type: 'renameFlow', flowId, newName });
}
}

0 comments on commit 7e9b444

Please sign in to comment.