1 import { Component } from '@angular/core';
2 import { AbstractControl, ValidationErrors, Validators } from '@angular/forms';
3 import { Router } from '@angular/router';
4 import { RgwUserAccountsService } from '~/app/shared/api/rgw-user-accounts.service';
5 import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
6 import { CdForm } from '~/app/shared/forms/cd-form';
7 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
8 import { Account } from '../models/rgw-user-accounts';
9 import { NotificationService } from '~/app/shared/services/notification.service';
10 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
11 import { CdValidators, isEmptyInputValue } from '~/app/shared/forms/cd-validators';
12 import { CdFormBuilder } from '~/app/shared/forms/cd-form-builder';
13 import { FormatterService } from '~/app/shared/services/formatter.service';
14 import { Observable, concat as observableConcat } from 'rxjs';
17 selector: 'cd-rgw-user-accounts-form',
18 templateUrl: './rgw-user-accounts-form.component.html',
19 styleUrls: ['./rgw-user-accounts-form.component.scss']
21 export class RgwUserAccountsFormComponent extends CdForm {
22 accountForm: CdFormGroup;
25 editing: boolean = false;
26 submitObservables: Observable<Object>[] = [];
29 private router: Router,
30 private actionLabels: ActionLabelsI18n,
31 private rgwUserAccountsService: RgwUserAccountsService,
32 private notificationService: NotificationService,
33 private formBuilder: CdFormBuilder
36 this.editing = this.router.url.includes('rgw/accounts/edit');
37 this.action = this.editing ? this.actionLabels.EDIT : this.actionLabels.CREATE;
38 this.resource = $localize`Account`;
43 private createForm() {
44 this.accountForm = this.formBuilder.group({
47 account_name: ['', Validators.required],
48 email: ['', CdValidators.email],
52 [CdValidators.requiredIf({ max_users_mode: '1' }), CdValidators.number(false)]
57 [CdValidators.requiredIf({ max_roles_mode: '1' }), CdValidators.number(false)]
62 [CdValidators.requiredIf({ max_group_mode: '1' }), CdValidators.number(false)]
64 max_access_keys_mode: [1],
67 [CdValidators.requiredIf({ max_access_keys_mode: '1' }), CdValidators.number(false)]
69 max_buckets_mode: [1],
72 [CdValidators.requiredIf({ max_buckets_mode: '1' }), CdValidators.number(false)]
74 account_quota_enabled: [false],
75 account_quota_max_size_unlimited: [true],
76 account_quota_max_size: [
79 CdValidators.composeIf(
81 account_quota_enabled: true,
82 account_quota_max_size_unlimited: false
84 [Validators.required, this.quotaMaxSizeValidator]
88 account_quota_max_objects_unlimited: [true],
89 account_quota_max_objects: [
92 CdValidators.requiredIf({
93 account_quota_enabled: true,
94 account_quota_max_objects_unlimited: false
96 Validators.pattern(/^[0-9]+$/)
99 bucket_quota_enabled: [false],
100 bucket_quota_max_size_unlimited: [true],
101 bucket_quota_max_size: [
104 CdValidators.composeIf(
106 bucket_quota_enabled: true,
107 bucket_quota_max_size_unlimited: false
109 [Validators.required, this.quotaMaxSizeValidator]
113 bucket_quota_max_objects_unlimited: [true],
114 bucket_quota_max_objects: [
117 CdValidators.requiredIf({
118 bucket_quota_enabled: true,
119 bucket_quota_max_objects_unlimited: false
121 Validators.pattern(/^[0-9]+$/)
128 * Validate the quota maximum size, e.g. 1096, 1K, 30M or 1.9MiB.
130 quotaMaxSizeValidator(control: AbstractControl): ValidationErrors | null {
131 if (isEmptyInputValue(control.value)) {
134 const m = RegExp('^(\\d+(\\.\\d+)?)\\s*(B|K(B|iB)?|M(B|iB)?|G(B|iB)?|T(B|iB)?)?$', 'i').exec(
138 return { quotaMaxSize: true };
140 const bytes = new FormatterService().toBytes(control.value);
141 return bytes < 1024 ? { quotaMaxSize: true } : null;
145 let notificationTitle: string = '';
146 if (this.accountForm.invalid) {
150 if (this.accountForm.pending) {
151 this.accountForm.setErrors({ cdSubmitButton: true });
156 const formvalue = this.accountForm.value;
157 const createPayload = {
158 account_id: formvalue.account_id,
159 account_name: formvalue.account_name,
160 email: formvalue.email,
161 tenant: formvalue.tenant,
162 max_users: this.getValueFromFormControl('max_users'),
163 max_buckets: this.getValueFromFormControl('max_buckets'),
164 max_roles: this.getValueFromFormControl('max_roles'),
165 max_group: this.getValueFromFormControl('max_group'),
166 max_access_keys: this.getValueFromFormControl('max_access_keys')
168 notificationTitle = $localize`Account created successfully`;
169 this.rgwUserAccountsService.create(createPayload).subscribe({
170 next: (account: Account) => {
171 this.accountForm.get('account_id').setValue(account.id);
172 this.setQuotaConfig();
173 this.notificationService.show(NotificationType.success, notificationTitle);
176 // Reset the 'Submit' button.
177 this.accountForm.setErrors({ cdSubmitButton: true });
184 const accountId: string = this.accountForm.get('account_id').value;
185 // Check if account quota has been modified.
186 if (this._isQuotaConfDirty('account')) {
187 const accountQuotaArgs = this._getQuotaArgs('account');
188 this.submitObservables.push(
189 this.rgwUserAccountsService.setQuota(accountId, accountQuotaArgs)
192 // Check if bucket quota has been modified.
193 if (this._isQuotaConfDirty('bucket')) {
194 const bucketQuotaArgs = this._getQuotaArgs('bucket');
195 this.submitObservables.push(this.rgwUserAccountsService.setQuota(accountId, bucketQuotaArgs));
197 // Finally execute all observables one by one in serial.
198 observableConcat(...this.submitObservables).subscribe({
200 // Reset the 'Submit' button.
201 this.accountForm.setErrors({ cdSubmitButton: true });
207 if (this.submitObservables.length == 0) {
213 * Helper function to get the arguments for the API request when any
214 * quota configuration has been modified.
216 private _getQuotaArgs(quotaType: string) {
218 quota_type: quotaType,
219 enabled: this.accountForm.getValue(`${quotaType}_quota_enabled`),
223 if (!this.accountForm.getValue(`${quotaType}_quota_max_size_unlimited`)) {
224 // Convert the given value to bytes.
225 const bytes = new FormatterService().toBytes(
226 this.accountForm.getValue(`${quotaType}_quota_max_size`)
228 // Finally convert the value to KiB.
229 result['max_size'] = (bytes / 1024).toFixed(0) as any;
231 if (!this.accountForm.getValue(`${quotaType}_quota_max_objects_unlimited`)) {
232 result['max_objects'] = `${this.accountForm.getValue(`${quotaType}_quota_max_objects`)}`;
238 * Check if any quota has been modified.
239 * @return {Boolean} Returns TRUE if the quota has been modified.
241 private _isQuotaConfDirty(quotaType: string): boolean {
242 if (this.accountForm.get(`${quotaType}_quota_enabled`).value) {
244 `${quotaType}_quota_enabled`,
245 `${quotaType}_quota_max_size_unlimited`,
246 `${quotaType}_quota_max_size`,
247 `${quotaType}_quota_max_objects_unlimited`,
248 `${quotaType}_quota_max_objects`
250 return this.accountForm.get(path).dirty;
256 onModeChange(mode: string, formControlName: string) {
258 // If 'Custom' mode is selected, then ensure that the form field
259 // 'Max. buckets' contains a valid value. Set it to default if
261 if (!this.accountForm.get(formControlName).valid) {
262 this.accountForm.patchValue({
263 [formControlName]: 1000
269 goToListView(): void {
270 this.router.navigate(['rgw/accounts']);
273 getValueFromFormControl(formControlName: string) {
274 const formvalue = this.accountForm.value;
275 return formvalue[`${formControlName}_mode`] == 1
276 ? formvalue[formControlName]
277 : formvalue[`${formControlName}_mode`];