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),
]),
);