Feature 11009 Ns Config Template as first class citizens in OSM

        - Added UI support
	- Added New page for NS config template under packages section
	- User can create, edit & delete template
	- Added template field under ns instance

Change-Id: I3e9a895b4415165d6c96b9840dee64cc197b40e4
Signed-off-by: SANDHYA.JS <sandhya.j@tataelxsi.co.in>
diff --git a/src/app/utilities/compose-packages/ComposePackages.html b/src/app/utilities/compose-packages/ComposePackages.html
index 1067054..03ec3b7 100644
--- a/src/app/utilities/compose-packages/ComposePackages.html
+++ b/src/app/utilities/compose-packages/ComposePackages.html
@@ -17,24 +17,56 @@
 -->
 <form [formGroup]="packagesForm" (ngSubmit)="createPackages()" autocomplete="off">
   <div class="modal-header">
-    <h4 class="modal-title" id="modal-basic-title">{{'CREATEPACKAGE' | translate}}</h4>
+    <h4 class="modal-title" *ngIf="!template" id="modal-basic-title">{{'CREATEPACKAGE' | translate}}</h4>
+    <h4 class="modal-title" *ngIf="params.page === 'ns-config-template'" id="modal-basic-title">
+      {{'PAGE.NSCONFIGTEMPLATE.CREATENSCONFIGTEMPLATE' | translate}}</h4>
+    <h4 class="modal-title" *ngIf="params.page === 'ns-config-template-edit'" id="modal-basic-title">
+      {{'PAGE.NSCONFIGTEMPLATE.EDITNSCONFIGTEMPLATE' | translate}}</h4>
     <button class="button-xs" type="button" class="close" (click)="activeModal.close()">
       <i class="fas fa-times-circle text-danger"></i>
     </button>
   </div>
   <div class="modal-body">
     <div class="form-group row mb-3">
-      <label class="col-sm-12 col-form-label mandatory-label" [ngClass]="{'text-danger': packagesForm.invalid === true && submitted === true}">{{'MANDATORYCHECK' | translate}}</label>
-      <label class="col-sm-6 col-form-label">{{'PACKAGE' | translate}} {{'NAME' | translate}}*</label>
+      <label class="col-sm-12 col-form-label mandatory-label"
+        [ngClass]="{'text-danger': packagesForm.invalid === true && submitted === true}">{{'MANDATORYCHECK' |
+        translate}}</label>
+      <label class="col-sm-6 col-form-label" *ngIf="!template else configtemplate">{{'PACKAGE' | translate}} {{'NAME' | translate}}*</label>
+      <ng-template #configtemplate>
+        <label class="col-sm-6 col-form-label">{{'NAME' | translate}}*</label>
+      </ng-template>
       <div class="col-sm-6">
-        <input type="text" class="form-control" placeholder="{{'PACKAGE' | translate}} {{'NAME' | translate}}"
-          formControlName="name" id="name" [ngClass]="{ 'is-invalid': submitted && f.name.errors }" required>
+        <input type="text" class="form-control" placeholder="{{'NAME' | translate}}" formControlName="name" id="name"
+          [ngClass]="{ 'is-invalid': submitted && f.name.errors }" required>
+      </div>
+    </div>
+    <div class="form-group row mb-3" *ngIf="template">
+      <label class="col-sm-6 col-form-label">{{'NSD' | translate}} {{'NAME' | translate}}*</label>
+      <div class="col-sm-6">
+        <ng-select formControlName="nsdId" id="nsd" [items]="nsdDetails"
+          placeholder="{{'SELECT' | translate}} {{'NSDNAME' | translate}}" bindLabel="nsName" bindValue="nsName"
+          [ngClass]="{ 'is-invalid': submitted && f.nsdId.errors }">
+        </ng-select>
+      </div>
+    </div>
+    <div class="form-group row mb-3" *ngIf="template">
+      <label class="col-sm-6 col-form-label">{{'CONFIG' | translate}}*</label>
+      <div class="col-sm-6">
+        <textarea rows="10" cols="50" class="form-control" placeholder="{{'YAMLCONFIG' | translate}}"
+          formControlName="config" id="config"></textarea>
+        <div class="fileupload-text mt-1 mb-1">{{'FILEUPLOADLABEL' | translate}}</div>
+        <div class="custom-file">
+          <input type="file" #fileInputConfig class="fileupload custom-file-input"
+            (change)="configFile($event.target.files)" id="customConfigFile">
+        </div>
       </div>
     </div>
   </div>
   <div class="modal-footer">
     <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
     <button type="submit" class="btn btn-primary">{{'CREATE' | translate}}</button>
