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
9 * # http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
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';
34 selector: 'app-msInstances',
35 templateUrl: './msInstances.component.html',
36 styleUrls: ['./msInstances.component.css'],
38 trigger('rowExpansionTrigger', [
40 transform: 'translateX(-10%)',
43 state('active', style({
44 transform: 'translateX(0)',
47 transition('* <=> *', animate('400ms cubic-bezier(0.86, 0, 0.07, 1)'))
52 export class MsInstancesComponent implements OnInit {
53 @ViewChild(Table, { static: false }) dt: Table;
54 @ViewChild('myInput', { static: false }) myInputVariable: ElementRef;
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);
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' }
71 selectedMsInstances: msInstance[] = [];
75 downloadItems: { label: string; command: () => void; }[];
76 showAddChangeMsInstance: boolean;
78 msInstanceChange: string = "change";
79 generatedBPs: any[] = [];
80 canGenerateSelectedBPs: boolean = false;
81 generateSelectedBPsTooltip: string = '';
83 // Json to add CS (Component Spec) to DB, returned from child
86 showCsAddDialog: boolean = false;
88 showViewCs: boolean =false;
89 msInstanceId: string = '';
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) { }
98 this.getAllInstances();
103 this.spinnerService.show();
104 this.msInstances = [];
105 this.loadTable = false;
107 this.msInstanceApi.getAllMsInstances()
108 .subscribe((data: any[]) => {
112 this.columns = this.cols.map(col => ({ title: col.header, dataKey: col.field }));
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;
123 /* * * * Call API to Change an MS Instance * * * */
124 addChangeMsInstance(jsonFromChildDialog) {
125 if (jsonFromChildDialog === null) {
126 this.showAddChangeMsInstance = false;
128 this.msInstanceApi.addChangeMsInstance("CHANGE", this.msInstanceId, jsonFromChildDialog).subscribe(
130 this.updateCurrentRow(data);
131 this.messageService.add({ key: 'changeSuccess', severity: 'success', summary: 'Success', detail: "MS Instance Updated", life: 5000 });
132 this.showAddChangeMsInstance = false;
135 if (errResponse.error.message) {
136 this.messageService.add({ key: 'instanceAddChangeError', severity: 'error', summary: 'Error', detail: errResponse.error.message, sticky: true });
138 this.messageService.add({ key: 'instanceAddChangeError', severity: 'error', summary: 'Error', detail: errResponse.error.status, sticky: true });
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');
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;
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';
181 compSpecAddMessage = 'Component Spec and Policy added '
184 this.addCsApi.addCsToCatalog(this.msInstanceId, this.csAddJson).subscribe(
186 this.messageService.add({ key: 'compSpecAdded', severity: 'success', summary: 'Success', detail: compSpecAddMessage, life: 5000 });
187 this.showCsAddDialog = false;
188 this.currentRow['activeSpec'] = true;
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});
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});
199 this.showCsAddDialog = false
203 /* * * * View Component Specs
206 showViewCsDialog(data){
207 this.msInstanceId = data['id']
208 this.msName = data['name']
209 this.msRelease = data['release']
210 this.showViewCs = true;
214 this.showViewCs = false
216 this.showViewCs = false
217 this.messageService.add({ key: 'csViewError', severity: 'error', summary: 'Error Message', detail: data.error.message, sticky: true });
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 });
231 /* * * * Check if generate selected blueprints button should be disabled and set tooltip message * * * */
232 checkCanGenerateBp() {
233 if (this.selectedMsInstances.length > 0) {
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'
245 if (elem.activeSpec === null) {
246 noActiveSpecs.push(elem.name)
247 this.generateSelectedBPsTooltip += elem.name
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 : '
257 for (let elem of noActiveSpecs) {
258 if (i === noActiveSpecs.length) {
259 this.generateSelectedBPsTooltip += '{' + elem + '}'
261 this.generateSelectedBPsTooltip += '{' + elem + '}, '
267 this.canGenerateSelectedBPs = false
268 this.generateSelectedBPsTooltip = "No Instances Selected"
272 /* * * * Generate multiple blueprint * * * */
273 successfulBpGens: number;
274 generateSelectedBlueprints(){
275 this.successfulBpGens = 0;
277 this.selectionLength = this.selectedMsInstances.length;
279 this.spinnerService.show();
282 for (let instance of this.selectedMsInstances) {
283 this.bpApis.postBlueprint(instance.id).subscribe((response) => {
285 }, (errResponse) => {
286 this.bpGenError(errResponse.error.message)
292 function timeout(ms) {
293 return new Promise(resolve => setTimeout(resolve, ms));
296 this.selectedMsInstances = []
299 /* * * * For BP Gen Successes * * * */
300 selectionLength: number;
303 this.successfulBpGens++;
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()
313 /* * * * For BP Gen Errors * * * */
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()
322 this.messageService.add({ key: 'bpGenMessage', severity: 'error', summary: 'Error Message', detail: err, life: 15000 });
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");
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");
337 /* * * * Stores filtered data in new array * * * */
338 onTableFiltered(values) {
339 if (values) { this.filteredRows = values; }
340 else { this.filteredRows = this.msInstances; }
343 /* * * * Export ms instance table to excel or csv * * * */
344 exportTable(exportTo) {
345 let downloadElements: any[] = []
347 //labels array not handled well by excel download so converted them to a single string
348 for(let row of this.filteredRows){
351 if(exportTo === "excel"){
352 if(row.metadata.labels !== undefined){
353 labels = row.metadata.labels.join(",")
356 labels = row.metadata.labels
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")
363 downloadElements.push({
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,
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,
389 if (exportTo === "csv") {
406 "Systems_Engineer_Id",
414 this.downloadService.exportTableData(exportTo, downloadElements, csvHeaders)
417 /* * * * Fill ms instance table * * * */
420 for (let elem of data) {
422 /* * * Now storing as dates (not strings) on DB, so need to convert old data (mm-dd-yyyy and m-d-yyyy) * * */
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')
431 pstDueDate = elem.metadata.pstDueDate
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')
442 eteDueDate = elem.metadata.eteDueDate
445 var tempElem: msInstance = {
448 tag: elem.msInfo.tag,
449 release: elem.release,
450 version: elem.version,
452 baseMsId: elem.msInfo.id,
453 baseMsName: elem.msInfo.name,
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,
468 pstDueDate: pstDueDate,
469 pstDueIteration: elem.metadata.pstDueIteration,
470 eteDueDate: eteDueDate,
471 eteDueIteration: elem.metadata.eteDueIteration,
472 activeSpec: elem.activeSpec
474 this.msInstances.push(tempElem)
477 this.filteredRows = this.msInstances
478 this.loadTable = true;
479 this.spinnerService.hide()
483 export interface msInstance{
495 systemsEngineer: string,
496 systemsEngineerId: string,
507 pstDueIteration: string,
509 eteDueIteration: string,