1 import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
2 import { AbstractControl, FormArray, FormControl, FormGroup, Validators } from '@angular/forms';
3 import { NgbActiveModal, NgbDateStruct, NgbTimeStruct } from '@ng-bootstrap/ng-bootstrap';
4 import { uniq } from 'lodash';
5 import { Observable, OperatorFunction, of, timer } from 'rxjs';
6 import { catchError, debounceTime, distinctUntilChanged, map, switchMap } from 'rxjs/operators';
7 import { CephfsSnapshotScheduleService } from '~/app/shared/api/cephfs-snapshot-schedule.service';
8 import { DirectoryStoreService } from '~/app/shared/api/directory-store.service';
9 import { ActionLabelsI18n, URLVerbs } from '~/app/shared/constants/app.constants';
10 import { Icons } from '~/app/shared/enum/icons.enum';
11 import { RepeatFrequency } from '~/app/shared/enum/repeat-frequency.enum';
12 import { RetentionFrequency } from '~/app/shared/enum/retention-frequency.enum';
13 import { CdForm } from '~/app/shared/forms/cd-form';
14 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
15 import { CdTableColumn } from '~/app/shared/models/cd-table-column';
16 import { FinishedTask } from '~/app/shared/models/finished-task';
17 import { RetentionPolicy, SnapshotScheduleFormValue } from '~/app/shared/models/snapshot-schedule';
18 import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
20 const VALIDATON_TIMER = 300;
21 const DEBOUNCE_TIMER = 300;
24 selector: 'cd-cephfs-snapshotschedule-form',
25 templateUrl: './cephfs-snapshotschedule-form.component.html',
26 styleUrls: ['./cephfs-snapshotschedule-form.component.scss']
28 export class CephfsSnapshotscheduleFormComponent extends CdForm implements OnInit {
33 repeatFrequencies = Object.entries(RepeatFrequency);
34 retentionFrequencies = Object.entries(RetentionFrequency);
36 currentTime!: NgbTimeStruct;
37 minDate!: NgbDateStruct;
39 snapScheduleForm!: CdFormGroup;
44 columns!: CdTableColumn[];
47 public activeModal: NgbActiveModal,
48 private actionLabels: ActionLabelsI18n,
49 private snapScheduleService: CephfsSnapshotScheduleService,
50 private taskWrapper: TaskWrapperService,
51 private cd: ChangeDetectorRef,
52 public directoryStore: DirectoryStoreService
55 this.resource = $localize`Snapshot schedule`;
57 const currentDatetime = new Date();
59 year: currentDatetime.getUTCFullYear(),
60 month: currentDatetime.getUTCMonth() + 1,
61 day: currentDatetime.getUTCDate()
64 hour: currentDatetime.getUTCHours(),
65 minute: currentDatetime.getUTCMinutes(),
66 second: currentDatetime.getUTCSeconds()
71 this.action = this.actionLabels.CREATE;
72 this.directoryStore.loadDirectories(this.id, '/', 3);
77 get retentionPolicies() {
78 return this.snapScheduleForm.get('retentionPolicies') as FormArray;
81 search: OperatorFunction<string, readonly string[]> = (input: Observable<string>) =>
83 debounceTime(DEBOUNCE_TIMER),
84 distinctUntilChanged(),
86 this.directoryStore.search(term, this.id).pipe(
95 this.snapScheduleForm = new CdFormGroup(
97 directory: new FormControl(undefined, {
98 validators: [Validators.required]
100 startDate: new FormControl(this.minDate, {
101 validators: [Validators.required]
103 startTime: new FormControl(this.currentTime, {
104 validators: [Validators.required]
106 repeatInterval: new FormControl(1, {
107 validators: [Validators.required, Validators.min(1)]
109 repeatFrequency: new FormControl(RepeatFrequency.Daily, {
110 validators: [Validators.required]
112 retentionPolicies: new FormArray([])
115 asyncValidators: [this.validateSchedule(), this.validateRetention()]
120 addRetentionPolicy() {
121 this.retentionPolicies.push(
123 retentionInterval: new FormControl(1),
124 retentionFrequency: new FormControl(RetentionFrequency.Daily)
127 this.cd.detectChanges();
130 removeRetentionPolicy(idx: number) {
131 this.retentionPolicies.removeAt(idx);
132 this.cd.detectChanges();
135 parseDatetime(date: NgbDateStruct, time?: NgbTimeStruct): string {
136 return `${date.year}-${date.month}-${date.day}T${time.hour || '00'}:${time.minute || '00'}:${
140 parseSchedule(interval: number, frequency: string): string {
141 return `${interval}${frequency}`;
144 parseRetentionPolicies(retentionPolicies: RetentionPolicy[]) {
145 return retentionPolicies
146 ?.filter((r) => r?.retentionInterval !== null && r?.retentionFrequency !== null)
147 ?.map?.((r) => `${r.retentionInterval}-${r.retentionFrequency}`)
152 if (this.snapScheduleForm.invalid) {
153 this.snapScheduleForm.setErrors({ cdSubmitButton: true });
157 const values = this.snapScheduleForm.value as SnapshotScheduleFormValue;
159 const snapScheduleObj = {
161 path: values.directory,
162 snap_schedule: this.parseSchedule(values.repeatInterval, values.repeatFrequency),
163 start: this.parseDatetime(values.startDate, values.startTime)
166 const retentionPoliciesValues = this.parseRetentionPolicies(values?.retentionPolicies);
167 if (retentionPoliciesValues) {
168 snapScheduleObj['retention_policy'] = retentionPoliciesValues;
172 .wrapTaskAroundCall({
173 task: new FinishedTask('cephfs/snapshot/schedule/' + URLVerbs.CREATE, {
174 path: snapScheduleObj.path
176 call: this.snapScheduleService.create(snapScheduleObj)
180 this.snapScheduleForm.setErrors({ cdSubmitButton: true });
183 this.activeModal.close();
189 return (frm: AbstractControl) => {
190 const directory = frm.get('directory');
191 const repeatFrequency = frm.get('repeatFrequency');
192 const repeatInterval = frm.get('repeatInterval');
193 return timer(VALIDATON_TIMER).pipe(
195 this.snapScheduleService
196 .checkScheduleExists(
199 repeatInterval?.value,
200 repeatFrequency?.value
203 map((exists: boolean) => {
205 repeatFrequency?.setErrors({ notUnique: true }, { emitEvent: true });
207 repeatFrequency?.setErrors(null);
217 getFormArrayItem(frm: FormGroup, frmArrayName: string, ctrl: string, idx: number) {
218 return (frm.get(frmArrayName) as FormArray)?.controls?.[idx]?.get?.(ctrl);
221 validateRetention() {
222 return (frm: FormGroup) => {
223 return timer(VALIDATON_TIMER).pipe(
225 const retentionList = (frm.get('retentionPolicies') as FormArray).controls?.map(
227 return ctrl.get('retentionFrequency').value;
230 if (uniq(retentionList)?.length !== retentionList?.length) {
231 this.getFormArrayItem(
234 'retentionFrequency',
235 retentionList.length - 1
241 return this.snapScheduleService
242 .checkRetentionPolicyExists(frm.get('directory').value, this.fsName, retentionList)
244 map(({ exists, errorIndex }) => {
246 this.getFormArrayItem(
249 'retentionFrequency',
251 )?.setErrors?.({ notUnique: true });
253 (frm.get('retentionPolicies') as FormArray).controls?.forEach?.((_, i) => {
254 this.getFormArrayItem(
257 'retentionFrequency',
259 )?.setErrors?.(null);