diff --git a/extensions/resource-deployment/src/interfaces.ts b/extensions/resource-deployment/src/interfaces.ts index f20366f93b..20450b7416 100644 --- a/extensions/resource-deployment/src/interfaces.ts +++ b/extensions/resource-deployment/src/interfaces.ts @@ -153,6 +153,9 @@ export interface FieldInfo { defaultValue?: string; confirmationRequired?: boolean; confirmationLabel?: string; + textValidationRequired?: boolean; + textValidationRegex?: string; + textValidationDescription?: string; min?: number; max?: number; required?: boolean; diff --git a/extensions/resource-deployment/src/ui/deployClusterWizard/pages/clusterSettingsPage.ts b/extensions/resource-deployment/src/ui/deployClusterWizard/pages/clusterSettingsPage.ts index 6196f1aa39..60608f9bb0 100644 --- a/extensions/resource-deployment/src/ui/deployClusterWizard/pages/clusterSettingsPage.ts +++ b/extensions/resource-deployment/src/ui/deployClusterWizard/pages/clusterSettingsPage.ts @@ -16,6 +16,8 @@ import { AuthenticationMode } from '../deployClusterWizardModel'; const localize = nls.loadMessageBundle(); const ConfirmPasswordName = 'ConfirmPassword'; +const clusterNameFieldDescription = localize('deployCluster.ClusterNameDescription', "The cluster name must consist only of alphanumeric lowercase characters or '-' and must start and end with an alphanumeric character."); + export class ClusterSettingsPage extends WizardPageBase { private inputComponents: InputComponents = {}; private activeDirectorySection!: azdata.FormComponent; @@ -37,7 +39,11 @@ export class ClusterSettingsPage extends WizardPageBase { label: localize('deployCluster.ClusterName', "Cluster name"), required: true, variableName: VariableNames.ClusterName_VariableName, - defaultValue: 'mssql-cluster' + defaultValue: 'mssql-cluster', + textValidationRequired: true, + textValidationRegex: '^[a-z0-9]$|^[a-z0-9][a-z0-9-]*[a-z0-9]$', + textValidationDescription: clusterNameFieldDescription, + description: clusterNameFieldDescription }, { type: FieldType.Text, label: localize('deployCluster.AdminUsername', "Admin username"), @@ -309,6 +315,13 @@ export class ClusterSettingsPage extends WizardPageBase { messages.push(getInvalidSQLPasswordMessage(localize('deployCluster.AdminPasswordField', "Password"))); } + this.validators.forEach(validator => { + const result = validator(); + if (!result.valid) { + messages.push(result.message); + } + }); + if (messages.length > 0) { this.wizard.wizardObject.message = { text: messages.length === 1 ? messages[0] : localize('deployCluster.ValidationError', "There are some errors on this page, click 'Show Details' to view the errors."), diff --git a/extensions/resource-deployment/src/ui/modelViewUtils.ts b/extensions/resource-deployment/src/ui/modelViewUtils.ts index 209beedbed..9622b7af9e 100644 --- a/extensions/resource-deployment/src/ui/modelViewUtils.ts +++ b/extensions/resource-deployment/src/ui/modelViewUtils.ts @@ -320,6 +320,27 @@ function processTextField(context: FieldContext): void { }); context.onNewInputComponentCreated(context.fieldInfo.variableName!, input); addLabelInputPairToContainer(context.view, context.components, label, input, context.fieldInfo.labelPosition); + + if (context.fieldInfo.textValidationRequired) { + let validationRegex: RegExp = new RegExp(context.fieldInfo.textValidationRegex!); + + const removeInvalidInputMessage = (): void => { + if (validationRegex.test(input.value!)) { // input is valid + removeValidationMessage(context.container, context.fieldInfo.textValidationDescription!); + } + }; + + context.onNewDisposableCreated(input.onTextChanged(() => { + removeInvalidInputMessage(); + })); + + const inputValidator: Validator = (): { valid: boolean; message: string; } => { + const inputIsValid = validationRegex.test(input.value!); + return { valid: inputIsValid, message: context.fieldInfo.textValidationDescription! }; + }; + context.onNewValidatorCreated(inputValidator); + + } } function processPasswordField(context: FieldContext): void {