From 132dafee35e1eb76ccd3cec9f248b9715a794ff7 Mon Sep 17 00:00:00 2001 From: Ashley McEntee <123661468+ashley-o0o@users.noreply.github.com> Date: Thu, 15 Aug 2024 10:30:00 -0400 Subject: [PATCH] Create delete connection type --- .../cypress/cypress/support/commands/odh.ts | 34 +++++++++- .../connectionTypes/connectionTypes.cy.ts | 29 +++++++++ .../connectionTypes/ConnectionTypesTable.tsx | 64 ++++++++++++------- .../ConnectionTypesTableRow.tsx | 9 ++- .../DeleteConnectionTypeModal.tsx | 58 +++++++++++++++++ 5 files changed, 168 insertions(+), 26 deletions(-) create mode 100644 frontend/src/pages/connectionTypes/DeleteConnectionTypeModal.tsx diff --git a/frontend/src/__tests__/cypress/cypress/support/commands/odh.ts b/frontend/src/__tests__/cypress/cypress/support/commands/odh.ts index 6677993ff7..55823a8407 100644 --- a/frontend/src/__tests__/cypress/cypress/support/commands/odh.ts +++ b/frontend/src/__tests__/cypress/cypress/support/commands/odh.ts @@ -568,6 +568,22 @@ declare global { }, response: OdhResponse<{ notebook: NotebookKind; isRunning: boolean }>, ) => Cypress.Chainable) & + (( + type: 'GET /api/service/pipelines/:namespace/:serviceName/apis/v2beta1/artifacts/:artifactId', + options: { + query: { view: string }; + path: { namespace: string; serviceName: string; artifactId: number }; + }, + response: OdhResponse, + ) => Cypress.Chainable) & + (( + type: 'GET /api/service/pipelines/:namespace/:serviceName/apis/v2beta1/artifacts/:artifactId', + options: { + query: { view: string }; + path: { namespace: string; serviceName: string; artifactId: number }; + }, + response: OdhResponse, + ) => Cypress.Chainable) & (( type: 'GET /api/service/pipelines/:namespace/:serviceName/apis/v2beta1/artifacts/:artifactId', options: { @@ -592,7 +608,23 @@ declare global { options: { path: { name: string }; }, - response: ConnectionTypeConfigMap, + response: OdhResponse, + ) => Cypress.Chainable) & + (( + type: 'GET /api/connection-types', + response: ConnectionTypeConfigMap[], + ) => Cypress.Chainable) & + (( + type: 'DELETE /api/connection-types/:name', + options: { path: { name: string } }, + response: OdhResponse, + ) => Cypress.Chainable) & + (( + type: 'PATCH /api/connection-types/:name', + options: { + path: { name: string }; + }, + response: { success: boolean; error: string }, ) => Cypress.Chainable); } } 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 1d27bcec0c..ca65bb8111 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 @@ -6,6 +6,7 @@ import { import { connectionTypesPage } from '~/__tests__/cypress/cypress/pages/connectionTypes'; import { mockDashboardConfig } from '~/__mocks__'; import { mockConnectionTypeConfigMap } from '~/__mocks__/mockConnectionType'; +import { deleteModal } from '~/__tests__/cypress/cypress/pages/components/DeleteModal'; it('Connection types should not be available for non product admins', () => { asProjectAdminUser(); @@ -76,4 +77,32 @@ describe('Connection types', () => { row2.shouldShowPreInstalledLabel(); row2.shouldBeDisabled(); }); + + it('should delete connection type', () => { + connectionTypesPage.visit(); + cy.interceptOdh( + 'DELETE /api/connection-types/:name', + { + path: { name: 'test-2' }, + }, + { success: true }, + ).as('delete'); + cy.interceptOdh('GET /api/connection-types', [ + mockConnectionTypeConfigMap({}), + mockConnectionTypeConfigMap({ + name: 'no-display-name', + displayName: '', + description: 'description 2', + username: 'Pre-installed', + enabled: false, + }), + ]); + + connectionTypesPage.shouldHaveConnectionTypes(); + connectionTypesPage.getConnectionTypeRow('Test display name').findKebabAction('Delete').click(); + deleteModal.findSubmitButton().should('be.disabled'); + deleteModal.findInput().fill('Test display name'); + deleteModal.findSubmitButton().should('be.enabled').click(); + cy.wait('@delete'); + }); }); diff --git a/frontend/src/pages/connectionTypes/ConnectionTypesTable.tsx b/frontend/src/pages/connectionTypes/ConnectionTypesTable.tsx index 0a7dc01506..2850e2da57 100644 --- a/frontend/src/pages/connectionTypes/ConnectionTypesTable.tsx +++ b/frontend/src/pages/connectionTypes/ConnectionTypesTable.tsx @@ -6,6 +6,7 @@ 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 { getCreatorFromK8sResource, getDescriptionFromK8sResource, @@ -21,6 +22,10 @@ const ConnectionTypesTable: React.FC = ({ connectionTypes, onUpdate }) => const [filterData, setFilterData] = React.useState(initialFilterData); const onClearFilters = React.useCallback(() => setFilterData(initialFilterData), [setFilterData]); + const [deleteConnectionType, setDeleteConnectionType] = React.useState< + ConnectionTypeConfigMapObj | undefined + >(); + const filteredConnectionTypes = React.useMemo( () => connectionTypes.filter((connectionType) => { @@ -48,29 +53,42 @@ const ConnectionTypesTable: React.FC = ({ connectionTypes, onUpdate }) => }; return ( - ( - - )} - toolbarContent={ - - } - disableItemCount - emptyTableView={} - /> + <> +
( + setDeleteConnectionType(connection)} + /> + )} + toolbarContent={ + + } + disableItemCount + emptyTableView={} + id="connectionTypes-list-table" + /> + { + if (deleted) { + onUpdate(); + } + setDeleteConnectionType(undefined); + }} + /> + ); }; diff --git a/frontend/src/pages/connectionTypes/ConnectionTypesTableRow.tsx b/frontend/src/pages/connectionTypes/ConnectionTypesTableRow.tsx index eece4e9186..8003a72c5d 100644 --- a/frontend/src/pages/connectionTypes/ConnectionTypesTableRow.tsx +++ b/frontend/src/pages/connectionTypes/ConnectionTypesTableRow.tsx @@ -17,9 +17,14 @@ import { type ConnectionTypesTableRowProps = { obj: ConnectionTypeConfigMapObj; onUpdate: () => void; + handleDelete: (cr: ConnectionTypeConfigMapObj) => void; }; -const ConnectionTypesTableRow: React.FC = ({ obj, onUpdate }) => { +const ConnectionTypesTableRow: React.FC = ({ + obj, + onUpdate, + handleDelete, +}) => { const navigate = useNavigate(); const notification = useNotification(); const [isUpdating, setIsUpdating] = React.useState(false); @@ -106,7 +111,7 @@ const ConnectionTypesTableRow: React.FC = ({ obj, }, { title: 'Delete', - isAriaDisabled: true, + onClick: () => handleDelete(obj), }, ]} /> diff --git a/frontend/src/pages/connectionTypes/DeleteConnectionTypeModal.tsx b/frontend/src/pages/connectionTypes/DeleteConnectionTypeModal.tsx new file mode 100644 index 0000000000..2947d274a1 --- /dev/null +++ b/frontend/src/pages/connectionTypes/DeleteConnectionTypeModal.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import { ConnectionTypeConfigMapObj } from '~/concepts/connectionTypes/types'; +import { getDisplayNameFromK8sResource } from '~/concepts/k8s/utils'; +import DeleteModal from '~/pages/projects/components/DeleteModal'; +import { deleteConnectionType } from '~/services/connectionTypesService'; + +type DeleteConnectionTypeModalProps = { + connectionType?: ConnectionTypeConfigMapObj; + 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 + ? getDisplayNameFromK8sResource(connectionType) + : 'this connection type'; + + 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} connection type will be deleted. Existing connections of this type + will not be affected. + + ); +}; + +export default DeleteConnectionTypeModal;