Fix throughput cost estimate in add collection panel (#1070)
This commit is contained in:
parent
2d945c8231
commit
665270296f
|
@ -5,7 +5,14 @@ import { Collection } from "../Contracts/ViewModels";
|
|||
import DocumentId from "../Explorer/Tree/DocumentId";
|
||||
import { extractFeatures } from "../Platform/Hosted/extractFeatures";
|
||||
import { updateUserContext } from "../UserContext";
|
||||
import { deleteDocument, getEndpoint, getFeatureEndpointOrDefault, queryDocuments, readDocument, updateDocument } from "./MongoProxyClient";
|
||||
import {
|
||||
deleteDocument,
|
||||
getEndpoint,
|
||||
getFeatureEndpointOrDefault,
|
||||
queryDocuments,
|
||||
readDocument,
|
||||
updateDocument,
|
||||
} from "./MongoProxyClient";
|
||||
|
||||
const databaseId = "testDB";
|
||||
|
||||
|
@ -260,11 +267,10 @@ describe("MongoProxyClient", () => {
|
|||
const features = extractFeatures(params);
|
||||
updateUserContext({
|
||||
authType: AuthType.AAD,
|
||||
features: features
|
||||
features: features,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("returns a local endpoint", () => {
|
||||
const endpoint = getFeatureEndpointOrDefault("readDocument");
|
||||
expect(endpoint).toEqual("https://localhost:12901/api/mongo/explorer");
|
||||
|
|
|
@ -268,7 +268,7 @@ export function deleteDocument(databaseId: string, collection: Collection, docum
|
|||
? documentId.partitionKeyProperty
|
||||
: "",
|
||||
};
|
||||
const endpoint = getFeatureEndpointOrDefault("deleteDocument");;
|
||||
const endpoint = getFeatureEndpointOrDefault("deleteDocument");
|
||||
|
||||
return window
|
||||
.fetch(`${endpoint}?${queryString.stringify(params)}`, {
|
||||
|
@ -336,11 +336,13 @@ export function createMongoCollectionWithProxy(
|
|||
}
|
||||
|
||||
export function getFeatureEndpointOrDefault(feature: string): string {
|
||||
return (hasFlag(userContext.features.mongoProxyAPIs, feature)) ? getEndpoint(userContext.features.mongoProxyEndpoint) : getEndpoint();
|
||||
return hasFlag(userContext.features.mongoProxyAPIs, feature)
|
||||
? getEndpoint(userContext.features.mongoProxyEndpoint)
|
||||
: getEndpoint();
|
||||
}
|
||||
|
||||
export function getEndpoint(customEndpoint?: string): string {
|
||||
let url = customEndpoint ? customEndpoint : (configContext.MONGO_BACKEND_ENDPOINT || configContext.BACKEND_ENDPOINT);
|
||||
let url = customEndpoint ? customEndpoint : configContext.MONGO_BACKEND_ENDPOINT || configContext.BACKEND_ENDPOINT;
|
||||
url += "/api/mongo/explorer";
|
||||
|
||||
if (userContext.authType === AuthType.EncryptedToken) {
|
||||
|
|
|
@ -1,45 +1,45 @@
|
|||
import * as React from "react";
|
||||
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
|
||||
import { AutopilotDocumentation, hoursInAMonth } from "../../../Shared/Constants";
|
||||
import { Urls, StyleConstants } from "../../../Common/Constants";
|
||||
import {
|
||||
getPriceCurrency,
|
||||
getCurrencySign,
|
||||
getAutoscalePricePerRu,
|
||||
getMultimasterMultiplier,
|
||||
computeRUUsagePriceHourly,
|
||||
getPricePerRu,
|
||||
estimatedCostDisclaimer,
|
||||
} from "../../../Utils/PricingUtils";
|
||||
import {
|
||||
ITextFieldStyles,
|
||||
DetailsList,
|
||||
DetailsListLayoutMode,
|
||||
DetailsRow,
|
||||
ICheckboxStyles,
|
||||
IStackProps,
|
||||
IStackTokens,
|
||||
IChoiceGroupStyles,
|
||||
Link,
|
||||
Text,
|
||||
IMessageBarStyles,
|
||||
ITextStyles,
|
||||
IDetailsRowStyles,
|
||||
IStackStyles,
|
||||
IColumn,
|
||||
IDetailsColumnStyles,
|
||||
IDetailsListStyles,
|
||||
IDetailsRowProps,
|
||||
IDetailsRowStyles,
|
||||
IDropdownStyles,
|
||||
IMessageBarStyles,
|
||||
ISeparatorStyles,
|
||||
IStackProps,
|
||||
IStackStyles,
|
||||
IStackTokens,
|
||||
ITextFieldStyles,
|
||||
ITextStyles,
|
||||
Link,
|
||||
MessageBar,
|
||||
MessageBarType,
|
||||
Stack,
|
||||
SelectionMode,
|
||||
Spinner,
|
||||
SpinnerSize,
|
||||
DetailsList,
|
||||
IColumn,
|
||||
SelectionMode,
|
||||
DetailsListLayoutMode,
|
||||
IDetailsRowProps,
|
||||
DetailsRow,
|
||||
IDetailsColumnStyles,
|
||||
Stack,
|
||||
Text,
|
||||
} from "@fluentui/react";
|
||||
import { isDirtyTypes, isDirty } from "./SettingsUtils";
|
||||
import * as React from "react";
|
||||
import { StyleConstants, Urls } from "../../../Common/Constants";
|
||||
import { AutopilotDocumentation, hoursInAMonth } from "../../../Shared/Constants";
|
||||
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
|
||||
import {
|
||||
computeRUUsagePriceHourly,
|
||||
estimatedCostDisclaimer,
|
||||
getAutoscalePricePerRu,
|
||||
getCurrencySign,
|
||||
getMultimasterMultiplier,
|
||||
getPriceCurrency,
|
||||
getPricePerRu,
|
||||
} from "../../../Utils/PricingUtils";
|
||||
import { isDirty, isDirtyTypes } from "./SettingsUtils";
|
||||
|
||||
export interface EstimatedSpendingDisplayProps {
|
||||
costType: JSX.Element;
|
||||
|
@ -223,14 +223,15 @@ export const getRuPriceBreakdown = (
|
|||
multimasterEnabled: isMultimaster,
|
||||
isAutoscale: isAutoscale,
|
||||
});
|
||||
const basePricePerRu: number = isAutoscale
|
||||
? getAutoscalePricePerRu(serverId, getMultimasterMultiplier(numberOfRegions, isMultimaster))
|
||||
: getPricePerRu(serverId);
|
||||
const multimasterMultiplier = getMultimasterMultiplier(numberOfRegions, isMultimaster);
|
||||
const pricePerRu: number = isAutoscale
|
||||
? getAutoscalePricePerRu(serverId, multimasterMultiplier)
|
||||
: getPricePerRu(serverId, multimasterMultiplier);
|
||||
return {
|
||||
hourlyPrice: hourlyPrice,
|
||||
hourlyPrice,
|
||||
dailyPrice: hourlyPrice * 24,
|
||||
monthlyPrice: hourlyPrice * hoursInAMonth,
|
||||
pricePerRu: basePricePerRu * getMultimasterMultiplier(numberOfRegions, isMultimaster),
|
||||
pricePerRu,
|
||||
currency: getPriceCurrency(serverId),
|
||||
currencySign: getCurrencySign(serverId),
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@ import { userContext } from "../../../../UserContext";
|
|||
import {
|
||||
calculateEstimateNumber,
|
||||
computeRUUsagePriceHourly,
|
||||
estimatedCostDisclaimer,
|
||||
getAutoscalePricePerRu,
|
||||
getCurrencySign,
|
||||
getMultimasterMultiplier,
|
||||
|
@ -42,11 +43,9 @@ export const CostEstimateText: FunctionComponent<CostEstimateTextProps> = ({
|
|||
const currency: string = getPriceCurrency(serverId);
|
||||
const currencySign: string = getCurrencySign(serverId);
|
||||
const multiplier = getMultimasterMultiplier(numberOfRegions, multimasterEnabled);
|
||||
const pricePerRu = isAutoscale
|
||||
? getAutoscalePricePerRu(serverId, multiplier) * multiplier
|
||||
: getPricePerRu(serverId) * multiplier;
|
||||
const pricePerRu = isAutoscale ? getAutoscalePricePerRu(serverId, multiplier) : getPricePerRu(serverId, multiplier);
|
||||
|
||||
const iconWithEstimatedCostDisclaimer: JSX.Element = <InfoTooltip>PricingUtils.estimatedCostDisclaimer</InfoTooltip>;
|
||||
const iconWithEstimatedCostDisclaimer: JSX.Element = <InfoTooltip>{estimatedCostDisclaimer}</InfoTooltip>;
|
||||
|
||||
if (isAutoscale) {
|
||||
return (
|
||||
|
|
|
@ -31,8 +31,8 @@ describe("hasFlag", () => {
|
|||
expect(hasFlag(singleFlagValue, desiredFlag)).toBe(true);
|
||||
expect(hasFlag(multipleFlagValues, desiredFlag)).toBe(true);
|
||||
expect(hasFlag(differentFlagValue, desiredFlag)).toBe(false);
|
||||
expect(hasFlag(multipleFlagValues, undefined as unknown as string)).toBe(false);
|
||||
expect(hasFlag(undefined as unknown as string, desiredFlag)).toBe(false);
|
||||
expect(hasFlag(undefined as unknown as string, undefined as unknown as string)).toBe(false);
|
||||
expect(hasFlag(multipleFlagValues, (undefined as unknown) as string)).toBe(false);
|
||||
expect(hasFlag((undefined as unknown) as string, desiredFlag)).toBe(false);
|
||||
expect(hasFlag((undefined as unknown) as string, (undefined as unknown) as string)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -125,7 +125,8 @@ export class OfferPricing {
|
|||
S3Price: 0.1344,
|
||||
Standard: {
|
||||
StartingPrice: 24 / hoursInAMonth, // per hour
|
||||
PricePerRU: 0.00008,
|
||||
SingleMasterPricePerRU: 0.00008,
|
||||
MultiMasterPricePerRU: 0.00016,
|
||||
PricePerGB: 0.25 / hoursInAMonth,
|
||||
},
|
||||
},
|
||||
|
@ -137,7 +138,8 @@ export class OfferPricing {
|
|||
S3Price: 0.6,
|
||||
Standard: {
|
||||
StartingPrice: OfferPricing.MonthlyPricing.mooncake.Standard.StartingPrice / hoursInAMonth, // per hour
|
||||
PricePerRU: 0.00051,
|
||||
SingleMasterPricePerRU: 0.00051,
|
||||
MultiMasterPricePerRU: 0.00102,
|
||||
PricePerGB: OfferPricing.MonthlyPricing.mooncake.Standard.PricePerGB / hoursInAMonth,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -2,11 +2,11 @@ import * as Constants from "./Constants";
|
|||
|
||||
export function computeRUUsagePrice(serverId: string, requestUnits: number): string {
|
||||
if (serverId === "mooncake") {
|
||||
const ruCharge = requestUnits * Constants.OfferPricing.HourlyPricing.mooncake.Standard.PricePerRU;
|
||||
const ruCharge = requestUnits * Constants.OfferPricing.HourlyPricing.mooncake.Standard.SingleMasterPricePerRU;
|
||||
return calculateEstimateNumber(ruCharge) + " " + Constants.OfferPricing.HourlyPricing.mooncake.Currency;
|
||||
}
|
||||
|
||||
const ruCharge = requestUnits * Constants.OfferPricing.HourlyPricing.default.Standard.PricePerRU;
|
||||
const ruCharge = requestUnits * Constants.OfferPricing.HourlyPricing.default.Standard.SingleMasterPricePerRU;
|
||||
return calculateEstimateNumber(ruCharge) + " " + Constants.OfferPricing.HourlyPricing.default.Currency;
|
||||
}
|
||||
|
||||
|
|
|
@ -150,7 +150,7 @@ describe("PricingUtils Tests", () => {
|
|||
expect(value).toBe(0.00012);
|
||||
});
|
||||
|
||||
it("should return 0.00048 for default cloud, 1RU, 2 region, multimaster enabled", () => {
|
||||
it("should return 0.00032 for default cloud, 1RU, 2 region, multimaster enabled", () => {
|
||||
const value = PricingUtils.computeRUUsagePriceHourly({
|
||||
serverId: "default",
|
||||
requestUnits: 1,
|
||||
|
@ -158,9 +158,9 @@ describe("PricingUtils Tests", () => {
|
|||
multimasterEnabled: true,
|
||||
isAutoscale: false,
|
||||
});
|
||||
expect(value).toBe(0.00048);
|
||||
expect(value).toBe(0.00032);
|
||||
});
|
||||
it("should return 0.00048 for default cloud, 1RU, 2 region, multimaster enabled, autoscale", () => {
|
||||
it("should return 0.00032 for default cloud, 1RU, 2 region, multimaster enabled, autoscale", () => {
|
||||
const value = PricingUtils.computeRUUsagePriceHourly({
|
||||
serverId: "default",
|
||||
requestUnits: 1,
|
||||
|
@ -168,7 +168,7 @@ describe("PricingUtils Tests", () => {
|
|||
multimasterEnabled: true,
|
||||
isAutoscale: true,
|
||||
});
|
||||
expect(value).toBe(0.00096);
|
||||
expect(value).toBe(0.00032);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -251,70 +251,47 @@ describe("PricingUtils Tests", () => {
|
|||
});
|
||||
|
||||
describe("getPricePerRu()", () => {
|
||||
it("should return 0.00008 for default clouds", () => {
|
||||
const value = PricingUtils.getPricePerRu("default");
|
||||
it("should return 0.00008 for single master default clouds", () => {
|
||||
const value = PricingUtils.getPricePerRu("default", 1);
|
||||
expect(value).toBe(0.00008);
|
||||
});
|
||||
|
||||
it("should return 0.00051 for mooncake", () => {
|
||||
const value = PricingUtils.getPricePerRu("mooncake");
|
||||
it("should return 0.00016 for multi master default clouds", () => {
|
||||
const value = PricingUtils.getPricePerRu("default", 2);
|
||||
expect(value).toBe(0.00016);
|
||||
});
|
||||
|
||||
it("should return 0.00051 for single master mooncake", () => {
|
||||
const value = PricingUtils.getPricePerRu("mooncake", 1);
|
||||
expect(value).toBe(0.00051);
|
||||
});
|
||||
|
||||
it("should return 0.00102 for multi master mooncake", () => {
|
||||
const value = PricingUtils.getPricePerRu("mooncake", 2);
|
||||
expect(value).toBe(0.00102);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getRegionMultiplier()", () => {
|
||||
describe("without multimaster", () => {
|
||||
it("should return 0 for undefined", () => {
|
||||
const value = PricingUtils.getRegionMultiplier(undefined, false);
|
||||
expect(value).toBe(0);
|
||||
});
|
||||
|
||||
it("should return 0 for -1", () => {
|
||||
const value = PricingUtils.getRegionMultiplier(-1, false);
|
||||
expect(value).toBe(0);
|
||||
});
|
||||
|
||||
it("should return 0 for 0", () => {
|
||||
const value = PricingUtils.getRegionMultiplier(0, false);
|
||||
expect(value).toBe(0);
|
||||
});
|
||||
|
||||
it("should return 1 for 1", () => {
|
||||
const value = PricingUtils.getRegionMultiplier(1, false);
|
||||
expect(value).toBe(1);
|
||||
});
|
||||
|
||||
it("should return 2 for 2", () => {
|
||||
const value = PricingUtils.getRegionMultiplier(2, false);
|
||||
expect(value).toBe(2);
|
||||
});
|
||||
it("should return 0 for undefined", () => {
|
||||
const value = PricingUtils.getRegionMultiplier(undefined);
|
||||
expect(value).toBe(0);
|
||||
});
|
||||
|
||||
describe("with multimaster", () => {
|
||||
it("should return 0 for undefined", () => {
|
||||
const value = PricingUtils.getRegionMultiplier(undefined, true);
|
||||
expect(value).toBe(0);
|
||||
});
|
||||
|
||||
it("should return 0 for -1", () => {
|
||||
const value = PricingUtils.getRegionMultiplier(-1, true);
|
||||
expect(value).toBe(0);
|
||||
});
|
||||
|
||||
it("should return 0 for 0", () => {
|
||||
const value = PricingUtils.getRegionMultiplier(0, true);
|
||||
expect(value).toBe(0);
|
||||
});
|
||||
|
||||
it("should return 1 for 1", () => {
|
||||
const value = PricingUtils.getRegionMultiplier(1, true);
|
||||
expect(value).toBe(1);
|
||||
});
|
||||
|
||||
it("should return 3 for 2", () => {
|
||||
const value = PricingUtils.getRegionMultiplier(2, true);
|
||||
expect(value).toBe(3);
|
||||
});
|
||||
it("should return 0 for -1", () => {
|
||||
const value = PricingUtils.getRegionMultiplier(-1);
|
||||
expect(value).toBe(0);
|
||||
});
|
||||
it("should return 0 for 0", () => {
|
||||
const value = PricingUtils.getRegionMultiplier(0);
|
||||
expect(value).toBe(0);
|
||||
});
|
||||
it("should return 1 for 1", () => {
|
||||
const value = PricingUtils.getRegionMultiplier(1);
|
||||
expect(value).toBe(1);
|
||||
});
|
||||
it("should return 2 for 2", () => {
|
||||
const value = PricingUtils.getRegionMultiplier(2);
|
||||
expect(value).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -376,7 +353,7 @@ describe("PricingUtils Tests", () => {
|
|||
true /* multimaster */
|
||||
);
|
||||
expect(value).toBe(
|
||||
"Cost (USD): <b>$0.19 hourly / $4.61 daily / $140.16 monthly </b> (2 regions, 400RU/s, $0.00016/RU)<p style='padding: 10px 0px 0px 0px;'><em>*This cost is an estimate and may vary based on the regions where your account is deployed and potential discounts applied to your account</em></p>"
|
||||
"Cost (USD): <b>$0.13 hourly / $3.07 daily / $93.44 monthly </b> (2 regions, 400RU/s, $0.00016/RU)<p style='padding: 10px 0px 0px 0px;'><em>*This cost is an estimate and may vary based on the regions where your account is deployed and potential discounts applied to your account</em></p>"
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -424,7 +401,7 @@ describe("PricingUtils Tests", () => {
|
|||
true /* multimaster */,
|
||||
false
|
||||
);
|
||||
expect(value).toBe("I acknowledge the estimated $4.61 daily cost for the throughput above.");
|
||||
expect(value).toBe("I acknowledge the estimated $3.07 daily cost for the throughput above.");
|
||||
});
|
||||
|
||||
it("should return 'I acknowledge the estimated $1.54 daily cost for the throughput above.' for 400RU/s on default cloud, 2 region, without multimaster", () => {
|
||||
|
|
|
@ -34,26 +34,18 @@ export function getRuToolTipText(): string {
|
|||
* Otherwise, return numberOfRegions
|
||||
* @param numberOfRegions
|
||||
*/
|
||||
export function getRegionMultiplier(numberOfRegions: number, multimasterEnabled: boolean): number {
|
||||
export function getRegionMultiplier(numberOfRegions: number): number {
|
||||
const normalizedNumberOfRegions: number = normalizeNumber(numberOfRegions);
|
||||
|
||||
if (normalizedNumberOfRegions <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (numberOfRegions === 1) {
|
||||
return numberOfRegions;
|
||||
}
|
||||
|
||||
if (multimasterEnabled) {
|
||||
return numberOfRegions + 1;
|
||||
}
|
||||
|
||||
return numberOfRegions;
|
||||
}
|
||||
|
||||
export function getMultimasterMultiplier(numberOfRegions: number, multimasterEnabled: boolean): number {
|
||||
const regionMultiplier: number = getRegionMultiplier(numberOfRegions, multimasterEnabled);
|
||||
const regionMultiplier: number = getRegionMultiplier(numberOfRegions);
|
||||
const multimasterMultiplier: number = !multimasterEnabled ? 1 : regionMultiplier > 1 ? 2 : 1;
|
||||
|
||||
return multimasterMultiplier;
|
||||
|
@ -66,10 +58,12 @@ export function computeRUUsagePriceHourly({
|
|||
multimasterEnabled,
|
||||
isAutoscale,
|
||||
}: ComputeRUUsagePriceHourlyArgs): number {
|
||||
const regionMultiplier: number = getRegionMultiplier(numberOfRegions, multimasterEnabled);
|
||||
const regionMultiplier: number = getRegionMultiplier(numberOfRegions);
|
||||
const multimasterMultiplier: number = getMultimasterMultiplier(numberOfRegions, multimasterEnabled);
|
||||
const pricePerRu = isAutoscale ? getAutoscalePricePerRu(serverId, multimasterMultiplier) : getPricePerRu(serverId);
|
||||
const ruCharge = requestUnits * pricePerRu * multimasterMultiplier * regionMultiplier;
|
||||
const pricePerRu = isAutoscale
|
||||
? getAutoscalePricePerRu(serverId, multimasterMultiplier)
|
||||
: getPricePerRu(serverId, multimasterMultiplier);
|
||||
const ruCharge = requestUnits * pricePerRu * regionMultiplier;
|
||||
|
||||
return Number(ruCharge.toFixed(5));
|
||||
}
|
||||
|
@ -149,12 +143,16 @@ export function getAutoscalePricePerRu(serverId: string, mmMultiplier: number):
|
|||
}
|
||||
}
|
||||
|
||||
export function getPricePerRu(serverId: string): number {
|
||||
export function getPricePerRu(serverId: string, mmMultiplier: number): number {
|
||||
if (serverId === "mooncake") {
|
||||
return Constants.OfferPricing.HourlyPricing.mooncake.Standard.PricePerRU;
|
||||
return mmMultiplier > 1
|
||||
? Constants.OfferPricing.HourlyPricing.mooncake.Standard.MultiMasterPricePerRU
|
||||
: Constants.OfferPricing.HourlyPricing.mooncake.Standard.SingleMasterPricePerRU;
|
||||
}
|
||||
|
||||
return Constants.OfferPricing.HourlyPricing.default.Standard.PricePerRU;
|
||||
return mmMultiplier > 1
|
||||
? Constants.OfferPricing.HourlyPricing.default.Standard.MultiMasterPricePerRU
|
||||
: Constants.OfferPricing.HourlyPricing.default.Standard.SingleMasterPricePerRU;
|
||||
}
|
||||
|
||||
export function getAutoPilotV3SpendHtml(maxAutoPilotThroughputSet: number, isDatabaseThroughput: boolean): string {
|
||||
|
@ -188,9 +186,7 @@ export function getEstimatedAutoscaleSpendHtml(
|
|||
const monthlyPrice: number = hourlyPrice * Constants.hoursInAMonth;
|
||||
const currency: string = getPriceCurrency(serverId);
|
||||
const currencySign: string = getCurrencySign(serverId);
|
||||
const pricePerRu =
|
||||
getAutoscalePricePerRu(serverId, getMultimasterMultiplier(regions, multimaster)) *
|
||||
getMultimasterMultiplier(regions, multimaster);
|
||||
const pricePerRu = getAutoscalePricePerRu(serverId, getMultimasterMultiplier(regions, multimaster));
|
||||
|
||||
return (
|
||||
`Estimated monthly cost (${currency}): <b>` +
|
||||
|
@ -219,7 +215,7 @@ export function getEstimatedSpendHtml(
|
|||
const monthlyPrice: number = hourlyPrice * Constants.hoursInAMonth;
|
||||
const currency: string = getPriceCurrency(serverId);
|
||||
const currencySign: string = getCurrencySign(serverId);
|
||||
const pricePerRu = getPricePerRu(serverId) * getMultimasterMultiplier(regions, multimaster);
|
||||
const pricePerRu = getPricePerRu(serverId, getMultimasterMultiplier(regions, multimaster));
|
||||
|
||||
return (
|
||||
`Cost (${currency}): <b>` +
|
||||
|
|
Loading…
Reference in New Issue