+    <button *ngIf="params.page === 'ns-config-template-edit'" type="submit" class="btn btn-primary">{{'EDIT' |
+      translate}}</button>
   </div>
 </form>
 <app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/utilities/compose-packages/ComposePackages.ts b/src/app/utilities/compose-packages/ComposePackages.ts
index 6f2a031..0dff17a 100644
--- a/src/app/utilities/compose-packages/ComposePackages.ts
+++ b/src/app/utilities/compose-packages/ComposePackages.ts
@@ -19,19 +19,21 @@
  * @file Info Compose Package Model
  */
 import { HttpClient, HttpHeaders } from '@angular/common/http';
-import { Component, Injector, Input, OnInit } from '@angular/core';
-import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { Component, ElementRef, Injector, Input, OnInit, ViewChild } from '@angular/core';
+import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
 import { Router } from '@angular/router';
 import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
 import { TranslateService } from '@ngx-translate/core';
 import { NotifierService } from 'angular-notifier';
-import { APIURLHEADER, ERRORDATA, URLPARAMS } from 'CommonModel';
+import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA, URLPARAMS } from 'CommonModel';
 import { DataService } from 'DataService';
 import { environment } from 'environment';
 import * as jsyaml from 'js-yaml';
+import { NSConfigData } from 'NSCONFIGTEMPLATEMODEL';
+import { NSDDetails } from 'NSDModel';
 import * as pako from 'pako';
 import { RestService } from 'RestService';
-import { SharedService } from 'SharedService';
+import { SharedService, isNullOrUndefined } from 'SharedService';
 
 /** This is added globally by the tar.js library */
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -64,13 +66,43 @@
   public packagesForm: FormGroup;
 
   /** Form submission Add */
-  public submitted: boolean = false;
+  public submitted = false;
 
   /** To handle loader status for API call @public */
-  public isLoadingResults: boolean = false;
+  public isLoadingResults = false;
 
   /** Give the message for the loading @public */
-  public message: string = 'PLEASEWAIT';
+  public message = 'PLEASEWAIT';
+
+  /** contains NSD name @public */
+  public nsName: {}[] = [];
+
+  /** contains NSD details @public */
+  public nsdDetails: {}[];
+
+  /** contains NSD details filtered by id @public */
+  public nsdName: string;
+
+  /** Contains config details @public */
+  public config: string;
+
+  /** contains NSD details filtered by name @public */
+  public nsId: string;
+
+  /** Check if template or not @public */
+  public template = false;
+
+  /** Data of NS config @public */
+  public details: NSConfigData;
+
+  /** Data of NF packages @public */
+  public nsConfigData: NSConfigData[] = [];
+
+  /** Element ref for fileInputConfig @public */
+  @ViewChild('fileInputConfig') fileInputConfig: ElementRef<HTMLInputElement>;
+
+  /** Element ref for fileInputConfigLabel @public */
+  @ViewChild('fileInputConfigLabel') fileInputConfigLabel: ElementRef<HTMLLabelElement>;
 
   /** FormBuilder instance added to the formBuilder @private */
   private formBuilder: FormBuilder;
@@ -84,15 +116,15 @@
   /** Controls the header form @private */
   private headers: HttpHeaders;
 
-  /** Create URL holds the end point of any packages @private */
-  private createURL: string;
-
-  /** Input contains component objects @private */
-  @Input() private params: URLPARAMS;
+  /** Input contains component objects @public */
+  @Input() public params: URLPARAMS;
 
   /** Holds the end point @private */
   private endPoint: string;
 
