Skip to content
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { SplitterProvider } from '../../Splitter/SplitterContext.tsx';
import { ListControlPlanesType } from '../../../lib/api/types/crate/controlPlanes.ts';
import { MemoryRouter } from 'react-router-dom';
import '@ui5/webcomponents-cypress-commands';
import { ControlPlaneCard } from './ControlPlaneCard.tsx';
import { useDeleteManagedControlPlane } from '../../../hooks/useDeleteManagedControlPlane.ts';
import { ListWorkspacesType } from '../../../lib/api/types/crate/listWorkspaces.ts';

describe('ControlPlaneCard', () => {
let deleteManagedControlPlaneCalled = false;
const fakeUseDeleteManagedControlPlane: typeof useDeleteManagedControlPlane = () => ({
deleteManagedControlPlane: async (): Promise<void> => {
deleteManagedControlPlaneCalled = true;
},
});

beforeEach(() => {
deleteManagedControlPlaneCalled = false;
});

it('deletes the workspace', () => {
const managedControlPlane: ListControlPlanesType = {
metadata: {
name: 'mcp-name',
},
} as unknown as ListControlPlanesType;

const workspace: ListWorkspacesType = {
metadata: {
name: 'workspaceName',
},
} as unknown as ListWorkspacesType;

cy.mount(
<MemoryRouter>
<SplitterProvider>
<ControlPlaneCard
controlPlane={managedControlPlane}
workspace={workspace}
projectName="projectName"
useDeleteManagedControlPlane={fakeUseDeleteManagedControlPlane}
/>
</SplitterProvider>
</MemoryRouter>,
);

cy.get("[data-testid='ControlPlaneCardMenu-opener']").click();
cy.contains('Delete ManagedControlPlane').click({ force: true });
cy.get('ui5-dialog[open]').find('ui5-input').typeIntoUi5Input('mcp-name');
cy.then(() => cy.wrap(deleteManagedControlPlaneCalled).should('equal', false));
cy.get('ui5-dialog[open]').find('ui5-button').contains('Delete').click();
cy.then(() => cy.wrap(deleteManagedControlPlaneCalled).should('equal', true));
});
});
33 changes: 12 additions & 21 deletions src/components/ControlPlanes/ControlPlaneCard/ControlPlaneCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,34 @@ import styles from './ControlPlaneCard.module.css';
import { KubectlDeleteMcp } from '../../Dialogs/KubectlCommandInfo/Controllers/KubectlDeleteMcp.tsx';
import { ListControlPlanesType, ReadyStatus } from '../../../lib/api/types/crate/controlPlanes.ts';
import { ListWorkspacesType } from '../../../lib/api/types/crate/listWorkspaces.ts';
import { useApiResourceMutation } from '../../../lib/api/useApiResource.ts';
import {
DeleteMCPResource,
DeleteMCPType,
PatchMCPResourceForDeletion,
PatchMCPResourceForDeletionBody,
} from '../../../lib/api/types/crate/deleteMCP.ts';

import { YamlViewButton } from '../../Yaml/YamlViewButton.tsx';
import { useToast } from '../../../context/ToastContext.tsx';
import { canConnectToMCP } from '../controlPlanes.ts';

import { Infobox } from '../../Ui/Infobox/Infobox.tsx';

import { ControlPlaneCardMenu } from './ControlPlaneCardMenu.tsx';
import { EditManagedControlPlaneWizardDataLoader } from '../../Wizards/CreateManagedControlPlane/EditManagedControlPlaneWizardDataLoader.tsx';
import { DISPLAY_NAME_ANNOTATION } from '../../../lib/api/types/shared/keyNames.ts';
import { useDeleteManagedControlPlane as _useDeleteManagedControlPlane } from '../../../hooks/useDeleteManagedControlPlane.ts';

interface Props {
controlPlane: ListControlPlanesType;
workspace: ListWorkspacesType;
projectName: string;
useDeleteManagedControlPlane?: typeof _useDeleteManagedControlPlane;
}

