From e92b6fe37e1091112b5531ce48ec677ad3313b92 Mon Sep 17 00:00:00 2001 From: Tristan WAGNER Date: Wed, 27 Nov 2024 16:46:08 +0100 Subject: [PATCH] feat(zimbra): initialize diagnostics page ref: MANAGER-16089 Signed-off-by: Tristan WAGNER --- packages/manager/apps/zimbra/package.json | 4 +- .../dashboard/Messages_fr_FR.json | 2 +- .../translations/domains/Messages_fr_FR.json | 3 +- .../domains/diagnostic/Messages_fr_FR.json | 64 +-- .../apps/zimbra/src/api/_mock_/domain.ts | 127 ++++- .../manager/apps/zimbra/src/api/domain/api.ts | 34 +- .../manager/apps/zimbra/src/api/domain/key.ts | 12 + .../apps/zimbra/src/api/domain/type.ts | 230 +++++++- .../zimbra/src/components/DiagnosticBadge.tsx | 79 --- .../layout-helpers/Dashboard/Dashboard.tsx | 5 +- .../layout-helpers/Dashboard/TabsPanel.tsx | 3 +- .../src/hooks/__test__/useDomain.spec.tsx | 9 +- .../__test__/useDomainDiagnostic.spec.tsx | 23 + .../__test__/useDomainsDiagnostic.spec.tsx | 23 + .../apps/zimbra/src/hooks/useDomain.ts | 25 +- .../zimbra/src/hooks/useDomainDiagnostic.ts | 40 ++ .../apps/zimbra/src/hooks/useDomainZone.ts | 25 + .../zimbra/src/hooks/useDomainsDiagnostic.ts | 36 ++ packages/manager/apps/zimbra/src/index.scss | 24 + .../Domains/ActionButtonDomain.component.tsx | 16 + .../dashboard/Domains/Diagnostics.page.tsx | 501 ++++++++++++++++++ .../src/pages/dashboard/Domains/Domains.tsx | 50 +- .../ModalDiagnosticDnsRecord.component.tsx | 262 --------- .../Domains/ModalEditDomain.component.tsx | 6 +- .../dashboard/Domains/VerifyDomain.page.tsx | 2 +- .../__test__/ActionButtonDomain.spec.tsx | 38 +- .../Domains/__test__/Diagnostics.spec.tsx | 40 ++ .../ModalsDiagnosticDNSRecord.spec.tsx | 71 --- .../zimbra/src/routes/routes.constants.ts | 5 +- .../manager/apps/zimbra/src/routes/routes.tsx | 67 ++- .../apps/zimbra/src/tracking.constant.ts | 7 + .../zimbra/src/utils/iamAction.constants.ts | 4 + .../manager/apps/zimbra/src/utils/index.ts | 2 +- .../apps/zimbra/src/utils/test.setup.tsx | 4 + .../apps/zimbra/src/utils/test.utils.ts | 5 + 35 files changed, 1302 insertions(+), 546 deletions(-) delete mode 100644 packages/manager/apps/zimbra/src/components/DiagnosticBadge.tsx create mode 100644 packages/manager/apps/zimbra/src/hooks/__test__/useDomainDiagnostic.spec.tsx create mode 100644 packages/manager/apps/zimbra/src/hooks/__test__/useDomainsDiagnostic.spec.tsx create mode 100644 packages/manager/apps/zimbra/src/hooks/useDomainDiagnostic.ts create mode 100644 packages/manager/apps/zimbra/src/hooks/useDomainZone.ts create mode 100644 packages/manager/apps/zimbra/src/hooks/useDomainsDiagnostic.ts create mode 100644 packages/manager/apps/zimbra/src/pages/dashboard/Domains/Diagnostics.page.tsx delete mode 100644 packages/manager/apps/zimbra/src/pages/dashboard/Domains/ModalDiagnosticDnsRecord.component.tsx create mode 100644 packages/manager/apps/zimbra/src/pages/dashboard/Domains/__test__/Diagnostics.spec.tsx delete mode 100644 packages/manager/apps/zimbra/src/pages/dashboard/Domains/__test__/ModalsDiagnosticDNSRecord.spec.tsx create mode 100644 packages/manager/apps/zimbra/src/utils/test.utils.ts diff --git a/packages/manager/apps/zimbra/package.json b/packages/manager/apps/zimbra/package.json index dd0fb36bf2b9..9c54de7438ff 100644 --- a/packages/manager/apps/zimbra/package.json +++ b/packages/manager/apps/zimbra/package.json @@ -62,7 +62,7 @@ "vitest": "^2.1.2" }, "regions": [ - "CA", - "EU" + "EU", + "CA" ] } diff --git a/packages/manager/apps/zimbra/public/translations/dashboard/Messages_fr_FR.json b/packages/manager/apps/zimbra/public/translations/dashboard/Messages_fr_FR.json index afbc1dfdf4c7..1ba1f765e4c3 100644 --- a/packages/manager/apps/zimbra/public/translations/dashboard/Messages_fr_FR.json +++ b/packages/manager/apps/zimbra/public/translations/dashboard/Messages_fr_FR.json @@ -4,8 +4,8 @@ "zimbra_dashboard_domains_add": "Ajouter un domaine", "zimbra_dashboard_domains_edit": "Configuration du domaine", "zimbra_dashboard_domains_delete": "Supprimer un domaine", - "zimbra_dashboard_domains_diagnostic": "Diagnostique", "zimbra_dashboard_domains_verify": "Vérification", + "zimbra_dashboard_domains_diagnostics": "Diagnostics", "zimbra_dashboard_email_accounts": "Compte email", "zimbra_dashboard_email_accounts_add": "Créer un compte email", "zimbra_dashboard_email_accounts_edit": "Modifier le compte", diff --git a/packages/manager/apps/zimbra/public/translations/domains/Messages_fr_FR.json b/packages/manager/apps/zimbra/public/translations/domains/Messages_fr_FR.json index 76294a8b3f6b..1176adf7deb9 100644 --- a/packages/manager/apps/zimbra/public/translations/domains/Messages_fr_FR.json +++ b/packages/manager/apps/zimbra/public/translations/domains/Messages_fr_FR.json @@ -1,9 +1,7 @@ { "zimbra_domains_add_domain_title": "Ajouter un domaine", "zimbra_domains_datagrid_account_label": "Nombre de comptes", - "zimbra_domains_datagrid_diagnostic_label": "Diagnostique", "zimbra_domains_datagrid_diagnostic_configuration_ok": "Configuration OK", - "zimbra_domains_datagrid_diagnostic_tooltip_title": "Diagnostic {{ diagType }}", "zimbra_domains_datagrid_domain_label": "Domaine", "zimbra_domains_datagrid_organization_label": "Organisation", "zimbra_domains_datagrid_status_label": "Statut", @@ -13,5 +11,6 @@ "zimbra_domains_datagrid_cname_tooltip": "Pour confirmer que vous êtes bien le titulaire de ce domaine, veuillez créer un enregistrement CNAME:

{{cname}} vers ovh.com

Vous avez 48 heures pour compléter cette étape.", "zimbra_domains_tooltip_configure": "Configurer", "zimbra_domains_tooltip_delete": "Supprimer", + "zimbra_domains_tooltip_diagnostics": "Diagnostics", "zimbra_domains_tooltip_need_organization": "Veuillez d'abord configurer une organisation" } diff --git a/packages/manager/apps/zimbra/public/translations/domains/diagnostic/Messages_fr_FR.json b/packages/manager/apps/zimbra/public/translations/domains/diagnostic/Messages_fr_FR.json index ded8a920ff98..e00cb370ace1 100644 --- a/packages/manager/apps/zimbra/public/translations/domains/diagnostic/Messages_fr_FR.json +++ b/packages/manager/apps/zimbra/public/translations/domains/diagnostic/Messages_fr_FR.json @@ -1,37 +1,31 @@ { - "zimbra_domain_modal_diagnostic_guide": "guide", - "zimbra_domain_modal_diagnostic_srv_title": "Diagnostique SRV", - "zimbra_domain_modal_diagnostic_srv_content_header": "Pour faciliter la configuration des boites mails sur vos terminaux, veuillez ajouter cette configuration à votre DNS (consulter notre )", - "zimbra_domain_modal_diagnostic_srv_content_header_ovh_hosted_domain_part1": "La configuration SRV permet la configuration automatique de vos boites mails sur vos clients de messagerie.", - "zimbra_domain_modal_diagnostic_srv_content_header_ovh_hosted_domain_part2": "Votre domaine est hébergé chez OVH Cloud, nous allons procéder à la configuration de votre champ SRV.", - "zimbra_domain_modal_diagnostic_srv_domain": "Domaine", - "zimbra_domain_modal_diagnostic_fields": "Champs", - "zimbra_domain_modal_diagnostic_srv_action_validate": "Valider", - "zimbra_domain_modal_diagnostic_srv_action_cancel": "Annuler", - "zimbra_domain_modal_diagnostic_srv_action_close": "Fermer", - "zimbra_domain_modal_diagnostic_field_subdomain": "Sous domaine :", - "zimbra_domain_modal_diagnostic_field_priority": "Priorité :", - "zimbra_domain_modal_diagnostic_field_weight": "Poids :", - "zimbra_domain_modal_diagnostic_field_port": "Port :", - "zimbra_domain_modal_diagnostic_field_target": "Cible :", - "zimbra_domain_modal_diagnostic_mx_title": "Diagnostique MX", - "zimbra_domain_modal_diagnostic_mx_content_header_part1": "La configuration de l'enregistrement MX permet de recevoir vos emails dans vos boites mail.", - "zimbra_domain_modal_diagnostic_mx_content_header_part2": "Votre domaine n'étant pas géré par OVH Cloud, cette action doit être effectuée manuellement.", - "zimbra_domain_modal_diagnostic_mx_content_header_part3": "Veuillez créer les champs MX sur ce domaine en donnant les mêmes informations que ci-dessous.", - "zimbra_domain_modal_diagnostic_mx_content_header_ovh_hosted_domain_part1": "La configuration de l'enregistrement MX permet de recevoir vos emails dans vos boites mail.", - "zimbra_domain_modal_diagnostic_mx_content_header_ovh_hosted_domain_part2": "Votre domaine étant hébergé chez OVH Cloud, nous allons effectuer la configuration pour vous.", - "zimbra_domain_modal_diagnostic_mx_content_header_ovh_hosted_domain_part3": "Souhaitez-vous commencer à recevoir les mails du domaine {{domain}} sur ce service ?", - "zimbra_domain_modal_diagnostic_mx_content_footer": "En cas de problème, consultez la documentation de votre fournisseur de domaine.", - "zimbra_domain_modal_diagnostic_mx_action_validate": "Confirmer", - "zimbra_domain_modal_diagnostic_mx_action_test": "Tester", - "zimbra_domain_modal_diagnostic_mx_action_cancel": "Annuler", - "zimbra_domain_modal_diagnostic_mx_action_close": "Fermer", - "zimbra_domain_modal_diagnostic_spf_title": "Diagnostique SPF", - "zimbra_domain_modal_diagnostic_spf_content_header": "Si le domaine n'est pas géré par le même identifiant client que le service Exchange ou si votre domaine n'est pas hébergé chez OVHcloud, la configuration automatique n'est pas possible. Veuillez créer le champ SPF sur ce domaine manuellement en donnant les mêmes informations que ci-dessous.", - "zimbra_domain_modal_diagnostic_spf_content_header_ovh_hosted_domain_part1": "SPF permet de valider qu'OVHCloud est un émetteur légitime de mails avec votre nom de domaine.", - "zimbra_domain_modal_diagnostic_spf_content_header_ovh_hosted_domain_part2": "Votre domaine étant hébergé chez OVHCloud, nous pouvons procéder à la configuration automatique.", - "zimbra_domain_modal_diagnostic_spf_content_header_ovh_hosted_domain_part3": "Souhaitez-vous effectuer la configuration ?", - "zimbra_domain_modal_diagnostic_spf_action_cancel": "Annuler", - "zimbra_domain_modal_diagnostic_spf_action_close": "Fermer", - "zimbra_domain_modal_diagnostic_spf_action_validate": "Ok" + "zimbra_domain_diagnostic_cta_back": "Retour aux domaines", + "zimbra_domain_diagnostic_cta_refresh": "Rafraîchir", + "zimbra_domain_diagnostic_cta_auto_configure": "Auto-configurer", + "zimbra_domain_diagnostics_loading_error": "Une erreur est survenue lors du chargement des diagnostics de votre domaine. Veuillez nous excuser et réessayer plus tard.", + "zimbra_domain_diagnostic_loading_error": "Aucune information à afficher pour ce diagnostic.", + "zimbra_domain_diagnostic_status": "Statut :", + "zimbra_domain_diagnostic_status_ok": "Configuration OK", + "zimbra_domain_diagnostic_status_error": "Erreur de configuration", + "zimbra_domain_diagnostic_status_warning": "Attention", + "zimbra_domain_diagnostic_information_message_mx_ok": "La configuration de votre compte Zimbra sur OVHcloud est fonctionnelle: les emails adressés à votre domaine sont correctement acheminés vers vos boîtes aux lettres Zimbra.

N'hésitez pas à consulter régulièrement les diagnostics pour vous assurer du bon fonctionnement de votre service.", + "zimbra_domain_diagnostic_information_message_mx_error": "Nous constatons une configuration MX qui ne correspond pas à celle attendue par votre service email.

Si vous ne recevez pas vos emails dans les boites de destination, merci de procéder à une reconfiguration de votre Zone DNS avec les informations ci-dessous.", + "zimbra_domain_diagnostic_information_message_mx_warning": "Nous constatons une configuration MX qui ne correspond pas à celle attendue par votre service email.

Si aucun email n’arrive dans vos boites mails, procédez à une reconfiguration.

Sinon, cette configuration est probablement volontaire. Contactez l’administrateur de votre service pour avoir plus d’informations.", + "zimbra_domain_diagnostic_information_banner_mx_error": "Erreur de configuration MX, vos emails n'arriveront probablement pas à vos boites emails", + "zimbra_domain_diagnostic_information_banner_mx_warning": "Configuration MX anormale", + "zimbra_domain_diagnostic_information_message_spf_ok": "Le protocole SPF est correctement configuré sur votre domaine.

Le SPF (Sender Policy Framework) est un mécanisme d'authentification du courrier électronique qui permet aux titulaires de domaines de spécifier quels serveurs sont autorisés à envoyer des emails en leur nom afin d'aider à prévenir l'usurpation d'identité des emails et les spams.

Votre configuration actuelle autorise bien l'envoi d'emails via les serveurs Zimbra.", + "zimbra_domain_diagnostic_information_message_spf_error": "Nous constatons une configuration SPF qui ne correspond pas à celle attendue par votre Service email.

Le SPF (Sender Policy Framework) est un mécanisme d'authentification du courrier électronique qui permet aux titulaires de domaines de spécifier quels serveurs sont autorisés à envoyer des e-mails en leur nom afin d'aider à prévenir l'usurpation d'identité des emails et les spams.

Votre configuration actuelle n'autorise pas les serveurs Zimbra à emettre des emails utilisant votre nom de domaine.

Veuillez compléter la Zone DNS de votre domaine avec les informations ci dessous.", + "zimbra_domain_diagnostic_information_message_spf_warning": "Nous ne détectons pas de configuration SPF sur votre domaine.", + "zimbra_domain_diagnostic_information_banner_spf_error": "Erreur de configuration SPF, vos emails émis pourrait être refusés par les destinataires.", + "zimbra_domain_diagnostic_information_banner_spf_warning": "Configuration SPF anormale", + "zimbra_domain_diagnostic_type": "Type :", + "zimbra_domain_diagnostic_access_guide": "Accéder au guide", + "zimbra_domain_diagnostic_subtitle": "Diagnostics du domaine {{domain}}", + "zimbra_domain_diagnostic_field_host": "Nom/Hôte :", + "zimbra_domain_diagnostic_field_name": "Nom :", + "zimbra_domain_diagnostic_field_value": "Valeur :", + "zimbra_domain_diagnostic_field_priority": "Priorité :", + "zimbra_domain_diagnostic_field_weight": "Poids :", + "zimbra_domain_diagnostic_field_port": "Port :", + "zimbra_domain_diagnostic_field_target": "Cible :" } diff --git a/packages/manager/apps/zimbra/src/api/_mock_/domain.ts b/packages/manager/apps/zimbra/src/api/_mock_/domain.ts index 1e3d5809d90f..ff83f5f89ca8 100644 --- a/packages/manager/apps/zimbra/src/api/_mock_/domain.ts +++ b/packages/manager/apps/zimbra/src/api/_mock_/domain.ts @@ -1,4 +1,4 @@ -import { DomainType } from '@/api/domain'; +import { DomainDiagnosisResponse, DomainType } from '@/api/domain'; import { ResourceStatus } from '@/api/api.type'; export const domainDetailMock: DomainType = { @@ -24,7 +24,21 @@ export const domainDetailMock: DomainType = { }, ], expectedDNSConfig: { - mx: [], + spf: 'string', + dkim: { + cnames: [ + { + name: 'string', + value: 'string', + }, + ], + }, + mx: [ + { + priority: 0, + target: 'string', + }, + ], ownership: { cname: 'test', }, @@ -64,10 +78,24 @@ export const domainsMock: DomainType[] = [ }, ], expectedDNSConfig: { - mx: [], + dkim: { + cnames: [ + { + name: 'string', + value: 'string', + }, + ], + }, + mx: [ + { + priority: 0, + target: 'string', + }, + ], ownership: { - cname: null, + cname: 'string', }, + spf: 'string', }, }, currentTasks: [ @@ -101,10 +129,24 @@ export const domainsMock: DomainType[] = [ }, ], expectedDNSConfig: { - mx: [], + dkim: { + cnames: [ + { + name: 'string', + value: 'string', + }, + ], + }, + mx: [ + { + priority: 0, + target: 'string', + }, + ], ownership: { - cname: 'mycname', + cname: 'string', }, + spf: 'string', }, }, currentTasks: [ @@ -118,3 +160,76 @@ export const domainsMock: DomainType[] = [ ]; export const domainZone: string[] = ['test.fr', 'mydomain.fr', 'domain.fr']; + +export const domainDiagnosticMock = { + domainId: '3fa85f64-5717-4562-b3fc-2c963f66afa6', + domainName: 'zimbra.fr', + error: { + code: 'BAD_CONFIGURATION', + message: 'string', + }, + recommendations: { + expectedDNSConfig: { + dkim: { + cnames: [ + { + name: 'string', + value: 'string', + }, + ], + }, + mx: [ + { + priority: 0, + target: 'string', + }, + ], + ownership: { + cname: 'string', + }, + spf: 'string', + }, + }, + result: { + dkim: { + errors: [ + { + code: 'INCORRECT_CNAME_RECORD', + message: 'The CNAME setup on your DNS records is incorrect.', + }, + ], + recordsFound: ['string'], + status: 'ERROR', + }, + mx: { + errors: [ + { + code: 'EXTERNAL_MX_RECORD', + message: 'string', + }, + ], + recordsFound: [ + { + priority: 0, + target: 'string', + }, + ], + status: 'WARNING', + }, + spf: { + errors: [ + { + code: 'DANGEROUS_SPF_POLICY', + message: 'string', + }, + ], + recordsFound: ['string'], + status: 'ERROR', + }, + }, + status: 'ERROR', +}; + +export const domainsDiagnosticMock = [ + domainDiagnosticMock, +] as DomainDiagnosisResponse[]; diff --git a/packages/manager/apps/zimbra/src/api/domain/api.ts b/packages/manager/apps/zimbra/src/api/domain/api.ts index 5a6809ca0108..f50e5fe9c752 100644 --- a/packages/manager/apps/zimbra/src/api/domain/api.ts +++ b/packages/manager/apps/zimbra/src/api/domain/api.ts @@ -1,7 +1,13 @@ import { fetchIcebergV2, v2, v6 } from '@ovh-ux/manager-core-api'; -import { DomainBodyParamsType, DomainType } from './type'; +import { + DomainBodyParamsType, + DomainDiagnosisResponse, + DomainType, + ZoneWithIAM, +} from './type'; import { getApiPath } from '../utils/apiPath'; import { APIV2_DEFAULT_PAGESIZE } from '@/utils'; +import { domainDiagnosticMock } from '../_mock_'; // GET @@ -29,6 +35,11 @@ export const getDomainsZoneList = async () => { return data; }; +export const getDomainZoneByName = async (name: string) => { + const { data } = await v6.get(`/domain/zone/${name}`); + return data; +}; + export const getZimbraPlatformDomainDetail = async ( platformId: string, domainId: string, @@ -41,6 +52,27 @@ export const getZimbraPlatformDomainDetail = async ( // POST +export const postZimbraPlatformDomainsDiagnostic = async ( + platformId: string, + domains: string[], + mock = false, +) => { + // TODO: remove + if (mock) { + return new Promise((resolve) => { + setTimeout(() => { + const [domainId] = domains; + resolve([{ ...domainDiagnosticMock, domainId }]); + }, 1000); + }); + } + const { data } = await v2.post( + `${getApiPath(platformId)}diagnostic/domain`, + { domains }, + ); + return data; +}; + export const postZimbraDomain = async ( platformId: string, params: DomainBodyParamsType, diff --git a/packages/manager/apps/zimbra/src/api/domain/key.ts b/packages/manager/apps/zimbra/src/api/domain/key.ts index 2024ac37c3ff..56745c19c286 100644 --- a/packages/manager/apps/zimbra/src/api/domain/key.ts +++ b/packages/manager/apps/zimbra/src/api/domain/key.ts @@ -12,9 +12,21 @@ export const getZimbraPlatformDomainsQueryKey = ( organizationId, ].filter(Boolean); +export const getZimbraPlatformDomainsDiagnosticQueryKey = ( + platformId: string, + domainIds: string[], +) => ['get', 'domain', 'zimbra', platformId, 'diagnostic', ...domainIds]; + export const getZimbraPlatformDomainQueryKey = ( platformId: string, domainId: string, ) => ['get', 'domain', 'zimbra', platformId, domainId]; export const getDomainsZoneListQueryKey = ['get', 'domain', 'zone']; + +export const getDomainZoneByNameQueryKey = (name: string) => [ + 'get', + 'domain', + 'zone', + name, +]; diff --git a/packages/manager/apps/zimbra/src/api/domain/type.ts b/packages/manager/apps/zimbra/src/api/domain/type.ts index 59234332be2c..e1f4fca82f88 100644 --- a/packages/manager/apps/zimbra/src/api/domain/type.ts +++ b/packages/manager/apps/zimbra/src/api/domain/type.ts @@ -18,15 +18,7 @@ export type DomainType = { status: string; updatedAt: string; accountsStatistics: AccountStatistics[]; - expectedDNSConfig: { - mx: Array<{ - priority: number; - target: string; - }>; - ownership: { - cname: string | null; - }; - }; + expectedDNSConfig: ExpectedDNSConfig; }; currentTasks: Array<{ id: string; @@ -40,3 +32,223 @@ export type DomainType = { organizationId: string; }; }; + +/** IAM resource metadata embedded in services models */ +export interface ResourceMetadata { + /** Resource display name */ + displayName?: string; + /** Unique identifier of the resource */ + id: string; + /** Resource tags. Tags that were internally computed are prefixed with ovh: */ + tags?: { [key: string]: string }; + /** Unique resource name used in policies */ + urn: string; +} + +/** Zone dns Management */ +export interface ZoneWithIAM { + /** Is DNSSEC supported by this zone */ + dnssecSupported: boolean; + /** hasDnsAnycast flag of the DNS zone */ + hasDnsAnycast: boolean; + /** IAM resource metadata */ + iam?: ResourceMetadata; + /** Last update date of the DNS zone */ + lastUpdate: string; + /** Zone name */ + name: string; + /** Name servers that host the DNS zone */ + nameServers: string[]; +} + +export type DomainDiagnosisTestResult = + | DomainDiagnosisTestMXResult + | DomainDiagnosisTestDKIMResult + | DomainDiagnosisTestSPFResult; + +/** Object representing domain diagnostic */ +export interface DomainDiagnosisResponse { + /** Id of the domain */ + domainId: string; + /** Name of the domain */ + domainName?: string; + /** List of errors when doing diagnostics */ + error?: DomainDiagnosisError; + /** Recommendations on how to configure a domain */ + recommendations?: DomainDiagnosisRecommendations; + /** Result of the diagnosis */ + result?: DomainDiagnosisResult; + /** Status of the diagnosis */ + status: DomainDiagnosisStatusEnum; +} + +/** Status of performed diagnostic process for given domain */ +export enum DomainDiagnosisStatusEnum { + 'ERROR' = 'ERROR', + 'OK' = 'OK', + 'PARTIAL' = 'PARTIAL', +} + +/** The result of the diagnosis */ +export interface DomainDiagnosisResult { + /** The result of the DKIM diagnosis */ + dkim: DomainDiagnosisTestDKIMResult; + /** The result of the MX diagnosis */ + mx: DomainDiagnosisTestMXResult; + /** The result of the SPF diagnosis */ + spf: DomainDiagnosisTestSPFResult; +} + +/** Details on the DKIM error diagnosis error */ +export interface DomainDiagnosisTestDKIMError { + /** The error code for the DKIM error */ + code: DomainDiagnosisTestDKIMErrorCodeEnum; + /** The error message for the DKIM error */ + message: string; +} + +/** Error code of DKIM Diagnosis */ +export enum DomainDiagnosisTestDKIMErrorCodeEnum { + 'INCORRECT_CNAME_RECORD' = 'INCORRECT_CNAME_RECORD', + 'INTERNAL_ERROR' = 'INTERNAL_ERROR', + 'MISSING_ONE_SELECTOR' = 'MISSING_ONE_SELECTOR', + 'OVH_NOT_INCLUDED' = 'OVH_NOT_INCLUDED', + 'TASK_FAILED' = 'TASK_FAILED', +} + +/** DKIM Diagnosis test result */ +export interface DomainDiagnosisTestDKIMResult { + /** List of error during DKIM diagnosis */ + errors: DomainDiagnosisTestDKIMError[]; + /** Found DKIM records */ + recordsFound: string[]; + /** */ + status: DomainDiagnosisTestStatusEnum; +} + +/** Details on the MX error diagnosis error */ +export interface DomainDiagnosisTestMXError { + /** The error code for the MX error */ + code: DomainDiagnosisTestMXErrorCodeEnum; + /** The error message for the MX error */ + message: string; +} + +/** Error code of the MX diagnosis */ +export enum DomainDiagnosisTestMXErrorCodeEnum { + 'EXTERNAL_MX_RECORD' = 'EXTERNAL_MX_RECORD', + 'INTERNAL_ERROR' = 'INTERNAL_ERROR', + 'MISSING_OVH_SERVER' = 'MISSING_OVH_SERVER', + 'NO_MX_RECORD' = 'NO_MX_RECORD', + 'OVH_MX_LOW_PRIORITY' = 'OVH_MX_LOW_PRIORITY', +} + +/** MX Diagnosis test result */ +export interface DomainDiagnosisTestMXResult { + /** List of errors during MX diagnosis */ + errors: DomainDiagnosisTestMXError[]; + /** Found MX records */ + recordsFound: MXRecord[]; + /** */ + status: DomainDiagnosisTestStatusEnum; +} + +/** Status of performed diagnostic process for given test */ +export enum DomainDiagnosisTestStatusEnum { + 'ERROR' = 'ERROR', + 'OK' = 'OK', + 'WARNING' = 'WARNING', +} + +/** Details on the SPF error diagnosis error */ +export interface DomainDiagnosisTestSPFError { + /** The error code for the SPF error */ + code: DomainDiagnosisTestSPFErrorCodeEnum; + /** The error message for the SPF error */ + message: string; +} + +/** Error code of the SPF diagnosis */ +export enum DomainDiagnosisTestSPFErrorCodeEnum { + 'DANGEROUS_SPF_POLICY' = 'DANGEROUS_SPF_POLICY', + 'INTERNAL_ERROR' = 'INTERNAL_ERROR', + 'INVALID_SPF_RECORD' = 'INVALID_SPF_RECORD', + 'MISSING_OVH_SERVER' = 'MISSING_OVH_SERVER', + 'MISSING_SPF_POLICY' = 'MISSING_SPF_POLICY', + 'MULTIPLE_SPF_RECORDS' = 'MULTIPLE_SPF_RECORDS', + 'NOT_RECOMMENDED_SPF_POLICY' = 'NOT_RECOMMENDED_SPF_POLICY', + 'NO_SPF_RECORD' = 'NO_SPF_RECORD', +} + +/** SPF Diagnosis test result */ +export interface DomainDiagnosisTestSPFResult { + /** List of errors during SPF diagnosis */ + errors: DomainDiagnosisTestSPFError[]; + /** Found SPF record */ + recordsFound: string[]; + /** */ + status: DomainDiagnosisTestStatusEnum; +} + +/** Errors of failed diagnostic */ +export interface DomainDiagnosisError { + /** Status of performed diagnostic process for given domain */ + code: DomainDiagnosisErrorCodeEnum; + /** Diagnosis message */ + message: string; +} + +/** Status of performed diagnostic process for given domain */ +export enum DomainDiagnosisErrorCodeEnum { + 'BAD_CONFIGURATION' = 'BAD_CONFIGURATION', + 'DOMAIN_IN_TRANSIENT_STATE' = 'DOMAIN_IN_TRANSIENT_STATE', + 'DOMAIN_NOT_FOUND' = 'DOMAIN_NOT_FOUND', + 'DOMAIN_NOT_VALIDATED' = 'DOMAIN_NOT_VALIDATED', + 'INTERNAL_ERROR' = 'INTERNAL_ERROR', +} + +/** Recommendations on how to configure a domain */ +export interface DomainDiagnosisRecommendations { + /** Expected configuration of a domain */ + expectedDNSConfig: ExpectedDNSConfig; +} + +/** Object representing a DNS configuration that should be applied */ +export interface ExpectedDNSConfig { + /** Recommended DKIM selectors */ + dkim: DKIMSelectors; + /** Recommended MX records for efficient mail delivery */ + mx: MXRecord[]; + /** DNS configuration to apply to verify ownership */ + ownership: DNSOwnership; + /** Recommended SPF value */ + spf: string; +} + +/** Object representing an MX record */ +export interface MXRecord { + /** Priority for that target server */ + priority: number; + /** Target server */ + target: string; +} + +/** Object representing a DNS configuration that should be applied to verify ownership */ +export interface DNSOwnership { + /** Identifier on zone of domain for verification */ + cname?: string; +} + +/** Object representing a DKIM selector objet which is a CName entry. */ +export interface DKIMSelectors { + /** List of CName to configure for DKIM. */ + cnames: CName[]; +} + +/** Object representing a DNS CName entry */ +export interface CName { + /** Name of the CName entry on customer DNS. */ + name: string; + /** Value of the CName entry on customer DNS. */ + value: string; +} diff --git a/packages/manager/apps/zimbra/src/components/DiagnosticBadge.tsx b/packages/manager/apps/zimbra/src/components/DiagnosticBadge.tsx deleted file mode 100644 index 4e09a0c6395e..000000000000 --- a/packages/manager/apps/zimbra/src/components/DiagnosticBadge.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import React from 'react'; -import { OdsTooltip, OdsText, OdsBadge } from '@ovhcloud/ods-components/react'; -import { - ODS_BADGE_COLOR, - ODS_BADGE_SIZE, - ODS_TEXT_PRESET, -} from '@ovhcloud/ods-components'; -import { useTranslation } from 'react-i18next'; -import { useNavigate } from 'react-router-dom'; -import { DnsRecordType } from '@/utils'; -import { useGenerateUrl } from '@/hooks'; - -type DiagnosticBadgeProps = { - diagType: DnsRecordType; - status?: ODS_BADGE_COLOR; - domainId?: string; -}; - -export const DiagnosticBadge: React.FC = ({ - diagType, - status, - domainId, -}) => { - const { t } = useTranslation('domains'); - const navigate = useNavigate(); - const hasAction = !!( - status === ODS_BADGE_COLOR.critical && - [DnsRecordType.MX, DnsRecordType.SPF, DnsRecordType.SRV].some( - (diag) => diag === diagType, - ) && - domainId - ); - - const href = useGenerateUrl('./diagnostic', 'path', { - domainId, - dnsRecordType: diagType, - isOvhDomain: 'false', - }); - - const handleChipClick = (e: React.MouseEvent) => { - e.stopPropagation(); - navigate(href); - }; - - const id = `diag-tooltip-trigger-${diagType}-${domainId}`; - - return ( - <> -
- -
- - {status === ODS_BADGE_COLOR.success && ( - -
- - {t('zimbra_domains_datagrid_diagnostic_tooltip_title', { - diagType, - })} - - - {t('zimbra_domains_datagrid_diagnostic_configuration_ok')} - -
-
- )} - - ); -}; diff --git a/packages/manager/apps/zimbra/src/components/layout-helpers/Dashboard/Dashboard.tsx b/packages/manager/apps/zimbra/src/components/layout-helpers/Dashboard/Dashboard.tsx index f06a924adba2..86799376d33d 100644 --- a/packages/manager/apps/zimbra/src/components/layout-helpers/Dashboard/Dashboard.tsx +++ b/packages/manager/apps/zimbra/src/components/layout-helpers/Dashboard/Dashboard.tsx @@ -116,7 +116,10 @@ export const Dashboard: React.FC = () => { urls.domains, urls.domainsEdit, urls.domainsDelete, - urls.domains_diagnostic, + urls.domains_diagnostic_mx, + urls.domains_diagnostic_spf, + urls.domains_diagnostic_srv, + urls.domains_diagnostic_dkim, ], platformId, ), diff --git a/packages/manager/apps/zimbra/src/components/layout-helpers/Dashboard/TabsPanel.tsx b/packages/manager/apps/zimbra/src/components/layout-helpers/Dashboard/TabsPanel.tsx index 1f7dd46bcb6f..7fdb914289ae 100644 --- a/packages/manager/apps/zimbra/src/components/layout-helpers/Dashboard/TabsPanel.tsx +++ b/packages/manager/apps/zimbra/src/components/layout-helpers/Dashboard/TabsPanel.tsx @@ -15,6 +15,7 @@ export type TabItemProps = { to: string; hidden?: boolean; isDisabled?: boolean; + component?: ReactNode; }; export type TabsProps = { @@ -57,7 +58,7 @@ const TabsPanel: React.FC = ({ tabs }) => { setActivePanel(activeTab.name); } } - }, [location.pathname]); + }, [location.pathname, tabs]); return ( diff --git a/packages/manager/apps/zimbra/src/hooks/__test__/useDomain.spec.tsx b/packages/manager/apps/zimbra/src/hooks/__test__/useDomain.spec.tsx index 38eaa0171ba9..4b45322ba0bc 100644 --- a/packages/manager/apps/zimbra/src/hooks/__test__/useDomain.spec.tsx +++ b/packages/manager/apps/zimbra/src/hooks/__test__/useDomain.spec.tsx @@ -7,9 +7,12 @@ import { wrapper } from '@/utils/test.provider'; describe('useDomain', () => { it('should return the detail of a domain', async () => { - const { result } = renderHook(() => useDomain(domainDetailMock.id), { - wrapper, - }); + const { result } = renderHook( + () => useDomain({ domainId: domainDetailMock.id }), + { + wrapper, + }, + ); await waitFor(() => { expect(result.current.isSuccess).toBe(true); diff --git a/packages/manager/apps/zimbra/src/hooks/__test__/useDomainDiagnostic.spec.tsx b/packages/manager/apps/zimbra/src/hooks/__test__/useDomainDiagnostic.spec.tsx new file mode 100644 index 000000000000..d95bfc990ae1 --- /dev/null +++ b/packages/manager/apps/zimbra/src/hooks/__test__/useDomainDiagnostic.spec.tsx @@ -0,0 +1,23 @@ +import { describe, expect } from 'vitest'; +import '@testing-library/jest-dom'; +import { renderHook, waitFor } from '@testing-library/react'; +import { domainDiagnosticMock, domainDetailMock } from '@/api/_mock_'; +import { useDomainDiagnostic } from '../useDomainDiagnostic'; +import { wrapper } from '@/utils/test.provider'; + +describe('useDomainDiagnostic', () => { + it('should return the diagnostic of a domain', async () => { + const { result } = renderHook( + () => useDomainDiagnostic({ domainId: domainDetailMock.id }), + { + wrapper, + }, + ); + + await waitFor(() => { + expect(result.current.isSuccess).toBe(true); + }); + + expect(result.current.data).toEqual(domainDiagnosticMock); + }); +}); diff --git a/packages/manager/apps/zimbra/src/hooks/__test__/useDomainsDiagnostic.spec.tsx b/packages/manager/apps/zimbra/src/hooks/__test__/useDomainsDiagnostic.spec.tsx new file mode 100644 index 000000000000..60e94c87487d --- /dev/null +++ b/packages/manager/apps/zimbra/src/hooks/__test__/useDomainsDiagnostic.spec.tsx @@ -0,0 +1,23 @@ +import { describe, expect } from 'vitest'; +import '@testing-library/jest-dom'; +import { renderHook, waitFor } from '@testing-library/react'; +import { domainsDiagnosticMock, domainDetailMock } from '@/api/_mock_'; +import { useDomainsDiagnostic } from '../useDomainsDiagnostic'; +import { wrapper } from '@/utils/test.provider'; + +describe('useDomainsDiagnostic', () => { + it('should return the diagnostics of a list of domains', async () => { + const { result } = renderHook( + () => useDomainsDiagnostic({ domainIds: [domainDetailMock.id] }), + { + wrapper, + }, + ); + + await waitFor(() => { + expect(result.current.isSuccess).toBe(true); + }); + + expect(result.current.data).toEqual(domainsDiagnosticMock); + }); +}); diff --git a/packages/manager/apps/zimbra/src/hooks/useDomain.ts b/packages/manager/apps/zimbra/src/hooks/useDomain.ts index b94ffe06f7b5..32ba6ead19d4 100644 --- a/packages/manager/apps/zimbra/src/hooks/useDomain.ts +++ b/packages/manager/apps/zimbra/src/hooks/useDomain.ts @@ -1,18 +1,33 @@ -import { useQuery } from '@tanstack/react-query'; +import { + useQuery, + UseQueryOptions, + UseQueryResult, +} from '@tanstack/react-query'; import { usePlatform } from '@/hooks'; import { + DomainType, getZimbraPlatformDomainDetail, getZimbraPlatformDomainQueryKey, } from '@/api/domain'; -export const useDomain = (domainId: string, noCache?: boolean) => { +type UseDomainParams = Omit & { + domainId: string; +}; + +export const useDomain = (params: UseDomainParams) => { + const { domainId, ...options } = params; const { platformId } = usePlatform(); return useQuery({ queryKey: getZimbraPlatformDomainQueryKey(platformId, domainId), queryFn: () => getZimbraPlatformDomainDetail(platformId, domainId), - enabled: !!platformId && !!domainId, - gcTime: noCache ? 0 : 5000, - }); + enabled: (query) => + (typeof options.enabled === 'function' + ? options.enabled(query) + : typeof options.enabled !== 'boolean' || options.enabled) && + !!platformId && + !!domainId, + ...options, + }) as UseQueryResult; }; diff --git a/packages/manager/apps/zimbra/src/hooks/useDomainDiagnostic.ts b/packages/manager/apps/zimbra/src/hooks/useDomainDiagnostic.ts new file mode 100644 index 000000000000..01455bb22021 --- /dev/null +++ b/packages/manager/apps/zimbra/src/hooks/useDomainDiagnostic.ts @@ -0,0 +1,40 @@ +import { + useQuery, + UseQueryOptions, + UseQueryResult, +} from '@tanstack/react-query'; +import { usePlatform } from '@/hooks'; +import { + DomainDiagnosisResponse, + getZimbraPlatformDomainsDiagnosticQueryKey, + postZimbraPlatformDomainsDiagnostic, +} from '@/api/domain'; + +type UseDomainDiagnosticParams = Omit< + UseQueryOptions, + 'queryKey' | 'queryFn' | 'select' +> & { + domainId: string; +}; + +export const useDomainDiagnostic = (params: UseDomainDiagnosticParams) => { + const { domainId, ...options } = params; + const { platformId } = usePlatform(); + + return useQuery({ + queryKey: getZimbraPlatformDomainsDiagnosticQueryKey(platformId, [ + domainId, + ]), + queryFn: () => postZimbraPlatformDomainsDiagnostic(platformId, [domainId]), + enabled: (query) => + (typeof options.enabled === 'function' + ? options.enabled(query) + : typeof options.enabled !== 'boolean' || options.enabled) && + !!platformId && + !!domainId, + select: (data: DomainDiagnosisResponse[]) => { + return data.find((diag) => diag.domainId === domainId); + }, + ...options, + }) as UseQueryResult; +}; diff --git a/packages/manager/apps/zimbra/src/hooks/useDomainZone.ts b/packages/manager/apps/zimbra/src/hooks/useDomainZone.ts new file mode 100644 index 000000000000..ce03e2d4a180 --- /dev/null +++ b/packages/manager/apps/zimbra/src/hooks/useDomainZone.ts @@ -0,0 +1,25 @@ +import { + useQuery, + UseQueryOptions, + UseQueryResult, +} from '@tanstack/react-query'; +import { getDomainZoneByName, ZoneWithIAM } from '@/api/domain'; +import { getDomainZoneByNameQueryKey } from '../api/domain/key'; + +type UseDomainZoneParams = Omit & { + name: string; +}; + +export const useDomainZone = (params: UseDomainZoneParams) => { + const { name, ...options } = params; + + return useQuery({ + queryKey: getDomainZoneByNameQueryKey(name), + queryFn: () => getDomainZoneByName(name), + enabled: (query) => + (typeof options.enabled === 'function' + ? options.enabled(query) + : typeof options.enabled !== 'boolean' || options.enabled) && !!name, + ...options, + }) as UseQueryResult; +}; diff --git a/packages/manager/apps/zimbra/src/hooks/useDomainsDiagnostic.ts b/packages/manager/apps/zimbra/src/hooks/useDomainsDiagnostic.ts new file mode 100644 index 000000000000..7a5f32b5e338 --- /dev/null +++ b/packages/manager/apps/zimbra/src/hooks/useDomainsDiagnostic.ts @@ -0,0 +1,36 @@ +import { + useQuery, + UseQueryOptions, + UseQueryResult, +} from '@tanstack/react-query'; +import { usePlatform } from '@/hooks'; +import { + DomainDiagnosisResponse, + getZimbraPlatformDomainsDiagnosticQueryKey, + postZimbraPlatformDomainsDiagnostic, +} from '@/api/domain'; + +type UseDomainsDiagnosticParams = Omit< + UseQueryOptions, + 'queryKey' | 'queryFn' +> & { + domainIds: string[]; +}; + +export const useDomainsDiagnostic = (params: UseDomainsDiagnosticParams) => { + const { domainIds, ...options } = params; + const { platformId } = usePlatform(); + + return useQuery({ + queryKey: getZimbraPlatformDomainsDiagnosticQueryKey(platformId, domainIds), + queryFn: () => postZimbraPlatformDomainsDiagnostic(platformId, domainIds), + enabled: (query) => + (typeof options.enabled === 'function' + ? options.enabled(query) + : typeof options.enabled !== 'boolean' || options.enabled) && + !!platformId && + !!domainIds && + !!domainIds.length, + ...options, + }) as UseQueryResult; +}; diff --git a/packages/manager/apps/zimbra/src/index.scss b/packages/manager/apps/zimbra/src/index.scss index 50a2cdae09fb..cde08d667bc7 100644 --- a/packages/manager/apps/zimbra/src/index.scss +++ b/packages/manager/apps/zimbra/src/index.scss @@ -47,3 +47,27 @@ ods-tag.org-tag::part(tag) { } } } +table.dns-fields { + border-collapse: collapse; + + tr { + // match Clipboard fixed height + focus style + height: 42px; + td { + height: 42px; + } + } + + td:has(ods-clipboard) { + display: flex; + align-items: center; + ods-clipboard { + flex: 1; + width: 100%; + } + } +} + +.diag-dns-icon { + color: var(--ods-color-critical-400); +} diff --git a/packages/manager/apps/zimbra/src/pages/dashboard/Domains/ActionButtonDomain.component.tsx b/packages/manager/apps/zimbra/src/pages/dashboard/Domains/ActionButtonDomain.component.tsx index e8862c15e9bd..cc2031a844a5 100644 --- a/packages/manager/apps/zimbra/src/pages/dashboard/Domains/ActionButtonDomain.component.tsx +++ b/packages/manager/apps/zimbra/src/pages/dashboard/Domains/ActionButtonDomain.component.tsx @@ -53,6 +53,14 @@ const ActionButtonDomain: React.FC = ({ navigate(hrefEditDomain); }; + const hrefDiagnosticsDomain = useGenerateUrl('./diagnostics/mx', 'path', { + domainId: domainItem.id, + }); + + const handleDiagnosticsDomainClick = () => { + navigate(hrefDiagnosticsDomain); + }; + const actionItems = [ { id: 1, @@ -63,6 +71,13 @@ const ActionButtonDomain: React.FC = ({ }, { id: 2, + onclick: handleDiagnosticsDomainClick, + label: t('zimbra_domains_tooltip_diagnostics'), + urn: platformUrn, + iamActions: [IAM_ACTIONS.domain.edit], + }, + { + id: 3, onclick: handleDeleteDomainClick, label: t('zimbra_domains_tooltip_delete'), urn: platformUrn, @@ -70,6 +85,7 @@ const ActionButtonDomain: React.FC = ({ color: ODS_BUTTON_COLOR.critical, }, ]; + return ( { + return diagnostic && diagnostic?.status !== DomainDiagnosisTestStatusEnum.OK; +}; + +const getStatusBadgeColor = ( + status: DomainDiagnosisTestStatusEnum, +): ODS_BADGE_COLOR => { + switch (status) { + case DomainDiagnosisTestStatusEnum.OK: + return ODS_BADGE_COLOR.success; + case DomainDiagnosisTestStatusEnum.ERROR: + return ODS_BADGE_COLOR.critical; + case DomainDiagnosisTestStatusEnum.WARNING: + return ODS_BADGE_COLOR.warning; + default: + return ODS_BADGE_COLOR.neutral; + } +}; + +type StatusBadgeProps = StyleReactProps & + Omit & { + status: DomainDiagnosisTestStatusEnum; + }; + +const StatusBadge = ({ status, ...props }: StatusBadgeProps) => { + const { t } = useTranslation('domains/diagnostic'); + return ( + + ); +}; + +const TabTitle = ({ + title, + hasError, +}: { + title: string; + hasError?: boolean; +}) => { + return ( + <> + {title} + {hasError && ( + + )} + + ); +}; + +const useDNSRecordConfigHelp = ({ + expectedDNSConfig, + recordType, +}: { + expectedDNSConfig: ExpectedDNSConfig; + recordType: DnsRecordType; +}) => { + const { t } = useTranslation('domains/diagnostic'); + switch (recordType) { + case DnsRecordType.MX: + return ( + <> + {expectedDNSConfig.mx.map(({ priority, target }) => ( + + + + + {t('zimbra_domain_diagnostic_field_priority')} + + {priority} + + + + + + {t('zimbra_domain_diagnostic_field_target')} + + + + + + ))} + + ); + case DnsRecordType.SPF: + return ( + + + + + {t('zimbra_domain_diagnostic_field_value')} + + + + + + ); + case DnsRecordType.DKIM: + return expectedDNSConfig?.dkim?.cnames?.map((cname, index) => ( + + + + + {`CNAME ${index + 1}`} + + + + + + + + {t('zimbra_domain_diagnostic_field_name')} + + + + + + + + + + {t('zimbra_domain_diagnostic_field_value')} + + + + + + + )); + case DnsRecordType.SRV: + default: + return <>; + } +}; + +const TabContent = ({ + diagnostic, + recordType, + expectedDNSConfig, + trackingName, + isAutoConfigurable, + guide = GUIDES_LIST.dns_configuration_guide, +}: { + diagnostic: DomainDiagnosisTestResult; + recordType: DnsRecordType; + expectedDNSConfig: ExpectedDNSConfig; + trackingName: string; + isAutoConfigurable: boolean; + guide?: Guide; +}) => { + const { t } = useTranslation('domains/diagnostic'); + const { trackClick } = useOvhTracking(); + const fieldHelpers = useDNSRecordConfigHelp({ + expectedDNSConfig, + recordType, + }); + + if (!diagnostic) { + return ( + + {t('zimbra_domain_diagnostic_loading_error')} + + ); + } + + const isOk = + diagnostic && diagnostic.status === DomainDiagnosisTestStatusEnum.OK; + const [error] = diagnostic.errors; + + return ( +
+ + {t('zimbra_domain_diagnostic_status')} + + + {!isOk && ( + + {t( + `zimbra_domain_diagnostic_information_banner_${recordType.toLowerCase()}_${diagnostic?.status.toLowerCase()}`, + )} + + )} + + + + {!isOk && ( + <> + {guide && ( + + )} + {isAutoConfigurable ? ( + { + trackClick({ + location: PageLocation.page, + buttonType: ButtonType.button, + actionType: 'action', + actions: [trackingName, AUTO_CONFIGURE_DOMAIN], + }); + }} + /> + ) : ( + + + + + + {fieldHelpers} + +
+ + + {t('zimbra_domain_diagnostic_type')} + + + {recordType === DnsRecordType.DKIM ? 'TXT' : recordType} + + +
+ )} + + )} +
+ ); +}; + +export default function DomainDiagnostics() { + const { t } = useTranslation('domains/diagnostic'); + const { platformId } = usePlatform(); + const location = useLocation(); + const [searchParams] = useSearchParams(); + const { trackClick } = useOvhTracking(); + const params = Object.fromEntries(searchParams.entries()); + const domainId = searchParams.get('domainId'); + const goBackUrl = useGenerateUrl('../..', 'href'); + + const { data: domain, isFetching, isError } = useDomainDiagnostic({ + domainId, + }); + + // autoconfigure is not yet ready on cerbo side, they will provide + // a boolean to let us know if it's auto configurable or no + + const expectedDNSConfig = domain?.recommendations?.expectedDNSConfig; + + const tabsList: TabItemProps[] = [ + { + name: DnsRecordType.MX, + trackingName: DOMAIN_DIAGNOSTICS_MX, + title: ( + + ), + to: useGenerateUrl('../diagnostics/mx', 'path', params), + pathMatchers: computePathMatchers( + [urls.domains_diagnostic_mx], + platformId, + ), + component: ( + + ), + }, + { + name: DnsRecordType.SRV, + trackingName: DOMAIN_DIAGNOSTICS_SRV, + title: , + to: useGenerateUrl('../diagnostics/srv', 'path', params), + hidden: !FEATURE_FLAGS.DOMAIN_DIAGNOSTICS_SRV, + pathMatchers: computePathMatchers( + [urls.domains_diagnostic_srv], + platformId, + ), + component: ( + + ), + }, + { + name: DnsRecordType.SPF, + trackingName: DOMAIN_DIAGNOSTICS_SPF, + title: ( + + ), + to: useGenerateUrl('../diagnostics/spf', 'path', params), + pathMatchers: computePathMatchers( + [urls.domains_diagnostic_spf], + platformId, + ), + component: ( + + ), + }, + { + name: DnsRecordType.DKIM, + trackingName: DOMAIN_DIAGNOSTICS_DKIM, + isDisabled: true, + title: ( + + ), + to: useGenerateUrl('../diagnostics/dkim', 'path', params), + pathMatchers: computePathMatchers( + [urls.domains_diagnostic_dkim], + platformId, + ), + component: ( + + ), + }, + ]; + + const currentTab = useMemo( + () => + tabsList.find((tab) => activatedTabs(tab.pathMatchers, location)) + ?.component || tabsList[0].component, + [location, domain, isFetching], + ); + + const handleRefreshClick = () => { + queryClient.invalidateQueries({ queryKey: ['get', 'domain'] }); + trackClick({ + location: PageLocation.page, + buttonType: ButtonType.button, + actionType: 'action', + actions: [DOMAIN_DIAGNOSTICS_REFRESH], + }); + }; + + return ( +
+ { + trackClick({ + location: PageLocation.page, + buttonType: ButtonType.button, + actionType: 'navigation', + actions: [DOMAIN_DIAGNOSTICS, BACK_PREVIOUS_PAGE], + }); + }} + label={t('zimbra_domain_diagnostic_cta_back')} + /> + + {t('zimbra_domain_diagnostic_subtitle', { + domain: domain?.domainName, + })} + +
+ +
+
+ +
+ {isFetching && !isError && } + {!isFetching && isError && ( + + {t('zimbra_domain_diagnostics_loading_error')} + + )} + {!isFetching && !isError && domain && currentTab} +
+ ); +} diff --git a/packages/manager/apps/zimbra/src/pages/dashboard/Domains/Domains.tsx b/packages/manager/apps/zimbra/src/pages/dashboard/Domains/Domains.tsx index 44f3fc1d7331..61d1d7cd743f 100644 --- a/packages/manager/apps/zimbra/src/pages/dashboard/Domains/Domains.tsx +++ b/packages/manager/apps/zimbra/src/pages/dashboard/Domains/Domains.tsx @@ -2,7 +2,6 @@ import React, { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { OdsText, OdsTooltip } from '@ovhcloud/ods-components/react'; import { - ODS_BADGE_COLOR, ODS_BUTTON_COLOR, ODS_BUTTON_SIZE, ODS_ICON_NAME, @@ -32,11 +31,8 @@ import { IAM_ACTIONS } from '@/utils/iamAction.constants'; import { DATAGRID_REFRESH_INTERVAL, DATAGRID_REFRESH_ON_MOUNT, - DnsRecordType, - FEATURE_FLAGS, } from '@/utils'; import Loading from '@/components/Loading/Loading'; -import { DiagnosticBadge } from '@/components/DiagnosticBadge'; import { DomainType } from '@/api/domain/type'; import { AccountStatistics, ResourceStatus } from '@/api/api.type'; import { BadgeStatus } from '@/components/BadgeStatus'; @@ -75,36 +71,6 @@ const columns: DatagridColumn[] = [ ), label: 'zimbra_domains_datagrid_account_number', }, - { - id: 'diagnostic', - cell: (item) => { - return ( -
- - - - -
- ); - }, - label: 'zimbra_domains_datagrid_diagnostic_label', - }, { id: 'status', cell: (item) => @@ -212,18 +178,10 @@ export default function Domains() { ) : ( <> - !( - !FEATURE_FLAGS.DOMAIN_DIAGNOSTICS && - c.id === 'diagnostic' - ), - ) - .map((column) => ({ - ...column, - label: t(column.label), - }))} + columns={columns.map((column) => ({ + ...column, + label: t(column.label), + }))} items={items} totalItems={items.length} hasNextPage={!isFetchingNextPage && hasNextPage} diff --git a/packages/manager/apps/zimbra/src/pages/dashboard/Domains/ModalDiagnosticDnsRecord.component.tsx b/packages/manager/apps/zimbra/src/pages/dashboard/Domains/ModalDiagnosticDnsRecord.component.tsx deleted file mode 100644 index 92c6f4d1849f..000000000000 --- a/packages/manager/apps/zimbra/src/pages/dashboard/Domains/ModalDiagnosticDnsRecord.component.tsx +++ /dev/null @@ -1,262 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useNavigate, useSearchParams } from 'react-router-dom'; -import { Trans, useTranslation } from 'react-i18next'; -import { OdsText } from '@ovhcloud/ods-components/react'; -import { ODS_MODAL_COLOR, ODS_TEXT_PRESET } from '@ovhcloud/ods-components'; -import { useGenerateUrl, useDomain } from '@/hooks'; -import Modal from '@/components/Modals/Modal'; -import { DomainType } from '@/api/domain'; -import { GUIDES_LIST } from '@/guides.constants'; -import GuideLink from '@/components/GuideLink'; -import { - DnsRecordType, - DnsRecordTypeKey, - getDnsRecordTypeKeyFromDnsRecordType, -} from '@/utils'; - -type ModalDiagnosticDnsRecordProps = { - domainId?: string; - dnsRecordType?: DnsRecordType; - isOvhDomain?: boolean; -}; - -const getContentHeaderKeys = ( - dnsRecordTypeKey: DnsRecordTypeKey, - isOvhDomain: boolean, -): string[] => { - switch (dnsRecordTypeKey) { - case DnsRecordTypeKey.MX: - return isOvhDomain - ? [ - 'zimbra_domain_modal_diagnostic_mx_content_header_ovh_hosted_domain_part1', - 'zimbra_domain_modal_diagnostic_mx_content_header_ovh_hosted_domain_part2', - 'zimbra_domain_modal_diagnostic_mx_content_header_ovh_hosted_domain_part3', - ] - : [ - 'zimbra_domain_modal_diagnostic_mx_content_header_part1', - 'zimbra_domain_modal_diagnostic_mx_content_header_part2', - 'zimbra_domain_modal_diagnostic_mx_content_header_part3', - ]; - case DnsRecordTypeKey.SRV: - return isOvhDomain - ? [ - 'zimbra_domain_modal_diagnostic_srv_content_header_ovh_hosted_domain_part1', - 'zimbra_domain_modal_diagnostic_srv_content_header_ovh_hosted_domain_part2', - ] - : ['zimbra_domain_modal_diagnostic_srv_content_header']; - - case DnsRecordTypeKey.SPF: - return isOvhDomain - ? [ - 'zimbra_domain_modal_diagnostic_spf_content_header_ovh_hosted_domain_part1', - 'zimbra_domain_modal_diagnostic_spf_content_header_ovh_hosted_domain_part2', - 'zimbra_domain_modal_diagnostic_spf_content_header_ovh_hosted_domain_part3', - ] - : ['zimbra_domain_modal_diagnostic_spf_content_header']; - default: - return []; - } -}; - -export default function ModalDiagnosticDnsRecord( - props: Readonly, -) { - const { t } = useTranslation('domains/diagnostic'); - const navigate = useNavigate(); - const goBackUrl = useGenerateUrl('..', 'path'); - const goBack = () => navigate(goBackUrl); - - const [searchParams] = useSearchParams(); - const domainId = searchParams.get('domainId') || props.domainId; - - // fetch these informations - // check CRB-387 for this - const isOvhDomain = - searchParams.get('isOvhDomain') === 'true' || props.isOvhDomain; - const dnsRecordType = - searchParams.get('dnsRecordType') || props.dnsRecordType; - const dnsRecordTypeKey = getDnsRecordTypeKeyFromDnsRecordType( - dnsRecordType as DnsRecordType, - ); - - const [domain, setDomain] = useState(); - const { data, isLoading } = useDomain(domainId); - - useEffect(() => { - setDomain(data); - }, [isLoading]); - - // this should be in data.currentState.expectedDNSConfig.srv - const srvFields = { - subdomain: '_autodiscover._tcp', - priority: '0', - weight: '0', - port: '443', - target: 'ex5.mail.ovh.net.', - }; - - // this should be in data.currentState.expectedDNSConfig.mx - const mxFields = [ - { priority: 1, target: 'mx0.mail.ovh.net' }, - { priority: 5, target: 'mx1.mail.ovh.net' }, - { priority: 10, target: 'mx2.mail.ovh.net' }, - { priority: 100, target: 'mx3.mail.ovh.net' }, - ]; - - // this should be in data.currentState.expectedDNSConfig.spf - const spfFields = { - subdomain: '_autodiscover._tcp', - target: '"v=spf1 include:mx.ovh.com ~all"', - }; - - const handleValidationClick = () => { - // send the request to fix the record - goBack(); - }; - - if (dnsRecordTypeKey === DnsRecordTypeKey.NONE) { - goBack(); - } - - const getPrimaryButtonProps = () => { - const buttonProps = { - label: t( - `zimbra_domain_modal_diagnostic_${dnsRecordTypeKey}_action_validate`, - ), - action: handleValidationClick, - testid: `diagnostic-${dnsRecordTypeKey}-modal-primary-btn`, - }; - - if (isOvhDomain) { - return buttonProps; - } - - if (dnsRecordType === DnsRecordType.MX) { - return { - ...buttonProps, - label: t(`zimbra_domain_modal_diagnostic_mx_action_test`), - }; - } - - return null; - }; - - return ( - - {domain && ( -
- {getContentHeaderKeys(dnsRecordTypeKey, isOvhDomain).map((key) => ( - - - ), - }} - /> - - ))} - {dnsRecordType === DnsRecordType.SRV && ( -
- - - {t( - `zimbra_domain_modal_diagnostic_${dnsRecordTypeKey}_domain`, - )} - - - - {domain?.currentState?.name} - -
- )} - {!isOvhDomain && ( -
- - - {t(`zimbra_domain_modal_diagnostic_fields`)} {dnsRecordType} - - -
- {mxFields && dnsRecordType === DnsRecordType.MX && ( -
- {mxFields.map(({ priority, target }) => ( - - {t(`zimbra_domain_modal_diagnostic_field_priority`)} - {priority} - {' ; '} - {t(`zimbra_domain_modal_diagnostic_field_target`)} - {target} - - ))} -
- )} - {(dnsRecordType === DnsRecordType.SRV || - dnsRecordType === DnsRecordType.SPF) && ( -
- {Object.entries( - dnsRecordType === DnsRecordType.SRV - ? srvFields - : spfFields, - ).map(([key, value]) => ( - - {t(`zimbra_domain_modal_diagnostic_field_${key}`)} - {value} - - ))} -
- )} -
-
- )} - {!isOvhDomain && dnsRecordType === DnsRecordType.MX ? ( - - - - ) : null} -
- )} -
- ); -} diff --git a/packages/manager/apps/zimbra/src/pages/dashboard/Domains/ModalEditDomain.component.tsx b/packages/manager/apps/zimbra/src/pages/dashboard/Domains/ModalEditDomain.component.tsx index 0f99b5d678d4..fda779e8cc39 100644 --- a/packages/manager/apps/zimbra/src/pages/dashboard/Domains/ModalEditDomain.component.tsx +++ b/packages/manager/apps/zimbra/src/pages/dashboard/Domains/ModalEditDomain.component.tsx @@ -46,9 +46,9 @@ export default function ModalEditDomain() { const { platformId } = usePlatform(); - const { data: detailDomain, isLoading: isLoadingDomain } = useDomain( - editDomainId, - ); + const { data: detailDomain, isLoading: isLoadingDomain } = useDomain({ + domainId: editDomainId, + }); const { data: organizationsList, isLoading: isLoadingOrganizations, diff --git a/packages/manager/apps/zimbra/src/pages/dashboard/Domains/VerifyDomain.page.tsx b/packages/manager/apps/zimbra/src/pages/dashboard/Domains/VerifyDomain.page.tsx index 70288a387b17..5fe42291f7da 100644 --- a/packages/manager/apps/zimbra/src/pages/dashboard/Domains/VerifyDomain.page.tsx +++ b/packages/manager/apps/zimbra/src/pages/dashboard/Domains/VerifyDomain.page.tsx @@ -32,7 +32,7 @@ export default function ValidateOwnership() { const { trackClick } = useOvhTracking(); const [searchParams] = useSearchParams(); const domainId = searchParams.get('domainId'); - const { data: domain, isFetching, isError } = useDomain(domainId); + const { data: domain, isFetching, isError } = useDomain({ domainId }); const cname = domain?.currentState?.expectedDNSConfig?.ownership?.cname; const goBackUrl = useGenerateUrl('..', 'href'); diff --git a/packages/manager/apps/zimbra/src/pages/dashboard/Domains/__test__/ActionButtonDomain.spec.tsx b/packages/manager/apps/zimbra/src/pages/dashboard/Domains/__test__/ActionButtonDomain.spec.tsx index eb2f0bfe0036..f0397d80769b 100644 --- a/packages/manager/apps/zimbra/src/pages/dashboard/Domains/__test__/ActionButtonDomain.spec.tsx +++ b/packages/manager/apps/zimbra/src/pages/dashboard/Domains/__test__/ActionButtonDomain.spec.tsx @@ -4,6 +4,7 @@ import ActionButtonDomain from '../ActionButtonDomain.component'; import { render } from '@/utils/test.provider'; import domainTranslation from '@/public/translations/domains/Messages_fr_FR.json'; import { domainDetailMock } from '@/api/_mock_'; +import { FEATURE_FLAGS } from '@/utils'; describe('Domains datagrid action menu', () => { it('we have good number of item with good content', () => { @@ -13,16 +14,35 @@ describe('Domains datagrid action menu', () => { const menuItems = container.querySelectorAll('ods-popover ods-button'); - expect(menuItems.length).toBe(2); + if (FEATURE_FLAGS.DOMAIN_DIAGNOSTICS) { + expect(menuItems.length).toBe(3); - expect(menuItems[0]).toHaveAttribute( - 'label', - domainTranslation.zimbra_domains_tooltip_configure, - ); + expect(menuItems[0]).toHaveAttribute( + 'label', + domainTranslation.zimbra_domains_tooltip_configure, + ); - expect(menuItems[1]).toHaveAttribute( - 'label', - domainTranslation.zimbra_domains_tooltip_delete, - ); + expect(menuItems[1]).toHaveAttribute( + 'label', + domainTranslation.zimbra_domains_tooltip_diagnostics, + ); + + expect(menuItems[2]).toHaveAttribute( + 'label', + domainTranslation.zimbra_domains_tooltip_delete, + ); + } else { + expect(menuItems.length).toBe(2); + + expect(menuItems[0]).toHaveAttribute( + 'label', + domainTranslation.zimbra_domains_tooltip_configure, + ); + + expect(menuItems[1]).toHaveAttribute( + 'label', + domainTranslation.zimbra_domains_tooltip_delete, + ); + } }); }); diff --git a/packages/manager/apps/zimbra/src/pages/dashboard/Domains/__test__/Diagnostics.spec.tsx b/packages/manager/apps/zimbra/src/pages/dashboard/Domains/__test__/Diagnostics.spec.tsx new file mode 100644 index 000000000000..93e5ec06083c --- /dev/null +++ b/packages/manager/apps/zimbra/src/pages/dashboard/Domains/__test__/Diagnostics.spec.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { describe, expect, vi } from 'vitest'; +import { useSearchParams } from 'react-router-dom'; +import Diagnostics from '../Diagnostics.page'; +import { render, waitFor } from '@/utils/test.provider'; +import { domainDetailMock } from '@/api/_mock_'; +import { odsTabIsSelected, tabContent } from '@/utils/test.utils'; +import { DnsRecordType } from '@/utils/dnsconfig.constants'; + +vi.mocked(useSearchParams).mockReturnValue([ + new URLSearchParams({ + domainId: domainDetailMock.id, + }), + vi.fn(), +]); + +describe('Domain diagnostics page', () => { + it('should display correctly and have 3 tabs', async () => { + const { queryByTestId, container } = render(); + + await waitFor(() => { + expect(queryByTestId('spinner')).toBeNull(); + }); + + expect(queryByTestId('refresh')).toBeInTheDocument(); + + const tabs = container.querySelectorAll('ods-tab'); + + expect(tabs.length).toBe(3); + + expect(container.querySelector(odsTabIsSelected(true))).toHaveAttribute( + 'id', + DnsRecordType.MX, + ); + + await waitFor(() => { + expect(queryByTestId(tabContent(DnsRecordType.MX))).toBeInTheDocument(); + }); + }); +}); diff --git a/packages/manager/apps/zimbra/src/pages/dashboard/Domains/__test__/ModalsDiagnosticDNSRecord.spec.tsx b/packages/manager/apps/zimbra/src/pages/dashboard/Domains/__test__/ModalsDiagnosticDNSRecord.spec.tsx deleted file mode 100644 index 0aba630ae253..000000000000 --- a/packages/manager/apps/zimbra/src/pages/dashboard/Domains/__test__/ModalsDiagnosticDNSRecord.spec.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import React from 'react'; -import 'element-internals-polyfill'; -import '@testing-library/jest-dom'; -import { vi, describe, expect } from 'vitest'; -import { useSearchParams } from 'react-router-dom'; -import { render } from '@/utils/test.provider'; -import { DnsRecordType } from '@/utils'; -import { domainDetailMock } from '@/api/_mock_'; -import ModalDiagnosticDnsRecord from '../ModalDiagnosticDnsRecord.component'; -import domainDiagnosticTranslation from '@/public/translations/domains/diagnostic/Messages_fr_FR.json'; - -vi.mocked(useSearchParams).mockReturnValue([ - new URLSearchParams({ - domainId: domainDetailMock.id, - }), - vi.fn(), -]); - -describe('Domain diagnostic modalc ', () => { - it('should display diagnostic modal', async () => { - const { findByText, getByTestId } = render( - , - ); - - expect( - await findByText( - domainDiagnosticTranslation.zimbra_domain_modal_diagnostic_srv_title, - ), - ).toBeVisible(); - - expect( - getByTestId('diagnostic-srv-modal-secondary-btn'), - ).toBeInTheDocument(); - }); -}); - -describe('Domain diagnostic modal MX', () => { - it('should display diagnostic modal', async () => { - const { findByText, getByTestId } = render( - , - ); - - expect( - await findByText( - domainDiagnosticTranslation.zimbra_domain_modal_diagnostic_mx_title, - ), - ).toBeVisible(); - - expect( - getByTestId('diagnostic-mx-modal-secondary-btn'), - ).toBeInTheDocument(); - }); -}); - -describe('Domain diagnostic modal SPF', () => { - it('should display diagnostic modal', async () => { - const { findByText, getByTestId } = render( - , - ); - - expect( - await findByText( - domainDiagnosticTranslation.zimbra_domain_modal_diagnostic_spf_title, - ), - ).toBeVisible(); - - expect( - getByTestId('diagnostic-spf-modal-secondary-btn'), - ).toBeInTheDocument(); - }); -}); diff --git a/packages/manager/apps/zimbra/src/routes/routes.constants.ts b/packages/manager/apps/zimbra/src/routes/routes.constants.ts index 336e40dadef8..eedbc22dfb28 100644 --- a/packages/manager/apps/zimbra/src/routes/routes.constants.ts +++ b/packages/manager/apps/zimbra/src/routes/routes.constants.ts @@ -8,7 +8,10 @@ export const urls = { domains: '/:serviceName/domains', domainsEdit: '/:serviceName/domains/edit', domainsDelete: '/:serviceName/domains/delete', - domains_diagnostic: '/:serviceName/domains/diagnostic', + domains_diagnostic_mx: '/:serviceName/domains/diagnostics/mx', + domains_diagnostic_srv: '/:serviceName/domains/diagnostics/srv', + domains_diagnostic_spf: '/:serviceName/domains/diagnostics/spf', + domains_diagnostic_dkim: '/:serviceName/domains/diagnostics/dkim', email_accounts: '/:serviceName/email_accounts', email_accounts_add: '/:serviceName/email_accounts/add', email_accounts_edit: '/:serviceName/email_accounts/settings', diff --git a/packages/manager/apps/zimbra/src/routes/routes.tsx b/packages/manager/apps/zimbra/src/routes/routes.tsx index f9dad78ac258..ec9ba248b160 100644 --- a/packages/manager/apps/zimbra/src/routes/routes.tsx +++ b/packages/manager/apps/zimbra/src/routes/routes.tsx @@ -16,6 +16,10 @@ import { DELETE_ORGANIZATION, DELETE_REDIRECTION, DOMAIN, + DOMAIN_DIAGNOSTICS_DKIM, + DOMAIN_DIAGNOSTICS_MX, + DOMAIN_DIAGNOSTICS_SPF, + DOMAIN_DIAGNOSTICS_SRV, EDIT_DOMAIN, EDIT_EMAIL_ACCOUNT, EDIT_MAILING_LIST, @@ -194,24 +198,73 @@ export const Routes: any = [ }, }, { - path: 'diagnostic', + path: 'verify', ...lazyRouteConfig(() => - import( - '@/pages/dashboard/Domains/ModalDiagnosticDnsRecord.component' - ), + import('@/pages/dashboard/Domains/VerifyDomain.page'), ), + handle: { + isOverridePage: true, + tracking: { + pageName: VERIFY_DOMAIN, + pageType: PageType.funnel, + }, + breadcrumbLabel: 'zimbra_dashboard_domains_verify', + }, }, { - path: 'verify', + path: 'diagnostics/mx', ...lazyRouteConfig(() => - import('@/pages/dashboard/Domains/VerifyDomain.page'), + import('@/pages/dashboard/Domains/Diagnostics.page'), ), handle: { isOverridePage: true, tracking: { - pageName: VERIFY_DOMAIN, + pageName: DOMAIN_DIAGNOSTICS_MX, + pageType: PageType.funnel, + }, + breadcrumbLabel: 'zimbra_dashboard_domains_diagnostics', + }, + }, + { + path: 'diagnostics/srv', + ...lazyRouteConfig(() => + import('@/pages/dashboard/Domains/Diagnostics.page'), + ), + handle: { + isOverridePage: true, + tracking: { + pageName: DOMAIN_DIAGNOSTICS_SRV, + pageType: PageType.funnel, + }, + breadcrumbLabel: 'zimbra_dashboard_domains_diagnostics', + }, + }, + { + path: 'diagnostics/spf', + ...lazyRouteConfig(() => + import('@/pages/dashboard/Domains/Diagnostics.page'), + ), + handle: { + isOverridePage: true, + tracking: { + pageName: DOMAIN_DIAGNOSTICS_SPF, + pageType: PageType.funnel, + }, + breadcrumbLabel: 'zimbra_dashboard_domains_diagnostics', + }, + }, + { + path: 'diagnostics/dkim', + ...lazyRouteConfig(() => + import('@/pages/dashboard/Domains/Diagnostics.page'), + ), + handle: { + isOverridePage: true, + tracking: { + pageName: DOMAIN_DIAGNOSTICS_DKIM, pageType: PageType.funnel, }, + breadcrumbLabel: 'zimbra_dashboard_domains_diagnostics', }, }, ], diff --git a/packages/manager/apps/zimbra/src/tracking.constant.ts b/packages/manager/apps/zimbra/src/tracking.constant.ts index f11939aa39ce..72d2c89d4056 100644 --- a/packages/manager/apps/zimbra/src/tracking.constant.ts +++ b/packages/manager/apps/zimbra/src/tracking.constant.ts @@ -37,6 +37,13 @@ export const ADD_DOMAIN = `add_${DOMAIN}`; export const EDIT_DOMAIN = `edit_${DOMAIN}`; export const DELETE_DOMAIN = `delete_${DOMAIN}`; export const VERIFY_DOMAIN = `verify_${DOMAIN}`; +export const AUTO_CONFIGURE_DOMAIN = `auto-configure_${DOMAIN}`; +export const DOMAIN_DIAGNOSTICS = `${DOMAIN}_diagnostics`; +export const DOMAIN_DIAGNOSTICS_REFRESH = `${DOMAIN_DIAGNOSTICS}_refresh`; +export const DOMAIN_DIAGNOSTICS_MX = `${DOMAIN_DIAGNOSTICS}_mx`; +export const DOMAIN_DIAGNOSTICS_SPF = `${DOMAIN_DIAGNOSTICS}_spf`; +export const DOMAIN_DIAGNOSTICS_SRV = `${DOMAIN_DIAGNOSTICS}_srv`; +export const DOMAIN_DIAGNOSTICS_DKIM = `${DOMAIN_DIAGNOSTICS}_dkim`; // ORGANIZATION export const ORGANIZATION = 'organization'; diff --git a/packages/manager/apps/zimbra/src/utils/iamAction.constants.ts b/packages/manager/apps/zimbra/src/utils/iamAction.constants.ts index c31fc82f84e1..ba6b34aecdfe 100644 --- a/packages/manager/apps/zimbra/src/utils/iamAction.constants.ts +++ b/packages/manager/apps/zimbra/src/utils/iamAction.constants.ts @@ -20,6 +20,10 @@ export const IAM_ACTIONS = { create: `${IAM_ACTIONS_PREFIX}domain/create`, delete: `${IAM_ACTIONS_PREFIX}domain/delete`, edit: `${IAM_ACTIONS_PREFIX}domain/edit`, + diagnose: `${IAM_ACTIONS_PREFIX}domain/diagnose`, + }, + zoneDNS: { + get: 'dnsZone:apiovh:get', }, mailingList: { create: `${IAM_ACTIONS_PREFIX}mailingList/create`, diff --git a/packages/manager/apps/zimbra/src/utils/index.ts b/packages/manager/apps/zimbra/src/utils/index.ts index 15e1e236eb1e..f087e7a05af3 100644 --- a/packages/manager/apps/zimbra/src/utils/index.ts +++ b/packages/manager/apps/zimbra/src/utils/index.ts @@ -11,7 +11,7 @@ export const FEATURE_FLAGS = { REDIRECTIONS_EDIT: false, AUTOREPLIES: false, MAILINGLISTS: false, - DOMAIN_DIAGNOSTICS: false, + DOMAIN_DIAGNOSTICS_SRV: false, ORDER: false, }; diff --git a/packages/manager/apps/zimbra/src/utils/test.setup.tsx b/packages/manager/apps/zimbra/src/utils/test.setup.tsx index 444ee2d65209..af9ea9196d17 100644 --- a/packages/manager/apps/zimbra/src/utils/test.setup.tsx +++ b/packages/manager/apps/zimbra/src/utils/test.setup.tsx @@ -9,6 +9,7 @@ import { aliasMock, domainZone, orderCatalogMock, + domainsDiagnosticMock, } from '@/api/_mock_'; import { AccountType } from '@/api/account'; import { DomainType } from '@/api/domain'; @@ -100,6 +101,9 @@ vi.mock('@/api/domain', async (importActual) => { getDomainsZoneList: vi.fn(() => { return Promise.resolve(domainZone); }), + postZimbraPlatformDomainsDiagnostic: vi.fn(() => { + return Promise.resolve(domainsDiagnosticMock); + }), putZimbraDomain: vi.fn(() => { return Promise.resolve(); }), diff --git a/packages/manager/apps/zimbra/src/utils/test.utils.ts b/packages/manager/apps/zimbra/src/utils/test.utils.ts new file mode 100644 index 000000000000..99e1f7620a0e --- /dev/null +++ b/packages/manager/apps/zimbra/src/utils/test.utils.ts @@ -0,0 +1,5 @@ +export const odsTabIsSelected = (state: boolean) => + `ods-tab[is-selected=${state.toString()}]`; +export const odsTabById = (id: string) => `ods-tab[id="${id}"]`; +export const tabContent = (discriminator: string) => + `tab-content-${discriminator}`;