+  /** ModalData instance of modal @private  */
+  private modalData: MODALCLOSERESPONSEDATA;
+
   /** Contains all methods related to shared @private */
   private sharedService: SharedService;
 
@@ -121,48 +153,191 @@
    * Lifecyle Hooks the trigger before component is instantiate
    */
   public ngOnInit(): void {
-    this.headers = new HttpHeaders({
-      'Content-Type': 'application/gzip',
-      Accept: 'application/json',
-      'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
-    });
     this.initializeForm();
+    if (this.params.page === 'ns-config-template') {
+      this.template = true;
+      this.getNsdPackageDetails();
+    } else if (this.params.page === 'ns-config-template-edit') {
+      this.template = true;
+      this.getNsdPackageDetails();
+      this.getFormControl('nsdId').disable();
+    } else {
+      this.getFormControl('nsdId').disable();
+      this.getFormControl('config').disable();
+    }
   }
 
   /** initialize Forms @public */
   public initializeForm(): void {
     this.packagesForm = this.formBuilder.group({
-      name: ['', [Validators.required]]
+      name: ['', [Validators.required]],
+      nsdId: [null, [Validators.required]],
+      config: [null]
+    });
+  }
+
+  /** Get NSD Package details @public */
+  public getNsdPackageDetails(): void {
+    this.restService.getResource(environment.NSDESCRIPTORSCONTENT_URL)
+      .subscribe((nsdPackageData: NSDDetails[]): void => {
+        nsdPackageData.forEach((nsData: NSDDetails): void => {
+          const names: {} = {
+            nsName: nsData.name,
+            nsId: nsData._id
+          };
+          this.nsName.push(names);
+        });
+        this.nsdDetails = this.nsName;
+        if (this.params.page === 'ns-config-template-edit') {
+          this.getNSConfigDetails(environment.NSCONFIGTEMPLATE_URL + '/' + this.params.id, this.nsdDetails);
+        }
+      }, (error: ERRORDATA): void => {
+        this.restService.handleError(error, 'get');
+      });
+  }
+
+  /** Get the NSD Content List & patch value in edit form @public */
+  public getNSConfigDetails(URL: string, name: {}[]): void {
+    this.restService.getResource(URL).subscribe((content: NSConfigData): void => {
+      this.nsConfigData.push(content);
+      this.details = this.nsConfigData[0];
+      const nsId: string = 'nsId';
+      // eslint-disable-next-line security/detect-object-injection
+      const nsdId: {}[] = name.filter((nsdData: {}[]): boolean => nsdData[nsId] === this.details.nsdId);
+      const nsName: string = 'nsName';
+      for (const data of nsdId) {
+        // eslint-disable-next-line security/detect-object-injection
+        this.nsdName = data[nsName];
+      }
+      if (!isNullOrUndefined(this.details.config)) {
+        this.config = jsyaml.dump(this.details.config);
+      }
+      this.packagesForm.patchValue({ name: this.details.name, nsdId: this.nsdName, config: this.config });
+      this.isLoadingResults = false;
+    }, (error: ERRORDATA): void => {
+      this.restService.handleError(error, 'get');
+      this.isLoadingResults = false;
     });
   }
 
   /** Create packages @public */
   public createPackages(): void {
     this.submitted = true;
+    this.modalData = {
+      message: 'Done'
+    };
     this.sharedService.cleanForm(this.packagesForm);
     if (!this.packagesForm.invalid) {
       this.isLoadingResults = true;
-      if (this.params.page === 'ns-package') {
-        this.endPoint = environment.NSDESCRIPTORSCONTENT_URL;
-      } else if (this.params.page === 'vnf-package') {
-        this.endPoint = environment.VNFPACKAGESCONTENT_URL;
-      }
-      const descriptor: string = this.packageYaml(this.params.page);
-      try {
-        // eslint-disable-next-line @typescript-eslint/no-explicit-any
-        const tar: any = new Tar();
-        const out: Uint8Array = tar.append(this.packagesForm.value.name + '/' + this.packagesForm.value.name + '.yaml',
-          descriptor, { type: '0' });
-        const gzipContent: Uint8Array = pako.gzip(out);
-        this.createPackageApi(gzipContent.buffer);
-      } catch (e) {
-        this.isLoadingResults = false;
-        this.notifierService.notify('error', this.translateService.instant('ERROR'));
+      if (this.params.page === 'ns-package' || this.params.page === 'vnf-package') {
+        if (this.params.page === 'ns-package') {
+          this.endPoint = environment.NSDESCRIPTORSCONTENT_URL;
+        } else if (this.params.page === 'vnf-package') {
+          this.endPoint = environment.VNFPACKAGESCONTENT_URL;
+        }
+        const descriptor: string = this.packageYaml(this.params.page);
+        try {
+          // eslint-disable-next-line @typescript-eslint/no-explicit-any
+          const tar: any = new Tar();
+          const out: Uint8Array = tar.append(this.packagesForm.value.name + '/' + this.packagesForm.value.name + '.yaml',
+            descriptor, { type: '0' });
+          const gzipContent: Uint8Array = pako.gzip(out);
+          this.createPackageApi(gzipContent.buffer);
+        } catch (e) {
+          this.isLoadingResults = false;
+          this.notifierService.notify('error', this.translateService.instant('ERROR'));
+        }
+      } else {
+        try {
+          this.headers = new HttpHeaders({
+            Accept: 'application/json',
+            'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+          });
+          if (this.params.page === 'ns-config-template') {
+            this.endPoint = environment.NSCONFIGTEMPLATE_URL;
+            this.createTemplate(this.endPoint);
+          } else if (this.params.page === 'ns-config-template-edit') {
+            this.endPoint = environment.NSCONFIGTEMPLATE_URL + '/' + this.params.id + '/' + 'template_content';
+            this.editTemplate(this.endPoint);
+          }
+        } catch (e) {
+          this.isLoadingResults = false;
+          this.notifierService.notify('error', this.translateService.instant('ERROR'));
+        }
       }
     }
   }
+
+  /** Post config template @public */
+  public createTemplate(urlHeader: string): void {
+    this.isLoadingResults = true;
+    const apiURLHeader: APIURLHEADER = {
+      url: urlHeader,
+      httpOptions: { headers: this.headers }
+    };
+    if (isNullOrUndefined(this.packagesForm.value.config) || this.packagesForm.value.config === '') {
+      delete this.packagesForm.value.config;
+    } else {
+      const validJSON: boolean = this.sharedService.checkJson(this.packagesForm.value.config);
+      if (validJSON) {
+        this.packagesForm.value.config = JSON.parse(this.packagesForm.value.config);
+      } else {
+        const getConfigJson: string = jsyaml.load(this.packagesForm.value.config, { json: true });
+        this.packagesForm.value.config = getConfigJson;
+      }
+    }
+    const nsName: string = 'nsName';
+    // eslint-disable-next-line security/detect-object-injection
+    const nsdId: {}[] = this.nsdDetails.filter((nsdData: {}[]): boolean => nsdData[nsName] === this.packagesForm.value.nsdId);
+    for (const data of nsdId) {
+      // eslint-disable-next-line @typescript-eslint/dot-notation
+      this.nsId = data['nsId'];
+    }
+    this.packagesForm.value.nsdId = this.nsId;
+    this.restService.postResource(apiURLHeader, (this.packagesForm.value)).subscribe((result: {}): void => {
+      this.activeModal.close(this.modalData);
+      this.isLoadingResults = false;
+      this.notifierService.notify('success', this.translateService.instant('PAGE.NSCONFIGTEMPLATE.TEMPLATECREATEDSUCCESSFULLY'));
+    }, (error: ERRORDATA): void => {
+      this.restService.handleError(error, 'post');
+      this.isLoadingResults = false;
+    });
+  }
+
+  /** Edit config template @public */
+  public editTemplate(urlHeader: string): void {
+    this.isLoadingResults = true;
+    const apiURLHeader: APIURLHEADER = {
+      url: urlHeader,
+      httpOptions: { headers: this.headers }
+    };
+    if (isNullOrUndefined(this.packagesForm.value.config) || this.packagesForm.value.config === '') {
+      delete this.packagesForm.value.config;
+    } else {
+      const validJSON: boolean = this.sharedService.checkJson(this.packagesForm.value.config);
+      if (validJSON) {
+        this.packagesForm.value.config = JSON.parse(this.packagesForm.value.config);
+      } else {
+        const getConfigJson: string = jsyaml.load(this.packagesForm.value.config, { json: true });
+        this.packagesForm.value.config = getConfigJson;
+      }
+    }
+    this.restService.putResource(apiURLHeader, (this.packagesForm.value)).subscribe((result: {}): void => {
+      this.activeModal.close(this.modalData);
+      this.isLoadingResults = false;
+      this.notifierService.notify('success', this.translateService.instant('PAGE.NSCONFIGTEMPLATE.TEMPLATEEDITEDSUCCESSFULLY'));
+    }, (error: ERRORDATA): void => {
+      this.restService.handleError(error, 'post');
+      this.isLoadingResults = false;
+    });
+  }
   /** Create packages @public */
   private createPackageApi(packageContent: ArrayBuffer | SharedArrayBuffer): void {
+    this.headers = new HttpHeaders({
+      'Content-Type': 'application/gzip',
+      Accept: 'application/json',
+      'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+    });
     const apiURLHeader: APIURLHEADER = {
       url: this.endPoint,
       httpOptions: { headers: this.headers }
@@ -176,6 +351,42 @@
       this.restService.handleError(error, 'post');
     });
   }
+  /** Config file process @private */
+  public configFile(files: FileList): void {
+    if (files && files.length === 1) {
+      const fileFormat: string = this.sharedService.fetchFileExtension(files).toLocaleLowerCase();
+      if (fileFormat === 'yaml' || fileFormat === 'yml') {
+        this.sharedService.getFileString(files, 'yaml').then((fileContent: string): void => {
+          this.packagesForm.get('config').setValue(fileContent);
+        }).catch((err: string): void => {
+          if (err === 'typeError') {
+            this.notifierService.notify('error', this.translateService.instant('YAMLFILETYPEERRROR'));
+          } else {
+            this.notifierService.notify('error', this.translateService.instant('ERROR'));
+          }
+          this.fileInputConfigLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE');
+          this.fileInputConfig.nativeElement.value = null;
+        });
+      } else if (fileFormat === 'json') {
+        this.sharedService.getFileString(files, 'json').then((fileContent: string): void => {
+          const getConfigJson: string = jsyaml.load(fileContent, { json: true });
+          this.packagesForm.get('config').setValue(JSON.stringify(getConfigJson));
+        }).catch((err: string): void => {
+          if (err === 'typeError') {
+            this.notifierService.notify('error', this.translateService.instant('JSONFILETYPEERRROR'));
+          } else {
+            this.notifierService.notify('error', this.translateService.instant('ERROR'));
+          }
+          this.fileInputConfigLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE');
+          this.fileInputConfig.nativeElement.value = null;
+        });
+      }
+    } else if (files && files.length > 1) {
+      this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION'));
+    }
+    this.fileInputConfigLabel.nativeElement.innerText = files[0].name;
+    this.fileInputConfig.nativeElement.value = null;
+  }
   /** Compose NS Packages @private */
   private composeNSPackages(id: string): void {
     let packageUrl: string;
@@ -241,4 +452,10 @@
     }
     return jsyaml.dump(packageYaml, { sortKeys: true });
   }
+
+  /** Used to get the AbstractControl of controlName passed @private */
+  private getFormControl(controlName: string): AbstractControl {
+    // eslint-disable-next-line security/detect-object-injection
+    return this.packagesForm.controls[controlName];
+  }
 }