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,
};