* RGW user/bucket "max. size" should be hidden when "user/bucket quota" is not enabled
* Only execute validators if necessary
Fixes: https://tracker.ceph.com/issues/39964
Signed-off-by: Volker Theile <vtheile@suse.com>
<!-- Maximum size -->
<div class="form-group"
[ngClass]="{'has-error': userForm.showError('user_quota_max_size', frm)}"
- *ngIf="!userForm.getValue('user_quota_max_size_unlimited')">
+ *ngIf="userForm.controls.user_quota_enabled.value && !userForm.getValue('user_quota_max_size_unlimited')">
<label class="control-label col-sm-3"
for="user_quota_max_size">
<ng-container i18n>Max. size</ng-container>
<!-- Maximum objects -->
<div class="form-group"
[ngClass]="{'has-error': userForm.showError('user_quota_max_objects', frm)}"
- *ngIf="!userForm.getValue('user_quota_max_objects_unlimited')">
+ *ngIf="userForm.controls.user_quota_enabled.value && !userForm.getValue('user_quota_max_objects_unlimited')">
<label class="control-label col-sm-3"
for="user_quota_max_objects">
<ng-container i18n>Max. objects</ng-container>
<!-- Maximum size -->
<div class="form-group"
[ngClass]="{'has-error': userForm.showError('bucket_quota_max_size', frm)}"
- *ngIf="!userForm.getValue('bucket_quota_max_size_unlimited')">
+ *ngIf="userForm.controls.bucket_quota_enabled.value && !userForm.getValue('bucket_quota_max_size_unlimited')">
<label class="control-label col-sm-3"
for="bucket_quota_max_size">
<ng-container i18n>Max. size</ng-container>
<!-- Maximum objects -->
<div class="form-group"
[ngClass]="{'has-error': userForm.showError('bucket_quota_max_objects', frm)}"
- *ngIf="!userForm.getValue('bucket_quota_max_objects_unlimited')">
+ *ngIf="userForm.controls.bucket_quota_enabled.value && !userForm.getValue('bucket_quota_max_objects_unlimited')">
<label class="control-label col-sm-3"
for="bucket_quota_max_objects">
<ng-container i18n>Max. objects</ng-container>
user_quota_max_size: [
null,
[
- CdValidators.requiredIf({
- user_quota_enabled: true,
- user_quota_max_size_unlimited: false
- }),
- this.quotaMaxSizeValidator
+ CdValidators.composeIf(
+ {
+ user_quota_enabled: true,
+ user_quota_max_size_unlimited: false
+ },
+ [Validators.required, this.quotaMaxSizeValidator]
+ )
]
],
user_quota_max_objects_unlimited: [true],
bucket_quota_max_size: [
null,
[
- CdValidators.requiredIf({
- bucket_quota_enabled: true,
- bucket_quota_max_size_unlimited: false
- }),
- this.quotaMaxSizeValidator
+ CdValidators.composeIf(
+ {
+ bucket_quota_enabled: true,
+ bucket_quota_max_size_unlimited: false
+ },
+ [Validators.required, this.quotaMaxSizeValidator]
+ )
]
],
bucket_quota_max_objects_unlimited: [true],
formHelper.expectError('x', 'notUnique');
}));
});
+
+ describe('composeIf', () => {
+ beforeEach(() => {
+ form = new CdFormGroup({
+ x: new FormControl(true),
+ y: new FormControl('abc'),
+ z: new FormControl('')
+ });
+ formHelper = new FormHelper(form);
+ });
+
+ it('should not error because all conditions are fulfilled', () => {
+ formHelper.setValue('z', 'zyx');
+ const validatorFn = CdValidators.composeIf(
+ {
+ x: true,
+ y: 'abc'
+ },
+ [Validators.required]
+ );
+ expect(validatorFn(form.get('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.composeIf(
+ {
+ x: false,
+ y: 'xyz'
+ },
+ [Validators.required]
+ );
+ // The validator must succeed because the prereqs do not match, so the
+ // validation of the 'z' control will be skipped.
+ expect(validatorFn(form.get('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.composeIf(
+ {
+ x: true,
+ y: 'abc'
+ },
+ [Validators.required]
+ );
+ // The validator must fail because the value of control 'z' is empty.
+ expect(validatorFn(form.get('z'))).toEqual({ required: true });
+ });
+ });
});
isWatched = true;
}
- // Check if all prerequisites matches.
+ // Check if all prerequisites met.
if (
!Object.keys(prerequisites).every((key) => {
return control.parent && control.parent.get(key).value === prerequisites[key];
};
}
+ /**
+ * Compose multiple validators into a single function that returns the union of
+ * the individual error maps for the provided control when the given prerequisites
+ * are fulfilled.
+ *
+ * @param {Object} prerequisites An object containing the prerequisites as
+ * key/value pairs.
+ * ### Example
+ * ```typescript
+ * {
+ * 'generate_key': true,
+ * 'username': 'Max Mustermann'
+ * }
+ * ```
+ * @param {ValidatorFn[]} validators List of validators that should be taken
+ * into action when the prerequisites are met.
+ * @return {ValidatorFn} Returns the validator function.
+ */
+ static composeIf(prerequisites: Object, validators: ValidatorFn[]): ValidatorFn {
+ let isWatched = false;
+ return (control: AbstractControl): ValidationErrors | null => {
+ if (!isWatched && control.parent) {
+ Object.keys(prerequisites).forEach((key) => {
+ control.parent.get(key).valueChanges.subscribe(() => {
+ control.updateValueAndValidity({ emitEvent: false });
+ });
+ });
+ isWatched = true;
+ }
+ // Check if all prerequisites are met.
+ if (
+ !Object.keys(prerequisites).every((key) => {
+ return control.parent && control.parent.get(key).value === prerequisites[key];
+ })
+ ) {
+ return null;
+ }
+ return Validators.compose(validators)(control);
+ };
+ }
+
/**
* Custom validation by passing a name for the error and a function as error condition.
*