summaryrefslogtreecommitdiffstats
path: root/mod2/ui/src/app/msInstances
diff options
context:
space:
mode:
Diffstat (limited to 'mod2/ui/src/app/msInstances')
-rw-r--r--mod2/ui/src/app/msInstances/msInstances.component.css148
-rw-r--r--mod2/ui/src/app/msInstances/msInstances.component.html221
-rw-r--r--mod2/ui/src/app/msInstances/msInstances.component.spec.ts127
-rw-r--r--mod2/ui/src/app/msInstances/msInstances.component.ts511
4 files changed, 1007 insertions, 0 deletions
diff --git a/mod2/ui/src/app/msInstances/msInstances.component.css b/mod2/ui/src/app/msInstances/msInstances.component.css
new file mode 100644
index 0000000..62c5ac8
--- /dev/null
+++ b/mod2/ui/src/app/msInstances/msInstances.component.css
@@ -0,0 +1,148 @@
+/*
+ * # ============LICENSE_START=======================================================
+ * # Copyright (c) 2020 AT&T Intellectual Property. All rights reserved.
+ * # ================================================================================
+ * # Licensed under the Apache License, Version 2.0 (the "License");
+ * # you may not use this file except in compliance with the License.
+ * # You may obtain a copy of the License at
+ * #
+ * # http://www.apache.org/licenses/LICENSE-2.0
+ * #
+ * # Unless required by applicable law or agreed to in writing, software
+ * # distributed under the License is distributed on an "AS IS" BASIS,
+ * # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * # See the License for the specific language governing permissions and
+ * # limitations under the License.
+ * # ============LICENSE_END=========================================================
+ */
+
+td{
+ word-break:break-all;
+ font-size: 12px;
+}
+
+th{
+ text-align: center;
+ font-size: 12px;
+}
+
+.table_column_filter{
+ width: 100%;
+ height: 20px;
+ font-size: 10px;
+}
+
+.table_div{
+ margin: 0px 50px 10px 20px;
+ min-width: 980px;
+ width: 98%;
+ border: 1px solid darkslategray;
+}
+
+.fa-refresh{
+ cursor: pointer;
+}
+
+textarea
+{
+ font-size: 12px;
+}
+
+.row-expand-layout{
+ display: grid;
+ grid-template-columns: 17% 30% 30% auto;
+ grid-gap: 10px;
+ grid-auto-rows: minmax(100px, auto);
+}
+
+.row-expand-card{
+ font-size: 12px;
+ border-radius: 5px;
+ border: 1px solid slategray;
+ padding: 10px;
+ /* This height prevents vertical scroll bar in Notes */
+ height: 92px;
+ overflow: hidden;
+ min-width: 195px;
+}
+
+.table_export_buttons_alignment{
+ margin-left: 5px;
+ margin-top: -32px;
+ float: left;
+}
+
+.table_export_button{
+ border-radius: 5px;
+ height: 22px;
+ font-size: 14px;
+ border: none;
+ margin-top: 4px;
+ margin-right: 7px;
+ display: inline-flex;
+}
+
+.table_caption_header{
+ margin-left: -18%;
+ width: 82%;
+ max-height: 25px;
+ display: inline-flex;
+}
+
+.table_global_filter{
+ width: 250px;
+ height: 25px;
+ margin-left: 15px;
+ font-size: 12px;
+}
+
+.table_title{
+ margin-left: 15%;
+}
+
+.table_actions_button{
+ background-color: transparent;
+ border: none;
+ width: 20px;
+ height: 20px;
+ vertical-align: middle;
+}
+
+.table_action_item{
+ outline: none;
+ font-size: 12px;
+}
+
+::ng-deep .mat-menu-content {
+ padding-top: 0px !important;
+ padding-bottom: 0px !important;
+}
+.mat-menu-item{
+ line-height:30px;
+ height:30px;
+}
+
+.greenStatus{
+ background-color: rgba(80, 233, 105, 0.87)
+}
+
+.redStatus{
+ background-color: rgba(255, 29, 29, 0.733)
+}
+
+.blueStatus{
+ background-color: rgba(0, 183, 255, 0.432)
+}
+
+.greyStatus{
+ background-color: rgba(150, 150, 150, 0.432)
+}
+
+.toast-newline {
+ white-space: pre-line;
+}
+
+.ui-state-highlight {
+ background-color: #878C94 !important;
+ color: black !important;
+} \ No newline at end of file
diff --git a/mod2/ui/src/app/msInstances/msInstances.component.html b/mod2/ui/src/app/msInstances/msInstances.component.html
new file mode 100644
index 0000000..1545de3
--- /dev/null
+++ b/mod2/ui/src/app/msInstances/msInstances.component.html
@@ -0,0 +1,221 @@
+<!--
+ # ============LICENSE_START=======================================================
+ # Copyright (c) 2020 AT&T Intellectual Property. All rights reserved.
+ # ================================================================================
+ # Licensed under the Apache License, Version 2.0 (the "License");
+ # you may not use this file except in compliance with the License.
+ # You may obtain a copy of the License at
+ #
+ # http://www.apache.org/licenses/LICENSE-2.0
+ #
+ # Unless required by applicable law or agreed to in writing, software
+ # distributed under the License is distributed on an "AS IS" BASIS,
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ # See the License for the specific language governing permissions and
+ # limitations under the License.
+ # ============LICENSE_END=========================================================
+ -->
+
+<ng4-loading-spinner [timeout]="1000000"></ng4-loading-spinner>
+<div class="table_div">
+ <p-table #dt *ngIf="loadTable" [columns]="cols" [value]="msInstances" sortMode="multiple" [paginator]="true"
+ [rows]="18" [rowsPerPageOptions]="[10,12,14,16,18,20,25,50]" selectionMode="multiple"
+ [(selection)]="selectedMsInstances" (onFilter)="onTableFiltered(dt.filteredValue)" dataKey="id">
+ <ng-template pTemplate="caption">
+
+ <div class="table_caption_header">
+ <!--Microservices Table Header-->
+ <div style="float: left;">
+ <!--Refresh-->
+ <i class="fa fa-refresh" (click)="getAllInstances()"></i>
+ <!--Global Filter-->
+ <input type="text" pInputText size="50" placeholder="Global Filter"
+ (input)="dt.filterGlobal($event.target.value, 'contains')"
+ class="table_global_filter">
+ <i class="fa fa-search" style="margin:4px 0 0 8px"></i>
+ </div>
+
+ <div class="table_title">
+ <h4><b>Microservice Instances</b></h4>
+ </div>
+ </div>
+
+ </ng-template>
+
+ <!--column headers-->
+ <ng-template pTemplate="header" let-columns>
+ <tr>
+ <th style="width: 3em"></th>
+ <th class="ui-state-highlight" *ngFor="let col of columns" style="outline: none; vertical-align: bottom; text-align: center;" [pSortableColumn]="col.field" [ngStyle]="{'width': col.width}">
+ {{col.header}}<br>
+ <p-sortIcon [field]="col.field" style="font-size: 8px;"></p-sortIcon>
+ </th>
+ <!--actions column-->
+ <th style="width: 7%;">
+ Actions
+ </th>
+ </tr>
+ <!--Second header row for individual column filters-->
+ <tr>
+ <th style="width: 3em"></th>
+ <th *ngFor="let col of columns" [ngSwitch]="col.field">
+ <input pInputText type="text" (input)="dt.filter($event.target.value, col.field, 'contains')" class="table_column_filter" placeholder="Filter">
+ </th>
+ <th>
+ <div style="text-align: center;">
+ <p-tableHeaderCheckbox style="padding-right: 5px;"></p-tableHeaderCheckbox>
+ <button pButton type="button" class="ui-button-secondary" (click)="checkCanGenerateBp()" [matMenuTriggerFor]="menu"
+ style="background-color: transparent; border: none; width: 20px; height: 20px; vertical-align: middle;">
+ <i class="pi pi-ellipsis-h" style="color: grey;"></i>
+ </button>
+ <mat-menu #menu="matMenu" xPosition="before">
+
+ <div style="background-color: rgba(128, 128, 128, 0.25);">
+ <span style="font-size: 12px; margin-left: 10px; font-weight: 500;"><i class="pi pi-plus"
+ style="font-size: 10px;"></i> Add</span>
+ </div>
+
+ <div matTooltip="{{generateSelectedBPsTooltip}}" [matTooltipDisabled]="canGenerateSelectedBPs" matTooltipPosition="left">
+ <button mat-menu-item (click)="generateSelectedBlueprints()" class="table_action_item" [disabled]="!canGenerateSelectedBPs" style="margin-top: 5px;">Generate Selected Blueprints</button>
+ </div>
+ </mat-menu>
+ </div>
+ </th>
+ </tr>
+ </ng-template>
+
+ <!--row data-->
+ <ng-template pTemplate="body" let-rowData let-expanded="expanded" let-msElem>
+ <tr>
+ <!--Column for row expand buttons-->
+ <td>
+ <a href="#" [pRowToggler]="rowData">
+ <i [ngClass]="expanded ? 'pi pi-chevron-down' : 'pi pi-chevron-right'"></i>
+ </a>
+ </td>
+
+ <td *ngFor="let col of cols">
+ <div *ngIf="col.field==='status'"
+ style="width: fit-content; width: -moz-max-content; padding: 0px 5px 0px 5px; border-radius: 3px; font-weight: 600;" [ngClass]="{
+ 'greenStatus' : msElem[col.field] === 'DEV_COMPLETE' || msElem[col.field] === 'CERTIFIED' || msElem[col.field] === 'PROD_DEPLOYED',
+ 'blueStatus' : msElem[col.field] === 'NEW' || msElem[col.field] === 'IN_DEV' || msElem[col.field] === 'IN_TEST'}">
+ {{msElem[col.field]}}
+ </div>
+ <div *ngIf="col.field!=='status'">{{msElem[col.field]}}</div>
+ </td>
+
+ <td>
+ <div style="text-align: center">
+ <p-tableCheckbox [value]="rowData" style="padding-right: 5px;"></p-tableCheckbox>
+ <button pButton type="button" class="ui-button-secondary" [matMenuTriggerFor]="menu"
+ style="background-color: transparent; border: none; width: 20px; height: 20px; vertical-align: middle;">
+ <i class="pi pi-ellipsis-h" style="color: grey;"></i>
+ </button>
+ <mat-menu #menu="matMenu" xPosition="before">
+
+ <div style="background-color: rgba(128, 128, 128, 0.25);">
+ <span style="font-size: 12px; margin-left: 10px; font-weight: 500;"><i class="pi pi-plus" style="font-size: 10px;"></i> Add</span>
+ </div>
+ <!-- * * * * Add component spec * * * * -->
+ <button mat-menu-item class="table_action_item" (click)="showAddCSDialog(rowData)">Component Spec...</button>
+ <!-- * * * * Generate Blueprint * * * * -->
+ <div matTooltip="No Active Component Spec" [matTooltipDisabled]="rowData.activeSpec!==null" matTooltipPosition="left">
+ <button mat-menu-item class="table_action_item" (click)="generateBlueprints(rowData)" [disabled]="rowData.activeSpec===null">Generate Blueprint</button>
+ </div>
+
+ <div style="background-color: rgba(128, 128, 128, 0.25);">
+ <span style="font-size: 12px; margin-left: 10px; font-weight: 500;"><i class="pi pi-search" style="font-size: 10px;"></i> View</span>
+ </div>
+ <!-- * * * * Go to spec files table * * * * -->
+ <div matTooltip="No Component Specs" [matTooltipDisabled]="rowData.activeSpec!==null" matTooltipPosition="left">
+ <button mat-menu-item class="table_action_item" (click)="viewCompSpecs(rowData)" [disabled]="rowData.activeSpec===null">Component Specs</button>
+ </div>
+ <!-- * * * * Go to blueprints table * * * * -->
+ <button mat-menu-item class="table_action_item" (click)="viewBlueprints(rowData)" [disabled]="rowData.activeSpec===null">Blueprints</button>
+
+ <div style="background-color: rgba(128, 128, 128, 0.25);">
+ <span style="font-size: 12px; margin-left: 10px; font-weight: 500;"><i class="pi pi-pencil"></i> Update</span>
+ </div>
+ <!-- * * * * Update ms instance record * * * * -->
+ <button mat-menu-item class="table_action_item" (click)="showAddChangeDialog(rowData)">Update MS Instance...</button>
+ </mat-menu>
+ </div>
+ </td>
+ </tr>
+ </ng-template>
+
+ <!--Row expand content-->
+ <ng-template pTemplate="rowexpansion" let-rowData let-columns="columns">
+ <tr>
+ <td [attr.colspan]="columns.length + 2">
+ <div class="row-expand-layout" [@rowExpansionTrigger]="'active'">
+ <!-- Audit Fields -->
+ <div class="row-expand-card" style="background-color: rgba(95, 158, 160, 0.295);">
+ <b>Created By:</b> {{rowData.metadata.createdBy}}<br>
+ <b>Created On:</b> {{rowData.metadata.createdOn}}<br>
+ <b>Updated By:</b> {{rowData.metadata.updatedBy}}<br>
+ <b>Updated On:</b> {{rowData.metadata.updatedOn}}
+ </div>
+ <!-- People -->
+ <div class="row-expand-card" style="background-color: rgba(160, 159, 95, 0.295)">
+ <b>Scrum Lead: </b>{{rowData.metadata.scrumLead}}
+ <span *ngIf="rowData.metadata.scrumLeadId"> ({{rowData.metadata.scrumLeadId}})</span><br/>
+ <b>Systems Engineer: </b>{{rowData.metadata.systemsEngineer}}
+ <span *ngIf="rowData.metadata.systemsEngineerId"> ({{rowData.metadata.systemsEngineerId}})</span><br/>
+ <b>Developer: </b>{{rowData.metadata.developer}}
+ <span *ngIf="rowData.metadata.developerId"> ({{rowData.metadata.developerId}})</span>
+ </div>
+ <!-- Notes -->
+ <div class="row-expand-card" style="background-color: rgba(100, 148, 237, 0.295); white-space: pre-line;">
+ <b>Notes:</b><br>
+ <p-scrollPanel [style]="{width: '100%', height: '62px'}">
+ <div style="font-size: 12px; word-break: normal;">{{rowData.metadata.notes}}</div>
+ </p-scrollPanel>
+ </div>
+ <!-- Labels -->
+ <div class="row-expand-card" style="background-color: rgba(76, 65, 225, 0.295)">
+ <b style="padding-bottom: 5px;">Labels:</b><br>
+ <div *ngFor="let label of rowData['metadata']['labels']"
+ style="display: inline-flex; margin-top: 5px;">
+ <div style="padding: 2px 7px 3px 0px;">
+ <span style="background-color: rgba(80, 80, 80, 0.185); padding: 5px; border-radius: 3px;">{{label}}</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </td>
+ </tr>
+ </ng-template>
+
+ </p-table>
+
+ <!--download buttons for exporting table to either csv or excel file-->
+ <div *ngIf="loadTable" class="table_export_buttons_alignment">
+ <button pButton type="button" class="table_export_button" (click)="exportTable('csv')" matTooltip="Export Table to CSV" matTooltipPosition="above" style="width: 55px;">
+ <i class="pi pi-file" style="margin-top: 3px; margin-left: 4px;"></i>
+ <label style="font-weight: 800; margin-top: 1px;">CSV</label>
+ </button>
+ <button pButton type="button" class="table_export_button" (click)="exportTable('excel')" matTooltip="Export Table to XLSX" matTooltipPosition="above" style="width: 65px; background-color: green;">
+ <i class="pi pi-file-excel" style="margin-top: 3px; margin-left: 4px;"></i>
+ <label style="font-weight: 800; margin-top: 1px">Excel</label>
+ </button>
+ </div>
+
+ <!-- Dialog to Change an MS Instance -->
+ <app-ms-instance-add *ngIf="showAddChangeMsInstance" [visible]="showAddChangeMsInstance" [msName]="msName" [msInstanceChange]="msInstanceChange" [currentRow]="currentRow" (handler)="addChangeMsInstance($event)"></app-ms-instance-add>
+
+ <!-- Dialog to Add a Component Spec -->
+ <app-comp-spec-add *ngIf="showCsAddDialog" [visible]="showCsAddDialog" (handler)="addNewCs($event)"></app-comp-spec-add>
+
+ <!--Pop-up for "Success" changing MS Instance-->
+ <p-toast key="changeSuccess"></p-toast>
+ <!--Pop-up for "Success" adding Component Spec-->
+ <p-toast key="compSpecAdded" [style]="{width: '400px'}"></p-toast>
+ <!--Pop-up for "Error" adding Component Spec-->
+ <p-toast class="toast-newline" key="errorOnCsAdd" [style]="{width: '700px'}"></p-toast>
+
+ <p-toast class="toast-newline" key="csViewError" [style]="{width: '700px'}"></p-toast>
+
+ <p-toast key="bpGenMessage" [style]="{width: '450px'}"></p-toast>
+
+</div> \ No newline at end of file
diff --git a/mod2/ui/src/app/msInstances/msInstances.component.spec.ts b/mod2/ui/src/app/msInstances/msInstances.component.spec.ts
new file mode 100644
index 0000000..5a26b58
--- /dev/null
+++ b/mod2/ui/src/app/msInstances/msInstances.component.spec.ts
@@ -0,0 +1,127 @@
+/*
+ * # ============LICENSE_START=======================================================
+ * # Copyright (c) 2020 AT&T Intellectual Property. All rights reserved.
+ * # ================================================================================
+ * # Licensed under the Apache License, Version 2.0 (the "License");
+ * # you may not use this file except in compliance with the License.
+ * # You may obtain a copy of the License at
+ * #
+ * # http://www.apache.org/licenses/LICENSE-2.0
+ * #
+ * # Unless required by applicable law or agreed to in writing, software
+ * # distributed under the License is distributed on an "AS IS" BASIS,
+ * # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * # See the License for the specific language governing permissions and
+ * # limitations under the License.
+ * # ============LICENSE_END=========================================================
+ */
+
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { MatMenuModule, MatTooltipModule } from '@angular/material';
+import { RouterTestingModule } from '@angular/router/testing';
+import { JwtHelperService, JWT_OPTIONS } from '@auth0/angular-jwt';
+import { Ng4LoadingSpinnerModule } from 'ng4-loading-spinner';
+import { MessageService } from 'primeng/api';
+import { ButtonModule } from 'primeng/button';
+import { CalendarModule } from 'primeng/calendar';
+import { DialogModule } from 'primeng/dialog';
+import { DropdownModule } from 'primeng/dropdown';
+import { ScrollPanelModule } from 'primeng/scrollpanel';
+import { TableModule } from 'primeng/table';
+import { ToastModule } from 'primeng/toast';
+import { CompSpecAddComponent } from '../comp-spec-add/comp-spec-add.component';
+import { MsInstanceAddComponent } from '../ms-instance-add/ms-instance-add.component';
+
+import { MsInstancesComponent } from './msInstances.component';
+
+describe('MsInstancesComponent', () => {
+ let component: MsInstancesComponent;
+ let fixture: ComponentFixture<MsInstancesComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [
+ MsInstancesComponent,
+ CompSpecAddComponent,
+ MsInstanceAddComponent
+ ],
+ imports: [
+ Ng4LoadingSpinnerModule,
+ TableModule,
+ MatMenuModule,
+ ScrollPanelModule,
+ ToastModule,
+ DialogModule,
+ DropdownModule,
+ FormsModule,
+ ReactiveFormsModule,
+ ButtonModule,
+ CalendarModule,
+ HttpClientTestingModule,
+ ToastModule,
+ RouterTestingModule,
+ MatTooltipModule
+ ],
+ providers: [
+ MessageService,
+ { provide: JWT_OPTIONS, useValue: JWT_OPTIONS },
+ JwtHelperService
+ ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MsInstancesComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it(`should fill msInstances Object`, () => {
+ const fixture = TestBed.createComponent(MsInstancesComponent);
+ const app = fixture.debugElement.componentInstance;
+
+ let mockMsInstance = [{
+ id: 'testId1234',
+ name: 'test-MS',
+ release: '2008',
+ version: '1.0.0',
+ status: 'New',
+ msInfo: {
+ id: 'testBaseMsId1234',
+ name: 'test Base Ms',
+ tag: 'test-MS-tag',
+ },
+ metadata: {
+ scrumLead: 'test',
+ scrumLeadId: 'testId',
+ systemsEngineer: 'test',
+ systemsEngineerId: 'testId',
+ developer: 'test',
+ developerId: 'testId',
+ pstDueDate: '01-01-2020 12:00',
+ pstDueIteration: '1.1',
+ eteDueDate: '01-01-2020 12:00',
+ eteDueIteration: '1.1',
+ createdBy: 'test',
+ createdOn: '01-01-2020 12:00',
+ updatedBy: 'test',
+ updatedOn: '01-01-2020 12:00',
+ notes: 'test',
+ labels: ['test'],
+ },
+ activeSpec: 'test'
+ }]
+
+ app.fillTable(mockMsInstance)
+
+ expect(app.loadTable).toEqual(true);
+ expect(app.msInstances.length).toEqual(1);
+ });
+});
diff --git a/mod2/ui/src/app/msInstances/msInstances.component.ts b/mod2/ui/src/app/msInstances/msInstances.component.ts
new file mode 100644
index 0000000..e011cb9
--- /dev/null
+++ b/mod2/ui/src/app/msInstances/msInstances.component.ts
@@ -0,0 +1,511 @@
+/*
+ * # ============LICENSE_START=======================================================
+ * # Copyright (c) 2020 AT&T Intellectual Property. All rights reserved.
+ * # ================================================================================
+ * # Licensed under the Apache License, Version 2.0 (the "License");
+ * # you may not use this file except in compliance with the License.
+ * # You may obtain a copy of the License at
+ * #
+ * # http://www.apache.org/licenses/LICENSE-2.0
+ * #
+ * # Unless required by applicable law or agreed to in writing, software
+ * # distributed under the License is distributed on an "AS IS" BASIS,
+ * # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * # See the License for the specific language governing permissions and
+ * # limitations under the License.
+ * # ============LICENSE_END=========================================================
+ */
+
+import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
+import { MatTableDataSource } from '@angular/material/table';
+import { Table } from 'primeng/table';
+import { MessageService } from 'primeng/api';
+import { trigger, state, style, transition, animate } from '@angular/animations';
+import { MicroserviceInstanceService } from '../services/microservice-instance.service';
+import { DatePipe } from '@angular/common';
+import { DeploymentArtifactService } from '../services/deployment-artifact.service';
+import { CompSpecAddService } from '../services/comp-spec-add.service';
+import { BreadcrumbService } from '../services/breadcrumb.service';
+import { Router } from '@angular/router';
+import { Ng4LoadingSpinnerService } from 'ng4-loading-spinner';
+import { DownloadService } from '../services/download.service';
+
+@Component({
+ selector: 'app-msInstances',
+ templateUrl: './msInstances.component.html',
+ styleUrls: ['./msInstances.component.css'],
+ animations: [
+ trigger('rowExpansionTrigger', [
+ state('void', style({
+ transform: 'translateX(-10%)',
+ opacity: 0
+ })),
+ state('active', style({
+ transform: 'translateX(0)',
+ opacity: 1
+ })),
+ transition('* <=> *', animate('400ms cubic-bezier(0.86, 0, 0.07, 1)'))
+ ])
+ ],
+ providers: [DatePipe]
+})
+export class MsInstancesComponent implements OnInit {
+ @ViewChild(Table, { static: false }) dt: Table;
+ @ViewChild('myInput', { static: false }) myInputVariable: ElementRef;
+
+
+ /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
+ msInstances: msInstance[] = [];
+ expandedItems: Array<any> = new Array<any>();
+ dataSource = new MatTableDataSource<msInstance>(this.msInstances);
+ cols: any[] = [
+ { field: 'baseMsName', header: 'MS Name' },
+ { field: 'tag', header: 'MS Tag' },
+ { field: 'release', header: 'Release', width: '7%' },
+ { field: 'pstDueDate', header: 'PST Date', width: '9%' },
+ { field: 'pstDueIteration', header: 'PST Iteration', width: '6.5%' },
+ { field: 'eteDueDate', header: 'ETE Date', width: '9%' },
+ { field: 'eteDueIteration', header: 'ETE Iteration', width: '6.5%' },
+ { field: 'status', header: 'Status', width: '125px' }
+ ];
+ selectedMsInstances: msInstance[] = [];
+ columns: any[];
+ loadTable: boolean;
+ filteredRows: any;
+ downloadItems: { label: string; command: () => void; }[];
+ showAddChangeMsInstance: boolean;
+ currentRow: any;
+ msInstanceChange: string = "change";
+ generatedBPs: any[] = [];
+ canGenerateSelectedBPs: boolean = false;
+ generateSelectedBPsTooltip: string = '';
+
+ // Json to add CS (Component Spec) to DB, returned from child
+ csAddJson: any;
+
+ showCsAddDialog: boolean = false;
+
+ showViewCs: boolean =false;
+ msInstanceId: string = '';
+ errorList: string[];
+
+ constructor(private spinnerService: Ng4LoadingSpinnerService, private msInstanceApi: MicroserviceInstanceService,
+ private bpApis: DeploymentArtifactService, private addCsApi: CompSpecAddService, private messageService: MessageService,
+ private datePipe: DatePipe, private router: Router, private downloadService: DownloadService, private bread: BreadcrumbService) { }
+
+ ngOnInit() {
+
+ this.getAllInstances();
+
+ }
+
+ getAllInstances() {
+ this.spinnerService.show();
+ this.msInstances = [];
+ this.loadTable = false;
+
+ this.msInstanceApi.getAllMsInstances()
+ .subscribe((data: any[]) => {
+ this.fillTable(data)
+ })
+
+ this.columns = this.cols.map(col => ({ title: col.header, dataKey: col.field }));
+ }
+
+
+ // * * * * * Show the Dialog to Change an MS Instance (<app-ms-instance-add> tag in the html) * * * * *
+ showAddChangeDialog(rowData) {
+ this.msInstanceId = rowData['id']
+ this.showAddChangeMsInstance = true;
+ this.currentRow = rowData;
+ }
+
+ /* * * * Call API to Change an MS Instance * * * */
+ addChangeMsInstance(jsonFromChildDialog) {
+ if (jsonFromChildDialog === null) {
+ this.showAddChangeMsInstance = false;
+ } else {
+ this.msInstanceApi.addChangeMsInstance("CHANGE", this.msInstanceId, jsonFromChildDialog).subscribe(
+ (data) => {
+ this.updateCurrentRow(data);
+ this.messageService.add({ key: 'changeSuccess', severity: 'success', summary: 'Success', detail: "MS Instance Updated", life: 5000 });
+ this.showAddChangeMsInstance = false;
+ },
+ (errResponse) => {
+ if (errResponse.error.message) {
+ this.messageService.add({ key: 'instanceAddChangeError', severity: 'error', summary: 'Error', detail: errResponse.error.message, sticky: true });
+ } else {
+ this.messageService.add({ key: 'instanceAddChangeError', severity: 'error', summary: 'Error', detail: errResponse.error.status, sticky: true });
+ }
+ }
+ )
+ }
+ }
+
+ updateCurrentRow(responseData) {
+ const newRow = responseData;
+ this.currentRow['release'] = newRow['release'];
+ this.currentRow['metadata']['scrumLead'] = newRow['metadata']['scrumLead'];
+ this.currentRow['metadata']['scrumLeadId'] = newRow['metadata']['scrumLeadId'];
+ this.currentRow['metadata']['systemsEngineer'] = newRow['metadata']['systemsEngineer'];
+ this.currentRow['metadata']['systemsEngineerId'] = newRow['metadata']['systemsEngineerId'];
+ this.currentRow['metadata']['developer'] = newRow['metadata']['developer'];
+ this.currentRow['metadata']['developerId'] = newRow['metadata']['developerId'];
+ this.currentRow['pstDueDate'] = this.datePipe.transform(newRow['metadata']['pstDueDate'], 'yyyy-MM-dd');
+ this.currentRow['pstDueIteration'] = newRow['metadata']['pstDueIteration'];
+ this.currentRow['eteDueDate'] = this.datePipe.transform(newRow['metadata']['eteDueDate'], 'yyyy-MM-dd');
+ this.currentRow['eteDueIteration'] = newRow['metadata']['eteDueIteration'];
+ this.currentRow['metadata']['labels'] = newRow['metadata']['labels'];
+ this.currentRow['metadata']['notes'] = newRow['metadata']['notes'];
+ this.currentRow['metadata']['updatedBy'] = newRow['metadata']['updatedBy'];
+ this.currentRow['metadata']['updatedOn'] = this.datePipe.transform(newRow['metadata']['updatedOn'], 'MM-dd-yyyy HH:mm');
+ }
+
+ // * * * * * Show the Dialog to Add a CS (in the html) * * * * *
+ // * * * * * Store the MS Instance ID for the URL and the "current row" to update when a CS is saved * * * * *
+ showAddCSDialog(rowData) {
+ this.showCsAddDialog = true;
+ this.msInstanceId = rowData['id'];
+ this.currentRow = rowData;
+ }
+
+ // * * * * * Add a CS * * * * *
+ addNewCs(jsonFromChildDialog) {
+ let compSpecAddMessage = '';
+ if (jsonFromChildDialog) {
+ this.csAddJson = jsonFromChildDialog;
+ if((JSON.parse(this.csAddJson)).policyJson === null){
+ compSpecAddMessage = 'Component Spec Added';
+ } else {
+ console.log("here")
+ compSpecAddMessage = 'Component Spec and Policy added '
+ }
+
+ this.addCsApi.addCsToCatalog(this.msInstanceId, this.csAddJson).subscribe(
+ (response: any) => {
+ this.messageService.add({ key: 'compSpecAdded', severity: 'success', summary: 'Success', detail: compSpecAddMessage, life: 5000 });
+ this.showCsAddDialog = false;
+ this.currentRow['activeSpec'] = true;
+ },
+ errResponse => {
+ if (errResponse.error.errors) {
+ this.messageService.add({ key: 'errorOnCsAdd', severity: 'error', summary: errResponse.error.message, detail: errResponse.error.errors.join('\n'), sticky: true});
+ } else {
+ let summary = errResponse.error.status + " - " + errResponse.error.error;
+ this.messageService.add({ key: 'errorOnCsAdd', severity: 'error', summary: summary, detail: errResponse.error.message, sticky: true});
+ }
+ });
+ } else {
+ this.showCsAddDialog = false
+ };
+ }
+
+/* * * * View Component Specs
+msName: string;
+msRelease: string;
+ showViewCsDialog(data){
+ this.msInstanceId = data['id']
+ this.msName = data['name']
+ this.msRelease = data['release']
+ this.showViewCs = true;
+ }
+ csView(data){
+ if(data===null){
+ this.showViewCs = false
+ } else {
+ this.showViewCs = false
+ this.messageService.add({ key: 'csViewError', severity: 'error', summary: 'Error Message', detail: data.error.message, sticky: true });
+ }
+ }
+ * * * */
+
+ /* * * * Generate single blueprint * * * */
+ generateBlueprints(data){
+ this.bpApis.postBlueprint(data['id']).subscribe((response) => {
+ this.messageService.add({ key: 'bpGenMessage', severity: 'success', summary: 'Success Message', detail: 'Blueprint Generated', life: 5000 });
+ }, (errResponse) => {
+ this.messageService.add({ key: 'bpGenMessage', severity: 'error', summary: 'Error Message', detail: errResponse.error.message, life: 15000 });
+ })
+ }
+
+ /* * * * Check if generate selected blueprints button should be disabled and set tooltip message * * * */
+ checkCanGenerateBp() {
+ if (this.selectedMsInstances.length > 0) {
+
+ let noActiveSpecs: string[] = [];
+ let checkReleases: boolean = true;
+ let firstRelease = this.selectedMsInstances[0]['release'];
+ for (let elem of this.selectedMsInstances) {
+ if (elem.release !== firstRelease){
+ checkReleases = false
+ this.canGenerateSelectedBPs = false
+ this.generateSelectedBPsTooltip = 'Cannot Generate Blueprints For Different Releases'
+ break
+ }
+ if (elem.activeSpec === null) {
+ noActiveSpecs.push(elem.name)
+ this.generateSelectedBPsTooltip += elem.name
+ }
+ }
+
+ if (noActiveSpecs.length < 1 && checkReleases) {
+ this.canGenerateSelectedBPs = true
+ } else if (noActiveSpecs.length > 0 && checkReleases){
+ this.canGenerateSelectedBPs = false
+ this.generateSelectedBPsTooltip = 'No Active Specs For : '
+ let i: number = 1;
+ for (let elem of noActiveSpecs) {
+ if (i === noActiveSpecs.length) {
+ this.generateSelectedBPsTooltip += '{' + elem + '}'
+ } else {
+ this.generateSelectedBPsTooltip += '{' + elem + '}, '
+ }
+ i++
+ }
+ }
+ } else {
+ this.canGenerateSelectedBPs = false
+ this.generateSelectedBPsTooltip = "No Instances Selected"
+ }
+ }
+
+ /* * * * Generate multiple blueprint * * * */
+ successfulBpGens: number;
+ generateSelectedBlueprints(){
+ this.successfulBpGens = 0;
+ this.count = 0;
+ this.selectionLength = this.selectedMsInstances.length;
+
+ this.spinnerService.show();
+
+ (async () => {
+ for (let instance of this.selectedMsInstances) {
+ this.bpApis.postBlueprint(instance.id).subscribe((response) => {
+ this.bpGenSuccess()
+ }, (errResponse) => {
+ this.bpGenError(errResponse.error.message)
+ })
+ await timeout(1500);
+ }
+ })();
+
+ function timeout(ms) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+ }
+
+ this.selectedMsInstances = []
+ }
+
+ /* * * * For BP Gen Successes * * * */
+ selectionLength: number;
+ count: number;
+ bpGenSuccess(){
+ this.successfulBpGens++;
+ this.count++;
+ if (this.count === this.selectionLength){
+ if(this.successfulBpGens > 0){
+ this.messageService.add({ key: 'bpGenMessage', severity: 'success', summary: 'Success Message', detail: 'Blueprints Generated', life: 5000 });
+ this.spinnerService.hide()
+ }
+ }
+ }
+
+ /* * * * For BP Gen Errors * * * */
+ bpGenError(err){
+ this.count++;
+ if (this.count === this.selectionLength) {
+ if (this.successfulBpGens > 0) {
+ this.messageService.add({ key: 'bpGenMessage', severity: 'success', summary: 'Success Message', detail: 'Blueprints Generated', life: 5000 });
+ this.spinnerService.hide()
+ }
+ }
+ this.messageService.add({ key: 'bpGenMessage', severity: 'error', summary: 'Error Message', detail: err, life: 15000 });
+ }
+
+ /* * * * View the Blueprints for the selected MS Instance * * * */
+ viewBlueprints(rowData) {
+ this.router.navigate(["blueprints"], {queryParams:{tag: rowData['tag'], release:rowData['release'] }});
+ this.bread.setBreadcrumbs("Blueprints", "add");
+ }
+
+ /* * * * View the Component Spec for the selected MS Instance * * * */
+ viewCompSpecs(rowData) {
+ this.router.navigate(["CompSpecs"], { queryParams: { instanceId: rowData['id'] }});
+ this.bread.setBreadcrumbs("Component Specs", "add");
+ }
+
+ /* * * * Stores filtered data in new array * * * */
+ onTableFiltered(values) {
+ if (values) { this.filteredRows = values; }
+ else { this.filteredRows = this.msInstances; }
+ }
+
+ /* * * * Export ms instance table to excel or csv * * * */
+ exportTable(exportTo) {
+ let downloadElements: any[] = []
+
+ //labels array not handled well by excel download so converted them to a single string
+ for(let row of this.filteredRows){
+ let labels;
+ let notes;
+ if(exportTo === "excel"){
+ if(row.metadata.labels !== undefined){
+ labels = row.metadata.labels.join(",")
+ }
+ } else {
+ labels = row.metadata.labels
+ }
+
+ if (row.metadata.notes !== null && row.metadata.notes !== undefined && row.metadata.notes !== '') {
+ notes = encodeURI(row.metadata.notes).replace(/%20/g, " ").replace(/%0A/g, "\\n")
+ }
+
+ downloadElements.push({
+ MS_Name: row.name,
+ MS_Tag: row.tag,
+ Release: row.release,
+ PST_Due_Date: row.pstDueDate,
+ PST_Due_Iteration: row.pstDueIteration,
+ ETE_Due_Date: row.eteDueDate,
+ ETE_Due_Iteration: row.eteDueIteration,
+ Status: row.status,
+ Created_By: row.metadata.createdBy,
+ Created_On: row.metadata.createdOn,
+ Updated_By: row.metadata.updatedBy,
+ Updated_On: row.metadata.updatedOn,
+ Scrum_Lead: row.metadata.scrumLead,
+ Scrum_Lead_Id: row.metadata.scrumLeadId,
+ Systems_Engineer: row.metadata.systemsEngineer,
+ Systems_Engineer_Id: row.metadata.systemsEngineerId,
+ Developer: row.metadata.developer,
+ Developer_Id: row.metadata.developerId,
+ Notes: notes,
+ Labels: labels
+ })
+ }
+
+ let csvHeaders = [];
+
+ if (exportTo === "csv") {
+ csvHeaders = [
+ "MS_Name",
+ "MS_Tag",
+ "Release",
+ "PST_Due_Date",
+ "PST_Due_Iteration",
+ "ETE_Due_Date",
+ "ETE_Due_Iteration",
+ "Status",
+ "Created_By",
+ "Created_On",
+ "Updated_By",
+ "Updated_On",
+ "Scrum_Lead",
+ "Scrum_Lead_Id",
+ "Systems_Engineer",
+ "Systems_Engineer_Id",
+ "Developer",
+ "Developer_Id",
+ "Notes",
+ "Labels"
+ ];
+ }
+
+ this.downloadService.exportTableData(exportTo, downloadElements, csvHeaders)
+ }
+
+ /* * * * Fill ms instance table * * * */
+ fillTable(data) {
+
+ for (let elem of data) {
+
+ /* * * Now storing as dates (not strings) on DB, so need to convert old data (mm-dd-yyyy and m-d-yyyy) * * */
+ let pstDueDate: any;
+ if (elem.metadata.pstDueDate && (elem.metadata.pstDueDate.length <= 11 &&
+ elem.metadata.pstDueDate.length > 7)) {
+ pstDueDate = new Date(elem.metadata.pstDueDate.replace(/-/g, '/')) // dash is invalid date format, FF fails
+ pstDueDate = this.datePipe.transform(pstDueDate, 'yyyy-MM-dd')
+ } else if (elem.metadata.pstDueDate) {
+ pstDueDate = this.datePipe.transform(elem.metadata.pstDueDate, 'yyyy-MM-dd')
+ } else {
+ pstDueDate = elem.metadata.pstDueDate
+ }
+
+ let eteDueDate: any;
+ if (elem.metadata.eteDueDate && (elem.metadata.eteDueDate.length <= 11 &&
+ elem.metadata.eteDueDate.length > 7)) {
+ eteDueDate = new Date(elem.metadata.eteDueDate.replace(/-/g, '/')) // dash is invalid date format, FF fails
+ eteDueDate = this.datePipe.transform(eteDueDate, 'yyyy-MM-dd')
+ } else if (elem.metadata.eteDueDate) {
+ eteDueDate = this.datePipe.transform(elem.metadata.eteDueDate, 'yyyy-MM-dd')
+ } else {
+ eteDueDate = elem.metadata.eteDueDate
+ }
+
+ var tempElem: msInstance = {
+ id: elem.id,
+ name: elem.name,
+ tag: elem.msInfo.tag,
+ release: elem.release,
+ version: elem.version,
+ status: elem.status,
+ baseMsId: elem.msInfo.id,
+ baseMsName: elem.msInfo.name,
+ metadata: {
+ scrumLead: elem.metadata.scrumLead,
+ scrumLeadId: elem.metadata.scrumLeadId,
+ systemsEngineer: elem.metadata.systemsEngineer,
+ systemsEngineerId: elem.metadata.systemsEngineerId,
+ developer: elem.metadata.developer,
+ developerId: elem.metadata.developerId,
+ createdBy: elem.metadata.createdBy,
+ createdOn: this.datePipe.transform(elem.metadata.createdOn, 'MM-dd-yyyy HH:mm'),
+ updatedBy: elem.metadata.updatedBy,
+ updatedOn: this.datePipe.transform(elem.metadata.updatedOn, 'MM-dd-yyyy HH:mm'),
+ notes: elem.metadata.notes,
+ labels: elem.metadata.labels,
+ },
+ pstDueDate: pstDueDate,
+ pstDueIteration: elem.metadata.pstDueIteration,
+ eteDueDate: eteDueDate,
+ eteDueIteration: elem.metadata.eteDueIteration,
+ activeSpec: elem.activeSpec
+ }
+ this.msInstances.push(tempElem)
+ }
+
+ this.filteredRows = this.msInstances
+ this.loadTable = true;
+ this.spinnerService.hide()
+ }
+}
+
+export interface msInstance{
+ id: string,
+ name: string,
+ tag: string,
+ release: string,
+ version: string,
+ status: string,
+ baseMsId: string,
+ baseMsName: string ,
+ metadata: {
+ scrumLead: string,
+ scrumLeadId: string,
+ systemsEngineer: string,
+ systemsEngineerId: string,
+ developer: string,
+ developerId: string,
+ createdBy: string,
+ createdOn: string,
+ updatedBy: string,
+ updatedOn: string,
+ notes: string,
+ labels: string[],
+ }
+ pstDueDate: string,
+ pstDueIteration: string,
+ eteDueDate: string,
+ eteDueIteration: string,
+ activeSpec: any
+} \ No newline at end of file