import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils"; import * as ko from "knockout"; import * as ViewModels from "../../../Contracts/ViewModels"; import ThroughputInputComponentAutoscaleV3 from "./ThroughputInputComponentAutoscaleV3.html"; import { KeyCodes } from "../../../Common/Constants"; import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel"; import { userContext } from "../../../UserContext"; import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants"; /** * Throughput Input: * * Creates a set of controls to input, sanitize and increase/decrease throughput * * How to use in your markup: * * * */ /** * Parameters for this component */ export interface ThroughputInputParams { /** * Callback triggered when the template is bound to the component (for testing purposes) */ onTemplateReady?: () => void; /** * Observable to bind the Throughput value to */ value: ViewModels.Editable; /** * Text to use as id for testing */ testId: string; /** * Text to use as aria-label */ ariaLabel?: ko.Observable; /** * Minimum value in the range */ minimum: ko.Observable; /** * Maximum value in the range */ maximum: ko.Observable; /** * Step value for increase/decrease */ step?: number; /** * Observable to bind the Throughput enabled status */ isEnabled?: ko.Observable; /** * Should show pricing controls */ costsVisible: ko.Observable; /** * RU price */ requestUnitsUsageCost: ko.Computed; // Our code assigns to ko.Computed, but unit test assigns to ko.Observable /** * State of the spending acknowledge checkbox */ spendAckChecked?: ko.Observable; /** * id of the spending acknowledge checkbox */ spendAckId?: ko.Observable; /** * spending acknowledge text */ spendAckText?: ko.Observable; /** * Show spending acknowledge controls */ spendAckVisible?: ko.Observable; /** * Display * to the left of the label */ showAsMandatory: boolean; /** * If true, it will display a text to prompt users to use unlimited collections to go beyond max for fixed */ isFixed: boolean; /** * Label of the provisioned throughut control */ label: ko.Observable; /** * Text of the info bubble for provisioned throughut control */ infoBubbleText?: ko.Observable; /** * Computed value that decides if value can exceed maximum allowable value */ canExceedMaximumValue?: ko.Computed; /** * CSS classes to apply on input element */ cssClass?: string; isAutoPilotSelected: ko.Observable; throughputAutoPilotRadioId: string; throughputProvisionedRadioId: string; throughputModeRadioName: string; maxAutoPilotThroughputSet: ViewModels.Editable; autoPilotUsageCost: ko.Computed; showAutoPilot?: ko.Observable; overrideWithAutoPilotSettings: ko.Observable; overrideWithProvisionedThroughputSettings: ko.Observable; freeTierExceedThroughputTooltip?: ko.Observable; freeTierExceedThroughputWarning?: ko.Observable; } export class ThroughputInputViewModel extends WaitsForTemplateViewModel { public ariaLabel: ko.Observable; public canExceedMaximumValue: ko.Computed; public step: ko.Computed; public testId: string; public value: ViewModels.Editable; public minimum: ko.Observable; public maximum: ko.Observable; public isEnabled: ko.Observable; public cssClass: string; public decreaseButtonAriaLabel: string; public increaseButtonAriaLabel: string; public costsVisible: ko.Observable; public requestUnitsUsageCost: ko.Computed; public spendAckChecked: ko.Observable; public spendAckId: ko.Observable; public spendAckText: ko.Observable; public spendAckVisible: ko.Observable; public showAsMandatory: boolean; public infoBubbleText: string | ko.Observable; public label: ko.Observable; public isFixed: boolean; public showAutoPilot: ko.Observable; public isAutoPilotSelected: ko.Observable; public throughputAutoPilotRadioId: string; public throughputProvisionedRadioId: string; public throughputModeRadioName: string; public maxAutoPilotThroughputSet: ko.Observable; public autoPilotUsageCost: ko.Computed; public minAutoPilotThroughput: ko.Observable; public overrideWithAutoPilotSettings: ko.Observable; public overrideWithProvisionedThroughputSettings: ko.Observable; public isManualThroughputInputFieldRequired: ko.Computed; public isAutoscaleThroughputInputFieldRequired: ko.Computed; public freeTierExceedThroughputTooltip: ko.Observable; public freeTierExceedThroughputWarning: ko.Observable; public showFreeTierExceedThroughputTooltip: ko.Computed; public showFreeTierExceedThroughputWarning: ko.Computed; public constructor(options: ThroughputInputParams) { super(); super.onTemplateReady((isTemplateReady: boolean) => { if (isTemplateReady && options.onTemplateReady) { options.onTemplateReady(); } }); const params: ThroughputInputParams = options; this.testId = params.testId || "ThroughputValue"; this.ariaLabel = ko.observable((params.ariaLabel && params.ariaLabel()) || ""); this.canExceedMaximumValue = params.canExceedMaximumValue || ko.computed(() => false); this.isEnabled = params.isEnabled || ko.observable(true); this.cssClass = params.cssClass || "textfontclr collid migration"; this.minimum = params.minimum; this.maximum = params.maximum; this.value = params.value; this.costsVisible = options.costsVisible; this.requestUnitsUsageCost = options.requestUnitsUsageCost; this.spendAckChecked = options.spendAckChecked || ko.observable(false); this.spendAckId = options.spendAckId || ko.observable(); this.spendAckText = options.spendAckText || ko.observable(); this.spendAckVisible = options.spendAckVisible || ko.observable(false); this.showAsMandatory = !!options.showAsMandatory; this.isFixed = !!options.isFixed; this.infoBubbleText = options.infoBubbleText || ko.observable(); this.label = options.label || ko.observable(); this.showAutoPilot = options.showAutoPilot !== undefined ? options.showAutoPilot : ko.observable(true); this.isAutoPilotSelected = options.isAutoPilotSelected || ko.observable(false); this.isAutoPilotSelected.subscribe((value) => { TelemetryProcessor.trace(Action.ToggleAutoscaleSetting, ActionModifiers.Mark, { changedSelectedValueTo: value ? ActionModifiers.ToggleAutoscaleOn : ActionModifiers.ToggleAutoscaleOff, dataExplorerArea: "Scale Tab V1", }); }); this.throughputAutoPilotRadioId = options.throughputAutoPilotRadioId; this.throughputProvisionedRadioId = options.throughputProvisionedRadioId; this.throughputModeRadioName = options.throughputModeRadioName; this.overrideWithAutoPilotSettings = options.overrideWithAutoPilotSettings || ko.observable(false); this.overrideWithProvisionedThroughputSettings = options.overrideWithProvisionedThroughputSettings || ko.observable(false); this.maxAutoPilotThroughputSet = options.maxAutoPilotThroughputSet || ko.observable(AutoPilotUtils.minAutoPilotThroughput); this.autoPilotUsageCost = options.autoPilotUsageCost; this.minAutoPilotThroughput = ko.observable(AutoPilotUtils.minAutoPilotThroughput); this.step = ko.pureComputed(() => { if (this.isAutoPilotSelected()) { return AutoPilotUtils.autoPilotIncrementStep; } return params.step || ThroughputInputViewModel._defaultStep; }); this.decreaseButtonAriaLabel = "Decrease throughput by " + this.step().toString(); this.increaseButtonAriaLabel = "Increase throughput by " + this.step().toString(); this.isManualThroughputInputFieldRequired = ko.pureComputed(() => this.isEnabled() && !this.isAutoPilotSelected()); this.isAutoscaleThroughputInputFieldRequired = ko.pureComputed( () => this.isEnabled() && this.isAutoPilotSelected() ); this.freeTierExceedThroughputTooltip = options.freeTierExceedThroughputTooltip || ko.observable(); this.freeTierExceedThroughputWarning = options.freeTierExceedThroughputWarning || ko.observable(); this.showFreeTierExceedThroughputTooltip = ko.pureComputed( () => !!this.freeTierExceedThroughputTooltip() && this.value() > 400 ); this.showFreeTierExceedThroughputWarning = ko.pureComputed( () => !!this.freeTierExceedThroughputWarning() && this.value() > 400 ); } public decreaseThroughput() { let offerThroughput: number = this._getSanitizedValue(); if (offerThroughput > this.minimum()) { offerThroughput -= this.step(); if (offerThroughput < this.minimum()) { offerThroughput = this.minimum(); } this.value(offerThroughput); } } public increaseThroughput() { let offerThroughput: number = this._getSanitizedValue(); if (offerThroughput < this.maximum() || this.canExceedMaximumValue()) { offerThroughput += this.step(); if (offerThroughput > this.maximum() && !this.canExceedMaximumValue()) { offerThroughput = this.maximum(); } this.value(offerThroughput); } } public onIncreaseKeyDown = (source: any, event: KeyboardEvent): boolean => { if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) { this.increaseThroughput(); event.stopPropagation(); return false; } return true; }; public onDecreaseKeyDown = (source: any, event: KeyboardEvent): boolean => { if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) { this.decreaseThroughput(); event.stopPropagation(); return false; } return true; }; private _getSanitizedValue(): number { let throughput = this.value(); if (this.isAutoPilotSelected()) { throughput = this.maxAutoPilotThroughputSet(); } return isNaN(throughput) ? 0 : Number(throughput); } private static _defaultStep: number = 100; } export const ThroughputInputComponentAutoPilotV3 = { viewModel: ThroughputInputViewModel, template: ThroughputInputComponentAutoscaleV3, };