Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
"label": "Öffentliche IP-Adresse",
"placeholder": "Bsp.: 185.26.17.45",
"hint": "Die öffentliche IP-Adresse Ihres VBR-Servers.",
"error": "Diese IP-Adresse scheint nicht gültig zu sein — überprüfen Sie das IPv4- oder IPv6-Format."
"error": "Diese IP-Adresse scheint nicht gültig zu sein — überprüfen Sie das IPv4-Format."
},
"veeam_client_ip": {
"label": "IP-Adresse des Veeam-Clients",
Expand All @@ -107,7 +107,7 @@
"label": "Private IP-Adresse (LAN)",
"placeholder": "Bsp.: 192.168.1.10",
"hint": "Lokale IP-Adresse Ihres VBR-Servers im internen Netzwerk.",
"error": "Diese IP-Adresse scheint nicht gültig zu sein — überprüfen Sie das IPv4- oder IPv6-Format."
"error": "Diese IP-Adresse scheint nicht gültig zu sein — überprüfen Sie das IPv4-Format."
},
"vault_name": {
"label": "Vault-Name",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
"label": "Public IP address",
"placeholder": "E.g.: 185.26.17.45",
"hint": "The public IP address of your VBR server.",
"error": "This IP address doesn't look valid — check the IPv4 or IPv6 format."
"error": "This IP address doesn't look valid — check the IPv4 format."
},
"veeam_client_ip": {
"label": "Veeam Client IP address",
Expand All @@ -107,7 +107,7 @@
"label": "Private IP address (LAN)",
"placeholder": "E.g.: 192.168.1.10",
"hint": "Local IP address of your VBR server on the internal network.",
"error": "This IP address doesn't look valid — check the IPv4 or IPv6 format."
"error": "This IP address doesn't look valid — check the IPv4 format."
},
"vault_name": {
"label": "Vault name",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
"label": "Dirección IP pública",
"placeholder": "Ej.: 185.26.17.45",
"hint": "La dirección IP pública de tu servidor VBR.",
"error": "Esta dirección IP no parece válida — comprueba el formato IPv4 o IPv6."
"error": "Esta dirección IP no parece válida — comprueba el formato IPv4."
},
"veeam_client_ip": {
"label": "Dirección IP del cliente Veeam",
Expand All @@ -107,7 +107,7 @@
"label": "Dirección IP privada (LAN)",
"placeholder": "Ej.: 192.168.1.10",
"hint": "Dirección IP local de tu servidor VBR en la red interna.",
"error": "Esta dirección IP no parece válida — comprueba el formato IPv4 o IPv6."
"error": "Esta dirección IP no parece válida — comprueba el formato IPv4."
},
"vault_name": {
"label": "Nombre del Vault",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
"label": "Adresse IP publique",
"placeholder": "Ex : 185.26.17.45",
"hint": "L'adresse IP publique de votre serveur VBR.",
"error": "Cette adresse IP ne semble pas valide — vérifiez le format IPv4 ou IPv6."
"error": "Cette adresse IP ne semble pas valide — vérifiez le format IPv4."
},
"veeam_client_ip": {
"label": "Adresse IP du client Veeam",
Expand All @@ -107,7 +107,7 @@
"label": "Adresse IP privée (LAN)",
"placeholder": "Ex : 192.168.1.10",
"hint": "Adresse IP locale de votre serveur VBR sur le réseau interne.",
"error": "Cette adresse IP ne semble pas valide — vérifiez le format IPv4 ou IPv6."
"error": "Cette adresse IP ne semble pas valide — vérifiez le format IPv4."
},
"vault_name": {
"label": "Nom du Vault",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
"label": "Adresse IP publique",
"placeholder": "Ex : 185.26.17.45",
"hint": "L'adresse IP publique de votre serveur VBR.",
"error": "Cette adresse IP ne semble pas valide — vérifiez le format IPv4 ou IPv6."
"error": "Cette adresse IP ne semble pas valide — vérifiez le format IPv4."
},
"veeam_client_ip": {
"label": "Adresse IP du client Veeam",
Expand All @@ -107,7 +107,7 @@
"label": "Adresse IP privée (LAN)",
"placeholder": "Ex : 192.168.1.10",
"hint": "Adresse IP locale de votre serveur VBR sur le réseau interne.",
"error": "Cette adresse IP ne semble pas valide — vérifiez le format IPv4 ou IPv6."
"error": "Cette adresse IP ne semble pas valide — vérifiez le format IPv4."
},
"vault_name": {
"label": "Nom du Vault",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
"label": "Indirizzo IP pubblico",
"placeholder": "Es.: 185.26.17.45",
"hint": "L'indirizzo IP pubblico del tuo server VBR.",
"error": "Questo indirizzo IP non sembra valido — controlla il formato IPv4 o IPv6."
"error": "Questo indirizzo IP non sembra valido — controlla il formato IPv4."
},
"veeam_client_ip": {
"label": "Indirizzo IP del client Veeam",
Expand All @@ -107,7 +107,7 @@
"label": "Indirizzo IP privato (LAN)",
"placeholder": "Es.: 192.168.1.10",
"hint": "Indirizzo IP locale del tuo server VBR sulla rete interna.",
"error": "Questo indirizzo IP non sembra valido — controlla il formato IPv4 o IPv6."
"error": "Questo indirizzo IP non sembra valido — controlla il formato IPv4."
},
"vault_name": {
"label": "Nome del Vault",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
"label": "Publiczny adres IP",
"placeholder": "Np.: 185.26.17.45",
"hint": "Publiczny adres IP Twojego serwera VBR.",
"error": "Ten adres IP nie wygląda na prawidłowy — sprawdź format IPv4 lub IPv6."
"error": "Ten adres IP nie wygląda na prawidłowy — sprawdź format IPv4."
},
"veeam_client_ip": {
"label": "Adres IP klienta Veeam",
Expand All @@ -107,7 +107,7 @@
"label": "Prywatny adres IP (LAN)",
"placeholder": "Np.: 192.168.1.10",
"hint": "Lokalny adres IP Twojego serwera VBR w sieci wewnętrznej.",
"error": "Ten adres IP nie wygląda na prawidłowy — sprawdź format IPv4 lub IPv6."
"error": "Ten adres IP nie wygląda na prawidłowy — sprawdź format IPv4."
},
"vault_name": {
"label": "Nazwa Vault",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
"label": "Endereço IP público",
"placeholder": "Ex.: 185.26.17.45",
"hint": "O endereço IP público do seu servidor VBR.",
"error": "Este endereço IP não parece válido — verifique o formato IPv4 ou IPv6."
"error": "Este endereço IP não parece válido — verifique o formato IPv4."
},
"veeam_client_ip": {
"label": "Endereço IP do cliente Veeam",
Expand All @@ -107,7 +107,7 @@
"label": "Endereço IP privado (LAN)",
"placeholder": "Ex.: 192.168.1.10",
"hint": "Endereço IP local do seu servidor VBR na rede interna.",
"error": "Este endereço IP não parece válido — verifique o formato IPv4 ou IPv6."
"error": "Este endereço IP não parece válido — verifique o formato IPv4."
},
"vault_name": {
"label": "Nome do Vault",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ interface BackupServerActionsCellProps {
backupServerId: string;
/** Désactive le menu entier : opération en cours sur la ligne. */
isDisabled: boolean;
/** URN du serveur, pour le check IAM des actions « modifier »/« supprimer ». */
/** URN de la ressource backupLicense, pour le check IAM des actions « modifier »/« supprimer ». */
urn?: string;
}

Expand All @@ -45,8 +45,8 @@ interface BackupServerActionsCellProps {
* pleinement cliquable/navigable : désactiver le bouton intérieur ne bloque pas la navigation.
* On utilise donc `onClick` + `navigate()` (jamais `href`) pour que `isDisabled` bloque *réellement*
* l'action — mêmes patron que `veeam-backup` (`DatagridCell.component.tsx::ActionCell`).
* Fail-closed : tant que `urn` est absent (contrat API non confirmé, cf. API-STATUS.md) ou que
* le check est en cours/négatif, l'entrée reste désactivée — jamais de bypass permissif.
* Fail-closed : tant que `urn` est absent ou que le check est en cours/négatif, l'entrée reste
* désactivée — jamais de bypass permissif.
*/
export default function BackupServerActionsCell({
backupServerId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ interface EditRecapPanelProps {
errorMessage: string | null;
isSaving: boolean;
onSave: () => void;
/** URN du serveur : accès direct par URL, donc protégé indépendamment du menu ⋮. */
/** URN de la ressource backupLicense : accès direct par URL, donc protégé indépendamment du menu ⋮. */
urn?: string;
}

Expand Down Expand Up @@ -114,13 +114,6 @@ export default function EditRecapPanel({
</OdsMessage>
)}

{/* Jamais désactivé côté validation : un clic avec formulaire invalide révèle les erreurs et
rouvre l'étape fautive (cf. EditBackupServer.page), feedback actionnable plutôt qu'un
bouton grisé silencieux — même parti pris que le CTA du tunnel de commande. `ManagerButton`
y ajoute un check IAM indépendant du menu ⋮ : protège aussi l'accès direct par URL.
Fail-closed explicite (`!urn`) : par défaut, `ManagerButton` autorise l'action tant que
`urn` est absent (bypass silencieux, cf. son code) — inacceptable ici, donc on force la
désactivation nous-mêmes tant que le contrat API ne garantit pas ce champ. */}
<ManagerButton
id={saveButtonId}
type="button"
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { fetchIcebergV2 } from '@ovh-ux/manager-core-api';

import { postJSON } from '@/data/api/Client.api';
import { BackupLicenseResource, CreateBackupLicenseParams } from '@/types/BackupLicense.type';
import { BackupServerResource } from '@/types/BackupServer.type';
import { BackupLicenseResource } from '@/types/BackupLicense.type';
import { getBackupLicensesRoute } from '@/utils/apiRoutes/apiRoutes';

export type GetBackupLicensesParams = {
Expand All @@ -19,15 +17,3 @@ export const getBackupLicenses = async ({
});
return data;
};

/** Ajout d'un serveur VBR supplémentaire (BKP-1217) : le vault existe déjà, on ne crée que la licence/serveur. */
export const createBackupLicense = ({
backupServicesId,
vspcTenantId,
body,
}: CreateBackupLicenseParams): Promise<BackupServerResource> =>
postJSON<BackupServerResource>(
'v2',
getBackupLicensesRoute(backupServicesId, vspcTenantId),
body,
);
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import { v2 } from '@ovh-ux/manager-core-api';
import { BackupServerResource } from '@/types/BackupServer.type';
import { getBackupServersRoute } from '@/utils/apiRoutes/apiRoutes';

import { getBackupServers } from './backupServers.requests';
import { createBackupServer, getBackupServers } from './backupServers.requests';

type MockableGet = Pick<AxiosInstance, 'get'>;
type MockablePost = Pick<AxiosInstance, 'post'>;

describe('getBackupServers', () => {
const mockGet = vi.fn();
Expand Down Expand Up @@ -46,3 +47,33 @@ describe('getBackupServers', () => {
await expect(getBackupServers(params)).rejects.toThrow('boom');
});
});

describe('createBackupServer', () => {
const mockPost = vi.fn();

beforeEach(() => {
vi.clearAllMocks();
Object.assign(v2 as unknown as MockablePost, { post: mockPost });
});

it('posts the payload to the backupServer route, without the scope ids', async () => {
mockPost.mockResolvedValue({ data: null });

await createBackupServer({
backupServicesId: 'service-1',
vspcTenantId: 'vspc-1',
backupLicensesId: 'license-1',
displayName: 'VBR-CUST-SERV-02',
licenseType: 'VEEAM_BACKUP_REPLICATION_ENTERPRISE_PLUS',
externalIps: ['203.0.113.10'],
privateIps: ['192.168.10.2'],
});

expect(mockPost).toHaveBeenCalledWith(getBackupServersRoute('service-1', 'vspc-1', 'license-1'), {
displayName: 'VBR-CUST-SERV-02',
licenseType: 'VEEAM_BACKUP_REPLICATION_ENTERPRISE_PLUS',
externalIps: ['203.0.113.10'],
privateIps: ['192.168.10.2'],
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ export type EditBackupServerParams = GetBackupServersParams & {
privateIps: string[];
};

export type CreateBackupServerParams = GetBackupServersParams & {
displayName: string;
licenseType: string;
externalIps: string[];
privateIps: string[];
};

export type DeleteBackupServerParams = GetBackupServersParams & {
backupServerId: string;
};
Expand All @@ -41,6 +48,22 @@ export const getBackupServers = async ({
}
};

/**
* Ajout d'un serveur VBR supplémentaire sur un vault déjà provisionné (BKP-1217) : un simple
* POST sur la ressource `backupServer`, plus de panier Agora pour ce flux.
*/
export const createBackupServer = async ({
backupServicesId,
vspcTenantId,
backupLicensesId,
...payload
}: CreateBackupServerParams): Promise<void> => {
await v2.post(
getBackupServersRoute(backupServicesId, vspcTenantId, backupLicensesId),
payload,
);
};

/**
* Édition d'un serveur VBR (BKP-1218). Nom et IP sont appliqués immédiatement ; un changement
* de `licenseType` est différé au 1er du mois suivant côté backend — le front envoie la cible
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { v6 } from '@ovh-ux/manager-core-api';

import { ServiceConsumption } from '@/types/Consumption.type';
import { LicenseConsumption, ServiceConsumption } from '@/types/Consumption.type';
import {
getLicenseConsumptionRoute,
getServiceConsumptionRoute,
Expand All @@ -13,7 +13,7 @@ export const getServiceConsumption = async (serviceId: string): Promise<ServiceC
};

/** Prix de la licence (§3.2) : pas de `quantity` à lire, `/consumption` sans `/element`. */
export const getLicenseConsumption = async (serviceId: string): Promise<ServiceConsumption[]> => {
const { data } = await v6.get<ServiceConsumption[]>(getLicenseConsumptionRoute(serviceId));
export const getLicenseConsumption = async (serviceId: string): Promise<LicenseConsumption> => {
const { data } = await v6.get<LicenseConsumption>(getLicenseConsumptionRoute(serviceId));
return data;
};
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ describe('usePrepareBackupLicensesCart', () => {
expect(answered).toHaveLength(12);
expect(answered).toEqual(
expect.arrayContaining([
{ label: 'backupserver-public-ip', value: '203.0.113.10' },
{ label: 'backupserver-public-ip', value: '203.0.113.10/32' },
{ label: 'license-type', value: LicenseApiValue.VDP_PREMIUM },
{ label: 'vault-azname', value: 'eu-west-par' },
]),
Expand Down Expand Up @@ -226,7 +226,7 @@ describe('usePrepareBackupLicensesCart', () => {
const emitted = await resolveApiRequests(requests);

expect(configurations(emitted)).toEqual(
expect.arrayContaining([{ label: 'backupserver-private-ip', value: '192.168.1.10' }]),
expect.arrayContaining([{ label: 'backupserver-private-ip', value: '192.168.1.10/32' }]),
);
});

Expand Down Expand Up @@ -319,10 +319,10 @@ describe('usePrepareBackupLicensesCart, with an existing backup-tenant', () => {

expect(sequence).toEqual([
'/order/cart',
`/order/cart/${MOCK_CART_ID}/assign`,
`/order/cartServiceOption/backupServices/${serviceName}`,
`/order/cart/${MOCK_CART_ID}/backupServices/options`,
`/order/cartServiceOption/backupServices/${serviceName}`,
`/order/cart/${MOCK_CART_ID}/assign`,
]);
});

Expand Down
Loading
Loading