Skip to content

Commit 46d8a8d

Browse files
committed
feat(backup-agent): add tenant agents subscription
ref: #BKP-498 Signed-off-by: Vincent Bonmarchand <[email protected]>
1 parent 6c5d226 commit 46d8a8d

File tree

19 files changed

+358
-23
lines changed

19 files changed

+358
-23
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"installed_agents": "Agents installés",
3+
"connected_vaults": "Vaults connectés",
4+
"billing_more_info": "Plus d'information sur la facturation",
5+
"number_of_connected_vaults": "{{connectedVaultCount}} connecté(s)",
6+
"number_of_installed_agents": "{{installedAgentsCount}} installé(s)"
7+
}

packages/manager/modules/backup-agent/src/components/CommonTiles/GeneralInformationsTile/GeneralInformationTile.component.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export type GeneralInformationTileProps<T extends { name: string }> = {
1212
isLoading: boolean;
1313
};
1414

15-
export function GeneralInformationTile<T extends { name: string }>({ resourceDetails, isLoading }: GeneralInformationTileProps<T>) {
15+
export function GeneralInformationTile<T extends { name: string }>({ resourceDetails, isLoading }: Readonly<GeneralInformationTileProps<T>>) {
1616
const { t } = useTranslation([NAMESPACES.DASHBOARD, NAMESPACES.STATUS, NAMESPACES.REGION, 'dashboard']);
1717
const { data: locationData, isLoading: isLocationLoading } = useLocationDetails(resourceDetails?.currentState.region)
1818

packages/manager/modules/backup-agent/src/data/api/tenants/tenants.requests.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export const getTenantDetails = async (tenantId: string) =>
2626
(await v2.get<Tenant>(getDetailsTenantRoute(tenantId))).data;
2727

2828
export const getVSPCTenantDetails = async (vspcTenantId: string) =>
29-
(await v2.get<Tenant>(getDetailsVspcTenantRoute(vspcTenantId))).data;
29+
(await v2.get<VSPCTenant>(getDetailsVspcTenantRoute(vspcTenantId))).data;
3030

3131

3232
export const getVSPCTenants = async (
@@ -36,3 +36,4 @@ export const getVSPCTenants = async (
3636

3737
export const deleteVSPCTenant = async (vspcTenantId: string): Promise<ApiResponse<string>> =>
3838
v2.delete(`${GET_VSPC_TENANTS_ROUTE}/${vspcTenantId}`);
39+

packages/manager/modules/backup-agent/src/data/hooks/tenants/useBackupTenantDetails.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,22 @@ export const useBackupTenantDetails = (
2626
select: (data): Resource<WithRegion<Tenant>> => mapTenantResourceToTenantResourceWithRegion(data),
2727
...options
2828
})
29+
30+
export const useBackupTenantDetailsMocks = ( {
31+
tenantId
32+
}: {
33+
tenantId: string;
34+
} & Partial<Omit<DefinedInitialDataOptions<Resource<Tenant>, unknown, Resource<WithRegion<Tenant>>>, "queryKey" | "queryFn">>,
35+
) =>
36+
useQuery({
37+
queryKey: BACKUP_TENANT_DETAILS_QUERY_KEY(tenantId),
38+
queryFn: () =>
39+
new Promise<{ data: Resource<Tenant> }>((resolve) => {
40+
console.log("🛜 mocking useBackupTenants api call...");
41+
setTimeout(() => {
42+
resolve({ data: TENANTS_MOCKS[1]! });
43+
});
44+
}),
45+
select: (res): Resource<WithRegion<Tenant>> =>
46+
mapTenantResourceToTenantResourceWithRegion(res.data),
47+
});

packages/manager/modules/backup-agent/src/data/hooks/tenants/useBackupTenants.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { UseQueryResult, useQuery } from '@tanstack/react-query';
1+
import { useQuery } from '@tanstack/react-query';
22

33
import { TENANTS_MOCKS } from '@/mocks/tenant/tenants.mock';
44
import {Tenant} from '@/types/Tenant.type';
@@ -29,3 +29,5 @@ export const useBackupTenantsMocks = () =>
2929
}),
3030
select: (res): Resource<WithRegion<Tenant>>[] => res.data.map((tenantResource) => mapTenantResourceToTenantResourceWithRegion(tenantResource)),
3131
});
32+
33+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { useQuery, DefinedInitialDataOptions } from "@tanstack/react-query";
2+
import {Resource} from "@/types/Resource.type";
3+
import {
4+
mapTenantResourceToTenantResourceWithRegion,
5+
} from "@/utils/mappers/mapTenantToTenantWithRegion";
6+
import {WithRegion} from "@/types/Utils.type";
7+
import { GET_VSPC_TENANTS_QUERY_KEY } from "./useVspcTenants";
8+
import { VSPCTenant } from "@/types/VspcTenant.type";
9+
import { VSPC_TENANTS_MOCKS } from "@/mocks/tenant/vspcTenants.mock";
10+
11+
export const BACKUP_VSPC_TENANT_DETAILS_QUERY_KEY = (vspcTenantID: string) => [...GET_VSPC_TENANTS_QUERY_KEY, vspcTenantID];
12+
13+
export const useBackupVSPCTenantDetails = (
14+
{
15+
tenantId,
16+
...options
17+
}: {
18+
tenantId: string;
19+
} & Partial<Omit<DefinedInitialDataOptions<Resource<VSPCTenant>, unknown, Resource<WithRegion<VSPCTenant>>>, "queryKey" | "queryFn">>,
20+
) => useQuery({
21+
queryFn: () => new Promise<Resource<VSPCTenant>>((resolve, reject) => {
22+
const result = VSPC_TENANTS_MOCKS.find(tenant => tenant.id === tenantId)
23+
result ? resolve(result) : reject(new Error('Tenant not found'))
24+
}),
25+
queryKey: BACKUP_VSPC_TENANT_DETAILS_QUERY_KEY(tenantId),
26+
select: (data): Resource<WithRegion<VSPCTenant>> => mapTenantResourceToTenantResourceWithRegion(data),
27+
...options
28+
})

