Skip to content

Commit

Permalink
feat(pci-block-storage): add deploy zone
Browse files Browse the repository at this point in the history
ref: TAPC-2112

Signed-off-by: Simon Chaumet <[email protected]>
  • Loading branch information
SimonChaumet committed Dec 20, 2024
1 parent 52cdc63 commit 11b0066
Show file tree
Hide file tree
Showing 16 changed files with 334 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,14 @@
"pci_projects_project_storages_blocks_add_save_form": "Création du volume en cours",
"pci_projects_project_storages_blocks_add_error_query": "Une erreur est survenue lors de la récupération des régions : {{ message }}",
"pci_projects_project_storages_blocks_add_success_message": "Le volume {{volume}} a été ajouté",
"pci_projects_project_storages_blocks_add_error_post": "Une erreur est survenue lors de l'ajout du volume {{ volume }} : {{ message }}"
"pci_projects_project_storages_blocks_add_error_post": "Une erreur est survenue lors de l'ajout du volume {{ volume }} : {{ message }}",
"pci_projects_project_storages_blocks_add_deployment_mode_title": "Sélectionnez un mode de déploiement",
"pci_projects_project_storages_blocks_add_deployment_mode_description": "Sélectionnez la configuration optimale pour garantir la disponibilité, la résilience et la latence appropriée de vos données en fonction de vos cas d'usage. <Link>En savoir plus</Link>",
"pci_projects_project_storages_blocks_add_deployment_mode_title_1AZ": "Région 1-AZ",
"pci_projects_project_storages_blocks_add_deployment_mode_description_1AZ": "Déploiement résilient et économique sur 1 zone de disponibilité.",
"pci_projects_project_storages_blocks_add_deployment_mode_title_3AZ": "Région 3-AZ",
"pci_projects_project_storages_blocks_add_deployment_mode_description_3AZ": "Déploiement haute résilience/haute disponibilité pour vos applications critiques sur 3 zones de disponibilité.",
"pci_projects_project_storages_blocks_add_deployment_mode_title_LZ": "Local Zone",
"pci_projects_project_storages_blocks_add_deployment_mode_description_LZ": "Déploiement de vos applications au plus près de vos utilisatrices et utilisateurs pour une faible latence et la résidence des données.",
"pci_projects_project_storages_blocks_add_deployment_mode_price_from": "A partir de {{price}} HT/Go/heure"
}
95 changes: 95 additions & 0 deletions packages/manager/apps/pci-block-storage/src/api/data/catalog.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { TAddon } from '@ovh-ux/manager-pci-common';
import { v6 } from '@ovh-ux/manager-core-api';

