Feature 11009 Ns Config Template as first class citizens in OSM
[osm/NG-UI.git] / src / app / utilities / compose-packages / ComposePackages.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 Info Compose Package Model
20  */
21 import { HttpClient, HttpHeaders } from '@angular/common/http';
22 import { Component, ElementRef, Injector, Input, OnInit, ViewChild } from '@angular/core';
23 import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
24 import { Router } from '@angular/router';
25 import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
26 import { TranslateService } from '@ngx-translate/core';
27 import { NotifierService } from 'angular-notifier';
28 import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA, URLPARAMS } from 'CommonModel';
29 import { DataService } from 'DataService';
30 import { environment } from 'environment';
31 import * as jsyaml from 'js-yaml';
32 import { NSConfigData } from 'NSCONFIGTEMPLATEMODEL';
33 import { NSDDetails } from 'NSDModel';
34 import * as pako from 'pako';
35 import { RestService } from 'RestService';
36 import { SharedService, isNullOrUndefined } from 'SharedService';
37
38 /** This is added globally by the tar.js library */
39 // eslint-disable-next-line @typescript-eslint/no-explicit-any
40 declare const Tar: any;
41
42 /**
43  * Creating component
44  * @Component takes ComposePackages.html as template url
45  */
46 @Component({
47   templateUrl: './ComposePackages.html',
48   styleUrls: ['./ComposePackages.scss']
49 })
50 /** Exporting a class @exports ComposePackages */
51 // eslint-disable-next-line @angular-eslint/component-class-suffix
52 export class ComposePackages implements OnInit {
53   /** Invoke service injectors @public */
54   public injector: Injector;
55
56   /** dataService to pass the data from one component to another @public */
57   public dataService: DataService;
58
59   /** Varaibles to hold http client @public */
60   public httpClient: HttpClient;
61
62   /** Instance for active modal service @public */
63   public activeModal: NgbActiveModal;
64
65   /** FormGroup instance added to the form @ html @public */
66   public packagesForm: FormGroup;
67
68   /** Form submission Add */
69   public submitted = false;
70
71   /** To handle loader status for API call @public */
72   public isLoadingResults = false;
73
74   /** Give the message for the loading @public */
75   public message = 'PLEASEWAIT';
76
77   /** contains NSD name @public */
78   public nsName: {}[] = [];
79
80   /** contains NSD details @public */
81   public nsdDetails: {}[];
82
83   /** contains NSD details filtered by id @public */
84   public nsdName: string;
85
86   /** Contains config details @public */
87   public config: string;
88
89   /** contains NSD details filtered by name @public */
90   public nsId: string;
91
92   /** Check if template or not @public */
93   public template = false;
94
95   /** Data of NS config @public */
96   public details: NSConfigData;
97
98   /** Data of NF packages @public */
99   public nsConfigData: NSConfigData[] = [];
100
101   /** Element ref for fileInputConfig @public */
102   @ViewChild('fileInputConfig') fileInputConfig: ElementRef<HTMLInputElement>;
103
104   /** Element ref for fileInputConfigLabel @public */
105   @ViewChild('fileInputConfigLabel') fileInputConfigLabel: ElementRef<HTMLLabelElement>;
106
107   /** FormBuilder instance added to the formBuilder @private */
108   private formBuilder: FormBuilder;
109
110   /** Instance of the rest service @private */
111   private restService: RestService;
112
113   /** Notifier service to popup notification @private */
114   private notifierService: NotifierService;
115
116   /** Controls the header form @private */
117   private headers: HttpHeaders;
118
119   /** Input contains component objects @public */
120   @Input() public params: URLPARAMS;
121
122   /** Holds the end point @private */
123   private endPoint: string;
124
125   /** ModalData instance of modal @private  */
126   private modalData: MODALCLOSERESPONSEDATA;
127
128   /** Contains all methods related to shared @private */
129   private sharedService: SharedService;
130
131   /** Holds teh instance of AuthService class of type AuthService @private */
132   private router: Router;
133
134   /** Contains tranlsate instance @private */
135   private translateService: TranslateService;
136
137   constructor(injector: Injector) {
138     this.injector = injector;
139     this.dataService = this.injector.get(DataService);
140     this.restService = this.injector.get(RestService);
141     this.activeModal = this.injector.get(NgbActiveModal);
142     this.notifierService = this.injector.get(NotifierService);
143     this.formBuilder = this.injector.get(FormBuilder);
144     this.router = this.injector.get(Router);
145     this.translateService = this.injector.get(TranslateService);
146     this.sharedService = this.injector.get(SharedService);
147   }
148
149   /** convenience getter for easy access to form fields */
150   get f(): FormGroup['controls'] { return this.packagesForm.controls; }
151
152   /**
153    * Lifecyle Hooks the trigger before component is instantiate
154    */
155   public ngOnInit(): void {
156     this.initializeForm();
157     if (this.params.page === 'ns-config-template') {
158       this.template = true;
159       this.getNsdPackageDetails();
160     } else if (this.params.page === 'ns-config-template-edit') {
161       this.template = true;
162       this.getNsdPackageDetails();
163       this.getFormControl('nsdId').disable();
164     } else {
165       this.getFormControl('nsdId').disable();
166       this.getFormControl('config').disable();
167     }
168   }
169
170   /** initialize Forms @public */
171   public initializeForm(): void {
172     this.packagesForm = this.formBuilder.group({
173       name: ['', [Validators.required]],
174       nsdId: [null, [Validators.required]],
175       config: [null]
176     });
177   }
178
179   /** Get NSD Package details @public */
180   public getNsdPackageDetails(): void {
181     this.restService.getResource(environment.NSDESCRIPTORSCONTENT_URL)
182       .subscribe((nsdPackageData: NSDDetails[]): void => {
183         nsdPackageData.forEach((nsData: NSDDetails): void => {
184           const names: {} = {
185             nsName: nsData.name,
186             nsId: nsData._id
187           };
188           this.nsName.push(names);
189         });
190         this.nsdDetails = this.nsName;
191         if (this.params.page === 'ns-config-template-edit') {
192           this.getNSConfigDetails(environment.NSCONFIGTEMPLATE_URL + '/' + this.params.id, this.nsdDetails);
193         }
194       }, (error: ERRORDATA): void => {
195         this.restService.handleError(error, 'get');
196       });
197   }
198
199   /** Get the NSD Content List & patch value in edit form @public */
200   public getNSConfigDetails(URL: string, name: {}[]): void {
201     this.restService.getResource(URL).subscribe((content: NSConfigData): void => {
202       this.nsConfigData.push(content);
203       this.details = this.nsConfigData[0];
204       const nsId: string = 'nsId';
205       // eslint-disable-next-line security/detect-object-injection
206       const nsdId: {}[] = name.filter((nsdData: {}[]): boolean => nsdData[nsId] === this.details.nsdId);
207       const nsName: string = 'nsName';
208       for (const data of nsdId) {
209         // eslint-disable-next-line security/detect-object-injection
210         this.nsdName = data[nsName];
211       }
212       if (!isNullOrUndefined(this.details.config)) {
213         this.config = jsyaml.dump(this.details.config);
214       }
215       this.packagesForm.patchValue({ name: this.details.name, nsdId: this.nsdName, config: this.config });
216       this.isLoadingResults = false;
217     }, (error: ERRORDATA): void => {
218       this.restService.handleError(error, 'get');
219       this.isLoadingResults = false;
220     });
221   }
222
223   /** Create packages @public */
224   public createPackages(): void {
225     this.submitted = true;
226     this.modalData = {
227       message: 'Done'
228     };
229     this.sharedService.cleanForm(this.packagesForm);
230     if (!this.packagesForm.invalid) {
231       this.isLoadingResults = true;
232       if (this.params.page === 'ns-package' || this.params.page === 'vnf-package') {
233         if (this.params.page === 'ns-package') {
234           this.endPoint = environment.NSDESCRIPTORSCONTENT_URL;
235         } else if (this.params.page === 'vnf-package') {
236           this.endPoint = environment.VNFPACKAGESCONTENT_URL;
237         }
238         const descriptor: string = this.packageYaml(this.params.page);
239         try {
240           // eslint-disable-next-line @typescript-eslint/no-explicit-any
241           const tar: any = new Tar();
242           const out: Uint8Array = tar.append(this.packagesForm.value.name + '/' + this.packagesForm.value.name + '.yaml',
243             descriptor, { type: '0' });
244           const gzipContent: Uint8Array = pako.gzip(out);
245           this.createPackageApi(gzipContent.buffer);
246         } catch (e) {
247           this.isLoadingResults = false;
248           this.notifierService.notify('error', this.translateService.instant('ERROR'));
249         }
250       } else {
251         try {
252           this.headers = new HttpHeaders({
253             Accept: 'application/json',
254             'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
255           });
256           if (this.params.page === 'ns-config-template') {
257             this.endPoint = environment.NSCONFIGTEMPLATE_URL;
258             this.createTemplate(this.endPoint);
259           } else if (this.params.page === 'ns-config-template-edit') {
260             this.endPoint = environment.NSCONFIGTEMPLATE_URL + '/' + this.params.id + '/' + 'template_content';
261             this.editTemplate(this.endPoint);
262           }
263         } catch (e) {
264           this.isLoadingResults = false;
265           this.notifierService.notify('error', this.translateService.instant('ERROR'));
266         }
267       }
268     }
269   }
270
271   /** Post config template @public */
272   public createTemplate(urlHeader: string): void {
273     this.isLoadingResults = true;
274     const apiURLHeader: APIURLHEADER = {
275       url: urlHeader,
276       httpOptions: { headers: this.headers }
277     };
278     if (isNullOrUndefined(this.packagesForm.value.config) || this.packagesForm.value.config === '') {
279       delete this.packagesForm.value.config;
280     } else {
281       const validJSON: boolean = this.sharedService.checkJson(this.packagesForm.value.config);
282       if (validJSON) {
283         this.packagesForm.value.config = JSON.parse(this.packagesForm.value.config);
284       } else {
285         const getConfigJson: string = jsyaml.load(this.packagesForm.value.config, { json: true });
286         this.packagesForm.value.config = getConfigJson;
287       }
288     }
289     const nsName: string = 'nsName';
290     // eslint-disable-next-line security/detect-object-injection
291     const nsdId: {}[] = this.nsdDetails.filter((nsdData: {}[]): boolean => nsdData[nsName] === this.packagesForm.value.nsdId);
292     for (const data of nsdId) {
293       // eslint-disable-next-line @typescript-eslint/dot-notation
294       this.nsId = data['nsId'];
295     }
296     this.packagesForm.value.nsdId = this.nsId;
297     this.restService.postResource(apiURLHeader, (this.packagesForm.value)).subscribe((result: {}): void => {
298       this.activeModal.close(this.modalData);
299       this.isLoadingResults = false;
300       this.notifierService.notify('success', this.translateService.instant('PAGE.NSCONFIGTEMPLATE.TEMPLATECREATEDSUCCESSFULLY'));
301     }, (error: ERRORDATA): void => {
302       this.restService.handleError(error, 'post');
303       this.isLoadingResults = false;
304     });
305   }
306
307   /** Edit config template @public */
308   public editTemplate(urlHeader: string): void {
309     this.isLoadingResults = true;
310     const apiURLHeader: APIURLHEADER = {
311       url: urlHeader,
312       httpOptions: { headers: this.headers }
313     };
314     if (isNullOrUndefined(this.packagesForm.value.config) || this.packagesForm.value.config === '') {
315       delete this.packagesForm.value.config;
316     } else {
317       const validJSON: boolean = this.sharedService.checkJson(this.packagesForm.value.config);
318       if (validJSON) {
319         this.packagesForm.value.config = JSON.parse(this.packagesForm.value.config);
320       } else {
321         const getConfigJson: string = jsyaml.load(this.packagesForm.value.config, { json: true });
322         this.packagesForm.value.config = getConfigJson;
323       }
324     }
325     this.restService.putResource(apiURLHeader, (this.packagesForm.value)).subscribe((result: {}): void => {
326       this.activeModal.close(this.modalData);
327       this.isLoadingResults = false;
328       this.notifierService.notify('success', this.translateService.instant('PAGE.NSCONFIGTEMPLATE.TEMPLATEEDITEDSUCCESSFULLY'));
329     }, (error: ERRORDATA): void => {
330       this.restService.handleError(error, 'post');
331       this.isLoadingResults = false;
332     });
333   }
334   /** Create packages @public */
335   private createPackageApi(packageContent: ArrayBuffer | SharedArrayBuffer): void {
336     this.headers = new HttpHeaders({
337       'Content-Type': 'application/gzip',
338       Accept: 'application/json',
339       'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
340     });
341     const apiURLHeader: APIURLHEADER = {
342       url: this.endPoint,
343       httpOptions: { headers: this.headers }
344     };
345     this.restService.postResource(apiURLHeader, packageContent).subscribe((result: { id: string }): void => {
346       this.isLoadingResults = false;
347       this.activeModal.close();
348       this.composeNSPackages(result.id);
349     }, (error: ERRORDATA): void => {
350       this.isLoadingResults = false;
351       this.restService.handleError(error, 'post');
352     });
353   }
354   /** Config file process @private */
355   public configFile(files: FileList): void {
356     if (files && files.length === 1) {
357       const fileFormat: string = this.sharedService.fetchFileExtension(files).toLocaleLowerCase();
358       if (fileFormat === 'yaml' || fileFormat === 'yml') {
359         this.sharedService.getFileString(files, 'yaml').then((fileContent: string): void => {
360           this.packagesForm.get('config').setValue(fileContent);
361         }).catch((err: string): void => {
362           if (err === 'typeError') {
363             this.notifierService.notify('error', this.translateService.instant('YAMLFILETYPEERRROR'));
364           } else {
365             this.notifierService.notify('error', this.translateService.instant('ERROR'));
366           }
367           this.fileInputConfigLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE');
368           this.fileInputConfig.nativeElement.value = null;
369         });
370       } else if (fileFormat === 'json') {
371         this.sharedService.getFileString(files, 'json').then((fileContent: string): void => {
372           const getConfigJson: string = jsyaml.load(fileContent, { json: true });
373           this.packagesForm.get('config').setValue(JSON.stringify(getConfigJson));
374         }).catch((err: string): void => {
375           if (err === 'typeError') {
376             this.notifierService.notify('error', this.translateService.instant('JSONFILETYPEERRROR'));
377           } else {
378             this.notifierService.notify('error', this.translateService.instant('ERROR'));
379           }
380           this.fileInputConfigLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE');
381           this.fileInputConfig.nativeElement.value = null;
382         });
383       }
384     } else if (files && files.length > 1) {
385       this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION'));
386     }
387     this.fileInputConfigLabel.nativeElement.innerText = files[0].name;
388     this.fileInputConfig.nativeElement.value = null;
389   }
390   /** Compose NS Packages @private */
391   private composeNSPackages(id: string): void {
392     let packageUrl: string;
393     if (this.params.page === 'ns-package') {
394       packageUrl = '/packages/ns/compose/';
395       this.notifierService.notify('success', this.packagesForm.value.name + ' ' +
396         this.translateService.instant('PAGE.NSPACKAGE.CREATEDSUCCESSFULLY'));
397     } else if (this.params.page === 'vnf-package') {
398       packageUrl = '/packages/vnf/compose/';
399       this.notifierService.notify('success', this.packagesForm.value.name + ' ' +
400         this.translateService.instant('PAGE.VNFPACKAGE.CREATEDSUCCESSFULLY'));
401     }
402     this.router.navigate([packageUrl, id]).catch((): void => {
403       // Catch Navigation Error
404     });
405   }
406   /** Deafult template for NS and VNF Packages @private */
407   private packageYaml(descriptorType: string): string {
408     let packageYaml: {} = {};
409     const composerName: string = 'NGUI Composer';
410     const composerDefaultVersion: string = '1.0';
411     if (descriptorType === 'ns-package') {
412       packageYaml = {
413         nsd: {
414           nsd: [
415             {
416               id: this.packagesForm.value.name,
417               name: this.packagesForm.value.name,
418               version: composerDefaultVersion,
419               description: this.packagesForm.value.name + ' descriptor',
420               designer: composerName,
421               df: [
422                 {
423                   id: 'default-df',
424                   'vnf-profile': []
425                 }
426               ]
427             }
428           ]
429         }
430       };
431     } else {
432       packageYaml = {
433         vnfd: {
434           id: this.packagesForm.value.name,
435           'product-name': this.packagesForm.value.name,
436           version: composerDefaultVersion,
437           description: this.packagesForm.value.name + ' descriptor',
438           provider: composerName,
439           df: [
440             {
441               id: 'default-df',
442               'instantiation-level': [],
443               'vdu-profile': []
444             }
445           ],
446           'ext-cpd': [],
447           vdu: [],
448           'sw-image-desc': [],
449           'virtual-storage-desc': []
450         }
451       };
452     }
453     return jsyaml.dump(packageYaml, { sortKeys: true });
454   }
455
456   /** Used to get the AbstractControl of controlName passed @private */
457   private getFormControl(controlName: string): AbstractControl {
458     // eslint-disable-next-line security/detect-object-injection
459     return this.packagesForm.controls[controlName];
460   }
461 }