diff --git a/packages/manager/apps/key-management-service/package.json b/packages/manager/apps/key-management-service/package.json index 1cbfc7518ab3..ce9ec544da68 100644 --- a/packages/manager/apps/key-management-service/package.json +++ b/packages/manager/apps/key-management-service/package.json @@ -34,6 +34,7 @@ "@ovhcloud/ods-themes": "^18.5.3", "@tanstack/react-query": "^5.51.21", "@tanstack/react-table": "^8.20.1", + "file-saver": "^2.0.5", "i18next": "^23.8.2", "i18next-http-backend": "^2.4.3", "react": "^18.2.0", @@ -49,6 +50,7 @@ "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.0.1", "@testing-library/user-event": "^14.5.2", + "@types/file-saver": "^2.0.7", "@vitejs/plugin-react": "^4.3.4", "@vitest/coverage-v8": "^2.1.9", "element-internals-polyfill": "^1.3.11", diff --git a/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_de_DE.json b/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_de_DE.json index beee71e655be..4422fe04301d 100644 --- a/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_de_DE.json +++ b/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_de_DE.json @@ -60,5 +60,8 @@ "key_management_service_dashboard_back_link": "Zurück zur KMS-Liste", "key_management_service_dashboard_field_label_kmip_count": "Anzahl KMIP-Objekte", "key_management_service_dashboard_field_label_service-keys_count": "Anzahl der Service Keys", - "logs": "Logs" + "logs": "Logs", + "key_management_service_dashboard_button_label_download_ca": "CA herunterladen", + "key_management_service_dashboard_button_label_download_rsa_ca": "CA RSA herunterladen", + "key_management_service_dashboard_error_download_ca": "Das Zertifikat konnte nicht heruntergeladen werden." } diff --git a/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_en_GB.json b/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_en_GB.json index 19e10eae08d5..e472f6d6ed25 100644 --- a/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_en_GB.json +++ b/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_en_GB.json @@ -60,5 +60,8 @@ "key_management_service_dashboard_back_link": "Back to KMS list", "key_management_service_dashboard_field_label_kmip_count": "Number of KMIP objects", "key_management_service_dashboard_field_label_service-keys_count": "Number of Service Keys", - "logs": "Logs" + "logs": "Logs", + "key_management_service_dashboard_button_label_download_ca": "Download CA", + "key_management_service_dashboard_button_label_download_rsa_ca": "Download CA RSA", + "key_management_service_dashboard_error_download_ca": "Unable to download certificate" } diff --git a/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_es_ES.json b/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_es_ES.json index 9ae1ba1fbdcf..f6ccf0085d6e 100644 --- a/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_es_ES.json +++ b/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_es_ES.json @@ -60,5 +60,8 @@ "key_management_service_dashboard_back_link": "Volver a la lista de KMS", "key_management_service_dashboard_field_label_kmip_count": "Número de objetos KMIP", "key_management_service_dashboard_field_label_service-keys_count": "Número de Service Keys", - "logs": "Logs" + "logs": "Logs", + "key_management_service_dashboard_button_label_download_ca": "Descargar CA", + "key_management_service_dashboard_button_label_download_rsa_ca": "Descargar CA RSA", + "key_management_service_dashboard_error_download_ca": "No se ha podido descargar el certificado" } diff --git a/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_fr_CA.json b/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_fr_CA.json index 6a63b5892daf..e7c5dcb26428 100644 --- a/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_fr_CA.json +++ b/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_fr_CA.json @@ -36,5 +36,8 @@ "key_management_service_dashboard_dashboard_field_state_toDelete": "À supprimer", "key_management_service_dashboard_dashboard_field_state_toSuspend": "À suspendre", "key_management_service_dashboard_field_label_kmip_count": "Nombre d'objets KMIP", - "key_management_service_dashboard_field_label_service-keys_count": "Nombre de Service Keys" + "key_management_service_dashboard_field_label_service-keys_count": "Nombre de Service Keys", + "key_management_service_dashboard_button_label_download_ca": "Télécharger CA", + "key_management_service_dashboard_button_label_download_rsa_ca": "Télécharger CA RSA", + "key_management_service_dashboard_error_download_ca": "Impossible de télécharger le certificat" } diff --git a/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_fr_FR.json b/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_fr_FR.json index 6a63b5892daf..e7c5dcb26428 100644 --- a/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_fr_FR.json +++ b/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_fr_FR.json @@ -36,5 +36,8 @@ "key_management_service_dashboard_dashboard_field_state_toDelete": "À supprimer", "key_management_service_dashboard_dashboard_field_state_toSuspend": "À suspendre", "key_management_service_dashboard_field_label_kmip_count": "Nombre d'objets KMIP", - "key_management_service_dashboard_field_label_service-keys_count": "Nombre de Service Keys" + "key_management_service_dashboard_field_label_service-keys_count": "Nombre de Service Keys", + "key_management_service_dashboard_button_label_download_ca": "Télécharger CA", + "key_management_service_dashboard_button_label_download_rsa_ca": "Télécharger CA RSA", + "key_management_service_dashboard_error_download_ca": "Impossible de télécharger le certificat" } diff --git a/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_it_IT.json b/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_it_IT.json index 241fcf524bdf..1acd2d155db2 100644 --- a/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_it_IT.json +++ b/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_it_IT.json @@ -60,5 +60,8 @@ "key_management_service_dashboard_back_link": "Ritorna alla lista dei KMS", "key_management_service_dashboard_field_label_kmip_count": "Numero di oggetti KMIP", "key_management_service_dashboard_field_label_service-keys_count": "Numero di Service Keys", - "logs": "Log" + "logs": "Log", + "key_management_service_dashboard_button_label_download_ca": "Scaricare CA", + "key_management_service_dashboard_button_label_download_rsa_ca": "Scaricare CA RSA", + "key_management_service_dashboard_error_download_ca": "Impossibile scaricare il certificato" } diff --git a/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_pl_PL.json b/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_pl_PL.json index c85b9860a1ed..3f678dc01efe 100644 --- a/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_pl_PL.json +++ b/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_pl_PL.json @@ -60,5 +60,8 @@ "key_management_service_dashboard_back_link": "Powrót do listy KMS", "key_management_service_dashboard_field_label_kmip_count": "Liczba obiektów KMIP", "key_management_service_dashboard_field_label_service-keys_count": "Liczba Service Keys", - "logs": "Logi" + "logs": "Logi", + "key_management_service_dashboard_button_label_download_ca": "Pobierz CA", + "key_management_service_dashboard_button_label_download_rsa_ca": "Pobierz CA RSA", + "key_management_service_dashboard_error_download_ca": "Nie można pobrać certyfikatu" } diff --git a/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_pt_PT.json b/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_pt_PT.json index da7b5c2b9624..c6bc265da3f6 100644 --- a/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_pt_PT.json +++ b/packages/manager/apps/key-management-service/public/translations/key-management-service/dashboard/Messages_pt_PT.json @@ -60,5 +60,8 @@ "key_management_service_dashboard_back_link": "Voltar à lista dos KMS", "key_management_service_dashboard_field_label_kmip_count": "Número de objetos KMIP", "key_management_service_dashboard_field_label_service-keys_count": "Número de Service Keys", - "logs": "Logs" + "logs": "Logs", + "key_management_service_dashboard_button_label_download_ca": "Descarregar CA", + "key_management_service_dashboard_button_label_download_rsa_ca": "Descarregar CA RSA", + "key_management_service_dashboard_error_download_ca": "Não é possível descarregar o certificado" } diff --git a/packages/manager/apps/key-management-service/src/components/dashboard/downloadKmsPublicCaLink/DownloadKmsPublicCaLink.spec.tsx b/packages/manager/apps/key-management-service/src/components/dashboard/downloadKmsPublicCaLink/DownloadKmsPublicCaLink.spec.tsx new file mode 100644 index 000000000000..b04da6492153 --- /dev/null +++ b/packages/manager/apps/key-management-service/src/components/dashboard/downloadKmsPublicCaLink/DownloadKmsPublicCaLink.spec.tsx @@ -0,0 +1,151 @@ +import React from 'react'; +import { describe, vi, expect, test, beforeEach, afterEach } from 'vitest'; +import { render, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { getOdsButtonByLabel } from '@ovh-ux/manager-core-test-utils'; +import { + CertificateType, + DownloadKmsPublicCaLink, +} from './DownloadKmsPublicCaLink'; +import * as api from '@/data/api/okms'; +import { initiateTextFileDownload } from '@/utils/dom/download'; +import { OKMS } from '@/types/okms.type'; + +const addErrorMock = vi.fn(); +vi.mock('@ovh-ux/manager-react-components', () => ({ + useNotifications: () => ({ + addError: addErrorMock, + }), +})); + +vi.mock('react-i18next', () => ({ + useTranslation: () => ({ + t: (translationKey: string) => translationKey, + }), +})); + +vi.mock('@/utils/dom/download', () => ({ + initiateTextFileDownload: vi.fn(), +})); + +const mockOkms = { + id: 'test-okms-id', + region: 'test-region', +} as OKMS; + +const mockCertificates = { + publicCA: + '-----BEGIN CERTIFICATE-----\nMIIDDummyCertificate\n-----END CERTIFICATE-----', + publicRsaCA: + '-----BEGIN CERTIFICATE-----\nMIIDummyRsaCertificate\n-----END CERTIFICATE-----', +}; + +const renderComponentAndGetLink = async ({ + type, + label, +}: { + type: CertificateType; + label: string; +}) => { + const { container } = render( + , + ); + + const downloadLink = await getOdsButtonByLabel({ + container, + label, + isLink: true, + }); + + return { downloadLink }; +}; + +describe('DownloadKmsPublicCaLink component tests suite', () => { + beforeEach(() => { + vi.spyOn(api, 'getOkmsPublicCa').mockResolvedValue(mockCertificates); + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + test('should render publicCa download link correctly', async () => { + const { downloadLink } = await renderComponentAndGetLink({ + type: 'publicCa', + label: 'key_management_service_dashboard_button_label_download_ca', + }); + expect(downloadLink).toBeInTheDocument(); + }); + + test('should render publicRsaCa download link correctly', async () => { + const { downloadLink } = await renderComponentAndGetLink({ + type: 'publicRsaCa', + label: 'key_management_service_dashboard_button_label_download_rsa_ca', + }); + expect(downloadLink).toBeInTheDocument(); + }); + + test('should download publicCa certificate when clicked', async () => { + const { downloadLink } = await renderComponentAndGetLink({ + type: 'publicCa', + label: 'key_management_service_dashboard_button_label_download_ca', + }); + + const user = userEvent.setup(); + await waitFor(() => user.click(downloadLink)); + + await waitFor(() => { + expect(api.getOkmsPublicCa).toHaveBeenCalledWith(mockOkms.id); + }); + + await waitFor(() => { + expect(initiateTextFileDownload).toHaveBeenCalledWith({ + text: mockCertificates.publicCA, + filename: 'okms_test-region_public_ca.pem', + }); + }); + }); + + test('should download publicRsaCa certificate when clicked', async () => { + const { downloadLink } = await renderComponentAndGetLink({ + type: 'publicRsaCa', + label: 'key_management_service_dashboard_button_label_download_rsa_ca', + }); + + const user = userEvent.setup(); + await waitFor(() => user.click(downloadLink)); + + await waitFor(() => { + expect(api.getOkmsPublicCa).toHaveBeenCalledWith(mockOkms.id); + }); + + await waitFor(() => { + expect(initiateTextFileDownload).toHaveBeenCalledWith({ + text: mockCertificates.publicRsaCA, + filename: 'okms_test-region_public_rsa_ca.pem', + }); + }); + }); + + test('should show error notification when download fails', async () => { + // Override the successful mock with an error + vi.spyOn(api, 'getOkmsPublicCa').mockRejectedValueOnce( + new Error('API Error'), + ); + + const { downloadLink } = await renderComponentAndGetLink({ + type: 'publicCa', + label: 'key_management_service_dashboard_button_label_download_ca', + }); + + const user = userEvent.setup(); + await waitFor(() => user.click(downloadLink)); + + await waitFor(() => { + // Error notification should be shown + expect(addErrorMock).toHaveBeenCalledWith( + 'key_management_service_dashboard_error_download_ca', + ); + }); + }); +}); diff --git a/packages/manager/apps/key-management-service/src/components/dashboard/downloadKmsPublicCaLink/DownloadKmsPublicCaLink.tsx b/packages/manager/apps/key-management-service/src/components/dashboard/downloadKmsPublicCaLink/DownloadKmsPublicCaLink.tsx new file mode 100644 index 000000000000..1293074da863 --- /dev/null +++ b/packages/manager/apps/key-management-service/src/components/dashboard/downloadKmsPublicCaLink/DownloadKmsPublicCaLink.tsx @@ -0,0 +1,82 @@ +import React, { useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useNotifications } from '@ovh-ux/manager-react-components'; +import { OdsLink, OdsSpinner } from '@ovhcloud/ods-components/react'; +import { ODS_BUTTON_COLOR } from '@ovhcloud/ods-components'; +import { getOkmsPublicCa } from '@/data/api/okms'; +import { initiateTextFileDownload } from '@/utils/dom/download'; +import { + PUBLIC_CA_FILENAME, + PUBLIC_RSA_CA_FILENAME, +} from './downloadKmsPublicCaLink.constants'; +import { OKMS } from '@/types/okms.type'; + +export type CertificateType = 'publicCa' | 'publicRsaCa'; + +type DownloadCaButtonProps = { + okms: OKMS; + type: CertificateType; +}; + +export const DownloadKmsPublicCaLink = ({ + okms, + type, +}: DownloadCaButtonProps) => { + const { t } = useTranslation('key-management-service/dashboard'); + const [loading, setLoading] = useState(false); + const { addError } = useNotifications(); + + const resources = useMemo( + () => ({ + publicCa: { + label: t('key_management_service_dashboard_button_label_download_ca'), + filename: PUBLIC_CA_FILENAME, + }, + publicRsaCa: { + label: t( + 'key_management_service_dashboard_button_label_download_rsa_ca', + ), + filename: PUBLIC_RSA_CA_FILENAME, + }, + }), + [], + ); + + const handleDownloadCa = async ( + event: React.MouseEvent, + ) => { + event.preventDefault(); + + try { + setLoading(true); + const certificates = await getOkmsPublicCa(okms.id); + + const content: Record = { + publicCa: certificates.publicCA, + publicRsaCa: certificates.publicRsaCA, + }; + + initiateTextFileDownload({ + text: content[type], + filename: resources[type].filename.replace('{region}', okms.region), + }); + } catch { + addError(t('key_management_service_dashboard_error_download_ca')); + } finally { + setLoading(false); + } + }; + + return ( +
+ + {loading && } +
+ ); +}; diff --git a/packages/manager/apps/key-management-service/src/components/dashboard/downloadKmsPublicCaLink/downloadKmsPublicCaLink.constants.tsx b/packages/manager/apps/key-management-service/src/components/dashboard/downloadKmsPublicCaLink/downloadKmsPublicCaLink.constants.tsx new file mode 100644 index 000000000000..b22ab7d7190c --- /dev/null +++ b/packages/manager/apps/key-management-service/src/components/dashboard/downloadKmsPublicCaLink/downloadKmsPublicCaLink.constants.tsx @@ -0,0 +1,2 @@ +export const PUBLIC_CA_FILENAME = 'okms_{region}_public_ca.pem'; +export const PUBLIC_RSA_CA_FILENAME = 'okms_{region}_public_rsa_ca.pem'; diff --git a/packages/manager/apps/key-management-service/src/components/layout-helpers/Create/RegionSelector.tsx b/packages/manager/apps/key-management-service/src/components/layout-helpers/Create/RegionSelector.tsx index 86ce245f02b0..adcfb9b3824c 100644 --- a/packages/manager/apps/key-management-service/src/components/layout-helpers/Create/RegionSelector.tsx +++ b/packages/manager/apps/key-management-service/src/components/layout-helpers/Create/RegionSelector.tsx @@ -23,7 +23,7 @@ import { } from '@ovh-ux/manager-react-shell-client'; import { useNavigate } from 'react-router-dom'; import { ErrorBanner, Region } from '@ovh-ux/manager-react-components'; -import { useOrderCatalogOKMS } from '@/data/hooks/useOrderCatalogOKMS'; +import { useOrderCatalogOkms } from '@/data/hooks/useOrderCatalogOkms'; import { ROUTES_URLS } from '@/routes/routes.constants'; import { CREATE_KMS_TEST_IDS } from '@/pages/create/createKms.constants'; @@ -46,7 +46,7 @@ const RegionSelector = ({ isError, error, isLoading, - } = useOrderCatalogOKMS(environment.getUser().ovhSubsidiary); + } = useOrderCatalogOkms(environment.getUser().ovhSubsidiary); const navigate = useNavigate(); if (isError && error) { @@ -73,7 +73,7 @@ const RegionSelector = ({ )} {orderCatalogOKMS && !isLoading && ( { }; }); +vi.mock('react-i18next', () => ({ + useTranslation: () => ({ + t: (translationKey: string) => translationKey, + }), +})); + describe('KmipTile component tests suite', () => { const kms: OKMS = { id: 'id', @@ -48,6 +55,14 @@ describe('KmipTile component tests suite', () => { expect(screen.queryByText(KMIP_RSA_LABEL)).not.toBeInTheDocument(); }); + + const downloadLink = await getOdsButtonByLabel({ + container, + label: 'key_management_service_dashboard_button_label_download_ca', + isLink: true, + }); + + expect(downloadLink).toBeVisible(); }); test('Should display KMIP tile with all kms data', async () => { @@ -56,11 +71,19 @@ describe('KmipTile component tests suite', () => { const { container } = renderComponent(kmsData); - await waitFor(() => { + await waitFor(async () => { expect(screen.getByText(KMIP_RSA_LABEL)).toBeVisible(); expect( container.querySelector(`ods-clipboard[value="${kmipRsaEndpoint}"]`), ).toBeVisible(); + + const downloadLinkRsa = await getOdsButtonByLabel({ + container, + label: 'key_management_service_dashboard_button_label_download_rsa_ca', + isLink: true, + }); + + expect(downloadLinkRsa).toBeVisible(); }); }); }); diff --git a/packages/manager/apps/key-management-service/src/components/layout-helpers/Dashboard/GeneralInformationsTiles/KmipTile.tsx b/packages/manager/apps/key-management-service/src/components/layout-helpers/Dashboard/GeneralInformationsTiles/KmipTile.tsx index b211b3fa499e..b2c055a027e9 100644 --- a/packages/manager/apps/key-management-service/src/components/layout-helpers/Dashboard/GeneralInformationsTiles/KmipTile.tsx +++ b/packages/manager/apps/key-management-service/src/components/layout-helpers/Dashboard/GeneralInformationsTiles/KmipTile.tsx @@ -6,6 +6,7 @@ import { import React from 'react'; import { OKMS } from '@/types/okms.type'; import { KMIP_ENPOINT_LABEL, KMIP_RSA_LABEL } from './KmipTile.constants'; +import { DownloadKmsPublicCaLink } from '@/components/dashboard/downloadKmsPublicCaLink/DownloadKmsPublicCaLink'; type KmipTileProps = { okmsData?: OKMS; @@ -16,7 +17,10 @@ const KmipTile = ({ okmsData }: KmipTileProps) => { id: 'kmip', label: KMIP_ENPOINT_LABEL, value: ( - +
+ + +
), }, ]; @@ -25,7 +29,13 @@ const KmipTile = ({ okmsData }: KmipTileProps) => { id: 'kmipRsa', label: KMIP_RSA_LABEL, value: ( - +
+ + +
), }); } diff --git a/packages/manager/apps/key-management-service/src/components/layout-helpers/Dashboard/GeneralInformationsTiles/RestApiTile.spec.tsx b/packages/manager/apps/key-management-service/src/components/layout-helpers/Dashboard/GeneralInformationsTiles/RestApiTile.spec.tsx index 3b4bdefedd29..e297a2c0428a 100644 --- a/packages/manager/apps/key-management-service/src/components/layout-helpers/Dashboard/GeneralInformationsTiles/RestApiTile.spec.tsx +++ b/packages/manager/apps/key-management-service/src/components/layout-helpers/Dashboard/GeneralInformationsTiles/RestApiTile.spec.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { describe, vi } from 'vitest'; import { screen, render, waitFor } from '@testing-library/react'; +import { getOdsButtonByLabel } from '@ovh-ux/manager-core-test-utils'; import { OKMS } from '@/types/okms.type'; import RestApiTile from './RestApiTile'; import { REST_ENDPOINT_LABEL, SWAGGER_UI_LABEL } from './RestApiTile.constants'; @@ -19,6 +20,12 @@ vi.mock('react-router-dom', async () => { }; }); +vi.mock('react-i18next', () => ({ + useTranslation: () => ({ + t: (translationKey: string) => translationKey, + }), +})); + describe('RestApiTile component tests suite', () => { const kms: OKMS = { id: 'id', @@ -41,7 +48,7 @@ describe('RestApiTile component tests suite', () => { test('Should display REST API tile with only all mandatory data', async () => { const { container } = renderComponent(kms); - await waitFor(() => { + await waitFor(async () => { expect(screen.getByText(REST_ENDPOINT_LABEL)).toBeVisible(); expect( @@ -54,6 +61,14 @@ describe('RestApiTile component tests suite', () => { `ods-link[href="${kms.swaggerEndpoint}"][label="${kms.swaggerEndpoint}"]`, ), ).toBeVisible(); + + const downloadLink = await getOdsButtonByLabel({ + container, + label: 'key_management_service_dashboard_button_label_download_ca', + isLink: true, + }); + + expect(downloadLink).toBeVisible(); }); }); }); diff --git a/packages/manager/apps/key-management-service/src/components/layout-helpers/Dashboard/GeneralInformationsTiles/RestApiTile.tsx b/packages/manager/apps/key-management-service/src/components/layout-helpers/Dashboard/GeneralInformationsTiles/RestApiTile.tsx index c1039d0c5801..e3c350425c70 100644 --- a/packages/manager/apps/key-management-service/src/components/layout-helpers/Dashboard/GeneralInformationsTiles/RestApiTile.tsx +++ b/packages/manager/apps/key-management-service/src/components/layout-helpers/Dashboard/GeneralInformationsTiles/RestApiTile.tsx @@ -13,6 +13,7 @@ import { } from '@ovh-ux/manager-react-shell-client'; import { OKMS } from '@/types/okms.type'; import { REST_ENDPOINT_LABEL, SWAGGER_UI_LABEL } from './RestApiTile.constants'; +import { DownloadKmsPublicCaLink } from '@/components/dashboard/downloadKmsPublicCaLink/DownloadKmsPublicCaLink'; type RestApiTileProps = { okmsData?: OKMS; @@ -26,7 +27,10 @@ const RestApiTile = ({ okmsData }: RestApiTileProps) => { id: 'restApi', label: REST_ENDPOINT_LABEL, value: ( - +
+ + +
), }, { diff --git a/packages/manager/apps/key-management-service/src/data/api/okms.ts b/packages/manager/apps/key-management-service/src/data/api/okms.ts index f5f9865186f8..9cfc267b4a57 100644 --- a/packages/manager/apps/key-management-service/src/data/api/okms.ts +++ b/packages/manager/apps/key-management-service/src/data/api/okms.ts @@ -1,96 +1,5 @@ -import { ColumnSort } from '@ovh-ux/manager-react-components'; -import { - fetchIcebergV2, - fetchIcebergV6, - apiClient, -} from '@ovh-ux/manager-core-api'; -import { OKMS } from '@/types/okms.type'; -import { defaultCompareFunction } from '@/data/api/utils'; - -type Response = unknown; -type Uuid = unknown; - -export type GetiamPolicyListParams = { - /** Pagination cursor */ - 'X-Pagination-Cursor': string; - /** Add extra information about resources in output */ - details: boolean; - /** Filter on the readOnly attribute */ - readOnly: boolean; -}; - -export const getiamPolicyListQueryKey = ['get/iam/policy']; - -/** - * : Retrieve all policies - */ -export const getiamPolicyList = async ( - params: GetiamPolicyListParams, -): Promise => apiClient.v2.get('/iam/policy', { data: params }); - -export type GetiamPolicyPolicyIdParams = { - /** Add extra information about resources in output */ - details: boolean; - /** Policy ID */ - policyId?: Uuid; -}; - -export const getiamPolicyPolicyIdQueryKey = ( - params: GetiamPolicyPolicyIdParams, -) => [`get/iam/policy/${params.policyId}`]; - -/** - * : Retrieve the given policy - */ -export const getiamPolicyPolicyId = async ( - params: GetiamPolicyPolicyIdParams, -): Promise => - apiClient.v2.get(`/iam/policy/${params.policyId}`, { data: params }); - -/** - * Get listing with iceberg V6 - */ -export const getListingIcebergV6 = async ({ - pageSize, - page, -}: { - pageSize: number; - page: number; -}) => { - try { - const List = await fetchIcebergV6({ - route: '/iam/policy', - pageSize, - page, - }).then(({ data, status, totalCount }) => ({ data, status, totalCount })); - return List; - } catch (error) { - return null; - } -}; - -/** - * Get listing with iceberg V2 - */ - -export const getListingIcebergV2 = async ({ - pageSize, - cursor, -}: { - pageSize: number; - cursor?: string; -}) => { - try { - const List = await fetchIcebergV2({ - route: '/iam/policy', - pageSize, - cursor, - }).then(({ data, status, cursorNext }) => ({ data, status, cursorNext })); - return List; - } catch (error) { - return null; - } -}; +import apiClient, { ApiResponse } from '@ovh-ux/manager-core-api'; +import { OKMS, OkmsPublicCa } from '@/types/okms.type'; export const getOkmsResourceQueryKey = (okmsId: string) => [ `get/okms/resource/${okmsId}`, @@ -98,17 +7,17 @@ export const getOkmsResourceQueryKey = (okmsId: string) => [ export const getOkmsServicesResourceListQueryKey = ['get/okms/resource']; -export const sortOKMS = (okms: OKMS[], sorting: ColumnSort): OKMS[] => { - const data = [...okms]; - - if (sorting) { - const { id: sortKey, desc } = sorting; - - data.sort(defaultCompareFunction(sortKey as keyof OKMS)); - if (desc) { - data.reverse(); - } - } +export const getOkmsResource = async ( + okmsId: string, +): Promise<{ data: OKMS }> => { + return apiClient.v2.get(`okms/resource/${okmsId}`); +}; +export const getOkmsPublicCa = async ( + okmsId: string, +): Promise => { + const { data }: ApiResponse = await apiClient.v2.get( + `okms/resource/${okmsId}?publicCA=true`, + ); return data; }; diff --git a/packages/manager/apps/key-management-service/src/data/hooks/useOKMS.ts b/packages/manager/apps/key-management-service/src/data/hooks/useOkms.ts similarity index 61% rename from packages/manager/apps/key-management-service/src/data/hooks/useOKMS.ts rename to packages/manager/apps/key-management-service/src/data/hooks/useOkms.ts index 6e254e0ed428..1ebd530dbb99 100644 --- a/packages/manager/apps/key-management-service/src/data/hooks/useOKMS.ts +++ b/packages/manager/apps/key-management-service/src/data/hooks/useOkms.ts @@ -1,24 +1,17 @@ import { useQuery } from '@tanstack/react-query'; -import apiClient from '@ovh-ux/manager-core-api'; import { useResourcesIcebergV2 } from '@ovh-ux/manager-react-components'; - import { OKMS } from '@/types/okms.type'; import { ErrorResponse } from '@/types/api.type'; import { + getOkmsResource, getOkmsResourceQueryKey, getOkmsServicesResourceListQueryKey, -} from '../api/okms'; - -export const getOKMSResource = async ( - okmsId: string, -): Promise<{ data: OKMS }> => { - return apiClient.v2.get(`okms/resource/${okmsId}`); -}; +} from '@/data/api/okms'; -export const useOKMSById = (okmsId: string) => { +export const useOkmsById = (okmsId: string) => { return useQuery<{ data: OKMS }, ErrorResponse>({ queryKey: getOkmsResourceQueryKey(okmsId), - queryFn: () => getOKMSResource(okmsId), + queryFn: () => getOkmsResource(okmsId), retry: false, ...{ keepPreviousData: true, @@ -26,7 +19,7 @@ export const useOKMSById = (okmsId: string) => { }); }; -export const useOKMSList = ({ pageSize }: { pageSize?: number }) => +export const useOkmsList = ({ pageSize }: { pageSize?: number }) => useResourcesIcebergV2({ route: '/okms/resource', queryKey: getOkmsServicesResourceListQueryKey, diff --git a/packages/manager/apps/key-management-service/src/data/hooks/useOrderCatalogOKMS.ts b/packages/manager/apps/key-management-service/src/data/hooks/useOrderCatalogOkms.ts similarity index 89% rename from packages/manager/apps/key-management-service/src/data/hooks/useOrderCatalogOKMS.ts rename to packages/manager/apps/key-management-service/src/data/hooks/useOrderCatalogOkms.ts index 432fee021caf..ad2236d2235a 100644 --- a/packages/manager/apps/key-management-service/src/data/hooks/useOrderCatalogOKMS.ts +++ b/packages/manager/apps/key-management-service/src/data/hooks/useOrderCatalogOkms.ts @@ -7,7 +7,7 @@ export type OrderCatalogProps = { ovhSubsidiary: string; }; -export const useOrderCatalogOKMS = (ovhSubsidiary: string) => { +export const useOrderCatalogOkms = (ovhSubsidiary: string) => { return useQuery({ queryKey: ['order/catalog/public/okms', ovhSubsidiary], queryFn: () => getOrderCatalogOKMS(ovhSubsidiary), diff --git a/packages/manager/apps/key-management-service/src/mocks/credentials/credentials.handler.ts b/packages/manager/apps/key-management-service/src/mocks/credentials/credentials.handler.ts index 1e5eb78dfdb8..fa4ecc3b582c 100644 --- a/packages/manager/apps/key-management-service/src/mocks/credentials/credentials.handler.ts +++ b/packages/manager/apps/key-management-service/src/mocks/credentials/credentials.handler.ts @@ -8,7 +8,7 @@ export type GetCredentialsMockParams = { }; const findCredentialByID = (params: PathParams) => - credentialMock.find(({ id }) => id === params.id); + credentialMock.find(({ id }) => id === params.credentialId); export const getCredentialsMock = ({ isCredentialKO, @@ -23,7 +23,7 @@ export const getCredentialsMock = ({ message: 'credentials error', }, } - : (_: unknown, params: PathParams) => findCredentialByID(params), + : credentialMock.slice(0, nbCredential), status: isCredentialKO ? 500 : 200, api: 'v2', }, @@ -31,8 +31,27 @@ export const getCredentialsMock = ({ url: '/okms/resource/:okmsId/credential/:credentialId', response: isCredentialKO ? { message: 'credential error' } - : credentialMock.slice(0, nbCredential), + : (_: unknown, params: PathParams) => findCredentialByID(params), status: isCredentialKO ? 500 : 200, api: 'v2', }, ]; + +export type DeleteCredentialsMockParams = { + isCredentialDeleteKO?: boolean; +}; + +export const deleteCredentialMock = ({ + isCredentialDeleteKO, +}: DeleteCredentialsMockParams): Handler[] => [ + { + url: '/okms/resource/:okmsId/credential/:credentialId', + method: 'delete', + response: { + status: isCredentialDeleteKO ? 500 : 200, + data: {}, + }, + status: isCredentialDeleteKO ? 500 : 200, + api: 'v2', + }, +]; diff --git a/packages/manager/apps/key-management-service/src/pages/credential/Credential.page.spec.tsx b/packages/manager/apps/key-management-service/src/pages/credential/Credential.page.spec.tsx new file mode 100644 index 000000000000..a426cb02374d --- /dev/null +++ b/packages/manager/apps/key-management-service/src/pages/credential/Credential.page.spec.tsx @@ -0,0 +1,82 @@ +import { screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { + WAIT_FOR_DEFAULT_OPTIONS, + assertTextVisibility, + getOdsButtonByLabel, +} from '@ovh-ux/manager-core-test-utils'; +import { renderTestApp } from '@/utils/tests/renderTestApp'; +import { labels } from '@/utils/tests/init.i18n'; +import { okmsMock } from '@/mocks/kms/okms.mock'; +import { credentialMock } from '@/mocks/credentials/credentials.mock'; +import { FEATURES } from '@/utils/feature-availability/feature-availability.constants'; + +const mockPageUrl = `/${okmsMock[0].id}/credentials/${credentialMock[0].id}`; + +describe('Credential dashboard test suite', () => { + it('should display an error if the API is KO', async () => { + await renderTestApp(mockPageUrl, { isCredentialKO: true }); + + await waitFor( + () => expect(screen.getByAltText('OOPS')).toBeInTheDocument(), + WAIT_FOR_DEFAULT_OPTIONS, + ); + }); + + it('should display the credential dashboard page', async () => { + await renderTestApp(mockPageUrl, { feature: FEATURES.LOGS }); + + await waitFor(() => { + expect( + screen.getByText( + labels.credentials.key_management_service_credential_dashboard_name, + ), + ).toBeVisible(); + }, WAIT_FOR_DEFAULT_OPTIONS); + }); + + it(`should navigate back to the credentials list on click on ${labels.credentials.key_management_service_credential_dashboard_backlink}`, async () => { + const user = userEvent.setup(); + const { container } = await renderTestApp(mockPageUrl); + + const backLink = await getOdsButtonByLabel({ + container, + label: + labels.credentials.key_management_service_credential_dashboard_backlink, + isLink: true, + }); + + user.click(backLink); + + await assertTextVisibility( + labels.credentials.key_management_service_credential_headline, + ); + }); + + it(`should navigate to the identity page on click on ${labels.credentials.key_management_service_credential_identities} `, async () => { + const identitiesTabLabel = + labels.credentials + .key_management_service_credential_dashboard_tab_identities; + const userTitleLabel = + labels.credentials + .key_management_service_credential_identities_user_title; + const userGroupsTitleLabel = + labels.credentials + .key_management_service_credential_identities_usergroup_title; + + const user = userEvent.setup(); + await renderTestApp(mockPageUrl); + + await waitFor( + () => expect(screen.getByText(identitiesTabLabel)).toBeEnabled(), + WAIT_FOR_DEFAULT_OPTIONS, + ); + + user.click(screen.getByText(identitiesTabLabel)); + + await waitFor(() => { + expect(screen.getByText(userTitleLabel)).toBeVisible(); + expect(screen.getByText(userGroupsTitleLabel)).toBeVisible(); + }, WAIT_FOR_DEFAULT_OPTIONS); + }); +}); diff --git a/packages/manager/apps/key-management-service/src/pages/credential/Credential.page.tsx b/packages/manager/apps/key-management-service/src/pages/credential/Credential.page.tsx index 915f4e0d68a2..3be2d4c5bb23 100644 --- a/packages/manager/apps/key-management-service/src/pages/credential/Credential.page.tsx +++ b/packages/manager/apps/key-management-service/src/pages/credential/Credential.page.tsx @@ -29,7 +29,7 @@ import { ROUTES_URLS } from '@/routes/routes.constants'; import Breadcrumb from '@/components/Breadcrumb/Breadcrumb'; import KmsGuidesHeader from '@/components/Guide/KmsGuidesHeader'; import { BreadcrumbItem } from '@/hooks/breadcrumb/useBreadcrumb'; -import { useOKMSById } from '@/data/hooks/useOKMS'; +import { useOkmsById } from '@/data/hooks/useOkms'; import { OKMS } from '@/types/okms.type'; import { OkmsCredential } from '@/types/okmsCredential.type'; @@ -49,7 +49,7 @@ const CredentialDashboard = () => { const location = useLocation(); const { okmsId, credentialId } = useParams(); - const { data: okms, isLoading: isLoadingKms, error: errorKms } = useOKMSById( + const { data: okms, isLoading: isLoadingKms, error: errorKms } = useOkmsById( okmsId, ); diff --git a/packages/manager/apps/key-management-service/src/pages/credential/create/CreateCredential.page.tsx b/packages/manager/apps/key-management-service/src/pages/credential/create/CreateCredential.page.tsx index 1f59eace7dd4..c7b00378fc65 100644 --- a/packages/manager/apps/key-management-service/src/pages/credential/create/CreateCredential.page.tsx +++ b/packages/manager/apps/key-management-service/src/pages/credential/create/CreateCredential.page.tsx @@ -11,7 +11,7 @@ import Breadcrumb from '@/components/Breadcrumb/Breadcrumb'; import KmsGuidesHeader from '@/components/Guide/KmsGuidesHeader'; import { BreadcrumbItem } from '@/hooks/breadcrumb/useBreadcrumb'; import { ROUTES_URLS } from '@/routes/routes.constants'; -import { useOKMSById } from '@/data/hooks/useOKMS'; +import { useOkmsById } from '@/data/hooks/useOkms'; import Loading from '@/components/Loading/Loading'; import { IdentityDataProvider } from '@/hooks/credential/useIdentityData'; import { useCreateOkmsCredential } from '@/data/hooks/useCreateOkmsCredential'; @@ -24,7 +24,7 @@ const CreateCredential = () => { const navigate = useNavigate(); const { trackPage } = useOvhTracking(); const { okmsId } = useParams(); - const { data: okms, isLoading, error } = useOKMSById(okmsId); + const { data: okms, isLoading, error } = useOkmsById(okmsId); const { t } = useTranslation('key-management-service/credential'); const [step, setStep] = useState(1); const [name, setName] = useState(null); diff --git a/packages/manager/apps/key-management-service/src/pages/credential/generalInformations/delete/DeleteCredentialModal.page.spec.tsx b/packages/manager/apps/key-management-service/src/pages/credential/generalInformations/delete/DeleteCredentialModal.page.spec.tsx new file mode 100644 index 000000000000..084b289fa589 --- /dev/null +++ b/packages/manager/apps/key-management-service/src/pages/credential/generalInformations/delete/DeleteCredentialModal.page.spec.tsx @@ -0,0 +1,126 @@ +import { vi } from 'vitest'; +import { screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { + getOdsButtonByLabel, + changeOdsInputValue, + assertOdsModalVisibility, + WAIT_FOR_DEFAULT_OPTIONS, +} from '@ovh-ux/manager-core-test-utils'; +import * as router from 'react-router-dom'; +import { renderTestApp } from '@/utils/tests/renderTestApp'; +import { okmsMock } from '@/mocks/kms/okms.mock'; +import { credentialMock } from '@/mocks/credentials/credentials.mock'; +import { labels } from '@/utils/tests/init.i18n'; + +const mockCredentialItem = credentialMock[0]; +const mockPageUrl = `/${okmsMock[0].id}/credentials/${mockCredentialItem.id}/delete`; +const mockCredentialListPageUrl = `/${okmsMock[0].id}/credentials`; + +const deleteModalTitleLabel = + labels.credentials.key_management_service_credential_delete_modal_headline; + +describe('Credential delete modal test suite', () => { + const mockNavigate = vi.fn(); + + beforeEach(() => { + mockNavigate.mockReset(); + vi.spyOn(router, 'useNavigate').mockImplementation(() => mockNavigate); + }); + + test('should display the delete modal', async () => { + const { container } = await renderTestApp(mockPageUrl); + + // Check modal is opened + await waitFor(() => { + assertOdsModalVisibility({ container, isVisible: true }); + expect(screen.getByText(deleteModalTitleLabel)).toBeVisible(); + }, WAIT_FOR_DEFAULT_OPTIONS); + }); + + test('should navigate and show a notification after successful deletion', async () => { + const user = userEvent.setup(); + const { container } = await renderTestApp(mockPageUrl); + + // Wait for modal to open + await waitFor(() => { + expect(screen.getByText(deleteModalTitleLabel)).toBeVisible(); + }, WAIT_FOR_DEFAULT_OPTIONS); + + await changeOdsInputValue({ + inputLabel: + labels.credentials + .key_management_service_credential_delete_modal_input_label, + inputValue: 'TERMINATE', + }); + + const submitButton = await getOdsButtonByLabel({ + container, + label: 'Résilier', // Label from MRC + disabled: false, + }); + + user.click(submitButton); + + // Check navigation + await waitFor(() => { + expect(mockNavigate).toHaveBeenCalledWith(mockCredentialListPageUrl, { + state: { deletingCredentialId: mockCredentialItem.id }, + }); + }, WAIT_FOR_DEFAULT_OPTIONS); + + // Check notification + await waitFor(() => { + expect( + screen.getByText( + labels.credentials.key_management_service_credential_delete_success, + ), + ).toBeVisible(); + }); + }); + + test('should navigate and show a notification after failed deletion', async () => { + const user = userEvent.setup(); + const { container } = await renderTestApp(mockPageUrl, { + isCredentialDeleteKO: true, + }); + + // Wait for modal to open + await waitFor(() => { + expect(screen.getByText(deleteModalTitleLabel)).toBeVisible(); + }, WAIT_FOR_DEFAULT_OPTIONS); + + await changeOdsInputValue({ + inputLabel: + labels.credentials + .key_management_service_credential_delete_modal_input_label, + inputValue: 'TERMINATE', + }); + + const submitButton = await getOdsButtonByLabel({ + container, + label: 'Résilier', // Label from MRC + timeout: WAIT_FOR_DEFAULT_OPTIONS.timeout, + disabled: false, + }); + + user.click(submitButton); + + // Check navigation + await waitFor( + () => expect(mockNavigate).toHaveBeenCalledWith('..'), + WAIT_FOR_DEFAULT_OPTIONS, + ); + + // Check notification + await waitFor(() => { + const notificationLabel = labels.credentials.key_management_service_credential_delete_error.replace( + ' {{error}}', + '', + ); + expect( + screen.getByText((content) => content.includes(notificationLabel)), + ).toBeVisible(); + }); + }); +}); diff --git a/packages/manager/apps/key-management-service/src/pages/credential/generalInformations/generalInformations.page.spec.tsx b/packages/manager/apps/key-management-service/src/pages/credential/generalInformations/generalInformations.page.spec.tsx new file mode 100644 index 000000000000..d03dc1d2b754 --- /dev/null +++ b/packages/manager/apps/key-management-service/src/pages/credential/generalInformations/generalInformations.page.spec.tsx @@ -0,0 +1,76 @@ +import { screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { + getOdsButtonByLabel, + assertOdsModalVisibility, + WAIT_FOR_DEFAULT_OPTIONS, +} from '@ovh-ux/manager-core-test-utils'; +import { renderTestApp } from '@/utils/tests/renderTestApp'; +import { okmsMock } from '@/mocks/kms/okms.mock'; +import { credentialMock } from '@/mocks/credentials/credentials.mock'; +import { labels } from '@/utils/tests/init.i18n'; + +const mockCredentialItem = credentialMock[0]; +const mockPageUrl = `/${okmsMock[0].id}/credentials/${mockCredentialItem.id}`; + +describe('Credential general informations test suite', () => { + test('should display the credentials details', async () => { + const { container } = await renderTestApp(mockPageUrl); + + const titleLabel = + labels.credentials + .key_management_service_credential_dashboard_tile_general_informations; + + await waitFor(() => { + expect(screen.getByText(titleLabel)).toBeVisible(); + expect(screen.getAllByText(mockCredentialItem.name)[0]).toBeVisible(); + expect( + screen.getAllByText(mockCredentialItem.description)[0], + ).toBeVisible(); + + const clipboardElement = container.querySelector( + `[value="${mockCredentialItem.id}"]`, + ); + expect(clipboardElement).toBeVisible(); + }, WAIT_FOR_DEFAULT_OPTIONS); + }); + + test('should navigate to the delete modal on click on delete button', async () => { + const user = userEvent.setup(); + const { container } = await renderTestApp(mockPageUrl); + + const deleteButtonLabel = + labels.credentials.key_management_service_credential_delete; + const deleteModalTitleLabel = + labels.credentials + .key_management_service_credential_delete_modal_headline; + + // Check modal is closed + await waitFor(async () => { + assertOdsModalVisibility({ container, isVisible: false }); + }, WAIT_FOR_DEFAULT_OPTIONS); + + // Wait for the delete button to be enabled by iam rights + await waitFor(async () => { + await getOdsButtonByLabel({ + container, + label: deleteButtonLabel, + disabled: false, + }); + }, WAIT_FOR_DEFAULT_OPTIONS); + + // Click button + const deleteButton = await getOdsButtonByLabel({ + container, + label: deleteButtonLabel, + disabled: false, + }); + user.click(deleteButton); + + // Check modal is opened + await waitFor(() => { + assertOdsModalVisibility({ container, isVisible: true }); + expect(screen.getByText(deleteModalTitleLabel)).toBeVisible(); + }, WAIT_FOR_DEFAULT_OPTIONS); + }); +}); diff --git a/packages/manager/apps/key-management-service/src/pages/dashboard/generalInformations/GeneralInformations.tsx b/packages/manager/apps/key-management-service/src/pages/dashboard/generalInformations/GeneralInformations.tsx index 26cfac3969c8..921e996ff795 100644 --- a/packages/manager/apps/key-management-service/src/pages/dashboard/generalInformations/GeneralInformations.tsx +++ b/packages/manager/apps/key-management-service/src/pages/dashboard/generalInformations/GeneralInformations.tsx @@ -4,7 +4,7 @@ import { DashboardGridLayout, useServiceDetails, } from '@ovh-ux/manager-react-components'; -import { useOKMSById } from '@/data/hooks/useOKMS'; +import { useOkmsById } from '@/data/hooks/useOkms'; import { ROUTES_URLS } from '@/routes/routes.constants'; import InformationsTile from '@/components/layout-helpers/Dashboard/GeneralInformationsTiles/InformationsTile'; import BillingInformationsTile from '@/components/layout-helpers/Dashboard/GeneralInformationsTiles/BillingInformationsTile'; @@ -14,7 +14,7 @@ import RestApiTile from '@/components/layout-helpers/Dashboard/GeneralInformatio function GeneralInformationsTab() { const { okmsId } = useParams(); - const { data: okms, error, isLoading: isOkmsLoading } = useOKMSById(okmsId); + const { data: okms, error, isLoading: isOkmsLoading } = useOkmsById(okmsId); const { data: okmsService, isLoading: isOkmsServiceLoading, diff --git a/packages/manager/apps/key-management-service/src/pages/dashboard/index.tsx b/packages/manager/apps/key-management-service/src/pages/dashboard/index.tsx index 8e593a9e6ced..139be3c5d635 100644 --- a/packages/manager/apps/key-management-service/src/pages/dashboard/index.tsx +++ b/packages/manager/apps/key-management-service/src/pages/dashboard/index.tsx @@ -19,7 +19,7 @@ import { ROUTES_URLS } from '@/routes/routes.constants'; import { BreadcrumbItem } from '@/hooks/breadcrumb/useBreadcrumb'; import { getOkmsResourceQueryKey } from '@/data/api/okms'; import { OKMS } from '@/types/okms.type'; -import { useOKMSById } from '@/data/hooks/useOKMS'; +import { useOkmsById } from '@/data/hooks/useOkms'; import { CHANGELOG_LINKS, SERVICE_KEYS_LABEL } from '@/constants'; import KmsTabs, { KmsTabProps, @@ -42,7 +42,7 @@ export default function DashboardPage() { isLoading: isOkmsLoading, isError: isOkmsError, error: okmsError, - } = useOKMSById(okmsId); + } = useOkmsById(okmsId); const { data: okmsServiceInfos, diff --git a/packages/manager/apps/key-management-service/src/pages/dashboard/logs/Logs.page.spec.tsx b/packages/manager/apps/key-management-service/src/pages/dashboard/logs/Logs.page.spec.tsx index 8dc3b78e9a78..81ac2defd9ce 100644 --- a/packages/manager/apps/key-management-service/src/pages/dashboard/logs/Logs.page.spec.tsx +++ b/packages/manager/apps/key-management-service/src/pages/dashboard/logs/Logs.page.spec.tsx @@ -22,8 +22,8 @@ vi.mock('react-router-dom', async (importOriginal) => { }; }); -vi.mock('@/data/hooks/useOKMS', () => ({ - useOKMSById: (id: string) => +vi.mock('@/data/hooks/useOkms', () => ({ + useOkmsById: (id: string) => ({ data: { data: { id, iam: { urn: `urn:${id}` } } } } as UseQueryResult< ApiResponse >), diff --git a/packages/manager/apps/key-management-service/src/pages/dashboard/logs/Logs.page.tsx b/packages/manager/apps/key-management-service/src/pages/dashboard/logs/Logs.page.tsx index a314b0119ede..9a23078164dd 100644 --- a/packages/manager/apps/key-management-service/src/pages/dashboard/logs/Logs.page.tsx +++ b/packages/manager/apps/key-management-service/src/pages/dashboard/logs/Logs.page.tsx @@ -5,12 +5,12 @@ import { } from '@ovh-ux/manager-react-components'; import React from 'react'; import { useParams } from 'react-router-dom'; -import { useOKMSById } from '@/data/hooks/useOKMS'; +import { useOkmsById } from '@/data/hooks/useOkms'; import { FEATURES } from '@/utils/feature-availability/feature-availability.constants'; export default function KmsLogs() { const { okmsId } = useParams(); - const { data: okms } = useOKMSById(okmsId); + const { data: okms } = useOkmsById(okmsId); const { data: features, isLoading } = useFeatureAvailability([FEATURES.LOGS]); diff --git a/packages/manager/apps/key-management-service/src/pages/listing/index.tsx b/packages/manager/apps/key-management-service/src/pages/listing/index.tsx index 0bad1e303fe9..ae65b5f9b3cd 100644 --- a/packages/manager/apps/key-management-service/src/pages/listing/index.tsx +++ b/packages/manager/apps/key-management-service/src/pages/listing/index.tsx @@ -21,7 +21,7 @@ import { PageLocation, useOvhTracking, } from '@ovh-ux/manager-react-shell-client'; -import { useOKMSList } from '@/data/hooks/useOKMS'; +import { useOkmsList } from '@/data/hooks/useOkms'; import { ROUTES_URLS } from '@/routes/routes.constants'; import { DatagridCellId, @@ -96,7 +96,7 @@ export default function Listing() { isError, isLoading, status, - } = useOKMSList({ + } = useOkmsList({ pageSize: 10, }); diff --git a/packages/manager/apps/key-management-service/src/pages/serviceKey/CreateKey.page.tsx b/packages/manager/apps/key-management-service/src/pages/serviceKey/CreateKey.page.tsx index d32384383091..0ce9923f5237 100644 --- a/packages/manager/apps/key-management-service/src/pages/serviceKey/CreateKey.page.tsx +++ b/packages/manager/apps/key-management-service/src/pages/serviceKey/CreateKey.page.tsx @@ -30,7 +30,7 @@ import { import { ServiceKeyNameErrorsType } from '@/utils/serviceKey/validateServiceKeyName'; import { useOkmsServiceKeyReference } from '@/data/hooks/useOkmsReferenceServiceKey'; import { useCreateOkmsServiceKey } from '@/data/hooks/useCreateOkmsServiceKey'; -import { useOKMSById } from '@/data/hooks/useOKMS'; +import { useOkmsById } from '@/data/hooks/useOkms'; import { BreadcrumbItem } from '@/hooks/breadcrumb/useBreadcrumb'; import KmsGuidesHeader from '@/components/Guide/KmsGuidesHeader'; import Breadcrumb from '@/components/Breadcrumb/Breadcrumb'; @@ -52,7 +52,7 @@ export default function CreateKey() { isLoading: okmsIsLoading, error: okmsError, refetch: refetchOkms, - } = useOKMSById(okmsId); + } = useOkmsById(okmsId); const { data: servicekeyReference, diff --git a/packages/manager/apps/key-management-service/src/pages/serviceKey/ProtectionLevelSection.component.tsx b/packages/manager/apps/key-management-service/src/pages/serviceKey/ProtectionLevelSection.component.tsx index 7a4ed94b02fa..377c738df715 100644 --- a/packages/manager/apps/key-management-service/src/pages/serviceKey/ProtectionLevelSection.component.tsx +++ b/packages/manager/apps/key-management-service/src/pages/serviceKey/ProtectionLevelSection.component.tsx @@ -9,14 +9,14 @@ import { ODS_CARD_COLOR, ODS_TEXT_PRESET } from '@ovhcloud/ods-components'; import { OdsText, OdsCard, OdsSkeleton } from '@ovhcloud/ods-components/react'; import { useTranslation } from 'react-i18next'; import { ShellContext } from '@ovh-ux/manager-react-shell-client'; -import { useOrderCatalogOKMS } from '@/data/hooks/useOrderCatalogOKMS'; +import { useOrderCatalogOkms } from '@/data/hooks/useOrderCatalogOkms'; export const ProtectionLevelSection: React.FC = () => { const { t } = useTranslation('key-management-service/serviceKeys'); const { environment } = React.useContext(ShellContext); const { ovhSubsidiary } = environment.getUser(); const userLocale = environment.getUserLocale(); - const { data: catalog, isLoading, isPending } = useOrderCatalogOKMS( + const { data: catalog, isLoading, isPending } = useOrderCatalogOkms( ovhSubsidiary, ); const [pricingData, setPricingData] = useState({ diff --git a/packages/manager/apps/key-management-service/src/pages/serviceKey/ServiceKey.page.tsx b/packages/manager/apps/key-management-service/src/pages/serviceKey/ServiceKey.page.tsx index ce62afdacdca..ef919c8c80fc 100644 --- a/packages/manager/apps/key-management-service/src/pages/serviceKey/ServiceKey.page.tsx +++ b/packages/manager/apps/key-management-service/src/pages/serviceKey/ServiceKey.page.tsx @@ -35,7 +35,7 @@ import { ServiceKeyType } from '@/components/serviceKey/serviceKeyType/serviceKe import { ROUTES_URLS } from '@/routes/routes.constants'; import { getOkmsServiceKeyResourceQueryKey } from '@/data/api/okmsServiceKey'; import { BreadcrumbItem } from '@/hooks/breadcrumb/useBreadcrumb'; -import { useOKMSById } from '@/data/hooks/useOKMS'; +import { useOkmsById } from '@/data/hooks/useOkms'; import ServiceKeyStateActions from '@/components/serviceKey/serviceKeyStateActions/ServiceKeyStateActions.component'; import { getOkmsResourceQueryKey } from '@/data/api/okms'; import { kmsIamActions } from '@/utils/iam/iam.constants'; @@ -53,7 +53,7 @@ export default function Key() { data: okms, isLoading: isLoadingOkms, error: okmsError, - } = useOKMSById(okmsId); + } = useOkmsById(okmsId); const { data: serviceKey, diff --git a/packages/manager/apps/key-management-service/src/types/okms.type.ts b/packages/manager/apps/key-management-service/src/types/okms.type.ts index 26433280ec69..f5a3552861fb 100644 --- a/packages/manager/apps/key-management-service/src/types/okms.type.ts +++ b/packages/manager/apps/key-management-service/src/types/okms.type.ts @@ -12,6 +12,11 @@ export type OKMS = { iam: IAM; }; +export type OkmsPublicCa = { + publicCA: string; + publicRsaCA: string; +}; + export type IAM = { displayName: string; id: string; diff --git a/packages/manager/apps/key-management-service/src/utils/dom/download.ts b/packages/manager/apps/key-management-service/src/utils/dom/download.ts new file mode 100644 index 000000000000..ae3965053369 --- /dev/null +++ b/packages/manager/apps/key-management-service/src/utils/dom/download.ts @@ -0,0 +1,17 @@ +import { saveAs } from 'file-saver'; + +/** + * Initiate a download of a text file with the given filename and text content. + */ +export function initiateTextFileDownload({ + filename, + text, +}: { + filename: string; + text: string; +}) { + const blob = new Blob([text], { + type: 'text/plain;charset=utf-8', + }); + saveAs(blob, filename); +} diff --git a/packages/manager/apps/key-management-service/src/utils/tests/renderTestApp.tsx b/packages/manager/apps/key-management-service/src/utils/tests/renderTestApp.tsx index d83786c14be6..a3a4ad797fa1 100644 --- a/packages/manager/apps/key-management-service/src/utils/tests/renderTestApp.tsx +++ b/packages/manager/apps/key-management-service/src/utils/tests/renderTestApp.tsx @@ -30,6 +30,8 @@ import { import { getCredentialsMock, GetCredentialsMockParams, + deleteCredentialMock, + DeleteCredentialsMockParams, } from '@/mocks/credentials/credentials.handler'; import { kmsServicesMock } from '@/mocks/services/services.mock'; import { @@ -57,7 +59,8 @@ export const renderTestApp = async ( GetFeatureAvailabilityMocksParams & GetCatalogKmsMocksParams & GetReferenceMockParams & - GetIamAuthorizationMockParams = {}, + GetIamAuthorizationMockParams & + DeleteCredentialsMockParams = {}, ) => { global.server?.resetHandlers( ...toMswHandlers([ @@ -70,6 +73,7 @@ export const renderTestApp = async ( ...getFeatureAvailabilityMocks(mockParams), ...getCatalogKmsMocks(mockParams), ...getReferenceMock(mockParams), + ...deleteCredentialMock(mockParams), ]), );