Sqlx az portal cost estimate ()

* 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

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

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

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