-
Notifications
You must be signed in to change notification settings - Fork 5.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #21041 from votdev/custom_validators
mgr/dashboard: Add custom validators Reviewed-by: Stephan Müller <smueller@suse.com> Reviewed-by: Ricardo Marques <rimarques@suse.com> Reviewed-by: Tiago Melo <tmelo@suse.com>
- Loading branch information
Showing
2 changed files
with
154 additions
and
0 deletions.
There are no files selected for viewing
89 changes: 89 additions & 0 deletions
89
src/pybind/mgr/dashboard/frontend/src/app/shared/validators/cd-validators.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import { FormControl, FormGroup } from '@angular/forms'; | ||
|
||
import { CdValidators } from './cd-validators'; | ||
|
||
describe('CdValidators', () => { | ||
describe('email', () => { | ||
it('should not error on an empty email address', () => { | ||
const control = new FormControl(''); | ||
expect(CdValidators.email(control)).toBeNull(); | ||
}); | ||
|
||
it('should not error on valid email address', () => { | ||
const control = new FormControl('dashboard@ceph.com'); | ||
expect(CdValidators.email(control)).toBeNull(); | ||
}); | ||
|
||
it('should error on invalid email address', () => { | ||
const control = new FormControl('xyz'); | ||
expect(CdValidators.email(control)).toEqual({'email': true}); | ||
}); | ||
}); | ||
|
||
describe('requiredIf', () => { | ||
let form: FormGroup; | ||
|
||
beforeEach(() => { | ||
form = new FormGroup({ | ||
x: new FormControl(true), | ||
y: new FormControl('abc'), | ||
z: new FormControl('') | ||
}); | ||
}); | ||
|
||
it('should not error because all conditions are fulfilled', () => { | ||
form.get('z').setValue('zyx'); | ||
const validatorFn = CdValidators.requiredIf({ | ||
'x': true, | ||
'y': 'abc' | ||
}); | ||
expect(validatorFn(form.controls['z'])).toBeNull(); | ||
}); | ||
|
||
it('should not error because of unmet prerequisites', () => { | ||
// Define prereqs that do not match the current values of the form fields. | ||
const validatorFn = CdValidators.requiredIf({ | ||
'x': false, | ||
'y': 'xyz' | ||
}); | ||
// The validator must succeed because the prereqs do not match, so the | ||
// validation of the 'z' control will be skipped. | ||
expect(validatorFn(form.controls['z'])).toBeNull(); | ||
}); | ||
|
||
it('should error because of an empty value', () => { | ||
// Define prereqs that force the validator to validate the value of | ||
// the 'z' control. | ||
const validatorFn = CdValidators.requiredIf({ | ||
'x': true, | ||
'y': 'abc' | ||
}); | ||
// The validator must fail because the value of control 'z' is empty. | ||
expect(validatorFn(form.controls['z'])).toEqual({'required': true}); | ||
}); | ||
|
||
it('should not error because of unsuccessful condition', () => { | ||
form.get('z').setValue('zyx'); | ||
// Define prereqs that force the validator to validate the value of | ||
// the 'z' control. | ||
const validatorFn = CdValidators.requiredIf({ | ||
'x': true, | ||
'z': 'zyx' | ||
}, () => false); | ||
expect(validatorFn(form.controls['z'])).toBeNull(); | ||
}); | ||
|
||
it('should error because of successful condition', () => { | ||
const conditionFn = (value) => { | ||
return value === 'abc'; | ||
}; | ||
// Define prereqs that force the validator to validate the value of | ||
// the 'y' control. | ||
const validatorFn = CdValidators.requiredIf({ | ||
'x': true, | ||
'z': '' | ||
}, conditionFn); | ||
expect(validatorFn(form.controls['y'])).toEqual({'required': true}); | ||
}); | ||
}); | ||
}); |
65 changes: 65 additions & 0 deletions
65
src/pybind/mgr/dashboard/frontend/src/app/shared/validators/cd-validators.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { | ||
AbstractControl, | ||
ValidationErrors, | ||
ValidatorFn, | ||
Validators | ||
} from '@angular/forms'; | ||
|
||
import * as _ from 'lodash'; | ||
|
||
type Prerequisites = { // tslint:disable-line | ||
[key: string]: any | ||
}; | ||
|
||
export function isEmptyInputValue(value: any): boolean { | ||
return value == null || value.length === 0; | ||
} | ||
|
||
export class CdValidators { | ||
/** | ||
* Validator that performs email validation. In contrast to the Angular | ||
* email validator an empty email will not be handled as invalid. | ||
*/ | ||
static email(control: AbstractControl): ValidationErrors | null { | ||
// Exit immediately if value is empty. | ||
if (isEmptyInputValue(control.value)) { | ||
return null; | ||
} | ||
return Validators.email(control); | ||
} | ||
|
||
/** | ||
* Validator that requires controls to fulfill the specified condition if | ||
* the specified prerequisites matches. If the prerequisites are fulfilled, | ||
* then the given function is executed and if it succeeds, the 'required' | ||
* validation error will be returned, otherwise null. | ||
* @param {Prerequisites} prerequisites An object containing the prerequisites. | ||
* ### Example | ||
* ```typescript | ||
* { | ||
* 'generate_key': true, | ||
* 'username': 'Max Mustermann' | ||
* } | ||
* ``` | ||
* Only if all prerequisites are fulfilled, then the validation of the | ||
* control will be triggered. | ||
* @param {Function | undefined} condition The function to be executed when all | ||
* prerequisites are fulfilled. If not set, then the {@link isEmptyInputValue} | ||
* function will be used by default. The control's value is used as function | ||
* argument. The function must return true to set the validation error. | ||
* @return {ValidatorFn} Returns the validator function. | ||
*/ | ||
static requiredIf(prerequisites: Prerequisites, condition?: Function | undefined): ValidatorFn { | ||
return (control: AbstractControl): ValidationErrors | null => { | ||
// Check if all prerequisites matches. | ||
if (!Object.keys(prerequisites).every((key) => { | ||
return (control.parent && control.parent.get(key).value === prerequisites[key]); | ||
})) { | ||
return null; | ||
} | ||
const success = _.isFunction(condition) ? condition.call(condition, control.value) : | ||
isEmptyInputValue(control.value); | ||
return success ? {'required': true} : null; | ||
}; | ||
} | ||
} |