type MCPWizardState = {
isOpen: boolean;
mode?: 'edit' | 'duplicate';
};
export const ControlPlaneCard = ({ controlPlane, workspace, projectName }: Props) => {
export const ControlPlaneCard = ({
controlPlane,
workspace,
projectName,
useDeleteManagedControlPlane = _useDeleteManagedControlPlane,
}: Props) => {
const [dialogDeleteMcpIsOpen, setDialogDeleteMcpIsOpen] = useState(false);
const toast = useToast();
const { t } = useTranslation();
const [managedControlPlaneWizardState, setManagedControlPlaneWizardState] = useState<MCPWizardState>({
isOpen: false,
Expand All @@ -54,11 +51,9 @@ export const ControlPlaneCard = ({ controlPlane, workspace, projectName }: Props
const handleIsManagedControlPlaneWizardOpen = (isOpen: boolean, mode?: 'edit' | 'duplicate') => {
setManagedControlPlaneWizardState({ isOpen, mode });
};
const { trigger: patchTrigger } = useApiResourceMutation<DeleteMCPType>(
PatchMCPResourceForDeletion(controlPlane.metadata.namespace, controlPlane.metadata.name),
);
const { trigger: deleteTrigger } = useApiResourceMutation<DeleteMCPType>(
DeleteMCPResource(controlPlane.metadata.namespace, controlPlane.metadata.name),
const { deleteManagedControlPlane } = useDeleteManagedControlPlane(
controlPlane.metadata.namespace,
controlPlane.metadata.name,
);

const name = controlPlane.metadata.name;
Expand Down Expand Up @@ -135,11 +130,7 @@ export const ControlPlaneCard = ({ controlPlane, workspace, projectName }: Props
}
isOpen={dialogDeleteMcpIsOpen}
setIsOpen={setDialogDeleteMcpIsOpen}
onDeletionConfirmed={async () => {
await patchTrigger(PatchMCPResourceForDeletionBody);
await deleteTrigger();
toast.show(t('ControlPlaneCard.deleteConfirmationDialog'));
}}
onDeletionConfirmed={deleteManagedControlPlane}
/>
<EditManagedControlPlaneWizardDataLoader
isOpen={managedControlPlaneWizardState.isOpen}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ export const ControlPlaneCardMenu: FC<ControlPlanesListMenuProps> = ({

return (
<>
<Button ref={buttonRef} icon="overflow" icon-end onClick={handleOpenerClick} />
<Button
ref={buttonRef}
icon="overflow"
icon-end
data-testid="ControlPlaneCardMenu-opener"
onClick={handleOpenerClick}
/>
<Menu
open={menuIsOpen}
opener={buttonRef.current}
Expand Down
2 changes: 1 addition & 1 deletion src/components/ControlPlanes/ControlPlanesListMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const ControlPlanesListMenu: FC<ControlPlanesListMenuProps> = ({

return (
<>
<Button icon="overflow" icon-end onClick={handleOpenerClick} />
<Button icon="overflow" icon-end data-testid="ControlPlanesListMenu-opener" onClick={handleOpenerClick} />
<Menu
ref={popoverRef}
open={open}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { ControlPlaneListWorkspaceGridTile } from './ControlPlaneListWorkspaceGridTile.tsx';
import { SplitterProvider } from '../../Splitter/SplitterContext.tsx';
import { useManagedControlPlanesQuery } from '../../../hooks/useManagedControlPlanesQuery.ts';
import { ControlPlaneType, ReadyStatus } from '../../../lib/api/types/crate/controlPlanes.ts';
import { MemoryRouter } from 'react-router-dom';
import { useDeleteWorkspace } from '../../../hooks/useDeleteWorkspace.ts';
import '@ui5/webcomponents-cypress-commands';
import { ListWorkspacesType } from '../../../lib/api/types/crate/listWorkspaces.ts';

describe('ControlPlaneListWorkspaceGridTile', () => {
let deleteWorkspaceCalled = false;
const fakeUseDeleteWorkspace: typeof useDeleteWorkspace = () => ({
deleteWorkspace: async (): Promise<void> => {
deleteWorkspaceCalled = true;
},
});

const fakeManagedControlPlanes: ControlPlaneType[] = [
{
metadata: {
name: 'mcp-a',
namespace: 'project-webapp-playground--ws-workspaceName',
creationTimestamp: '2024-05-28T10:00:00Z',
},
spec: {
authentication: {
enableSystemIdentityProvider: true,
},
components: {
crossplane: undefined,
btpServiceOperator: undefined,
externalSecretsOperator: undefined,
kyverno: undefined,
flux: undefined,
landscaper: undefined,
},
},
status: {
status: ReadyStatus.Ready,
conditions: [],
access: undefined,
},
},
{
metadata: {
annotations: {
'openmcp.cloud/created-by': 'andreas.kienle@sap.com',
'openmcp.cloud/display-name': '',
},
name: 'd056765-all',
namespace: 'project-webapp-playground--ws-d056765',
creationTimestamp: '2024-05-28T10:00:00Z',
},
spec: {
authentication: {
enableSystemIdentityProvider: true,
},
components: {
crossplane: undefined,
btpServiceOperator: undefined,
externalSecretsOperator: undefined,
kyverno: undefined,
flux: undefined,
landscaper: undefined,
},
},
status: {
status: ReadyStatus.Ready,
conditions: [],
access: undefined,
},
},
{
metadata: {
annotations: {
'openmcp.cloud/created-by': 'andreas.kienle@sap.com',
'openmcp.cloud/display-name': '',
},
name: 'flux',
namespace: 'project-webapp-playground--ws-d056765',
creationTimestamp: '2024-05-28T10:00:00Z',
},
spec: {
authentication: {
enableSystemIdentityProvider: true,
},
components: {
crossplane: undefined,
btpServiceOperator: undefined,
externalSecretsOperator: undefined,
kyverno: undefined,
flux: undefined,
landscaper: undefined,
},
},
status: {
status: ReadyStatus.Ready,
conditions: [],
access: undefined,
},
},
];

const fakeUseManagedControlPlanesQuery: typeof useManagedControlPlanesQuery = () => ({
managedControlPlanes: fakeManagedControlPlanes,
error: undefined,
});

beforeEach(() => {
deleteWorkspaceCalled = false;
});

it('deletes the workspace', () => {
const workspace: ListWorkspacesType = {
metadata: {
name: 'workspaceName',
},
spec: {
members: [],
},
} as unknown as ListWorkspacesType;

cy.mount(
<MemoryRouter>
<SplitterProvider>
<ControlPlaneListWorkspaceGridTile
workspace={workspace}
projectName="some-project"
useManagedControlPlanesQuery={fakeUseManagedControlPlanesQuery}
useDeleteWorkspace={fakeUseDeleteWorkspace}
/>
</SplitterProvider>
</MemoryRouter>,
);

cy.get("[data-testid='ControlPlanesListMenu-opener']").click();
cy.contains('Delete workspace').click({ force: true });
cy.get('ui5-dialog[open]').find('ui5-input').typeIntoUi5Input('workspaceName');
cy.then(() => cy.wrap(deleteWorkspaceCalled).should('equal', false));
cy.get('ui5-dialog[open]').find('ui5-button').contains('Delete').click();
cy.then(() => cy.wrap(deleteWorkspaceCalled).should('equal', true));
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ import { ControlPlaneCard } from '../ControlPlaneCard/ControlPlaneCard.tsx';
import { ListWorkspacesType, isWorkspaceReady } from '../../../lib/api/types/crate/listWorkspaces.ts';
import { useMemo, useState } from 'react';
import { MembersAvatarView } from './MembersAvatarView.tsx';
import { useApiResource } from '../../../lib/api/useApiResource.ts';
import { DISPLAY_NAME_ANNOTATION } from '../../../lib/api/types/shared/keyNames.ts';
import { DeleteConfirmationDialog } from '../../Dialogs/DeleteConfirmationDialog.tsx';
import { KubectlDeleteWorkspace } from '../../Dialogs/KubectlCommandInfo/Controllers/KubectlDeleteWorkspace.tsx';
import { ListControlPlanes } from '../../../lib/api/types/crate/controlPlanes.ts';
import IllustratedError from '../../Shared/IllustratedError.tsx';
import { APIError } from '../../../lib/api/error.ts';
import { useTranslation } from 'react-i18next';
Expand All @@ -23,16 +21,19 @@ import styles from './WorkspacesList.module.css';
import { ControlPlanesListMenu } from '../ControlPlanesListMenu.tsx';
import { CreateManagedControlPlaneWizardContainer } from '../../Wizards/CreateManagedControlPlane/CreateManagedControlPlaneWizardContainer.tsx';
import { useDeleteWorkspace as _useDeleteWorkspace } from '../../../hooks/useDeleteWorkspace.ts';
import { useManagedControlPlanesQuery as _useManagedControlPlanesQuery } from '../../../hooks/useManagedControlPlanesQuery.ts';

interface Props {
projectName: string;
workspace: ListWorkspacesType;
useManagedControlPlanesQuery?: typeof _useManagedControlPlanesQuery;
useDeleteWorkspace?: typeof _useDeleteWorkspace;
}

export function ControlPlaneListWorkspaceGridTile({
projectName,
workspace,
useManagedControlPlanesQuery = _useManagedControlPlanesQuery,
useDeleteWorkspace = _useDeleteWorkspace,
}: Props) {
const [isCreateManagedControlPlaneWizardOpen, setIsCreateManagedControlPlaneWizardOpen] = useState(false);
Expand All @@ -46,7 +47,7 @@ export function ControlPlaneListWorkspaceGridTile({

const [dialogDeleteWsIsOpen, setDialogDeleteWsIsOpen] = useState(false);

const { data: controlplanes, error: cpsError } = useApiResource(ListControlPlanes(projectName, workspaceName));
const { managedControlPlanes, error: cpsError } = useManagedControlPlanesQuery(projectName, workspaceName);
const { deleteWorkspace } = useDeleteWorkspace(projectName, projectNamespace, workspaceName);

const { mcpCreationGuide } = useLink();
Expand Down Expand Up @@ -138,7 +139,7 @@ export function ControlPlaneListWorkspaceGridTile({
>
{errorView ? (
errorView
) : controlplanes?.length === 0 ? (
) : managedControlPlanes?.length === 0 ? (
<IllustratedBanner
title={t('IllustratedBanner.titleMessage')}
subtitle={t('IllustratedBanner.subtitleMessage')}
Expand All @@ -164,10 +165,10 @@ export function ControlPlaneListWorkspaceGridTile({
) : (
<div className={styles.wrapper}>
<div className={styles.grid}>
{controlplanes?.map((cp) => (
{managedControlPlanes?.map((mcp) => (
<ControlPlaneCard
key={`${cp.metadata.name}--${cp.metadata.namespace}`}
controlPlane={cp}
key={`${mcp.metadata.name}--${mcp.metadata.namespace}`}
controlPlane={mcp}
projectName={projectName}
workspace={workspace}
/>
Expand Down
Loading
Loading