mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Added a dynamic Cost Summary section to SQL MIAA Deployment Wizard (#17420)
* Added valueprovider for pricing. Pushing this for troubleshooting help. * Committing changes for troubleshooting help. Moved InputValueType to typings file. * Add readonly inputs to list * Fixed ordering of package.json merge items * Estimated cost moved to input page, ValueProvider only takes in a triggerfields[] and not a single string, fixed pricing logic. * Removed pricingModel.ts * Reverted some comments and code changes that were used in debugging. * Changed some values from localizedConstants to single-quote constants' * Changed some values from localizedConstants to single-quote constants' * Added copyright header to pricingUtils.ts * Removed try catch in extension.ts valueproviders, made some values in PricingUtils.ts top-level instead of exporting. * Minor changes, added some comments and localized USD. * Changes pricingutils classes to be constants, and added disposable to Hookupvalueprovider Co-authored-by: Candice Ye <canye@microsoft.com> Co-authored-by: chgagnon <chgagnon@microsoft.com>
This commit is contained in:
117
extensions/arc/src/common/pricingUtils.ts
Normal file
117
extensions/arc/src/common/pricingUtils.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { InputValueType } from 'resource-deployment';
|
||||
import * as loc from '../localizedConstants';
|
||||
|
||||
export const SqlManagedInstanceGeneralPurpose = {
|
||||
tierName: loc.generalPurposeLabel,
|
||||
basePricePerCore: 80,
|
||||
licenseIncludedPricePerCore: 153,
|
||||
maxMemorySize: 128,
|
||||
maxVCores: 24,
|
||||
|
||||
replicaOptions: [
|
||||
{
|
||||
text: loc.replicaOne,
|
||||
value: 1,
|
||||
}
|
||||
],
|
||||
|
||||
defaultReplicaValue: 1
|
||||
};
|
||||
|
||||
const SqlManagedInstanceBusinessCritical = {
|
||||
tierName: loc.businessCriticalLabel,
|
||||
|
||||
// Set to real values when BC is ready
|
||||
basePricePerCore: 0,
|
||||
licenseIncludedPricePerCore: 0,
|
||||
|
||||
replicaOptions: [
|
||||
{
|
||||
text: loc.replicaTwo,
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
text: loc.replicaThree,
|
||||
value: 3,
|
||||
}
|
||||
],
|
||||
|
||||
defaultReplicaValue: 3
|
||||
};
|
||||
|
||||
export const SqlManagedInstancePricingLink: string = 'https://aka.ms/ArcSQLBilling';
|
||||
|
||||
export const serviceTierVarName = 'AZDATA_NB_VAR_SQL_SERVICE_TIER';
|
||||
export const devUseVarName = 'AZDATA_NB_VAR_SQL_DEV_USE';
|
||||
export const vcoresLimitVarName = 'AZDATA_NB_VAR_SQL_CORES_LIMIT';
|
||||
export const licenseTypeVarName = 'AZDATA_NB_VAR_SQL_LICENSE_TYPE';
|
||||
|
||||
// Estimated base price for one vCore.
|
||||
export function estimatedBasePriceForOneVCore(mapping: { [key: string]: InputValueType }): number {
|
||||
let price = 0;
|
||||
if (mapping[devUseVarName] === 'true') {
|
||||
price = 0;
|
||||
} else if (mapping[devUseVarName] === 'false') {
|
||||
if (mapping[serviceTierVarName] === SqlManagedInstanceGeneralPurpose.tierName) {
|
||||
price = SqlManagedInstanceGeneralPurpose.basePricePerCore;
|
||||
} else if (mapping[serviceTierVarName] === SqlManagedInstanceBusinessCritical.tierName) {
|
||||
price = SqlManagedInstanceBusinessCritical.basePricePerCore;
|
||||
}
|
||||
}
|
||||
return price;
|
||||
}
|
||||
|
||||
// Estimated SQL server license price for one vCore.
|
||||
export function estimatedSqlServerLicensePriceForOneVCore(mapping: { [key: string]: InputValueType }): number {
|
||||
let price = 0;
|
||||
if (mapping[devUseVarName] === 'true') {
|
||||
price = 0;
|
||||
} else if (mapping[devUseVarName] === 'false') {
|
||||
if (mapping[serviceTierVarName] === SqlManagedInstanceGeneralPurpose.tierName) {
|
||||
price = SqlManagedInstanceGeneralPurpose.licenseIncludedPricePerCore - SqlManagedInstanceGeneralPurpose.basePricePerCore;
|
||||
} else if (mapping[serviceTierVarName] === SqlManagedInstanceBusinessCritical.tierName) {
|
||||
price = SqlManagedInstanceBusinessCritical.licenseIncludedPricePerCore - SqlManagedInstanceBusinessCritical.basePricePerCore;
|
||||
}
|
||||
}
|
||||
return price;
|
||||
}
|
||||
|
||||
// Full price for one vCore. This is shown on the cost summary card.
|
||||
export function fullPriceForOneVCore(mapping: { [key: string]: InputValueType }): number {
|
||||
return estimatedBasePriceForOneVCore(mapping) + estimatedSqlServerLicensePriceForOneVCore(mapping);
|
||||
}
|
||||
|
||||
// Gets number of vCores limit specified
|
||||
export function numCores(mapping: { [key: string]: InputValueType }): number {
|
||||
return mapping[vcoresLimitVarName] ? <number>mapping[vcoresLimitVarName] : 0;
|
||||
}
|
||||
|
||||
// Full price for all selected vCores.
|
||||
export function vCoreFullPriceForAllCores(mapping: { [key: string]: InputValueType }): number {
|
||||
return fullPriceForOneVCore(mapping) * numCores(mapping);
|
||||
}
|
||||
|
||||
// SQL Server License price for all vCores. This is shown on the cost summary card if customer has SQL server license.
|
||||
export function vCoreSqlServerLicensePriceForAllCores(mapping: { [key: string]: InputValueType }): number {
|
||||
return estimatedSqlServerLicensePriceForOneVCore(mapping) * numCores(mapping);
|
||||
}
|
||||
|
||||
// If the customer doesn't already have SQL Server License, AHB discount is set to zero because the price will be included
|
||||
// in the total cost. If they already have it (they checked the box), then a discount will be applied.
|
||||
export function azureHybridBenefitDiscount(mapping: { [key: string]: InputValueType }): number {
|
||||
if (mapping[licenseTypeVarName] === 'true') {
|
||||
return vCoreSqlServerLicensePriceForAllCores(mapping);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Total price that will be charged to a customer. Is shown on the cost summary card.
|
||||
export function total(mapping: { [key: string]: InputValueType }): number {
|
||||
return vCoreFullPriceForAllCores(mapping) - azureHybridBenefitDiscount(mapping);
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import { ConnectToControllerDialog } from './ui/dialogs/connectControllerDialog'
|
||||
import { AzureArcTreeDataProvider } from './ui/tree/azureArcTreeDataProvider';
|
||||
import { ControllerTreeNode } from './ui/tree/controllerTreeNode';
|
||||
import { TreeNode } from './ui/tree/treeNode';
|
||||
import * as pricing from './common/pricingUtils';
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext): Promise<arc.IExtension> {
|
||||
IconPathHelper.setExtensionContext(context);
|
||||
@@ -61,6 +62,38 @@ export async function activate(context: vscode.ExtensionContext): Promise<arc.IE
|
||||
const rdApi = <rd.IExtension>vscode.extensions.getExtension(rd.extension.name)?.exports;
|
||||
context.subscriptions.push(rdApi.registerOptionsSourceProvider(new ArcControllersOptionsSourceProvider(treeDataProvider)));
|
||||
|
||||
// Register valueprovider for getting the calculated cost per VCore.
|
||||
context.subscriptions.push(rdApi.registerValueProvider({
|
||||
id: 'params-to-cost-per-vcore',
|
||||
getValue: async (mapping: { [key: string]: rd.InputValueType }) => {
|
||||
return pricing.fullPriceForOneVCore(mapping);
|
||||
}
|
||||
}));
|
||||
|
||||
// Register valueprovider for getting the number of CPU VCores Limit input by the user.
|
||||
context.subscriptions.push(rdApi.registerValueProvider({
|
||||
id: 'params-to-vcore-limit',
|
||||
getValue: async (mapping: { [key: string]: rd.InputValueType }) => {
|
||||
return 'x ' + pricing.numCores(mapping).toString();
|
||||
}
|
||||
}));
|
||||
|
||||
// Register valueprovider for getting the amount of hybrid benefit discount to be applied.
|
||||
context.subscriptions.push(rdApi.registerValueProvider({
|
||||
id: 'params-to-hybrid-benefit-discount',
|
||||
getValue: async (mapping: { [key: string]: rd.InputValueType }) => {
|
||||
return '- ' + pricing.azureHybridBenefitDiscount(mapping).toString();
|
||||
}
|
||||
}));
|
||||
|
||||
// Register valueprovider for getting the total estimated cost.
|
||||
context.subscriptions.push(rdApi.registerValueProvider({
|
||||
id: 'params-to-estimated-cost',
|
||||
getValue: async (mapping: { [key: string]: rd.InputValueType }) => {
|
||||
return pricing.total(mapping).toString() + ' ' + loc.USD;
|
||||
}
|
||||
}));
|
||||
|
||||
return arcApi(treeDataProvider);
|
||||
}
|
||||
|
||||
|
||||
@@ -274,6 +274,14 @@ export function connectionString(type: string): string { return localize({ key:
|
||||
export function copyConnectionStringToClipboard(type: string): string { return localize({ key: 'arc.copyConnectionStringToClipboard', comment: ['{0} is the name of the type of connection string (e.g. Java)'] }, "Copy {0} Connection String to clipboard", type); }
|
||||
export function copyValueToClipboard(valueName: string): string { return localize({ key: 'arc.copyValueToClipboard', comment: ['{0} is the name of the type of value being copied (e.g. Coordinator endpoint)'] }, "Copy {0} to clipboard", valueName); }
|
||||
|
||||
// Pricing Constants
|
||||
export const replicaOne = localize('arc.replicaOne', "1 replica");
|
||||
export const replicaTwo = localize('arc.replicaTwo', "2 replicas");
|
||||
export const replicaThree = localize('arc.replicaThree', "3 replicas");
|
||||
export const generalPurposeLabel = localize('arc.generalPurposeLabel', "General Purpose (Up to 24 vCores and 128 Gi of RAM, standard high availability)");
|
||||
export const businessCriticalLabel = localize('arc.businessCriticalLabel', "[PREVIEW] Business Critical (Unlimited vCores and RAM, advanced high availability)");
|
||||
export const USD = localize('arc.USD', "USD");
|
||||
|
||||
// Errors
|
||||
export const pgConnectionRequired = localize('arc.pgConnectionRequired', "A connection is required to show and set database engine settings.");
|
||||
export const miaaConnectionRequired = localize('arc.miaaConnectionRequired', "A connection is required to list the databases on this instance.");
|
||||
|
||||
Reference in New Issue
Block a user