From c534b2d74b5cb425876a45f65ed88ceab3636df1 Mon Sep 17 00:00:00 2001 From: siddjoshi-ms <86025894+siddjoshi-ms@users.noreply.github.com> Date: Wed, 8 Jun 2022 10:16:43 -0700 Subject: [PATCH] Sqlx az portal cost estimate (#1264) * added isZoneRedundant to rp call * Pass region item everywhere * cost breakdown string added --- src/SelfServe/SqlX/SqlX.rp.ts | 25 ++++++++--------------- src/SelfServe/SqlX/SqlX.tsx | 35 ++++++++++++++++++++++++++------- src/SelfServe/SqlX/SqlxTypes.ts | 6 +++++- 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/src/SelfServe/SqlX/SqlX.rp.ts b/src/SelfServe/SqlX/SqlX.rp.ts index d224990bb..9cb430971 100644 --- a/src/SelfServe/SqlX/SqlX.rp.ts +++ b/src/SelfServe/SqlX/SqlX.rp.ts @@ -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> => { +export const getRegions = async (): Promise> => { const telemetryData = { feature: "Calculate approximate cost", function: "getRegions", @@ -149,8 +150,6 @@ export const getRegions = async (): Promise> => { const getRegionsTimestamp = selfServeTraceStart(telemetryData); try { - const regions = new Array(); - const response = await armRequestWithoutPolling({ host: configContext.ARM_ENDPOINT, path: getGeneralPath(userContext.subscriptionId, userContext.resourceGroup, userContext.databaseAccount.name), @@ -158,20 +157,12 @@ export const getRegions = async (): Promise> => { 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(); + return new Array(); } }; @@ -179,7 +170,7 @@ const getFetchPricesPathForRegion = (subscriptionId: string): string => { return `/subscriptions/${subscriptionId}/providers/Microsoft.CostManagement/fetchPrices`; }; -export const getPriceMapAndCurrencyCode = async (regions: Array): Promise => { +export const getPriceMapAndCurrencyCode = async (regions: Array): Promise => { const telemetryData = { feature: "Calculate approximate cost", function: "getPriceMapAndCurrencyCode", @@ -191,7 +182,7 @@ export const getPriceMapAndCurrencyCode = async (regions: Array): Promis try { const priceMap = new Map>(); let currencyCode; - for (const region of regions) { + for (const regionItem of regions) { const regionPriceMap = new Map(); const response = await armRequestWithoutPolling({ @@ -202,7 +193,7 @@ export const getPriceMapAndCurrencyCode = async (regions: Array): Promis queryParams: { filter: "armRegionName eq '" + - region + + regionItem.locationName.split(" ").join("").toLowerCase() + "' and serviceFamily eq 'Databases' and productName eq 'Azure Cosmos DB Dedicated Gateway - General Purpose'", }, }); @@ -215,7 +206,7 @@ export const getPriceMapAndCurrencyCode = async (regions: Array): Promis } regionPriceMap.set(item.skuName, item.retailPrice); } - priceMap.set(region, regionPriceMap); + priceMap.set(regionItem.locationName, regionPriceMap); } selfServeTraceSuccess(telemetryData, getPriceMapAndCurrencyCodeTimestamp); diff --git a/src/SelfServe/SqlX/SqlX.tsx b/src/SelfServe/SqlX/SqlX.tsx index 76623cd97..a760d8d97 100644 --- a/src/SelfServe/SqlX/SqlX.tsx +++ b/src/SelfServe/SqlX/SqlX.tsx @@ -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>; let currencyCode: string; -let regions: Array; +let regions: Array; 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); diff --git a/src/SelfServe/SqlX/SqlxTypes.ts b/src/SelfServe/SqlX/SqlxTypes.ts index 7ca2fe264..3360df734 100644 --- a/src/SelfServe/SqlX/SqlxTypes.ts +++ b/src/SelfServe/SqlX/SqlxTypes.ts @@ -48,10 +48,14 @@ export type PriceItem = { }; export type RegionsResponse = { + properties: RegionsProperties; +}; + +export type RegionsProperties = { locations: Array; - location: string; }; export type RegionItem = { locationName: string; + isZoneRedundant: boolean; };