]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/blob
b383eaa00270ae9fa8f3739fc9956db00776a022
[ceph-ci.git] /
1 import { Component, OnInit } from '@angular/core';
2 import { FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms';
3 import { ActivatedRoute, Router } from '@angular/router';
4
5 import * as _ from 'lodash';
6
7 import { ConfigurationService } from '../../../../shared/api/configuration.service';
8 import { NotificationType } from '../../../../shared/enum/notification-type.enum';
9 import { CdFormGroup } from '../../../../shared/forms/cd-form-group';
10 import { CdValidators } from '../../../../shared/forms/cd-validators';
11 import { NotificationService } from '../../../../shared/services/notification.service';
12 import { ConfigFormCreateRequestModel } from './configuration-form-create-request.model';
13 import { ConfigFormModel } from './configuration-form.model';
14
15 @Component({
16   selector: 'cd-configuration-form',
17   templateUrl: './configuration-form.component.html',
18   styleUrls: ['./configuration-form.component.scss']
19 })
20 export class ConfigurationFormComponent implements OnInit {
21   configForm: CdFormGroup;
22   response: ConfigFormModel;
23   type: string;
24   inputType: string;
25   humanReadableType: string;
26   minValue: number;
27   maxValue: number;
28   patternHelpText: string;
29   availSections = ['global', 'mon', 'mgr', 'osd', 'mds', 'client'];
30
31   constructor(
32     private route: ActivatedRoute,
33     private router: Router,
34     private configService: ConfigurationService,
35     private notificationService: NotificationService
36   ) {
37     this.createForm();
38   }
39
40   createForm() {
41     const formControls = {
42       name: new FormControl({ value: null }),
43       desc: new FormControl({ value: null }),
44       long_desc: new FormControl({ value: null }),
45       values: new FormGroup({}),
46       default: new FormControl({ value: null }),
47       daemon_default: new FormControl({ value: null }),
48       services: new FormControl([])
49     };
50
51     this.availSections.forEach((section) => {
52       formControls.values.addControl(section, new FormControl(null));
53     });
54
55     this.configForm = new CdFormGroup(formControls);
56     this.configForm._filterValue = (value) => {
57       return value;
58     };
59   }
60
61   ngOnInit() {
62     this.route.params.subscribe((params: { name: string }) => {
63       const configName = params.name;
64       this.configService.get(configName).subscribe((resp: ConfigFormModel) => {
65         this.setResponse(resp);
66       });
67     });
68   }
69
70   getType(type: string): any {
71     const knownTypes = [
72       {
73         name: 'uint64_t',
74         inputType: 'number',
75         humanReadable: 'Positive integer value',
76         defaultMin: 0,
77         patternHelpText: 'The entered value needs to be a positive number.',
78         isNumberType: true,
79         allowsNegative: false
80       },
81       {
82         name: 'int64_t',
83         inputType: 'number',
84         humanReadable: 'Integer value',
85         patternHelpText: 'The entered value needs to be a number.',
86         isNumberType: true,
87         allowsNegative: true
88       },
89       {
90         name: 'size_t',
91         inputType: 'number',
92         humanReadable: 'Positive integer value (size)',
93         defaultMin: 0,
94         patternHelpText: 'The entered value needs to be a positive number.',
95         isNumberType: true,
96         allowsNegative: false
97       },
98       {
99         name: 'secs',
100         inputType: 'number',
101         humanReadable: 'Positive integer value (secs)',
102         defaultMin: 1,
103         patternHelpText: 'The entered value needs to be a positive number.',
104         isNumberType: true,
105         allowsNegative: false
106       },
107       {
108         name: 'double',
109         inputType: 'number',
110         humanReadable: 'Decimal value',
111         patternHelpText: 'The entered value needs to be a number or decimal.',
112         isNumberType: true,
113         allowsNegative: true
114       },
115       { name: 'std::string', inputType: 'text', humanReadable: 'Text', isNumberType: false },
116       {
117         name: 'entity_addr_t',
118         inputType: 'text',
119         humanReadable: 'IPv4 or IPv6 address',
120         patternHelpText: 'The entered value needs to be a valid IP address.',
121         isNumberType: false
122       },
123       {
124         name: 'uuid_d',
125         inputType: 'text',
126         humanReadable: 'UUID',
127         patternHelpText:
128           'The entered value is not a valid UUID, e.g.: 67dcac9f-2c03-4d6c-b7bd-1210b3a259a8',
129         isNumberType: false
130       },
131       { name: 'bool', inputType: 'checkbox', humanReadable: 'Boolean value', isNumberType: false }
132     ];
133
134     let currentType = null;
135
136     knownTypes.forEach((knownType) => {
137       if (knownType.name === type) {
138         currentType = knownType;
139       }
140     });
141
142     if (currentType !== null) {
143       return currentType;
144     }
145
146     throw new Error('Found unknown type "' + type + '" for config option.');
147   }
148
149   getValidators(configOption: any): ValidatorFn[] {
150     const typeParams = this.getType(configOption.type);
151     this.patternHelpText = typeParams.patternHelpText;
152
153     if (typeParams.isNumberType) {
154       const validators = [];
155
156       if (configOption.max && configOption.max !== '') {
157         this.maxValue = configOption.max;
158         validators.push(Validators.max(configOption.max));
159       }
160
161       if ('min' in configOption && configOption.min !== '') {
162         this.minValue = configOption.min;
163         validators.push(Validators.min(configOption.min));
164       } else if ('defaultMin' in typeParams) {
165         this.minValue = typeParams.defaultMin;
166         validators.push(Validators.min(typeParams.defaultMin));
167       }
168
169       if (configOption.type === 'double') {
170         validators.push(CdValidators.decimalNumber());
171       } else {
172         validators.push(CdValidators.number(typeParams.allowsNegative));
173       }
174
175       return validators;
176     } else if (configOption.type === 'entity_addr_t') {
177       return [CdValidators.ip()];
178     } else if (configOption.type === 'uuid_d') {
179       return [CdValidators.uuid()];
180     }
181   }
182
183   getStep(type: string, value: number): number | undefined {
184     const numberTypes = ['uint64_t', 'int64_t', 'size_t', 'secs'];
185
186     if (numberTypes.includes(type)) {
187       return 1;
188     }
189
190     if (type === 'double') {
191       if (value !== null) {
192         const stringVal = value.toString();
193         if (stringVal.indexOf('.') !== -1) {
194           // Value type double and contains decimal characters
195           const decimal = value.toString().split('.');
196           return Math.pow(10, -decimal[1].length);
197         }
198       }
199
200       return 0.1;
201     }
202
203     return undefined;
204   }
205
206   setResponse(response: ConfigFormModel) {
207     this.response = response;
208     const validators = this.getValidators(response);
209
210     this.configForm.get('name').setValue(response.name);
211     this.configForm.get('desc').setValue(response.desc);
212     this.configForm.get('long_desc').setValue(response.long_desc);
213     this.configForm.get('default').setValue(response.default);
214     this.configForm.get('daemon_default').setValue(response.daemon_default);
215     this.configForm.get('services').setValue(response.services);
216
217     if (this.response.value) {
218       this.response.value.forEach((value) => {
219         // Check value type. If it's a boolean value we need to convert it because otherwise we
220         // would use the string representation. That would cause issues for e.g. checkboxes.
221         let sectionValue = null;
222         if (value.value === 'true') {
223           sectionValue = true;
224         } else if (value.value === 'false') {
225           sectionValue = false;
226         } else {
227           sectionValue = value.value;
228         }
229         this.configForm
230           .get('values')
231           .get(value.section)
232           .setValue(sectionValue);
233       });
234     }
235
236     this.availSections.forEach((section) => {
237       this.configForm
238         .get('values')
239         .get(section)
240         .setValidators(validators);
241     });
242
243     const currentType = this.getType(response.type);
244     this.type = currentType.name;
245     this.inputType = currentType.inputType;
246     this.humanReadableType = currentType.humanReadable;
247   }
248
249   createRequest(): ConfigFormCreateRequestModel | null {
250     const values = [];
251
252     this.availSections.forEach((section) => {
253       const sectionValue = this.configForm.getValue(section);
254       if (sectionValue) {
255         values.push({ section: section, value: sectionValue });
256       }
257     });
258
259     if (!_.isEqual(this.response.value, values)) {
260       const request = new ConfigFormCreateRequestModel();
261       request.name = this.configForm.getValue('name');
262       request.value = values;
263       return request;
264     }
265
266     return null;
267   }
268
269   submit() {
270     const request = this.createRequest();
271
272     if (request) {
273       this.configService.create(request).subscribe(
274         () => {
275           this.notificationService.show(
276             NotificationType.success,
277             'Config option ' + request.name + ' has been updated.',
278             'Update config option'
279           );
280           this.router.navigate(['/configuration']);
281         },
282         () => {
283           this.configForm.setErrors({ cdSubmitButton: true });
284         }
285       );
286     }
287
288     this.router.navigate(['/configuration']);
289   }
290 }