packages/manager/modules/backup-agent/src/data/hooks/tenants/useVspcTenants.ts

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
import { UseQueryResult, useQuery } from '@tanstack/react-query';
1+
import { DefinedInitialDataOptions, UseQueryResult, useQueries, useQuery } from '@tanstack/react-query';
22

33
import { VSPC_TENANTS_MOCKS } from '@/mocks/tenant/vspcTenants.mock';
44
import { VSPCTenant } from '@/types/VspcTenant.type';
55

6-
import { getVSPCTenants } from '../../api/tenants/tenants.requests';
6+
import { getVSPCTenantDetails, getVSPCTenants } from '../../api/tenants/tenants.requests';
77
import { BACKUP_TENANTS_QUERY_KEY } from './useBackupTenants';
8-
import {Resource} from "@/types/Resource.type";
8+
import { Resource } from "@/types/Resource.type";
9+
import { useMemo } from 'react';
10+
import { AssociatedTenantVSPC } from '@/types/Tenant.type';
11+
import { countBackupAgents } from '@/utils/countBackupAgents';
912

1013
type TUseVSPCTenantsResult = UseQueryResult<Resource<VSPCTenant>[], Error>;
1114

@@ -29,3 +32,32 @@ export const useVSPCTenantsMocks = (): TUseVSPCTenantsResult =>
2932
});
3033
}),
3134
});
35+
36+
export const useInstalledBackupAgents = ({
37+
vspcTenants,
38+
}: {
39+
vspcTenants: AssociatedTenantVSPC[];
40+
} & Partial<Omit<DefinedInitialDataOptions<Resource<VSPCTenant>, unknown>, "queryKey" | "queryFn">>) => {
41+
const vspcTenantIds = useMemo(() => {
42+
if (!vspcTenants) return [];
43+
return vspcTenants.map((vspc) => vspc.id);
44+
}, [vspcTenants]);
45+
const vspcTenantQueries = useQueries({
46+
queries: vspcTenantIds.map((vspcTenantId) => ({
47+
queryKey: ['vspcTenantDetails', vspcTenantId],
48+
queryFn: () => getVSPCTenantDetails(vspcTenantId),
49+
enabled: !!vspcTenantId,
50+
})),
51+
combine: (results) => {
52+
const tenants = results
53+
.map((r) => r.data)
54+
.filter((tenant): tenant is VSPCTenant => !!tenant);
55+
56+
return {
57+
installedBackupAgents: countBackupAgents(tenants),
58+
isLoading: results.some((q) => q.isLoading),
59+
}
60+
},
61+
});
62+
return vspcTenantQueries;
63+
};

