Fix mod ui build issues
[dcaegen2/platform.git] / mod2 / ui / src / app / msInstances / msInstances.component.ts
1 /* 
2  *  # ============LICENSE_START=======================================================
3  *  # Copyright (c) 2020 AT&T Intellectual Property. All rights reserved.
4  *  # ================================================================================
5  *  # Licensed under the Apache License, Version 2.0 (the "License");
6  *  # you may not use this file except in compliance with the License.
7  *  # You may obtain a copy of the License at
8  *  #
9  *  #      http://www.apache.org/licenses/LICENSE-2.0
10  *  #
11  *  # Unless required by applicable law or agreed to in writing, software
12  *  # distributed under the License is distributed on an "AS IS" BASIS,
13  *  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  # See the License for the specific language governing permissions and
15  *  # limitations under the License.
16  *  # ============LICENSE_END=========================================================
17  */
18
19 import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
20 import { MatTableDataSource } from '@angular/material/table';
21 import { Table } from 'primeng/table';
22 import { MessageService } from 'primeng/api';
23 import { trigger, state, style, transition, animate } from '@angular/animations';
24 import { MicroserviceInstanceService } from '../services/microservice-instance.service';
25 import { DatePipe } from '@angular/common';
26 import { DeploymentArtifactService } from '../services/deployment-artifact.service';
27 import { CompSpecAddService } from '../services/comp-spec-add.service';
28 import { BreadcrumbService } from '../services/breadcrumb.service';
29 import { Router } from '@angular/router';
30 import { Ng4LoadingSpinnerService } from 'ng4-loading-spinner';
31 import { DownloadService } from '../services/download.service';
32
33 @Component({
34   selector: 'app-msInstances',
35   templateUrl: './msInstances.component.html',
36   styleUrls: ['./msInstances.component.css'],
37   animations: [
38     trigger('rowExpansionTrigger', [
39       state('void', style({
40         transform: 'translateX(-10%)',
41         opacity: 0
42       })),
43       state('active', style({
44         transform: 'translateX(0)',
45         opacity: 1
46       })),
47       transition('* <=> *', animate('400ms cubic-bezier(0.86, 0, 0.07, 1)'))
48     ])
49   ],
50   providers: [DatePipe]
51 })
52 export class MsInstancesComponent implements OnInit {
53   @ViewChild(Table, { static: false }) dt: Table;
54   @ViewChild('myInput', { static: false }) myInputVariable: ElementRef;
55   
56
57   /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
58   msInstances: msInstance[] = [];
59   expandedItems: Array<any> = new Array<any>();
60   dataSource = new MatTableDataSource<msInstance>(this.msInstances);
61   cols: any[] = [
62     { field: 'baseMsName', header: 'MS Name' },
63     { field: 'tag', header: 'MS Tag' },
64     { field: 'release', header: 'Release', width: '7%' },
65     { field: 'pstDueDate', header: 'PST Date', width: '9%' },
66     { field: 'pstDueIteration', header: 'PST Iteration', width: '6.5%' },
67     { field: 'eteDueDate', header: 'ETE Date', width: '9%' },
68     { field: 'eteDueIteration', header: 'ETE Iteration', width: '6.5%' },
69     { field: 'status', header: 'Status', width: '125px' }
70   ];
71   selectedMsInstances: msInstance[] = [];
72   columns: any[];
73   loadTable: boolean;
74   filteredRows: any;
75   downloadItems: { label: string; command: () => void; }[];
76   showAddChangeMsInstance: boolean;
77   currentRow: any;
78   msInstanceChange: string = "change";
79   generatedBPs: any[] = [];
80   canGenerateSelectedBPs: boolean = false;
81   generateSelectedBPsTooltip: string = '';
82
83   // Json to add CS (Component Spec) to DB, returned from child
84   csAddJson: any;
85
86   showCsAddDialog: boolean = false;
87
88   showViewCs: boolean =false;
89   msInstanceId: string = '';
90   errorList: string[];
91
92   constructor(private spinnerService: Ng4LoadingSpinnerService, private msInstanceApi: MicroserviceInstanceService,
93               private bpApis: DeploymentArtifactService, private addCsApi: CompSpecAddService, private messageService: MessageService,
94               private datePipe: DatePipe, private router: Router, private downloadService: DownloadService, private bread: BreadcrumbService) { }
95
96   ngOnInit() {
97
98     this.getAllInstances();
99     
100   }
101
102   getAllInstances() {
103     this.spinnerService.show();
104     this.msInstances = [];
105     this.loadTable = false;
106     
107     this.msInstanceApi.getAllMsInstances()
108       .subscribe((data: any[]) => {
109         this.fillTable(data)
110       })
111
112     this.columns = this.cols.map(col => ({ title: col.header, dataKey: col.field }));
113   }
114
115
116   // * * * * * Show the Dialog to Change an MS Instance (<app-ms-instance-add> tag in the html) * * * * *
117   showAddChangeDialog(rowData) {
118       this.msInstanceId = rowData['id']  
119       this.showAddChangeMsInstance = true;
120       this.currentRow = rowData;
121   }
122
123     /* * * * Call API to Change an MS Instance * * * */
124   addChangeMsInstance(jsonFromChildDialog) {
125     if (jsonFromChildDialog === null) {
126           this.showAddChangeMsInstance = false;
127       } else {
128           this.msInstanceApi.addChangeMsInstance("CHANGE", this.msInstanceId, jsonFromChildDialog).subscribe(
129               (data) => {
130                 this.updateCurrentRow(data);  
131                 this.messageService.add({ key: 'changeSuccess', severity: 'success', summary: 'Success', detail: "MS Instance Updated", life: 5000 });
132                 this.showAddChangeMsInstance = false;
133               },
134               (errResponse) => {
135                 if (errResponse.error.message) {
136                   this.messageService.add({ key: 'instanceAddChangeError', severity: 'error', summary: 'Error', detail: errResponse.error.message, sticky: true });
137                 } else {
138                   this.messageService.add({ key: 'instanceAddChangeError', severity: 'error', summary: 'Error', detail: errResponse.error.status, sticky: true });
139                 }
140               }
141           )
142       }
143   }
144
145   updateCurrentRow(responseData) {
146     const newRow = responseData;
147     this.currentRow['release']                       = newRow['release'];
148     this.currentRow['metadata']['scrumLead']         = newRow['metadata']['scrumLead'];
149     this.currentRow['metadata']['scrumLeadId']       = newRow['metadata']['scrumLeadId'];
150     this.currentRow['metadata']['systemsEngineer']   = newRow['metadata']['systemsEngineer'];
151     this.currentRow['metadata']['systemsEngineerId'] = newRow['metadata']['systemsEngineerId'];
152     this.currentRow['metadata']['developer']         = newRow['metadata']['developer'];
153     this.currentRow['metadata']['developerId']       = newRow['metadata']['developerId'];
154     this.currentRow['pstDueDate']                    = this.datePipe.transform(newRow['metadata']['pstDueDate'], 'yyyy-MM-dd');
155     this.currentRow['pstDueIteration']               = newRow['metadata']['pstDueIteration'];
156     this.currentRow['eteDueDate']                    = this.datePipe.transform(newRow['metadata']['eteDueDate'], 'yyyy-MM-dd');
157     this.currentRow['eteDueIteration']               = newRow['metadata']['eteDueIteration'];
158     this.currentRow['metadata']['labels']            = newRow['metadata']['labels'];
159     this.currentRow['metadata']['notes']             = newRow['metadata']['notes'];
160     this.currentRow['metadata']['updatedBy']         = newRow['metadata']['updatedBy'];
161     this.currentRow['metadata']['updatedOn']         = this.datePipe.transform(newRow['metadata']['updatedOn'], 'MM-dd-yyyy HH:mm');
162   }
163
164   // * * * * * Show the Dialog to Add a CS (in the html) * * * * *
165   // * * * * * Store the MS Instance ID for the URL and the "current row" to update when a CS is saved * * * * *
166   showAddCSDialog(rowData) {
167     this.showCsAddDialog = true;
168     this.msInstanceId = rowData['id'];
169     this.currentRow = rowData;
170   }
171
172   // * * * * * Add a CS * * * * *
173   addNewCs(jsonFromChildDialog) {
174     let compSpecAddMessage = '';
175     if (jsonFromChildDialog) {
176       this.csAddJson = jsonFromChildDialog;
177       if((JSON.parse(this.csAddJson)).policyJson === null){
178         compSpecAddMessage = 'Component Spec Added';
179       } else {
180         console.log("here")
181         compSpecAddMessage = 'Component Spec and Policy added '
182       }
183       
184       this.addCsApi.addCsToCatalog(this.msInstanceId, this.csAddJson).subscribe(
185         (response: any) => {
186           this.messageService.add({ key: 'compSpecAdded', severity: 'success', summary: 'Success', detail: compSpecAddMessage, life: 5000 });
187           this.showCsAddDialog = false;
188           this.currentRow['activeSpec'] = true;
189         },
190         errResponse => {
191             if (errResponse.error.errors) {
192                 this.messageService.add({ key: 'errorOnCsAdd', severity: 'error', summary: errResponse.error.message, detail: errResponse.error.errors.join('\n'), sticky: true});
193             } else {
194                 let summary = errResponse.error.status + " - " + errResponse.error.error;
195                 this.messageService.add({ key: 'errorOnCsAdd', severity: 'error', summary: summary, detail: errResponse.error.message, sticky: true});
196             }
197          });
198     } else {
199         this.showCsAddDialog = false
200     };
201   }
202
203 /* * * * View Component Specs 
204 msName: string;
205 msRelease: string;
206   showViewCsDialog(data){
207     this.msInstanceId = data['id']
208     this.msName = data['name']
209     this.msRelease = data['release']
210     this.showViewCs = true;
211   }
212   csView(data){
213     if(data===null){
214       this.showViewCs = false
215     } else {
216       this.showViewCs = false
217       this.messageService.add({ key: 'csViewError', severity: 'error', summary: 'Error Message', detail: data.error.message, sticky: true });    
218     }
219   } 
220   * * * */
221
222   /* * * * Generate single blueprint * * * */
223   generateBlueprints(data){
224     this.bpApis.postBlueprint(data['id']).subscribe((response) => { 
225       this.messageService.add({ key: 'bpGenMessage', severity: 'success', summary: 'Success Message', detail: 'Blueprint Generated', life: 5000 });          
226     }, (errResponse) => { 
227       this.messageService.add({ key: 'bpGenMessage', severity: 'error', summary: 'Error Message', detail: errResponse.error.message, life: 15000 });          
228     })
229   }
230
231   /* * * * Check if generate selected blueprints button should be disabled and set tooltip message * * * */
232   checkCanGenerateBp() {
233     if (this.selectedMsInstances.length > 0) {
234
235       let noActiveSpecs: string[] = [];
236       let checkReleases: boolean = true;
237       let firstRelease = this.selectedMsInstances[0]['release'];
238       for (let elem of this.selectedMsInstances) {
239         if (elem.release !== firstRelease){
240           checkReleases = false
241           this.canGenerateSelectedBPs = false
242           this.generateSelectedBPsTooltip = 'Cannot Generate Blueprints For Different Releases'
243           break
244         }
245         if (elem.activeSpec === null) {
246           noActiveSpecs.push(elem.name)
247           this.generateSelectedBPsTooltip += elem.name
248         }
249       }
250
251       if (noActiveSpecs.length < 1 && checkReleases) {
252         this.canGenerateSelectedBPs = true
253       } else if (noActiveSpecs.length > 0 && checkReleases){
254         this.canGenerateSelectedBPs = false
255         this.generateSelectedBPsTooltip = 'No Active Specs For :  '
256         let i: number = 1;
257         for (let elem of noActiveSpecs) {
258           if (i === noActiveSpecs.length) {
259             this.generateSelectedBPsTooltip += '{' + elem + '}'
260           } else {
261             this.generateSelectedBPsTooltip += '{' + elem + '}, '
262           }
263           i++
264         }
265       }
266     } else  {
267       this.canGenerateSelectedBPs = false
268       this.generateSelectedBPsTooltip = "No Instances Selected"
269     }
270   }
271   
272   /* * * * Generate multiple blueprint * * * */
273   successfulBpGens: number;
274   generateSelectedBlueprints(){
275     this.successfulBpGens = 0;
276     this.count = 0;
277     this.selectionLength = this.selectedMsInstances.length;
278
279     this.spinnerService.show();
280
281     (async () => {
282       for (let instance of this.selectedMsInstances) {
283         this.bpApis.postBlueprint(instance.id).subscribe((response) => {
284           this.bpGenSuccess()          
285         }, (errResponse) => {
286             this.bpGenError(errResponse.error.message)
287         })
288         await timeout(1500);
289       }
290     })();
291
292     function timeout(ms) {
293       return new Promise(resolve => setTimeout(resolve, ms));
294     }
295
296     this.selectedMsInstances = []
297   }
298
299   /* * * * For BP Gen Successes * * * */
300   selectionLength: number;
301   count: number;
302   bpGenSuccess(){
303     this.successfulBpGens++;
304     this.count++;
305     if (this.count === this.selectionLength){
306       if(this.successfulBpGens > 0){
307         this.messageService.add({ key: 'bpGenMessage', severity: 'success', summary: 'Success Message', detail: 'Blueprints Generated', life: 5000 });
308         this.spinnerService.hide()
309       }
310     }
311   }
312
313   /* * * * For BP Gen Errors * * * */
314   bpGenError(err){
315     this.count++;
316     if (this.count === this.selectionLength) {
317       if (this.successfulBpGens > 0) {
318         this.messageService.add({ key: 'bpGenMessage', severity: 'success', summary: 'Success Message', detail: 'Blueprints Generated', life: 5000 });
319         this.spinnerService.hide()
320       }
321     }
322     this.messageService.add({ key: 'bpGenMessage', severity: 'error', summary: 'Error Message', detail: err, life: 15000 });
323   }
324
325   /* * * * View the Blueprints for the selected MS Instance * * * */
326   viewBlueprints(rowData) {
327     this.router.navigate(["blueprints"], {queryParams:{tag: rowData['tag'], release:rowData['release'] }});
328     this.bread.setBreadcrumbs("Blueprints", "add");
329   }
330
331   /* * * * View the Component Spec for the selected MS Instance * * * */
332   viewCompSpecs(rowData) {
333     this.router.navigate(["CompSpecs"], { queryParams: { instanceId: rowData['id'] }});
334     this.bread.setBreadcrumbs("Component Specs", "add");
335   }
336
337   /* * * * Stores filtered data in new array * * * */
338   onTableFiltered(values) {
339     if (values) { this.filteredRows = values; }
340     else { this.filteredRows = this.msInstances; }
341   }
342
343   /* * * * Export ms instance table to excel or csv * * * */
344   exportTable(exportTo) {
345     let downloadElements: any[] = []
346
347     //labels array not handled well by excel download so converted them to a single string
348     for(let row of this.filteredRows){
349       let labels;
350       let notes;
351       if(exportTo === "excel"){
352         if(row.metadata.labels !== undefined){
353           labels = row.metadata.labels.join(",")
354         }
355       } else {
356         labels = row.metadata.labels
357       }
358
359       if (row.metadata.notes !== null && row.metadata.notes !== undefined && row.metadata.notes !== '') {
360         notes = encodeURI(row.metadata.notes).replace(/%20/g, " ").replace(/%0A/g, "\\n")
361       }
362
363       downloadElements.push({
364         MS_Name: row.name,
365         MS_Tag: row.tag,
366         Release: row.release,
367         PST_Due_Date: row.pstDueDate,
368         PST_Due_Iteration: row.pstDueIteration,
369         ETE_Due_Date: row.eteDueDate,
370         ETE_Due_Iteration: row.eteDueIteration,
371         Status: row.status,
372         Created_By: row.metadata.createdBy,
373         Created_On: row.metadata.createdOn,
374         Updated_By: row.metadata.updatedBy,
375         Updated_On: row.metadata.updatedOn,
376         Scrum_Lead: row.metadata.scrumLead,
377         Scrum_Lead_Id: row.metadata.scrumLeadId,
378         Systems_Engineer: row.metadata.systemsEngineer,
379         Systems_Engineer_Id: row.metadata.systemsEngineerId,
380         Developer: row.metadata.developer,
381         Developer_Id: row.metadata.developerId,
382         Notes: notes,
383         Labels: labels
384       })
385     }
386
387     let csvHeaders = [];
388
389     if (exportTo === "csv") {
390       csvHeaders = [
391         "MS_Name", 
392         "MS_Tag", 
393         "Release", 
394         "PST_Due_Date", 
395         "PST_Due_Iteration", 
396         "ETE_Due_Date", 
397         "ETE_Due_Iteration", 
398         "Status", 
399         "Created_By", 
400         "Created_On", 
401         "Updated_By", 
402         "Updated_On", 
403         "Scrum_Lead", 
404         "Scrum_Lead_Id", 
405         "Systems_Engineer", 
406         "Systems_Engineer_Id", 
407         "Developer", 
408         "Developer_Id", 
409         "Notes", 
410         "Labels"
411       ];
412     }
413
414     this.downloadService.exportTableData(exportTo, downloadElements, csvHeaders)
415   }
416   
417     /* * * * Fill ms instance table * * * */
418     fillTable(data) {
419
420         for (let elem of data) {
421
422           /* * * Now storing as dates (not strings) on DB, so need to convert old data (mm-dd-yyyy and m-d-yyyy) * * */
423           let pstDueDate: any;
424           if  (elem.metadata.pstDueDate && (elem.metadata.pstDueDate.length <= 11 &&
425                                                         elem.metadata.pstDueDate.length > 7)) {
426               pstDueDate = new Date(elem.metadata.pstDueDate.replace(/-/g, '/'))  // dash is invalid date format, FF fails
427               pstDueDate = this.datePipe.transform(pstDueDate, 'yyyy-MM-dd')
428           } else if (elem.metadata.pstDueDate) {
429               pstDueDate = this.datePipe.transform(elem.metadata.pstDueDate, 'yyyy-MM-dd')
430           } else {
431               pstDueDate = elem.metadata.pstDueDate
432           }
433
434           let eteDueDate: any;
435           if  (elem.metadata.eteDueDate && (elem.metadata.eteDueDate.length <= 11 &&
436                                                         elem.metadata.eteDueDate.length > 7)) {
437               eteDueDate = new Date(elem.metadata.eteDueDate.replace(/-/g, '/'))  // dash is invalid date format, FF fails
438               eteDueDate = this.datePipe.transform(eteDueDate, 'yyyy-MM-dd')
439           } else if (elem.metadata.eteDueDate) {
440               eteDueDate = this.datePipe.transform(elem.metadata.eteDueDate, 'yyyy-MM-dd')
441           } else {
442               eteDueDate = elem.metadata.eteDueDate
443           }
444
445             var tempElem: msInstance = {
446                 id:         elem.id,
447                 name:       elem.name,
448                 tag:        elem.msInfo.tag,
449                 release:    elem.release,
450                 version:    elem.version,
451                 status:     elem.status,
452                 baseMsId:   elem.msInfo.id,
453                 baseMsName: elem.msInfo.name,
454                 metadata: {
455                     scrumLead:         elem.metadata.scrumLead,
456                     scrumLeadId:       elem.metadata.scrumLeadId,
457                     systemsEngineer:   elem.metadata.systemsEngineer,
458                     systemsEngineerId: elem.metadata.systemsEngineerId,
459                     developer:         elem.metadata.developer,
460                     developerId:       elem.metadata.developerId,
461                     createdBy:         elem.metadata.createdBy,
462                     createdOn:         this.datePipe.transform(elem.metadata.createdOn, 'MM-dd-yyyy HH:mm'),
463                     updatedBy:         elem.metadata.updatedBy,
464                     updatedOn:         this.datePipe.transform(elem.metadata.updatedOn, 'MM-dd-yyyy HH:mm'),
465                     notes:             elem.metadata.notes,
466                     labels:            elem.metadata.labels,
467                 },
468                 pstDueDate:      pstDueDate,
469                 pstDueIteration: elem.metadata.pstDueIteration,
470                 eteDueDate:      eteDueDate,
471                 eteDueIteration: elem.metadata.eteDueIteration,
472                 activeSpec:      elem.activeSpec
473             }
474             this.msInstances.push(tempElem)
475         }
476         
477         this.filteredRows = this.msInstances
478         this.loadTable = true;    
479         this.spinnerService.hide()
480     }
481 }
482
483 export interface msInstance{
484   id: string,
485   name: string,
486   tag: string,
487   release: string,
488   version: string,
489   status: string,
490   baseMsId: string, 
491   baseMsName: string ,
492   metadata: {
493     scrumLead: string,
494     scrumLeadId: string,
495     systemsEngineer: string,
496     systemsEngineerId: string,
497     developer: string,
498     developerId: string,
499     createdBy: string,
500     createdOn: string,
501     updatedBy: string,
502     updatedOn: string,
503     notes: string,
504     labels: string[],
505   }
506   pstDueDate: string,
507   pstDueIteration: string,
508   eteDueDate: string,
509   eteDueIteration: string,
510   activeSpec: any
511 }