Fix Bug 2291: No option to update VIM Account in NG-UI
[osm/NG-UI.git] / src / app / vim-accounts / new-vimaccount / NewVimaccountComponent.ts
1 /*
2  Copyright 2020 TATA ELXSI
3
4  Licensed under the Apache License, Version 2.0 (the 'License');
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7
8   http://www.apache.org/licenses/LICENSE-2.0
9
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15
16  Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
17 */
18 /**
19  * @file Vim Account Component.
20  */
21 import { isNullOrUndefined } from 'util';
22 import { HttpHeaders } from '@angular/common/http';
23 import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core';
24 import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
25 import { ActivatedRoute, Router } from '@angular/router';
26 import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
27 import { TranslateService } from '@ngx-translate/core';
28 import { NotifierService } from 'angular-notifier';
29 import 'codemirror/addon/dialog/dialog';
30 import 'codemirror/addon/display/autorefresh';
31 import 'codemirror/addon/display/fullscreen';
32 import 'codemirror/addon/edit/closebrackets';
33 import 'codemirror/addon/edit/matchbrackets';
34 import 'codemirror/addon/fold/brace-fold';
35 import 'codemirror/addon/fold/foldcode';
36 import 'codemirror/addon/fold/foldgutter';
37 import 'codemirror/addon/search/search';
38 import 'codemirror/addon/search/searchcursor';
39 import 'codemirror/keymap/sublime';
40 import 'codemirror/lib/codemirror';
41 import 'codemirror/mode/javascript/javascript';
42 import 'codemirror/mode/markdown/markdown';
43 import 'codemirror/mode/yaml/yaml';
44 import {
45   APIURLHEADER, CONFIGCONSTANT, ERRORDATA, MODALCLOSERESPONSEDATA, TYPEAWS, TYPEAZURE, TYPEOPENSTACK, TYPEOPENVIMNEBULA, TYPEOTERS,
46   TYPESECTION, TYPEVMWARE, VIM_TYPES
47 } from 'CommonModel';
48 import { environment } from 'environment';
49 import * as jsyaml from 'js-yaml';
50 import { RestService } from 'RestService';
51 import { SharedService } from 'SharedService';
52 import { VimAccountDetails } from 'VimAccountModel';
53 import { WarningComponent } from 'WarningComponent';
54 /**
55  * Creating component
56  * @Component takes NewVimaccountComponent.html as template url
57  */
58 @Component({
59   selector: 'app-new-vimaccount',
60   templateUrl: './NewVimaccountComponent.html',
61   styleUrls: ['./NewVimaccountComponent.scss']
62 })
63 /** Exporting a class @exports NewVimaccountComponent */
64 export class NewVimaccountComponent implements OnInit {
65   /** To inject services @public */
66   public injector: Injector;
67
68   /** FormGroup vim New Account added to the form @ html @public */
69   public vimNewAccountForm: FormGroup;
70
71   /** Supported Vim type for the dropdown */
72   public vimType: TYPESECTION[];
73
74   /** Supported Vim type for the dropdown */
75   public selectedVimType: string;
76
77   /** Form submission Add */
78   public submitted: boolean = false;
79
80   /** Showing more details of collapase */
81   public isCollapsed: boolean = false;
82
83   /** Check the Projects loading results @public */
84   public isLocationLoadingResults: boolean = false;
85
86   /** Give the message for the loading @public */
87   public message: string = 'PLEASEWAIT';
88
89   /** Set the check value  @public */
90   public check: boolean = false;
91
92   /** Handle the formate Change @public */
93   public defaults: {} = {
94     'text/x-yaml': ''
95   };
96
97   /** To Set Mode @public */
98   public mode: string = 'text/x-yaml';
99
100   /** To Set Mode @public */
101   public modeDefault: string = 'yaml';
102
103   /** options @public */
104   public options: {} = {
105     // eslint-disable-next-line no-invalid-this
106     mode: this.modeDefault,
107     showCursorWhenSelecting: true,
108     autofocus: false,
109     autoRefresh: true,
110     lineNumbers: true,
111     lineWrapping: true,
112     foldGutter: true,
113     gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
114     autoCloseBrackets: true,
115     matchBrackets: true,
116     theme: 'neat',
117     keyMap: 'sublime'
118   };
119
120   /** Data @public */
121   public data: string = '';
122
123   /** contains vim ID @public */
124   public vimID: string;
125
126   /** Element ref for fileInput @public */
127   @ViewChild('fileInput', { static: true }) public fileInput: ElementRef;
128
129   /** Element ref for fileInput @public */
130   @ViewChild('fileInputLabel', { static: true }) public fileInputLabel: ElementRef;
131
132   /** Contains all methods related to shared @private */
133   public sharedService: SharedService;
134
135   /** Check key for the Edit form @public */
136   public checkFormKeys: string[] =
137     [
138       'name',
139       'vim_type',
140       'vim_tenant_name',
141       'description',
142       'vim_url',
143       'schema_type',
144       'vim_user',
145       'vim_password',
146       'locationName',
147       'latitude',
148       'longitude',
149       'config'
150     ];
151
152   /** Contains config details in edit @public */
153   public config: {};
154
155   /** Contains latitude value @public  */
156   public latitude: string;
157
158   /** Contains longitude value @public  */
159   public longitude: string;
160
161   /** Contains location value @public */
162   public locationName: string;
163
164   /** Contains VIMAccount Details @private */
165   private details: VimAccountDetails;
166
167   /** Contains config with location @private */
168   private configLocation: string;
169
170   /** Check for config length @private */
171   // eslint-disable-next-line @typescript-eslint/no-magic-numbers
172   private configLength: number = 3;
173
174   /** Instance of the rest service @private */
175   private restService: RestService;
176
177   /** Holds the instance of router class @private */
178   private router: Router;
179
180   /** Controls the header form @private */
181   private headers: HttpHeaders;
182
183   /** FormBuilder instance added to the formBuilder @private */
184   private formBuilder: FormBuilder;
185
186   /** Notifier service to popup notification @private */
187   private notifierService: NotifierService;
188
189   /** Contains tranlsate instance @private */
190   private translateService: TranslateService;
191
192   /** Holds teh instance of AuthService class of type AuthService @private */
193   private activatedRoute: ActivatedRoute;
194
195   /** Instance of the modal service @private */
196   private modalService: NgbModal;
197
198   constructor(injector: Injector) {
199     this.injector = injector;
200     this.restService = this.injector.get(RestService);
201     this.formBuilder = this.injector.get(FormBuilder);
202     this.router = this.injector.get(Router);
203     this.notifierService = this.injector.get(NotifierService);
204     this.translateService = this.injector.get(TranslateService);
205     this.sharedService = this.injector.get(SharedService);
206     this.activatedRoute = this.injector.get(ActivatedRoute);
207     this.modalService = this.injector.get(NgbModal);
208   }
209
210   /** convenience getter for easy access to form fields */
211   get f(): FormGroup['controls'] { return this.vimNewAccountForm.controls; }
212
213   /**
214    * Lifecyle Hooks the trigger before component is instantiate
215    */
216   public ngOnInit(): void {
217     this.vimID = this.activatedRoute.snapshot.paramMap.get('id');
218     this.vimType = VIM_TYPES;
219     this.headers = new HttpHeaders({
220       Accept: 'application/json',
221       'Content-Type': 'application/json',
222       'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
223     });
224     this.initializeForm();
225     if (!isNullOrUndefined(this.vimID)) {
226       this.getVIMDetails(this.vimID);
227     }
228   }
229
230   /** VIM Initialize Forms @public */
231   public initializeForm(): void {
232     this.vimNewAccountForm = this.formBuilder.group({
233       name: [null, Validators.required],
234       vim_type: [null, Validators.required],
235       vim_tenant_name: [null, Validators.required],
236       description: [null],
237       vim_url: [null, [Validators.required, Validators.pattern(this.sharedService.REGX_URL_PATTERN)]],
238       schema_type: [''],
239       vim_user: [null, Validators.required],
240       vim_password: [null, Validators.required],
241       locationName: [''],
242       latitude: ['', Validators.pattern(this.sharedService.REGX_LAT_PATTERN)],
243       longitude: ['', Validators.pattern(this.sharedService.REGX_LONG_PATTERN)],
244       config: this.paramsBuilder()
245     });
246   }
247
248   /** Generate params for config @public */
249   public paramsBuilder(): FormGroup {
250     return this.formBuilder.group({
251       location: [null]
252     });
253   }
254
255   /** Fetching the vim details from get api @protected */
256   private getVIMDetails(id: string): void {
257     this.isLocationLoadingResults = true;
258     this.restService.getResource(environment.VIMACCOUNTS_URL + '/' + id).subscribe((vimAccountsData: VimAccountDetails) => {
259       this.details = vimAccountsData;
260       if (!isNullOrUndefined(this.details.config.location)) {
261         this.configLocation = this.details.config.location;
262         if (this.configLocation.indexOf(',') !== -1) {
263           this.locationName = this.configLocation.split(',')[0];
264           this.latitude = this.configLocation.split(',')[1];
265           this.longitude = this.configLocation.split(',')[2];
266         }
267       }
268       delete this.details.config.location;
269       this.getFormControl('schema_type').disable();
270       this.getFormControl('vim_url').disable();
271       this.getFormControl('vim_type').disable();
272       this.config = { ...this.details.config };
273       this.details.vim_password = '';
274       this.setEditValue(this.details, this.checkFormKeys);
275       this.isLocationLoadingResults = false;
276     }, (error: ERRORDATA) => {
277       this.restService.handleError(error, 'get');
278       this.isLocationLoadingResults = false;
279     });
280   }
281
282   /** Set the value for the Edit Section @public */
283   public setEditValue(formValues: VimAccountDetails, checkKey: string[]): void {
284     Object.keys(formValues).forEach((name: string): void => {
285       if (checkKey.includes(name)) {
286         if (name === 'config') {
287           this.loadConfig();
288           this.getFormControl('locationName').patchValue(this.locationName);
289           this.getFormControl('latitude').patchValue(this.latitude);
290           this.getFormControl('longitude').patchValue(this.longitude);
291         }
292         else {
293           // eslint-disable-next-line security/detect-object-injection
294           this.getFormControl(name).setValue(formValues[name], { onlySelf: true });
295           this.getFormControl(name).updateValueAndValidity();
296         }
297       }
298     });
299   }
300
301   /** On modal submit newVimAccountSubmit will called @public */
302   public newVimAccountSubmit(): void {
303     this.submitted = true;
304
305     if (!this.vimNewAccountForm.invalid) {
306       this.isLocationLoadingResults = true;
307       this.sharedService.cleanForm(this.vimNewAccountForm, 'vim');
308       if (!isNullOrUndefined(this.data) && this.data !== '') {
309         Object.assign(this.vimNewAccountForm.value.config, jsyaml.load(this.data.toString(), { json: true }));
310       } else {
311         Object.keys(this.vimNewAccountForm.value.config).forEach((res: string): void => {
312           if (res !== 'location') {
313             // eslint-disable-next-line @typescript-eslint/no-dynamic-delete, security/detect-object-injection
314             delete this.vimNewAccountForm.value.config[res];
315           }
316         });
317       }
318       if (!isNullOrUndefined(this.vimNewAccountForm.value.latitude) && !isNullOrUndefined(this.vimNewAccountForm.value.longitude)) {
319         this.vimNewAccountForm.value.config.location = this.vimNewAccountForm.value.locationName + ',' +
320           this.vimNewAccountForm.value.longitude + ',' +
321           this.vimNewAccountForm.value.latitude;
322       }
323
324       if (isNullOrUndefined(this.vimNewAccountForm.value.config.location)) {
325         delete this.vimNewAccountForm.value.config.location;
326       }
327
328       Object.keys(this.vimNewAccountForm.value.config).forEach((res: string): void => {
329         // eslint-disable-next-line security/detect-object-injection
330         if (isNullOrUndefined(this.vimNewAccountForm.value.config[res]) || this.vimNewAccountForm.value.config[res] === '') {
331           // eslint-disable-next-line @typescript-eslint/no-dynamic-delete, security/detect-object-injection
332           delete this.vimNewAccountForm.value.config[res];
333         }
334       });
335       if (!isNullOrUndefined(this.vimID)) {
336         this.editVIM();
337       } else {
338         this.createNewVIM();
339       }
340     }
341   }
342
343   /** Create a new VIM Account @public */
344   public createNewVIM(): void {
345     const apiURLHeader: APIURLHEADER = {
346       url: environment.VIMACCOUNTS_URL,
347       httpOptions: { headers: this.headers }
348     };
349     delete this.vimNewAccountForm.value.locationName;
350     delete this.vimNewAccountForm.value.latitude;
351     delete this.vimNewAccountForm.value.longitude;
352     this.restService.postResource(apiURLHeader, this.vimNewAccountForm.value)
353       .subscribe((result: { id: string }): void => {
354         this.notifierService.notify('success', this.translateService.instant('PAGE.VIM.CREATEDSUCCESSFULLY'));
355         this.isLocationLoadingResults = false;
356         this.router.navigate(['vim/info/' + result.id]).catch((): void => {
357           // Error Cached;
358         });
359       }, (error: ERRORDATA): void => {
360         this.restService.handleError(error, 'post');
361         this.isLocationLoadingResults = false;
362       });
363   }
364
365   /** Create a edit VIM Account @public */
366   public editVIM(): void {
367     const apiURLHeader: APIURLHEADER = {
368       url: environment.VIMACCOUNTS_URL + '/' + this.vimID,
369       httpOptions: { headers: this.headers }
370     };
371     delete this.vimNewAccountForm.value.locationName;
372     delete this.vimNewAccountForm.value.latitude;
373     delete this.vimNewAccountForm.value.longitude;
374     this.restService.patchResource(apiURLHeader, this.vimNewAccountForm.value)
375       .subscribe((result: { id: string }): void => {
376         this.notifierService.notify('success', this.translateService.instant('PAGE.VIM.UPDATEDSUCCESSFULLY'));
377         this.isLocationLoadingResults = false;
378         this.router.navigate(['vim/info/' + this.vimID]).catch((): void => {
379           // Error Cached;
380         });
381       }, (error: ERRORDATA): void => {
382         this.restService.handleError(error, 'post');
383         this.isLocationLoadingResults = false;
384       });
385   }
386   /** HandleChange function @public */
387   public handleChange($event: string): void {
388     this.data = $event;
389   }
390
391   /** Routing to VIM Account Details Page @public */
392   public onVimAccountBack(): void {
393     this.router.navigate(['vim/details']).catch((): void => {
394       // Error Cached
395     });
396   }
397
398   /** Drag and drop feature and fetchind the details of files  @private */
399   public filesDropped(files: FileList): void {
400     if (files && files.length === 1) {
401       this.sharedService.getFileString(files, 'yaml').then((fileContent: string): void => {
402         const getJson: string = jsyaml.load(fileContent, { json: true });
403         this.defaults['text/x-yaml'] = fileContent;
404         this.data = fileContent;
405       }).catch((err: string): void => {
406         if (err === 'typeError') {
407           this.notifierService.notify('error', this.translateService.instant('YAMLFILETYPEERRROR'));
408         } else {
409           this.notifierService.notify('error', this.translateService.instant('ERROR'));
410         }
411         this.fileInputLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE');
412         this.fileInput.nativeElement.value = null;
413       });
414     } else if (files && files.length > 1) {
415       this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION'));
416     }
417     this.fileInputLabel.nativeElement.innerText = files[0].name;
418     this.fileInput.nativeElement.value = null;
419   }
420
421   /** Check data is empty or not to load config @public */
422   public checkData(): void {
423     if (this.data !== '' && this.data.length > this.configLength) {
424       // eslint-disable-next-line security/detect-non-literal-fs-filename
425       const modalRef: NgbModalRef = this.modalService.open(WarningComponent, { backdrop: 'static' });
426       modalRef.componentInstance.heading = this.translateService.instant('PAGE.VIMDETAILS.VIMHEADER');
427       modalRef.componentInstance.confirmationMessage = this.translateService.instant('PAGE.VIMDETAILS.VIMCONTENT');
428       modalRef.componentInstance.submitMessage = this.translateService.instant('PAGE.VIMDETAILS.VIMSUBMIT');
429       modalRef.result.then((result: MODALCLOSERESPONSEDATA): void => {
430         if (result.message === CONFIGCONSTANT.done) {
431           this.loadSampleConfig();
432         }
433       }).catch((): void => {
434         // Catch Navigation Error
435       });
436     } else if (this.data.length < this.configLength || this.data === '') {
437       this.loadSampleConfig();
438     }
439   }
440
441   /** Load sample config based on VIM type @public */
442   public loadSampleConfig(): void {
443     if (this.selectedVimType === 'openstack') {
444       this.defaults['text/x-yaml'] = jsyaml.dump(TYPEOPENSTACK);
445       this.data = JSON.stringify(TYPEOPENSTACK, null, '\t');
446     } else if (this.selectedVimType === 'aws') {
447       this.defaults['text/x-yaml'] = jsyaml.dump(TYPEAWS);
448       this.data = JSON.stringify(TYPEAWS, null, '\t');
449     } else if (this.selectedVimType === 'vmware') {
450       this.defaults['text/x-yaml'] = jsyaml.dump(TYPEVMWARE);
451       this.data = JSON.stringify(TYPEVMWARE, null, '\t');
452     } else if (this.selectedVimType === 'openvim' || this.selectedVimType === 'opennebula') {
453       this.defaults['text/x-yaml'] = jsyaml.dump(TYPEOPENVIMNEBULA);
454       this.data = JSON.stringify(TYPEOPENVIMNEBULA, null, '\t');
455     } else if (this.selectedVimType === 'azure' || this.selectedVimType === 'opennebula') {
456       this.defaults['text/x-yaml'] = jsyaml.dump(TYPEAZURE);
457       this.data = JSON.stringify(TYPEAZURE, null, '\t');
458     } else {
459       this.defaults['text/x-yaml'] = jsyaml.dump(TYPEOTERS);
460       this.data = JSON.stringify(TYPEOTERS, null, '\t');
461     }
462   }
463
464   /** Load sample config based on VIM type in edit @public */
465   public loadConfig(): void {
466     this.clearConfig();
467     if (this.details.vim_type === 'openstack') {
468       this.defaults['text/x-yaml'] = jsyaml.dump(this.config);
469       this.data = JSON.stringify(this.config, null, '\t');
470     } else if (this.details.vim_type === 'aws') {
471       this.defaults['text/x-yaml'] = jsyaml.dump(this.config);
472       this.data = JSON.stringify(this.config, null, '\t');
473     } else if (this.details.vim_type === 'vmware') {
474       this.defaults['text/x-yaml'] = jsyaml.dump(this.config);
475       this.data = JSON.stringify(this.config, null, '\t');
476     } else if (this.details.vim_type === 'openvim' || this.details.vim_type === 'opennebula') {
477       this.defaults['text/x-yaml'] = jsyaml.dump(this.config);
478       this.data = JSON.stringify(this.config, null, '\t');
479     } else if (this.details.vim_type === 'azure' || this.details.vim_type === 'opennebula') {
480       this.defaults['text/x-yaml'] = jsyaml.dump(this.config);
481       this.data = JSON.stringify(this.config, null, '\t');
482     } else {
483       this.defaults['text/x-yaml'] = jsyaml.dump(this.config);
484       this.data = JSON.stringify(this.config, null, '\t');
485     }
486   }
487
488   /** Clear config parameters @public */
489   public clearConfig(): void {
490     this.check = true;
491     if (this.data !== '' && this.data.length > this.configLength) {
492       // eslint-disable-next-line security/detect-non-literal-fs-filename
493       const modalRef: NgbModalRef = this.modalService.open(WarningComponent, { backdrop: 'static' });
494       modalRef.componentInstance.heading = this.translateService.instant('PAGE.VIMDETAILS.VIMHEADER');
495       modalRef.componentInstance.confirmationMessage = this.translateService.instant('PAGE.VIMDETAILS.CLEARCONTENT');
496       modalRef.componentInstance.submitMessage = this.translateService.instant('PAGE.VIMDETAILS.VIMSUBMIT');
497       modalRef.result.then((result: MODALCLOSERESPONSEDATA): void => {
498         if (result.message === CONFIGCONSTANT.done) {
499           this.defaults['text/x-yaml'] = '';
500           this.data = '';
501           this.fileInput.nativeElement.value = null;
502         }
503       }).catch((): void => {
504         // Catch Navigation Error
505       });
506     } else {
507       this.defaults['text/x-yaml'] = '';
508       this.data = '';
509       this.fileInput.nativeElement.value = null;
510     }
511   }
512
513   /** Used to get the AbstractControl of controlName passed @private */
514   private getFormControl(controlName: string): AbstractControl {
515     // eslint-disable-next-line security/detect-object-injection
516     return this.vimNewAccountForm.controls[controlName];
517   }
518 }