]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/blob
3ced71f0264825bc9c04b40dbca0be46e88dbd73
[ceph-ci.git] /
1 import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
2 import { FormControl, Validators } from '@angular/forms';
3
4 import _ from 'lodash';
5 import { ReplaySubject } from 'rxjs';
6
7 import { Icons } from '~/app/shared/enum/icons.enum';
8 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
9 import {
10   RbdConfigurationEntry,
11   RbdConfigurationSourceField,
12   RbdConfigurationType
13 } from '~/app/shared/models/configuration';
14 import { FormatterService } from '~/app/shared/services/formatter.service';
15 import { RbdConfigurationService } from '~/app/shared/services/rbd-configuration.service';
16
17 @Component({
18   selector: 'cd-rbd-configuration-form',
19   templateUrl: './rbd-configuration-form.component.html',
20   styleUrls: ['./rbd-configuration-form.component.scss']
21 })
22 export class RbdConfigurationFormComponent implements OnInit {
23   @Input()
24   form: CdFormGroup;
25   @Input()
26   initializeData = new ReplaySubject<{
27     initialData: RbdConfigurationEntry[];
28     sourceType: RbdConfigurationSourceField;
29   }>(1);
30   @Output()
31   changes = new EventEmitter<any>();
32
33   icons = Icons;
34
35   ngDataReady = new EventEmitter<any>();
36   initialData: RbdConfigurationEntry[];
37   configurationType = RbdConfigurationType;
38   sectionVisibility: { [key: string]: boolean } = {};
39
40   constructor(
41     public formatterService: FormatterService,
42     public rbdConfigurationService: RbdConfigurationService
43   ) {}
44
45   ngOnInit() {
46     const configFormGroup = this.createConfigurationFormGroup();
47     this.form.addControl('configuration', configFormGroup);
48
49     // Listen to changes and emit the values to the parent component
50     configFormGroup.valueChanges.subscribe(() => {
51       this.changes.emit(this.getDirtyValues.bind(this));
52     });
53
54     if (this.initializeData) {
55       this.initializeData.subscribe((data: Record<string, any>) => {
56         this.initialData = data.initialData;
57         const dataType = data.sourceType;
58         this.rbdConfigurationService.getWritableOptionFields().forEach((option) => {
59           const optionData = data.initialData
60             .filter((entry: Record<string, any>) => entry.name === option.name)
61             .pop();
62           if (optionData && optionData['source'] === dataType) {
63             this.form.get(`configuration.${option.name}`).setValue(optionData['value']);
64           }
65         });
66         this.ngDataReady.emit();
67       });
68     }
69
70     this.rbdConfigurationService
71       .getWritableSections()
72       .forEach((section) => (this.sectionVisibility[section.class] = false));
73   }
74
75   getDirtyValues(includeLocalValues = false, localFieldType?: RbdConfigurationSourceField) {
76     if (includeLocalValues && !localFieldType) {
77       const msg =
78         'ProgrammingError: If local values shall be included, a proper localFieldType argument has to be provided, too';
79       throw new Error(msg);
80     }
81     const result = {};
82
83     this.rbdConfigurationService.getWritableOptionFields().forEach((config) => {
84       const control: any = this.form.get('configuration').get(config.name);
85       const dirty = control.dirty;
86
87       if (this.initialData && this.initialData[config.name] === control.value) {
88         return; // Skip controls with initial data loaded
89       }
90
91       if (dirty || (includeLocalValues && control['source'] === localFieldType)) {
92         if (control.value === null) {
93           result[config.name] = control.value;
94         } else if (config.type === RbdConfigurationType.bps) {
95           result[config.name] = this.formatterService.toBytes(control.value);
96         } else if (config.type === RbdConfigurationType.milliseconds) {
97           result[config.name] = this.formatterService.toMilliseconds(control.value);
98         } else if (config.type === RbdConfigurationType.iops) {
99           result[config.name] = this.formatterService.toIops(control.value);
100         } else {
101           result[config.name] = control.value;
102         }
103       }
104     });
105
106     return result;
107   }
108
109   /**
110    * Dynamically create form controls.
111    */
112   private createConfigurationFormGroup() {
113     const configFormGroup = new CdFormGroup({});
114
115     this.rbdConfigurationService.getWritableOptionFields().forEach((c) => {
116       let control: FormControl;
117       if (
118         c.type === RbdConfigurationType.milliseconds ||
119         c.type === RbdConfigurationType.iops ||
120         c.type === RbdConfigurationType.bps
121       ) {
122         let initialValue = 0;
123         _.forEach(this.initialData, (configList) => {
124           if (configList['name'] === c.name) {
125             initialValue = configList['value'];
126           }
127         });
128         control = new FormControl(initialValue, Validators.min(0));
129       } else {
130         throw new Error(
131           `Type ${c.type} is unknown, you may need to add it to RbdConfiguration class`
132         );
133       }
134       configFormGroup.addControl(c.name, control);
135     });
136
137     return configFormGroup;
138   }
139
140   /**
141    * Reset the value. The inherited value will be used instead.
142    */
143   reset(optionName: string) {
144     const formControl: any = this.form.get('configuration').get(optionName);
145     if (formControl.disabled) {
146       formControl.setValue(formControl['previousValue'] || 0);
147       formControl.enable();
148       if (!formControl['previousValue']) {
149         formControl.markAsPristine();
150       }
151     } else {
152       formControl['previousValue'] = formControl.value;
153       formControl.setValue(null);
154       formControl.markAsDirty();
155       formControl.disable();
156     }
157   }
158
159   isDisabled(optionName: string) {
160     return this.form.get('configuration').get(optionName).disabled;
161   }
162
163   toggleSectionVisibility(className: string) {
164     this.sectionVisibility[className] = !this.sectionVisibility[className];
165   }
166 }