diff --git a/frontend/src/__tests__/cypress/cypress/tests/mocked/connectionTypes/connectionTypes.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/mocked/connectionTypes/connectionTypes.cy.ts index a0f74f3bbe..3fc0a116ad 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/mocked/connectionTypes/connectionTypes.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/mocked/connectionTypes/connectionTypes.cy.ts @@ -103,4 +103,10 @@ describe('Connection types', () => { row2.findEnableSwitch().click(); row2.findEnableStatus().should('have.text', 'Enabling...'); }); + + it.only('should delete connection type', () => { + connectionTypesPage.visit(); + connectionTypesPage.shouldHaveConnectionTypes(); + + }); }); diff --git a/frontend/src/k8sTypes.ts b/frontend/src/k8sTypes.ts index 5c5b918ffc..5f28923b7a 100644 --- a/frontend/src/k8sTypes.ts +++ b/frontend/src/k8sTypes.ts @@ -1279,6 +1279,22 @@ export type AcceleratorProfileKind = K8sResourceCommon & { }; }; +export type ConnectionTypeKind = K8sResourceCommon & { + metadata: { + name: string; + annotations?: Partial<{ + 'opendatahub.io/modified-date': string; + }>; + }; + spec: { + displayName: string; + enabled: boolean; + identifier: string; + description?: string; + tolerations?: Toleration[]; + }; +}; + // In the SDK TResource extends from K8sResourceCommon, but both kind and apiVersion are mandatory export type K8sResourceListResult> = { apiVersion: string; diff --git a/frontend/src/pages/connectionTypes/ConnectionTypesTable.tsx b/frontend/src/pages/connectionTypes/ConnectionTypesTable.tsx index 830ed8f7e3..6ab29e91d0 100644 --- a/frontend/src/pages/connectionTypes/ConnectionTypesTable.tsx +++ b/frontend/src/pages/connectionTypes/ConnectionTypesTable.tsx @@ -6,19 +6,28 @@ import ConnectionTypesTableRow from '~/pages/connectionTypes/ConnectionTypesTabl import ConnectionTypesTableToolbar from '~/pages/connectionTypes/ConnectionTypesTableToolbar'; import { ConnectionTypeConfigMapObj } from '~/concepts/connectionTypes/types'; import { Table } from '~/components/table'; +import DeleteConnectionTypeModal from '~/pages/connectionTypes/DeleteConnectionTypeModal'; +import { ConnectionTypeKind } from '~/k8sTypes'; -interface ConnectionTypesTableProps { +type ConnectionTypesTableProps = { connectionTypes: ConnectionTypeConfigMapObj[]; onUpdate: () => void; + refreshConnectionTypes: () => void; } + const ConnectionTypesTable: React.FC = ({ connectionTypes, onUpdate, + refreshConnectionTypes, }) => { const [filterData, setFilterData] = React.useState(initialFilterData); const onClearFilters = React.useCallback(() => setFilterData(initialFilterData), [setFilterData]); + const [deleteConnectionType, setDeleteConnectionType] = React.useState< + ConnectionTypeKind | undefined + >(); + const filteredConnectionTypes = connectionTypes.filter((connectionType) => { const keywordFilter = filterData.Keyword?.toLowerCase(); const createFilter = filterData['Created by']?.toLowerCase(); @@ -51,6 +60,7 @@ const ConnectionTypesTable: React.FC = ({ }; return ( + <> = ({ key={connectionType.metadata.name} obj={connectionType} onUpdate={onUpdate} - /> + connectionType={connectionType} + handleDelete={(connectionType) => setDeleteConnectionType(connectionType)}/> )} toolbarContent={ = ({ emptyTableView={} id="connectionTypes-list-table" /> + { + if (deleted) { + refreshConnectionTypes(); + } + setDeleteConnectionType(undefined); + }} + /> + + ); }; diff --git a/frontend/src/pages/connectionTypes/ConnectionTypesTableRow.tsx b/frontend/src/pages/connectionTypes/ConnectionTypesTableRow.tsx index 758d21ed38..7c075841d4 100644 --- a/frontend/src/pages/connectionTypes/ConnectionTypesTableRow.tsx +++ b/frontend/src/pages/connectionTypes/ConnectionTypesTableRow.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Td, Tr } from '@patternfly/react-table'; +import { ActionsColumn, Td, Tr } from '@patternfly/react-table'; import { Flex, Icon, @@ -15,13 +15,17 @@ import { ExclamationCircleIcon } from '@patternfly/react-icons'; import { ConnectionTypeConfigMapObj } from '~/concepts/connectionTypes/types'; import { relativeTime } from '~/utilities/time'; import { updateConnectionTypeEnabled } from '~/services/connectionTypesService'; +import { ConnectionTypeKind } from '~/k8sTypes'; type ConnectionTypesTableRowProps = { obj: ConnectionTypeConfigMapObj; onUpdate: () => void; + connectionType: ConnectionTypeKind; + handleDelete: (cr: ConnectionTypeKind) => void; }; -const ConnectionTypesTableRow: React.FC = ({ obj, onUpdate }) => { + +const ConnectionTypesTableRow: React.FC = ({ obj, onUpdate, connectionType, handleDelete })=> { const [statusMessage, setStatusMessage] = React.useState(); const [errorMessage, setErrorMessage] = React.useState(); const pendingEnabledState = React.useRef<'true' | 'false' | undefined>(); @@ -112,7 +116,18 @@ const ConnectionTypesTableRow: React.FC = ({ obj, ) : null} + + ); }; diff --git a/frontend/src/pages/connectionTypes/DeleteConnectionTypeModal.tsx b/frontend/src/pages/connectionTypes/DeleteConnectionTypeModal.tsx new file mode 100644 index 0000000000..3dc75ebfa5 --- /dev/null +++ b/frontend/src/pages/connectionTypes/DeleteConnectionTypeModal.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import DeleteModal from '~/pages/projects/components/DeleteModal'; +import { ConnectionTypeKind } from '~/k8sTypes'; +import { deleteConnectionType } from '~/services/connectionTypesService'; + +type DeleteConnectionTypeModalProps = { + connectionType?: ConnectionTypeKind; + onClose: (deleted: boolean) => void; +}; + +const DeleteConnectionTypeModal: React.FC = ({ + connectionType, + onClose, +}) => { + const [isDeleting, setIsDeleting] = React.useState(false); + const [error, setError] = React.useState(); + + const onBeforeClose = (deleted: boolean) => { + onClose(deleted); + setIsDeleting(false); + setError(undefined); + }; + + const deleteName = connectionType?.spec.displayName || 'this accelerator profile'; + + return ( + onBeforeClose(false)} + submitButtonLabel="Delete" + onDelete={() => { + if (connectionType) { + setIsDeleting(true); + deleteConnectionType(connectionType.metadata.name) + .then(() => { + onBeforeClose(true); + }) + .catch((e) => { + setError(e); + setIsDeleting(false); + }); + } + }} + deleting={isDeleting} + error={error} + deleteName={deleteName} + > + The {deleteName} accelerator profile will be deleted and will no longer be available + for use with new workbenches and runtimes. Existing resources using this profile will retain + it unless a new profile is selected. + + ); +}; + +export default DeleteConnectionTypeModal; diff --git a/frontend/src/services/connectionTypeService.ts b/frontend/src/services/connectionTypeService.ts new file mode 100644 index 0000000000..1ddaa2cbd4 --- /dev/null +++ b/frontend/src/services/connectionTypeService.ts @@ -0,0 +1,38 @@ +import axios from '~/utilities/axios'; +import { ConnectionTypeKind } from '~/k8sTypes'; +import { ResponseStatus } from '~/types'; + +export const createConnectionType = ( + connectionType: ConnectionTypeKind['spec'], +): Promise => { + const url = '/api/connection-type'; + return axios + .post(url, connectionType) + .then((response) => response.data) + .catch((e) => { + throw new Error(e.response.data.message); + }); +}; + +export const deleteConnectionType = (name: string): Promise => { + const url = `/api/connection-types/${name}`; + return axios + .delete(url) + .then((response) => response.data) + .catch((e) => { + throw new Error(e.response.data.message); + }); +}; + +export const updateConnectionType = ( + name: string, + spec: Partial, +): Promise => { + const url = `/api/connection-types/${name}`; + return axios + .put(url, spec) + .then((response) => response.data) + .catch((e) => { + throw new Error(e.response.data.message); + }); +};
+ handleDelete(connectionType), + }, + ]} + /> +