Sqlx az portal cost estimate (#1264)

* added isZoneRedundant to rp call

* Pass region item everywhere

* cost breakdown string added
This commit is contained in:
siddjoshi-ms 2022-06-08 10:16:43 -07:00 committed by GitHub
parent 98fb7a5fd6
commit c534b2d74b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 25 deletions

View File

@ -7,6 +7,7 @@ import SqlX from "./SqlX";
import {
FetchPricesResponse,
PriceMapAndCurrencyCode,
RegionItem,
RegionsResponse,
SqlxServiceResource,
UpdateDedicatedGatewayRequestParameters,
@ -139,7 +140,7 @@ const getGeneralPath = (subscriptionId: string, resourceGroup: string, name: str
return `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.DocumentDB/databaseAccounts/${name}`;
};
export const getRegions = async (): Promise<Array<string>> => {
export const getRegions = async (): Promise<Array<RegionItem>> => {
const telemetryData = {
feature: "Calculate approximate cost",
function: "getRegions",
@ -149,8 +150,6 @@ export const getRegions = async (): Promise<Array<string>> => {
const getRegionsTimestamp = selfServeTraceStart(telemetryData);
try {
const regions = new Array<string>();
const response = await armRequestWithoutPolling<RegionsResponse>({
host: configContext.ARM_ENDPOINT,
path: getGeneralPath(userContext.subscriptionId, userContext.resourceGroup, userContext.databaseAccount.name),
@ -158,20 +157,12 @@ export const getRegions = async (): Promise<Array<string>> => {
apiVersion: "2021-04-01-preview",
});
if (response.result.location !== undefined) {
regions.push(response.result.location.split(" ").join("").toLowerCase());
} else {
for (const location of response.result.locations) {
regions.push(location.locationName.split(" ").join("").toLowerCase());
}
}
selfServeTraceSuccess(telemetryData, getRegionsTimestamp);
return regions;
return response.result.properties.locations;
} catch (err) {
const failureTelemetry = { err, selfServeClassName: SqlX.name };
selfServeTraceFailure(failureTelemetry, getRegionsTimestamp);
return new Array<string>();
return new Array<RegionItem>();
}
};
@ -179,7 +170,7 @@ const getFetchPricesPathForRegion = (subscriptionId: string): string => {
return `/subscriptions/${subscriptionId}/providers/Microsoft.CostManagement/fetchPrices`;
};
export const getPriceMapAndCurrencyCode = async (regions: Array<string>): Promise<PriceMapAndCurrencyCode> => {
export const getPriceMapAndCurrencyCode = async (regions: Array<RegionItem>): Promise<PriceMapAndCurrencyCode> => {
const telemetryData = {
feature: "Calculate approximate cost",
function: "getPriceMapAndCurrencyCode",
@ -191,7 +182,7 @@ export const getPriceMapAndCurrencyCode = async (regions: Array<string>): Promis
try {
const priceMap = new Map<string, Map<string, number>>();
let currencyCode;
for (const region of regions) {
for (const regionItem of regions) {
const regionPriceMap = new Map<string, number>();
const response = await armRequestWithoutPolling<FetchPricesResponse>({
@ -202,7 +193,7 @@ export const getPriceMapAndCurrencyCode = async (regions: Array<string>): Promis
queryParams: {
filter:
"armRegionNameeq '" +
region +
regionItem.locationName.split(" ").join("").toLowerCase() +
"'andserviceFamilyeq 'Databases' and productName eq 'Azure Cosmos DB Dedicated Gateway - General Purpose'",
},
});
@ -215,7 +206,7 @@ export const getPriceMapAndCurrencyCode = async (regions: Array<string>): Promis
}
regionPriceMap.set(item.skuName, item.retailPrice);
}
priceMap.set(region, regionPriceMap);
priceMap.set(regionItem.locationName, regionPriceMap);
}
selfServeTraceSuccess(telemetryData, getPriceMapAndCurrencyCodeTimestamp);

View File

@ -1,3 +1,4 @@
import { RegionItem } from "SelfServe/SqlX/SqlxTypes";
import { IsDisplayable, OnChange, PropertyInfo, RefreshOptions, Values } from "../Decorators";
import {
selfServeTrace,
@ -208,7 +209,7 @@ const ApproximateCostDropDownInfo: Info = {
let priceMap: Map<string, Map<string, number>>;
let currencyCode: string;
let regions: Array<string>;
let regions: Array<RegionItem>;
const calculateCost = (skuName: string, instanceCount: number): Description => {
const telemetryData = {
@ -221,27 +222,47 @@ const calculateCost = (skuName: string, instanceCount: number): Description => {
try {
let costPerHour = 0;
for (const region of regions) {
const incrementalCost = priceMap.get(region).get(skuName.replace("Cosmos.", ""));
let costBreakdown = "";
for (const regionItem of regions) {
const incrementalCost = priceMap.get(regionItem.locationName).get(skuName.replace("Cosmos.", ""));
if (incrementalCost === undefined) {
throw new Error("Value not found in map.");
throw new Error(`${regionItem.locationName} not found in price map.`);
} else if (incrementalCost === 0) {
throw new Error(`${regionItem.locationName} cost per hour = 0`);
}
costPerHour += incrementalCost;
let regionalInstanceCount = instanceCount;
if (regionItem.isZoneRedundant) {
regionalInstanceCount = Math.ceil(instanceCount * 1.5);
}
const regionalCostPerHour = incrementalCost * regionalInstanceCount;
costBreakdown += `
${regionItem.locationName} ${regionItem.isZoneRedundant ? "(AZ)" : ""}
${regionalCostPerHour} ${currencyCode} (${regionalInstanceCount} instances * ${incrementalCost} ${currencyCode})\
`;
if (regionalCostPerHour === 0) {
throw new Error(`${regionItem.locationName} Cost per hour = 0`);
}
costPerHour += regionalCostPerHour;
}
if (costPerHour === 0) {
throw new Error("Cost per hour = 0");
}
costPerHour *= instanceCount;
costPerHour = Math.round(costPerHour * 100) / 100;
selfServeTraceSuccess(telemetryData, calculateCostTimestamp);
return {
textTKey: `${costPerHour} ${currencyCode}`,
textTKey: `${costPerHour} ${currencyCode}
${costBreakdown}`,
type: DescriptionType.Text,
};
} catch (err) {
alert(err);
const failureTelemetry = { err, regions, priceMap, selfServeClassName: SqlX.name };
selfServeTraceFailure(failureTelemetry, calculateCostTimestamp);

View File

@ -48,10 +48,14 @@ export type PriceItem = {
};
export type RegionsResponse = {
properties: RegionsProperties;
};
export type RegionsProperties = {
locations: Array<RegionItem>;
location: string;
};
export type RegionItem = {
locationName: string;
isZoneRedundant: boolean;
};