export type TPricing = {
capacities: string[];
mode: string;
Expand Down Expand Up @@ -26,3 +29,95 @@ export type TPricing = {
promotions: unknown[];
engagementConfiguration?: unknown;
};

export type TRegionGroup = {
name: string;
tags: string[];
};

export type TModelGroup = {
name: string;
type: string;
tags: string[];
};

export type TRegion = {
name: string;
type: '3-az' | 'region' | 'localzone';
availabilityZone: string[];
isInMaintenance: boolean;
isUp: boolean;
isActivated: boolean;
country: string;
regionGroup: string;
datacenter: string;
};

export type TVolumePricing = Omit<
TAddon['pricings'][number],
'interval' | 'intervalUnit'
> & {
regions: TRegion['name'][];
interval: 'day' | 'hour' | 'month' | 'none';
};

export type TVolumeAddon = Omit<TAddon, 'pricings'> & {
groups: TRegionGroup['name'][];
pricings: TVolumePricing[];
pricingType: 'consumption' | string;
};

export type TVolumeCatalog = {
modelsGroups: TModelGroup[];
regionsGroups: TRegionGroup[];
regions: TRegion[];
models: TVolumeAddon[];
};

export const getVolumeCatalog = async (
projectId: string,
): Promise<TVolumeCatalog> =>
(await v6.get<TVolumeCatalog>(`/cloud/project/${projectId}/catalog/volume`))
.data;

export function getLeastPrice(pricings: TVolumePricing[]) {
return pricings.reduce<number | null>(
(leastPrice, p) =>
leastPrice === null ? p.price : Math.min(p.price, leastPrice),
null,
);
}

export function mapPricesToGroups(
groups: TRegionGroup[],
regions: TRegion[],
models: TVolumeAddon[],
): (TRegionGroup & { leastPrice: number })[] {
return groups.map((group) => {
const groupRegions = regions
.filter((r) => r.type === group.name)
.map((r) => r.name);

return {
...group,
leastPrice: models
.filter((m) => m.pricingType === 'consumption')
.map((m) =>
getLeastPrice(
m.pricings.filter(
(p) =>
p.capacities.includes('consumption') &&
p.regions.some((r) => groupRegions.includes(r)),
),
),
)
.reduce<number | null>(
(leastPrice, modelLeastPrice) =>
leastPrice === null
? modelLeastPrice
: Math.min(modelLeastPrice, leastPrice),
null,
),
};
});
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import { useQuery } from '@tanstack/react-query';
import { useMe } from '@ovh-ux/manager-react-components';
import { getCatalog } from '@ovh-ux/manager-pci-common';
import { getVolumeCatalog } from '@/api/data/catalog';

export const getCatalogQuery = (ovhSubsidiary: string) => ({
queryKey: ['catalog'],
queryFn: () => getCatalog(ovhSubsidiary),
});

/**
* @deprecated use {@link useVolumeCatalog} instead
*/
export const useCatalog = () => {
const { me } = useMe();
return useQuery({
...getCatalogQuery(me?.ovhSubsidiary),
enabled: !!me,
});
};

export const useVolumeCatalog = (projectId: string) =>
useQuery({
queryKey: ['projects', projectId, 'catalog', 'volume'],
queryFn: () => getVolumeCatalog(projectId),
});

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,21 @@ import {
} from '@ovhcloud/ods-components';

import { useTranslation } from 'react-i18next';
import { TAddon } from '@ovh-ux/manager-pci-common';
import { PriceEstimate } from '@/pages/new/components/PriceEstimate';
import { HighSpeedV2Infos } from '@/pages/new/components/HighSpeedV2Infos';
import { TLocalisation } from '@/api/hooks/useRegions';
import { StepState } from '@/pages/new/hooks/useStep';
import { useRegionsQuota } from '@/api/hooks/useQuota';
import { useVolumeMaxSize } from '@/api/data/quota';
import { TVolumeAddon } from '@/api/data/catalog';

export const VOLUME_MIN_SIZE = 10; // 10 Gio
export const VOLUME_UNLIMITED_QUOTA = -1; // Should be 10 * 1024 (but API is wrong)

interface CapacityStepProps {
projectId: string;
region: TLocalisation;
volumeType: TAddon;
volumeType: TVolumeAddon;
step: StepState;
onSubmit: (volumeCapacity: number) => void;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { useParams } from 'react-router-dom';
import {
OsdsChip,
OsdsSpinner,
OsdsText,
} from '@ovhcloud/ods-components/react';
import { useTranslation } from 'react-i18next';
import { ODS_CHIP_SIZE, ODS_SPINNER_SIZE } from '@ovhcloud/ods-components';
import { useMemo, useState } from 'react';
import { TilesInputComponent } from '@ovh-ux/manager-react-components';
import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming';
import { mapPricesToGroups, TRegionGroup } from '@/api/data/catalog';
import { useVolumeCatalog } from '@/api/hooks/useCatalog';

export const DeploymentModeStep = () => {
const { t } = useTranslation(['add', 'common']);
const { projectId } = useParams();

const { data, isPending } = useVolumeCatalog(projectId);

const mappedGroups = useMemo(
() =>
data
? mapPricesToGroups(data.regionsGroups, data.regions, data.models)
: [],
[data],
);

const [selectedRegionGroup, setSelectedRegionGroup] = useState<
null | ReturnType<typeof mapPricesToGroups>[number]
>(null);

// if (isPending) return <OsdsSkeleton />;
return <OsdsSpinner inline size={ODS_SPINNER_SIZE.md} />;

return (
<div>
<TilesInputComponent<ReturnType<typeof mapPricesToGroups>[number]>
items={mappedGroups}
value={selectedRegionGroup}
onInput={setSelectedRegionGroup}
label={(group) => (
<div className="w-full">
<div className="border-solid border-0 border-b border-b-[#85d9fd] py-3 d-flex">
<OsdsText>
{t(
`pci_projects_project_storages_blocks_add_deployment_mode_title_${group.name}`,
)}
</OsdsText>
</div>
<div className="py-3">
{group.tags.includes('is_new') ? (
<div>
<OsdsChip
className="ms-3"
color={ODS_THEME_COLOR_INTENT.success}
size={ODS_CHIP_SIZE.sm}
inline
>
{t('common:pci_projects_project_storages_blocks_new')}
</OsdsChip>
Gratuit
</div>
) : (
group.leastPrice !== null &&
t(
'pci_projects_project_storages_blocks_add_deployment_mode_price_from',
{ price: group.leastPrice },
)
)}
</div>
</div>
)}
/>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
ODS_THEME_TYPOGRAPHY_SIZE,
} from '@ovhcloud/ods-common-theming';
import { useTranslation } from 'react-i18next';
import { TAddon } from '@ovh-ux/manager-pci-common';
import { TVolumeAddon } from '@/api/data/catalog';

export const HIGHSPEED_V2_PLANCODE = 'volume.high-speed-gen2.consumption';

Expand All @@ -17,7 +17,10 @@ export function getDisplayUnit(unit: string) {
return unit;
}

export function computeBandwidthToAllocate(capacity: number, addon: TAddon) {
export function computeBandwidthToAllocate(
capacity: number,
addon: TVolumeAddon,
) {
const { bandwidth } = addon.blobs.technical;
const allocatedBandwidth = capacity * bandwidth.level;
const maxBandwidthInMb = bandwidth.max * 1000;
Expand All @@ -27,7 +30,7 @@ export function computeBandwidthToAllocate(capacity: number, addon: TAddon) {
: `${maxBandwidthInMb} ${getDisplayUnit(bandwidth.unit)}`;
}

export function computeIopsToAllocate(capacity: number, addon: TAddon) {
export function computeIopsToAllocate(capacity: number, addon: TVolumeAddon) {
const { volume } = addon.blobs.technical;
const allocatedIops = capacity * volume.iops.level;

Expand All @@ -38,7 +41,7 @@ export function computeIopsToAllocate(capacity: number, addon: TAddon) {

export interface HighSpeedV2InfosProps {
volumeCapacity: number;
volumeType: TAddon;
volumeType: TVolumeAddon;
}

export function HighSpeedV2Infos({
Expand Down
Loading

0 comments on commit 11b0066

Please sign in to comment.