mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-29 09:35:38 -05:00
* 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>
214 lines
7.9 KiB
TypeScript
214 lines
7.9 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import * as azdata from 'azdata';
|
|
import { InputValueType } from 'resource-deployment';
|
|
import * as vscode from 'vscode';
|
|
import { isUndefinedOrEmpty, throwUnless } from '../../common/utils';
|
|
|
|
export interface ValidationResult {
|
|
valid: boolean;
|
|
message?: string;
|
|
}
|
|
|
|
export type Validator = () => Promise<ValidationResult>;
|
|
|
|
|
|
export type OnValidation = (isValid: boolean) => Promise<void>;
|
|
export type ValueGetter = () => Promise<InputValueType>;
|
|
export type TargetValueGetter = (variable: string) => Promise<InputValueType>;
|
|
export type OnTargetValidityChangedGetter = (variable: string) => vscode.Event<boolean>;
|
|
|
|
export const enum ValidationType {
|
|
IsInteger = 'is_integer',
|
|
Regex = 'regex_match',
|
|
LessThanOrEqualsTo = '<=',
|
|
GreaterThanOrEqualsTo = '>=',
|
|
NotEqualTo = '!='
|
|
}
|
|
|
|
export type ValidationInfo = RegexValidationInfo | IntegerValidationInfo | ComparisonValidationInfo;
|
|
|
|
export interface ValidationInfoBase {
|
|
readonly type: ValidationType,
|
|
readonly description: string,
|
|
}
|
|
|
|
export type IntegerValidationInfo = ValidationInfoBase;
|
|
|
|
export interface RegexValidationInfo extends ValidationInfoBase {
|
|
readonly regex: string | RegExp
|
|
}
|
|
|
|
export interface ComparisonValidationInfo extends ValidationInfoBase {
|
|
readonly target: string
|
|
}
|
|
|
|
export abstract class Validation {
|
|
private _description: string;
|
|
protected readonly _target?: string;
|
|
|
|
get target(): string | undefined {
|
|
return this._target;
|
|
}
|
|
get description(): string {
|
|
return this._description;
|
|
}
|
|
// gets the validation result for this validation object
|
|
abstract validate(): Promise<ValidationResult>;
|
|
|
|
protected getValue(): Promise<InputValueType> {
|
|
return this._valueGetter();
|
|
}
|
|
|
|
protected getTargetValue(variable: string): Promise<InputValueType> {
|
|
return this._targetValueGetter!(variable);
|
|
}
|
|
|
|
constructor(validation: ValidationInfo, protected _valueGetter: ValueGetter, protected _targetValueGetter?: TargetValueGetter, protected _onTargetValidityChangedGetter?: OnTargetValidityChangedGetter, protected _onNewDisposableCreated?: (disposable: vscode.Disposable) => void) {
|
|
this._description = validation.description;
|
|
}
|
|
|
|
}
|
|
|
|
export class IntegerValidation extends Validation {
|
|
constructor(validation: IntegerValidationInfo, valueGetter: ValueGetter) {
|
|
super(validation, valueGetter);
|
|
}
|
|
|
|
private async isIntegerOrEmptyOrUndefined(): Promise<boolean> {
|
|
const value = await this.getValue();
|
|
return (isUndefinedOrEmpty(value))
|
|
? true
|
|
: (typeof value === 'string')
|
|
? Number.isInteger(parseFloat(value))
|
|
: Number.isInteger(value);
|
|
}
|
|
|
|
async validate(): Promise<ValidationResult> {
|
|
const isValid = await this.isIntegerOrEmptyOrUndefined();
|
|
return {
|
|
valid: isValid,
|
|
message: isValid ? undefined : this.description
|
|
};
|
|
}
|
|
}
|
|
|
|
export class RegexValidation extends Validation {
|
|
private _regex: RegExp;
|
|
|
|
get regex(): RegExp {
|
|
return this._regex;
|
|
}
|
|
|
|
constructor(validation: RegexValidationInfo, valueGetter: ValueGetter) {
|
|
super(validation, valueGetter);
|
|
throwUnless(validation.regex !== undefined);
|
|
this._regex = (typeof validation.regex === 'string') ? new RegExp(validation.regex) : validation.regex;
|
|
}
|
|
|
|
async validate(): Promise<ValidationResult> {
|
|
const value = (await this.getValue())?.toString();
|
|
const isValid = isUndefinedOrEmpty(value) ? true : this.regex.test(value!);
|
|
return {
|
|
valid: isValid,
|
|
message: isValid ? undefined : this.description
|
|
};
|
|
}
|
|
}
|
|
|
|
export abstract class Comparison extends Validation {
|
|
protected override _target: string; // comparison object requires a target so override the base optional setting.
|
|
protected _ensureOnTargetValidityChangeListenerAdded = false;
|
|
|
|
override get target(): string {
|
|
return this._target;
|
|
}
|
|
|
|
protected onTargetValidityChanged(onTargetValidityChangedAction: (e: boolean) => Promise<void>): void {
|
|
const onValidityChanged = this._onTargetValidityChangedGetter!(this.target);
|
|
this._onNewDisposableCreated!(onValidityChanged(isValid => onTargetValidityChangedAction(isValid)));
|
|
}
|
|
|
|
constructor(validation: ComparisonValidationInfo, valueGetter: ValueGetter, targetValueGetter: TargetValueGetter, onTargetValidityChangedGetter: OnTargetValidityChangedGetter, onNewDisposableCreated: (disposable: vscode.Disposable) => void) {
|
|
super(validation, valueGetter, targetValueGetter, onTargetValidityChangedGetter, onNewDisposableCreated);
|
|
throwUnless(validation.target !== undefined);
|
|
this._target = validation.target;
|
|
}
|
|
|
|
private validateOnTargetValidityChange() {
|
|
if (!this._ensureOnTargetValidityChangeListenerAdded) {
|
|
this._ensureOnTargetValidityChangeListenerAdded = true;
|
|
this.onTargetValidityChanged(async (isTargetValid: boolean) => {
|
|
if (isTargetValid) { // if target is valid
|
|
await this.validate();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
abstract isComparisonSuccessful(): Promise<boolean>;
|
|
async validate(): Promise<ValidationResult> {
|
|
this.validateOnTargetValidityChange();
|
|
const isValid = await this.isComparisonSuccessful();
|
|
return {
|
|
valid: isValid,
|
|
message: isValid ? undefined : this.description
|
|
};
|
|
}
|
|
}
|
|
|
|
export class LessThanOrEqualsValidation extends Comparison {
|
|
async isComparisonSuccessful() {
|
|
const value = (await this.getValue());
|
|
const targetValue = (await this.getTargetValue(this.target));
|
|
return (isUndefinedOrEmpty(value) || isUndefinedOrEmpty(targetValue)) ? true : value! <= targetValue!;
|
|
}
|
|
}
|
|
|
|
export class GreaterThanOrEqualsValidation extends Comparison {
|
|
async isComparisonSuccessful() {
|
|
const value = (await this.getValue());
|
|
const targetValue = (await this.getTargetValue(this.target));
|
|
return (isUndefinedOrEmpty(value) || isUndefinedOrEmpty(targetValue)) ? true : value! >= targetValue!;
|
|
}
|
|
}
|
|
|
|
export class NotEqualValidation extends Comparison {
|
|
async isComparisonSuccessful() {
|
|
const value = (await this.getValue());
|
|
const targetValue = this.target;
|
|
return (isUndefinedOrEmpty(value) || isUndefinedOrEmpty(targetValue)) ? true : value!.toString() !== targetValue!;
|
|
}
|
|
}
|
|
|
|
export function createValidation(validation: ValidationInfo, valueGetter: ValueGetter, targetValueGetter?: TargetValueGetter, onTargetValidityChangedGetter?: OnTargetValidityChangedGetter, onDisposableCreated?: (disposable: vscode.Disposable) => void): Validation {
|
|
switch (validation.type) {
|
|
case ValidationType.Regex: return new RegexValidation(<RegexValidationInfo>validation, valueGetter);
|
|
case ValidationType.IsInteger: return new IntegerValidation(<IntegerValidationInfo>validation, valueGetter);
|
|
case ValidationType.LessThanOrEqualsTo: return new LessThanOrEqualsValidation(<ComparisonValidationInfo>validation, valueGetter, targetValueGetter!, onTargetValidityChangedGetter!, onDisposableCreated!);
|
|
case ValidationType.GreaterThanOrEqualsTo: return new GreaterThanOrEqualsValidation(<ComparisonValidationInfo>validation, valueGetter, targetValueGetter!, onTargetValidityChangedGetter!, onDisposableCreated!);
|
|
case ValidationType.NotEqualTo: return new NotEqualValidation(<ComparisonValidationInfo>validation, valueGetter, targetValueGetter!, onTargetValidityChangedGetter!, onDisposableCreated!);
|
|
default: throw new Error(`unknown validation type:${validation.type}`); //dev error
|
|
}
|
|
}
|
|
|
|
export async function validateInputBoxComponent(component: azdata.InputBoxComponent, validations: Validation[] = []): Promise<boolean> {
|
|
let valid = true;
|
|
let message = '';
|
|
for (const validation of validations) {
|
|
const result = await validation.validate();
|
|
if (!result.valid) {
|
|
valid = false;
|
|
message = validation.description;
|
|
break; //bail out on first failure, remaining validations are processed after this one has been fixed by the user.
|
|
}
|
|
}
|
|
if ((component.validationErrorMessage ?? '') !== message) { // Update the message if needed
|
|
component.validationErrorMessage = message;
|
|
}
|
|
return valid;
|
|
}
|