packages/manager/modules/backup-agent/src/mocks/tenant/tenants.mock.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,8 @@ export const TENANTS_MOCKS: Resource<Tenant>[] = [
1212
id: '1999f2f7-5140-4000-t1',
1313
name: 'Tenant1',
1414
vaults: [
15-
{
16-
region: 'eu-west-par',
17-
id: '1999f2f7-5140-4000-t1v',
18-
name: 'Vault1',
19-
performance: 'HIGHPERF',
20-
resourceName: 'tenant1-vault1',
21-
status: 'CREATING',
22-
type: 'BUNDLE',
23-
},
2415
],
2516
vspcTenants: [
26-
{
27-
region: 'eu-west-par',
28-
id: '1999f2f7-5140-4000-t1vspc',
29-
name: 'VspcTenant1',
30-
status: 'CREATING',
31-
},
3217
],
3318
},
3419
currentTasks: [],
@@ -65,6 +50,24 @@ export const TENANTS_MOCKS: Resource<Tenant>[] = [
6550
status: 'DELETING',
6651
type: 'BUNDLE',
6752
},
53+
{
54+
region: 'eu-west-rbx',
55+
id: '1999f2f7-5140-4000-t3v',
56+
name: 'Vault2',
57+
performance: 'HIGHPERF',
58+
resourceName: 'tenant3-vault3',
59+
status: 'DELETING',
60+
type: 'BUNDLE',
61+
},
62+
{
63+
region: 'eu-west-rbx',
64+
id: '1999f2f7-5140-4000-t4v',
65+
name: 'Vault2',
66+
performance: 'HIGHPERF',
67+
resourceName: 'tenant3-vault3',
68+
status: 'DELETING',
69+
type: 'BUNDLE',
70+
},
6871
],
6972
vspcTenants: [
7073
{

packages/manager/modules/backup-agent/src/pages/services/dashboard/general-information/GeneralInformation.page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { BillingInformationsTileStandard } from "@ovh-ux/manager-billing-informa
33
import { GeneralInformationTenantTile } from "./_components/general-information-tenant-tile/GeneralInformationTenantTile.component";
44
import { useParams } from "react-router-dom";
55
import { useBackupTenantDetails } from "@/data/hooks/tenants/useBackupTenantDetails";
6+
import { SubscriptionTile } from "./_components/subscription-tile/SubscriptionTile.component";
67

78

89
export default function GeneralInformationPage() {
@@ -12,6 +13,7 @@ export default function GeneralInformationPage() {
1213
return (
1314
<section className="max-w-6xl mx-auto px-12 grid md:grid-cols-2 lg:grid-cols-3 grid-cols-1 gap-8">
1415
<GeneralInformationTenantTile tenantId={tenantId!} />
16+
<SubscriptionTile tenantId={tenantResource?.id} />
1517
<BillingInformationsTileStandard resourceName={tenantResource?.id} />
1618
</section>
1719
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { vi } from "vitest"
2+
import { render, screen } from "@testing-library/react"
3+
import { NAMESPACES } from "@ovh-ux/manager-common-translations";
4+
import { mockVaults } from "@/mocks/vaults/vaults";
5+
6+
import { SubscriptionTile } from "../subscription-tile/SubscriptionTile.component"
7+
import { TENANTS_MOCKS } from "@/mocks/tenant/tenants.mock";
8+
import { MemoryRouter } from "react-router-dom";
9+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
10+
11+
const LABELS_VISIBLES = [`${NAMESPACES.BILLING}:subscription`]
12+
13+
14+
const { useBackupVaultDetailsMock } = vi.hoisted(() => ({
15+
useBackupVaultDetailsMock: vi.fn()
16+
}))
17+
18+
vi.mock('@/data/hooks/vaults/getVaultDetails', () => ({
19+
useBackupVaultDetails: useBackupVaultDetailsMock
20+
}))
21+
22+
const { useBackupTenantDetailsMock } = vi.hoisted(() => ({
23+
useBackupTenantDetailsMock: vi.fn()
24+
}))
25+
26+
vi.mock('@/data/hooks/tenants/useBackupTenantDetails', () => ({
27+
useBackupTenantDetails: useBackupTenantDetailsMock
28+
}))
29+
30+
vi.mock("react-i18next", () => ({
31+
useTranslation: vi.fn().mockReturnValue({
32+
t: vi.fn().mockImplementation((key: string) => key),
33+
})
34+
}))
35+
36+
37+
describe('SubscriptionTile', () => {
38+
it("Should render SubscriptionTile component", async () => {
39+
const queryClient = new QueryClient();
40+
useBackupVaultDetailsMock.mockReturnValue({ data: mockVaults[0]!, isLoading: false })
41+
useBackupTenantDetailsMock.mockReturnValue({ data: TENANTS_MOCKS[0]!, isLoading: false })
42+
const { container } = render(<QueryClientProvider client={queryClient}><MemoryRouter><SubscriptionTile tenantId={mockVaults[0]!.id} /></MemoryRouter></QueryClientProvider>)
43+
44+
await expect(container).toBeAccessible();
45+
46+
LABELS_VISIBLES.forEach(label => {
47+
expect(screen.getByText(label)).toBeVisible()
48+
})
49+
})
50+
});

0 commit comments

Comments
 (0)