Adding Template button to new service instance + cypress test + filter 99/98999/6
authorYoav Schneiderman <yoav.schneiderman@intl.att.com>
Tue, 3 Dec 2019 10:55:39 +0000 (12:55 +0200)
committerIttay Stern <ittay.stern@att.com>
Wed, 4 Dec 2019 15:24:17 +0000 (17:24 +0200)
Issue-ID: VID-724
Change-Id: Ie69a32dd6b74de57fd747a92935c1ce5edb86351
Signed-off-by: Yoav Schneiderman <yoav.schneiderman@intl.att.com>
Signed-off-by: Ittay Stern <ittay.stern@att.com>
15 files changed:
vid-webpack-master/cypress/integration/iFrames/instantiation.templates.modal.e2e.ts [new file with mode: 0644]
vid-webpack-master/cypress/support/jsonBuilders/mocks/jsons/flags.cypress.json
vid-webpack-master/package.json
vid-webpack-master/src/app/shared/components/ellipsis/ellipsis.component.ts
vid-webpack-master/src/app/shared/components/genericFormPopup/generic-form-popup.component.html
vid-webpack-master/src/app/shared/components/genericFormPopup/generic-form-popup.component.ts
vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.component.html [new file with mode: 0644]
vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.component.scss [new file with mode: 0644]
vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.component.ts [new file with mode: 0644]
vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.service.spec.ts [new file with mode: 0644]
vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.service.ts [new file with mode: 0644]
vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.row.model.ts [new file with mode: 0644]
vid-webpack-master/src/app/shared/pipes/searchFilter/search-filter.pipe.spec.ts
vid-webpack-master/src/app/shared/pipes/searchFilter/search-filter.pipe.ts
vid-webpack-master/src/app/shared/shared.module.ts

diff --git a/vid-webpack-master/cypress/integration/iFrames/instantiation.templates.modal.e2e.ts b/vid-webpack-master/cypress/integration/iFrames/instantiation.templates.modal.e2e.ts
new file mode 100644 (file)
index 0000000..b862e56
--- /dev/null
@@ -0,0 +1,110 @@
+///<reference path="../../../node_modules/cypress/types/index.d.ts"/>
+describe('Template', () => {
+
+  beforeEach(() => {
+    cy.clearSessionStorage();
+    cy.setReduxState();
+    cy.preventErrorsOnLoading();
+    cy.initAAIMock();
+    cy.initVidMock();
+    cy.login();
+  });
+
+  afterEach(() => {
+    cy.screenshot();
+  });
+
+  it('when open service popup should show template button', function () {
+    cy.readFile('cypress/support/jsonBuilders/mocks/jsons/flags.cypress.json').then((flags) => {
+      cy.server()
+        .route({
+          method: 'GET',
+          delay: 0,
+          status: 200,
+          url: Cypress.config('baseUrl') + "/flags**",
+          response: {
+            "FLAG_VF_MODULE_RESUME_STATUS_CREATE": false,
+            "FLAG_2004_INSTANTIATION_TEMPLATES_POPUP": true
+          }
+        }).as('initFlags');
+    });
+
+    const asyncInstantiation = [
+      {
+        "id": 8,
+        "created": 1525075968000,
+        "modified": 1525075971000,
+        "action": "INSTANTIATE",
+        "createdId": null,
+        "modifiedId": null,
+        "rowNum": null,
+        "auditUserId": null,
+        "auditTrail": null,
+        "jobId": "5c2cd8e5-27d0-42e3-85a1-85db5eaba459",
+        "templateId": "d42ba7c8-9e19-4e34-ae2c-d8af3f24498e",
+        "userId": "16807000",
+        "aLaCarte": false,
+        "msoRequestId": "c0011670-0e1a-4b74-945d-8bf5aede1d9c",
+        "jobStatus": "FAILED",
+        "statusModifiedDate": 1525075968000,
+        "hidden": false,
+        "pause": false,
+        "owningEntityId": "d61e6f2d-12fa-4cc2-91df-7c244011d6fc",
+        "owningEntityName": "WayneHolland",
+        "project": "WATKINS",
+        "aicZoneId": "NFT1",
+        "aicZoneName": "NFTJSSSS-NFT1",
+        "tenantId": "bae71557c5bb4d5aac6743a4e5f1d054",
+        "tenantName": "AIN Web Tool-15-D-testalexandria",
+        "regionId": "hvf6",
+        "regionName": null,
+        "serviceType": "TYLER SILVIA",
+        "subscriberName": "e433710f-9217-458d-a79d-1c7aff376d89",
+        "serviceInstanceId": null,
+        "serviceInstanceName": "nWUfl instance name_002",
+        "serviceModelId": "e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0",
+        "serviceModelName": "action-data",
+        "serviceModelVersion": "1.0",
+        "createdBulkDate": 1525075968000,
+        "isRetryEnabled": true
+      }
+    ];
+
+    cy.route(Cypress.config('baseUrl') + "/asyncInstantiation**", asyncInstantiation);
+
+    cy.openIframe('/app/ui/#/servicePopup?serviceModelId=2f80c596-27e5-4ca9-b5bb-e03a7fd4c0fd&isCreate=true');
+    cy.getElementByDataTestsId('templateButton').contains('Template')
+      .getElementByDataTestsId('templateButton').click({force: true}) // Open template Modal
+      .getElementByDataTestsId('template-modal-title').contains('Templates') // Check Modal header
+      .getElementByDataTestsId('description-part-1').contains('The following list presents previous instantiations done for this model in this version.')
+      .getElementByDataTestsId('description-part-2').contains('You may use one of them as a baseline for your instantiation or start from scratch.')
+      .getElementByDataTestsId('description-part-3').contains('Once you selecting one allows you to change the data before start instantiating.')
+
+
+    //check table headers
+    cy.get(`#header-userId`).contains('User ID');
+    cy.get(`#header-createDate`).contains('Date');
+    cy.get(`#header-instanceName`).contains('Instance Name');
+    cy.get(`#header-instantiationStatus`).contains('Instantiation Status');
+    cy.get(`#header-region`).contains('Region');
+    cy.get(`#header-tenant`).contains('Tenant');
+    cy.get(`#header-aicZone`).contains('AIC Zone');
+
+    // check table body row
+    cy.getElementByDataTestsId(`userId-${asyncInstantiation[0].jobId}`).contains('16807000');
+    cy.getElementByDataTestsId(`createDate-${asyncInstantiation[0].jobId}`).contains('2018-04-30 11:12:48');
+    cy.getElementByDataTestsId(`instanceName-${asyncInstantiation[0].jobId}`).contains('nWUfl instance name_002');
+    cy.getElementByDataTestsId(`instantiationStatus-${asyncInstantiation[0].jobId}`).contains('FAILED');
+    cy.getElementByDataTestsId(`region-${asyncInstantiation[0].jobId}`).contains('hvf6 (WAYNEHOLLAND)');
+    cy.getElementByDataTestsId(`tenant-${asyncInstantiation[0].jobId}`).contains('AIN Web Tool-15-D-testalexandria');
+    cy.getElementByDataTestsId(`aicZone-${asyncInstantiation[0].jobId}`).contains('NFTJSSSS-NFT1');
+
+
+    //check load button is disabled
+    cy.getElementByDataTestsId('LoadTemplateButton').should('be.disabled')
+    cy.getElementByDataTestsId('row-5c2cd8e5-27d0-42e3-85a1-85db5eaba459').click();
+    cy.getElementByDataTestsId('LoadTemplateButton').should('not.be.disabled')
+
+  });
+});
+
index b829361..0f5e152 100644 (file)
@@ -19,5 +19,6 @@
   "FLAG_1911_INSTANTIATION_ORDER_BUTTON_IN_ASYNC_ALACARTE": false,
   "FLAG_2002_VNF_PLATFORM_MULTI_SELECT" : false,
   "FLAG_2002_VFM_UPGRADE_ADDITIONAL_OPTIONS": true,
-  "FLAG_2004_INSTANTIATION_STATUS_FILTER": true
+  "FLAG_2004_INSTANTIATION_STATUS_FILTER": true,
+  "FLAG_2004_INSTANTIATION_TEMPLATES_POPUP" : false
 }
index f76ab87..f2f9136 100644 (file)
@@ -56,6 +56,7 @@
     "install": "0.12.2",
     "jest-image-snapshot": "2.8.1",
     "jest-preset-angular": "6.0.2",
+    "moment": "^2.24.0",
     "ng-multiselect-dropdown": "0.1.3",
     "ng2-bootstrap-modal": "1.0.1",
     "ngx-bootstrap": "^2.0.2",
index 07f4d48..a7b8ac7 100644 (file)
@@ -3,18 +3,20 @@ import {HighlightPipe} from "../../pipes/highlight/highlight-filter.pipe";
 import * as _ from 'lodash';
 
 @Component({
-  selector : 'custom-ellipsis',
+  selector: 'custom-ellipsis',
   template: `
     <span
-          sdc-tooltip
-          class="ellipsis"
-          id="{{id}}"
-          [innerHtml]="displayValue | safe : 'html'"
-          [ngClass]="{'breakWord' : breakWord == true}"
-          [tooltip-text]="value">
+      sdc-tooltip
+      class="ellipsis"
+      [attr.data-tests-id]="dataTestId"
+      id="{{id}}"
+      [innerHtml]="displayValue | safe : 'html'"
+      [ngStyle]="{'white-space' :  showDots ? 'nowrap' : 'initial'}"
+      [ngClass]="{'breakWord' : breakWord == true}"
+      [tooltip-text]="value">
       </span>`,
-  styles : [
-    `
+  styles: [
+      `
       .ellipsis {
         white-space: nowrap;
         overflow: hidden;
@@ -23,30 +25,33 @@ import * as _ from 'lodash';
         width: 99%;
         text-align: left;
       }
-      
+
       .breakWord {
         word-wrap: break-word;
         white-space: normal;
       }
     `
   ],
-  providers : [HighlightPipe]
+  providers: [HighlightPipe]
 })
-export class EllipsisComponent implements OnChanges{
-  @Input() value : string;
-  @Input() id : string;
-  @Input() hightlight : string;
-  @Input() breakWord : boolean = false;
+export class EllipsisComponent implements OnChanges {
+  @Input() value: string;
+  @Input() id: string;
+  @Input() hightlight: string;
+  @Input() breakWord: boolean = false;
+  @Input() dataTestId: string;
+  @Input() showDots: boolean = false;
+
+  displayValue: string;
 
-  displayValue : string;
-  constructor(private _highlightPipe : HighlightPipe){
+  constructor(private _highlightPipe: HighlightPipe) {
     this.displayValue = this.value;
   }
 
   ngOnChanges(changes: SimpleChanges): void {
     this.displayValue = this.value;
-    if(!_.isNil(this.hightlight)){
-      this.displayValue = this._highlightPipe.transform(this.value ,this.hightlight ? this.hightlight : '');
+    if (!_.isNil(this.hightlight)) {
+      this.displayValue = this._highlightPipe.transform(this.valuethis.hightlight ? this.hightlight : '');
     }
   }
 }
index 57064f6..5b8dfa9 100644 (file)
@@ -3,14 +3,13 @@
     <div class="modal-header">
       <button type="button"
               class="close"
-              (click)="formPopupDetails?.onCancel(formPopupDetails.that,dynamicForm)" >&times;
+              (click)="formPopupDetails?.onCancel(formPopupDetails.that,dynamicForm)">&times;
       </button>
       <span [attr.data-tests-id]="'create-modal-title'"
             class="modal-title">{{formPopupDetails?.title}}
       </span>
     </div>
     <div class="modal-body popup-content">
-
       <div class="header-left">
         <div>MODEL: <span>"{{formPopupDetails?.leftSubTitle}}"</span></div>
       </div>
         <model-information [modelInformationItems]="formPopupDetails?.modelInformationItems"></model-information>
       </div>
 
-      <div  class="instance-form">
+      <div class="instance-form">
         <div style="position: relative;height: 100%;overflow: auto;">
-          <label id="notification-area"  *ngIf="shouldShowNotification() == true" style="color: #959595;font-size: 12px;left: 30px;margin-left: 30px;">Data entered will apply to all service instances</label>
-          <generic-form  [formControls]="formPopupDetails?.formControlList"
-                         [dynamicInputs]="formPopupDetails?.dynamicInputsControlList"
-                         (onFormChanged)="dynamicForm = $event" ></generic-form>
+          <label id="notification-area" *ngIf="shouldShowNotification() == true"
+                 style="color: #959595;font-size: 12px;left: 30px;margin-left: 30px;">Data entered will apply to all
+            service instances</label>
+          <generic-form [formControls]="formPopupDetails?.formControlList"
+                        [dynamicInputs]="formPopupDetails?.dynamicInputsControlList"
+                        (onFormChanged)="dynamicForm = $event"></generic-form>
         </div>
       </div>
 
         </div>
       </div>
       <div class="col-md-6" style="padding: 15px;padding-right: 35px;">
+        <button
+          *ngIf="showTemplateBtn"
+          [attr.data-tests-id]="'templateButton'"
+          type="button" class="btn btn-success submit"
+          (click)="openTemplateModal()"
+        ><span>Template</span></button>
         <button
           [attr.data-tests-id]="'cancelButton'"
           type="button" class="btn btn-default cancel"
index d0e2d4b..cadce7d 100644 (file)
@@ -13,16 +13,17 @@ import {AaiService} from "../../services/aaiService/aai.service";
 import {GenericFormPopupService} from "./generic-form-popup.service";
 import {FormControlModel} from "../../models/formControlModels/formControl.model";
 import {FormGeneralErrorsService} from "../formGeneralErrors/formGeneralErrors.service";
+import {InstantiationTemplatesModalComponent} from "./instantiationTemplatesModal/instantiation.templates.modal.component";
 
 
 export interface PopupModel {
-  type : PopupType;
-  uuidData : UUIDData;
-  node : ITreeNode;
-  isUpdateMode : boolean;
+  type: PopupType;
+  uuidData: UUIDData;
+  node: ITreeNode;
+  isUpdateMode: boolean;
 }
 
-export enum PopupType{
+export enum PopupType {
   SERVICE = 'service',
   VNF = 'vnf',
   NETWORK = 'network',
@@ -33,36 +34,39 @@ export enum PopupType{
 
 
 @Component({
-  selector : 'generic-form-popup',
-  templateUrl : 'generic-form-popup.component.html',
-  styleUrls : ['generic-form-popup.component.scss']
+  selector: 'generic-form-popup',
+  templateUrl: 'generic-form-popup.component.html',
+  styleUrls: ['generic-form-popup.component.scss']
 })
 
-export class GenericFormPopupComponent extends DialogComponent<PopupModel, boolean> implements OnInit, OnDestroy{
-  formPopupDetails : FormPopupDetails = null;
-  dynamicForm : FormGroup;
-  type : PopupType;
-  uuidData : UUIDData;
-  isUpdateMode : boolean;
-  node : ITreeNode = null;
-  hasGeneralApiError : boolean = false;
+export class GenericFormPopupComponent extends DialogComponent<PopupModel, boolean> implements OnInit, OnDestroy {
+  formPopupDetails: FormPopupDetails = null;
+  dynamicForm: FormGroup;
+  type: PopupType;
+  uuidData: UUIDData;
+  showTemplateBtn: boolean = false;
+  isUpdateMode: boolean;
+  node: ITreeNode = null;
+  hasGeneralApiError: boolean = false;
   parentElementClassName = 'content';
   errorMsg = 'Page contains errors. Please see details next to the relevant fields.';
 
   servicesQty = 1;
   quantityOptions = _.range(1, 51)
-  constructor(dialogService:  DialogService ,
-              private _iframeService : IframeService,
+
+  constructor(dialogService: DialogService,
+              private _iframeService: IframeService,
               private _store: NgRedux<AppState>,
-              private _servicePopupService : ServicePopupService,
-              private _activatedRoute : ActivatedRoute,
-              private _aaiService : AaiService,
+              private _servicePopupService: ServicePopupService,
+              private _activatedRoute: ActivatedRoute,
+              private _aaiService: AaiService,
+              private _dialogService: DialogService,
               private _route: ActivatedRoute,
-              private _genericFormPopupService : GenericFormPopupService){
+              private _genericFormPopupService: GenericFormPopupService) {
     super(dialogService);
   }
 
-  closeDialog(that) : void{
+  closeDialog(that): void {
     this._iframeService.removeClassCloseModal(that.parentElementClassName);
     this.dialogService.removeDialog(this);
     setTimeout(() => {
@@ -70,7 +74,7 @@ export class GenericFormPopupComponent extends DialogComponent<PopupModel, boole
     }, 15);
   }
 
-  shouldShowNotification() : boolean {
+  shouldShowNotification(): boolean {
     return this.formPopupDetails && this.formPopupDetails.UUIDData['bulkSize'] > 1
   }
 
@@ -79,17 +83,18 @@ export class GenericFormPopupComponent extends DialogComponent<PopupModel, boole
       .queryParams
       .subscribe(params => {
         console.log('changed');
-        if(params['serviceModelId'] && params['isCreate']=="true"){
-          this._genericFormPopupService.initReduxOnCreateNewService().then((serviceModelId : string)=>{
+        if (params['serviceModelId'] && params['isCreate'] == "true") {
+          this._genericFormPopupService.initReduxOnCreateNewService().then((serviceModelId: string) => {
             this.uuidData = <any>{
-              bulkSize : 1,
-              isMacro : this._store.getState().service.serviceHierarchy[serviceModelId].service.vidNotions.instantiationType === 'Macro',
-              type : PopupType.SERVICE,
+              bulkSize: 1,
+              isMacro: this._store.getState().service.serviceHierarchy[serviceModelId].service.vidNotions.instantiationType === 'Macro',
+              type: PopupType.SERVICE,
               serviceId: serviceModelId,
               popupService: this._servicePopupService,
             };
+            this.showTemplateBtn = !!this._store.getState().global.flags["FLAG_2004_INSTANTIATION_TEMPLATES_POPUP"];
 
-            this.uuidData.popupService.closeDialogEvent.subscribe((that)=>{
+            this.uuidData.popupService.closeDialogEvent.subscribe((that) => {
               this.closeDialog(that);
             });
 
@@ -105,12 +110,12 @@ export class GenericFormPopupComponent extends DialogComponent<PopupModel, boole
         }
       });
 
-    FormGeneralErrorsService.checkForErrorTrigger.subscribe(()=>{
+    FormGeneralErrorsService.checkForErrorTrigger.subscribe(() => {
       this.hasSomeError(this.formPopupDetails, this.dynamicForm);
     });
-    
-    if(!_.isNil(this.uuidData)){
-      this.uuidData.popupService.closeDialogEvent.subscribe((that)=>{
+
+    if (!_.isNil(this.uuidData)) {
+      this.uuidData.popupService.closeDialogEvent.subscribe((that) => {
         this.closeDialog(that);
       });
 
@@ -119,28 +124,33 @@ export class GenericFormPopupComponent extends DialogComponent<PopupModel, boole
     }
   }
 
-  hasSomeError(formPopupDetails : FormPopupDetails, form : FormGroup) : boolean{
-    if(_.isNil(formPopupDetails)) return false;
+  hasSomeError(formPopupDetails: FormPopupDetails, form: FormGroup): boolean {
+    if (_.isNil(formPopupDetails)) return false;
     else {
-      for(let controlName in form.controls){
-        if(form.controls[controlName].errors){
+      for (let controlName in form.controls) {
+        if (form.controls[controlName].errors) {
           let error: string[] = Object.keys(form.controls[controlName].errors);
-          if(error.length === 1 && error[0] === 'required'){
+          if (error.length === 1 && error[0] === 'required') {
             continue;
-          }else if(Object.keys(form.controls[controlName].errors).length > 0  ){
+          } else if (Object.keys(form.controls[controlName].errors).length > 0) {
             return true;
           }
         }
       }
     }
 
-    return formPopupDetails.formControlList.filter((item : FormControlModel) => item.type === 'DROPDOWN' && item['hasEmptyOptions'] && item.isRequired()).length > 0
+    return formPopupDetails.formControlList.filter((item: FormControlModel) => item.type === 'DROPDOWN' && item['hasEmptyOptions'] && item.isRequired()).length > 0
+  }
+
+
+  openTemplateModal = (): void => {
+    this._dialogService.addDialog(InstantiationTemplatesModalComponent, {});
   }
 }
 
 
-export class UUIDData extends Object{
-  type : string;
-  popupService : any;
+export class UUIDData extends Object {
+  type: string;
+  popupService: any;
 }
 
diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.component.html b/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.component.html
new file mode 100644 (file)
index 0000000..c231c60
--- /dev/null
@@ -0,0 +1,161 @@
+<div id="template-popup" class="modal-dialog">
+  <div class="modal-content">
+    <div class="modal-header">
+      <button type="button"
+              class="close"
+              (click)="closeModal()">&times;
+      </button>
+      <span [attr.data-tests-id]="'template-modal-title'"
+            class="modal-title">Templates
+      </span>
+    </div>
+    <div class="modal-body templateModalBody">
+      <div class="row description-section">
+        <div class="col-md-6">
+          <div [attr.data-tests-id]="'description-part-1'">The following list presents previous instantiations done for
+            this model in this version.
+          </div>
+          <div [attr.data-tests-id]="'description-part-2'">You may use one of them as a baseline for your instantiation
+            or start from scratch.
+          </div>
+          <div [attr.data-tests-id]="'description-part-3'">Once you selecting one allows you to change the data before
+            start instantiating.
+          </div>
+        </div>
+        <div class="col-md-6">
+          <input
+            (keypress)="onFilterKeypress($event)"
+            class="filter-input form-control input-text"
+            placeholder="Filter...">
+        </div>
+
+      </div>
+      <div class="row" style="margin-left: 0;margin-right: 0;padding: 20px;">
+        <table id="member-table" class="table table-bordered" style="table-layout: fixed">
+          <thead class="thead-dark">
+          <tr>
+            <th class="header-title" id="header-userId">User ID</th>
+            <th class="header-title" id="header-createDate" style="width: 21ch;">Date</th>
+            <th class="header-title" id="header-instanceName" style="max-width: 50ch;">Instance Name</th>
+            <th class="header-title" id="header-instantiationStatus">Instantiation Status</th>
+            <th class="header-title" id="header-summary">Summary</th>
+            <th class="header-title" id="header-region">Region</th>
+            <th class="header-title" id="header-tenant">Tenant</th>
+            <th class="header-title" id="header-aicZone">AIC Zone</th>
+          </tr>
+          </thead>
+          <tbody>
+          <tr class="member-table-row"
+              *ngFor="let item of filterTableData;"
+              (click)="selectedJobId = item.jobId"
+              [ngClass]="{'selected' : selectedJobId === item.jobId}"
+              [attr.data-tests-id]="'row-' + item.jobId">
+            <td>
+              <div>
+                <custom-ellipsis
+                  [dataTestId]="'userId-' + item.jobId"
+                  [id]="item.userId"
+                  [value]="item.userId"
+                  [breakWord]="true">
+                </custom-ellipsis>
+              </div>
+            </td>
+            <td style="width: 21ch;">
+              <div>
+                <custom-ellipsis
+                  [dataTestId]="'createDate-' + item.jobId"
+                  [id]="item.createDate"
+                  [value]="item.createDate"
+                  [breakWord]="true">
+                </custom-ellipsis>
+              </div>
+            </td>
+            <td style="max-width: 50ch;">
+              <div>
+                <custom-ellipsis
+                  [showDots]="true"
+                  [dataTestId]="'instanceName-' + item.jobId"
+                  [id]="item.instanceName"
+                  [value]="item.instanceName"
+                  [breakWord]="true">
+                </custom-ellipsis>
+              </div>
+            </td>
+            <td>
+              <div>
+                <custom-ellipsis
+                  [showDots]="true"
+                  [dataTestId]="'instantiationStatus-' + item.jobId"
+                  [id]="item.instantiationStatus"
+                  [value]="item.instantiationStatus"
+                  [breakWord]="true">
+                </custom-ellipsis>
+              </div>
+            </td>
+            <td>
+              <div>
+                <custom-ellipsis
+                  [dataTestId]="'summary-' + item.jobId"
+                  [id]="item.summary"
+                  [value]="item.summary"
+                  [breakWord]="true">
+                </custom-ellipsis>
+              </div>
+            </td>
+            <td>
+              <div>
+                <custom-ellipsis
+                  [showDots]="true"
+                  [dataTestId]="'region-' + item.jobId"
+                  [id]="item.region"
+                  [value]="item.region"
+                  [breakWord]="true">
+                </custom-ellipsis>
+              </div>
+            </td>
+            <td>
+              <div>
+                <custom-ellipsis
+                  [showDots]="true"
+                  [dataTestId]="'tenant-' + item.jobId"
+                  [id]="item.tenant"
+                  [value]="item.tenant"
+                  [breakWord]="true">
+                </custom-ellipsis>
+              </div>
+            </td>
+            <td>
+              <div>
+                <custom-ellipsis
+                  [dataTestId]="'aicZone-' + item.jobId"
+                  [id]="item.aicZone"
+                  [value]="item.aicZone"
+                  [breakWord]="true">
+                </custom-ellipsis>
+              </div>
+            </td>
+          </tr>
+          </tbody>
+        </table>
+      </div>
+
+    </div>
+    <div class="modal-footer row" style="padding: 0">
+      <div class="col-md-6">
+      </div>
+      <div class="col-md-6" style="padding: 15px;padding-right: 35px;">
+        <button
+          [disabled]="selectedJobId === null"
+          [attr.data-tests-id]="'LoadTemplateButton'"
+          type="button" class="btn btn-primary submit"
+          (click)="loadTemplate()"><span>Load Template</span>
+        </button>
+        <button
+          [attr.data-tests-id]="'startFromScratchButton'"
+          type="button" class="btn btn-success submit startFromScratchButton"
+          (click)="closeModal()"><span>Start from Scratch</span>
+        </button>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.component.scss b/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.component.scss
new file mode 100644 (file)
index 0000000..23c950c
--- /dev/null
@@ -0,0 +1,192 @@
+$grid-border: 1px #d2d2d2 solid;
+
+#template-popup {
+  color: #191919;
+
+  thead {
+    background: #F8F8F8;
+  }
+  .left-panel {
+    background: #f2f2f2;
+    border-right: $grid-border;
+  }
+
+  .header-common {
+    height: 100%;
+    align-items: center;
+    display: flex;
+    font-family: OpenSans-Semibold;
+    font-size: 12px;
+  }
+
+  .header-text {
+    padding-left: 30px;
+    @extend .header-common;
+  }
+
+  .header-left {
+    grid-area: header-left;
+    @extend .header-text;
+    @extend .left-panel;
+    border-bottom: $grid-border;
+
+    span {
+      font-family: OpenSans-Regular;
+      font-size: 14px;
+    }
+  ;
+  }
+
+  .header-right {
+    grid-area: header-right;
+
+    @extend .header-text;
+  }
+
+  .quantity-label {
+    grid-area: quantity-label;
+    @extend .header-common;
+    height: 100%;
+    font-family: OpenSans-Regular;
+  }
+
+  input[type="number"]:hover::-webkit-inner-spin-button {
+    height: 20px;
+  }
+
+  .popup-content {
+    display: grid;
+    grid-template-columns: 400px auto 30px 93px;
+    grid-template-rows: 50px calc(100vh - 180px);
+    grid-template-areas: "header-left header-right quantity-label quantity" "model-information instance-form instance-form instance-form";
+    padding: 0;
+  }
+}
+
+.modal {
+  background-color: #191919;
+  opacity: 0.8;
+}
+
+.modal-dialog {
+  position: relative;
+  width: auto;
+  margin: 0;
+}
+
+@media (min-width: 1150px) {
+  .popup-content {
+    grid-template-rows: 30px 680px;
+  }
+}
+
+.modal-content {
+  border-radius: 0;
+  box-shadow: none;
+  border: none;
+  min-height: calc(100vh);
+
+}
+
+.modal-footer {
+  padding: 0;
+  position: absolute;
+  bottom: 0;
+  width: 100%;
+
+  .cancel {
+    width: 120px;
+    height: 36px;
+    background: #ffffff;
+    border: 1px solid #009fdb;
+    border-radius: 2px;
+
+    span {
+      font-family: OpenSans-Regular;
+      font-size: 14px;
+      color: #009fdb;
+      line-height: 16px;
+    }
+  }
+
+  .startFromScratchButton {
+    width: 150px !important;
+  }
+
+  .submit {
+    width: 120px;
+    height: 36px;
+    background: #009fdb;
+    border-radius: 2px;
+    border-color: #009fdb;
+
+
+    span {
+      font-family: OpenSans-Regular;
+      font-size: 14px;
+      color: #FFFFFF;
+      line-height: 16px;
+    }
+  }
+}
+
+.modal-header {
+  background-color: #009fdb;
+
+  padding-bottom: 13px;
+  padding-top: 13px;
+  padding-left: 29px;
+  padding-right: 21px;
+
+  .close {
+    font-size: 32px;
+    font-weight: 200;
+    color: #d8d8d8;
+    text-shadow: none;
+    filter: none;
+    opacity: 1;
+  }
+
+  .modal-title {
+    font-family: OpenSans-Regular;
+    font-size: 24px;
+    color: #fff;
+    line-height: 34px;
+  }
+}
+
+.modal-body {
+  padding: 0;
+  height: calc(85vh);
+
+  .description-section {
+    padding: 20px;
+    font-size: 20px;
+  }
+
+  .filter-input {
+    float: right;
+    width: 25%;
+  }
+
+
+  td.loadTemplateButton {
+    text-align: center;
+    vertical-align: middle;
+  }
+
+  td {
+    text-align: center;
+    vertical-align: middle;
+    padding-left: 5px;
+    padding-right: 5px;
+  }
+
+  .member-table-row:hover {
+    background: #80808033 !important;
+  }
+
+  .member-table-row.selected {
+    background: #009fdbb5 !important;
+  }
+}
diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.component.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.component.ts
new file mode 100644 (file)
index 0000000..56abe5b
--- /dev/null
@@ -0,0 +1,55 @@
+import {Component, OnDestroy, OnInit} from "@angular/core";
+import {DialogComponent, DialogService} from "ng2-bootstrap-modal";
+import {IframeService} from "../../../utils/iframe.service";
+import {ActivatedRoute} from "@angular/router";
+import {ServiceInfoService} from "../../../server/serviceInfo/serviceInfo.service";
+import {InstantiationTemplatesModalService} from "./instantiation.templates.modal.service";
+import {InstantiationTemplatesRowModel} from "./instantiation.templates.row.model";
+
+@Component({
+  selector: 'template-modal',
+  templateUrl: 'instantiation.templates.modal.component.html',
+  styleUrls: ['instantiation.templates.modal.component.scss']
+})
+
+export class InstantiationTemplatesModalComponent extends DialogComponent<string, boolean> implements OnInit, OnDestroy {
+
+  selectedJobId : string = null;
+  templateModalComponentService: InstantiationTemplatesModalService;
+  originalTableData: InstantiationTemplatesRowModel[] = [];
+  filterTableData : InstantiationTemplatesRowModel[] = [];
+
+  constructor(dialogService: DialogService,
+              private _iframeService: IframeService,
+              private _serviceInfoService: ServiceInfoService,
+              private _templateModalComponentService: InstantiationTemplatesModalService,
+              private _route: ActivatedRoute) {
+    super(dialogService);
+    this.templateModalComponentService = _templateModalComponentService;
+  }
+
+  ngOnInit(): void {
+    this._route
+      .queryParams
+      .subscribe(params => {
+        this._serviceInfoService.getServicesJobInfo(true, params['serviceModelId']).subscribe((jobs) => {
+          this.originalTableData = this._templateModalComponentService.convertResponseToUI(jobs);
+          this.filterTableData = this.originalTableData;
+        });
+      });
+  }
+
+  loadTemplate = () => {
+
+  };
+
+
+  onFilterKeypress = (event : KeyboardEvent) => {
+    //event.target.value
+      console.log(event.altKey);
+  };
+
+  closeModal(): void {
+    this.dialogService.removeDialog(this);
+  }
+}
diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.service.spec.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.service.spec.ts
new file mode 100644 (file)
index 0000000..f3bd915
--- /dev/null
@@ -0,0 +1,133 @@
+import {getTestBed, TestBed} from '@angular/core/testing';
+import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
+import {InstantiationTemplatesModalService} from "./instantiation.templates.modal.service";
+import {AaiService} from "../../../services/aaiService/aai.service";
+import {ActivatedRoute} from "@angular/router";
+import {IframeService} from "../../../utils/iframe.service";
+import {NgRedux} from "@angular-redux/store";
+import {FeatureFlagsService} from "../../../services/featureFlag/feature-flags.service";
+import {InstantiationTemplatesRowModel} from "./instantiation.templates.row.model";
+
+
+class ActivatedRouteMock<T> {
+  queryParams() {
+    return {
+      serviceModelId: '6e59c5de-f052-46fa-aa7e-2fca9d674c44'
+    }
+  }
+}
+
+class MockAppStore {
+
+}
+
+describe('instantiation templates modal service', () => {
+  let injector;
+  let service: InstantiationTemplatesModalService;
+  let httpMock: HttpTestingController;
+  let _aaiService: AaiService;
+  let _activatedRoute: ActivatedRoute;
+
+  beforeAll(done => (async () => {
+    TestBed.configureTestingModule({
+      imports: [HttpClientTestingModule],
+      providers: [InstantiationTemplatesModalService,
+        IframeService,
+        AaiService,
+        FeatureFlagsService,
+        {provide: ActivatedRoute, useClass: ActivatedRouteMock},
+        {provide: NgRedux, useClass: MockAppStore}
+      ]
+    });
+    await TestBed.compileComponents();
+
+    injector = getTestBed();
+    service = injector.get(InstantiationTemplatesModalService);
+    httpMock = injector.get(HttpTestingController);
+    _aaiService = injector.get(AaiService);
+    _activatedRoute = injector.get(ActivatedRoute);
+
+  })().then(done).catch(done.fail));
+
+
+  test('service should be defined', () => {
+    expect(service).toBeDefined();
+  });
+
+
+  test('convertResponseToUI - should return table data', () => {
+    const jobs = [{
+      "id": 5,
+      "created": 1524995555000,
+      "modified": 1524995556000,
+      "action": "INSTANTIATE",
+      "createdId": null,
+      "modifiedId": null,
+      "rowNum": null,
+      "auditUserId": null,
+      "auditTrail": null,
+      "jobId": "9f88fdb5-bb47-4bf3-8c5f-98f1ad0ec87c",
+      "templateId": "ce4ec177-cfc8-483e-8a2c-b7aea53fd740",
+      "userId": "16807000",
+      "msoRequestId": "c0011670-0e1a-4b74-945d-8bf5aede1d91",
+      "requestId": null,
+      "jobStatus": "FAILED",
+      "statusModifiedDate": 1524995555000,
+      "hidden": false,
+      "pause": false,
+      "owningEntityId": "aaa1",
+      "owningEntityName": "aaa1",
+      "project": "WATKINS",
+      "aicZoneId": "BAN1",
+      "aicZoneName": "VSDKYUTP-BAN1",
+      "tenantId": "1178612d2b394be4834ad77f567c0af2",
+      "tenantName": "AIN Web Tool-15-D-SSPtestcustome",
+      "regionId": "hvf6",
+      "regionName": null,
+      "serviceType": "TYLER SILVIA",
+      "subscriberName": "e433710f-9217-458d-a79d-1c7aff376d89",
+      "serviceInstanceId": null,
+      "serviceInstanceName": 'serviceInstanceName',
+      "serviceModelId": "e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0",
+      "serviceModelName": "ComplexService",
+      "serviceModelVersion": "1.0",
+      "createdBulkDate": 1524995555000,
+      "isRetryEnabled": false
+    }];
+    const tableRows: InstantiationTemplatesRowModel[] = service.convertResponseToUI(jobs);
+    expect(tableRows).toHaveLength(1);
+    expect(tableRows[0].userId).toEqual('16807000');
+    expect(tableRows[0].createDate).toEqual('2018-04-29 12:52:35');
+    expect(tableRows[0].instanceName).toEqual('serviceInstanceName');
+    expect(tableRows[0].instantiationStatus).toEqual('FAILED');
+    expect(tableRows[0].region).toEqual('hvf6 (AAA1)');
+    expect(tableRows[0].tenant).toEqual('AIN Web Tool-15-D-SSPtestcustome');
+    expect(tableRows[0].aicZone).toEqual('VSDKYUTP-BAN1');
+    expect(tableRows[0].jobId).toEqual('9f88fdb5-bb47-4bf3-8c5f-98f1ad0ec87c');
+  });
+
+
+  test('getCloudOwner should remove "-att" from owningEntityName : "att-owner', () => {
+    let result: InstantiationTemplatesRowModel = new InstantiationTemplatesRowModel({
+      owningEntityName: 'att-owner',
+      regionId: 'regionId'
+    });
+    expect(result.region).toEqual('regionId (OWNER)');
+  });
+
+  test('getCloudOwner should not return owningEntityName if not exist', () => {
+    let result: InstantiationTemplatesRowModel = new InstantiationTemplatesRowModel({owningEntityName: null, regionId: 'regionId'});
+    expect(result.region).toEqual('regionId');
+  });
+
+  test('getInstanceName should  return instance name id exist if not exist', () => {
+    let result: InstantiationTemplatesRowModel = new InstantiationTemplatesRowModel({serviceInstanceName: 'instanceName'});
+    expect(result.instanceName).toEqual('instanceName');
+  });
+
+  test('getInstanceName should return <Automatically generated> if instance name not exist', () => {
+    let result: InstantiationTemplatesRowModel = new InstantiationTemplatesRowModel({});
+    expect(result.instanceName).toEqual('<Automatically generated>');
+  });
+
+});
diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.service.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.service.ts
new file mode 100644 (file)
index 0000000..7126da3
--- /dev/null
@@ -0,0 +1,15 @@
+import {Injectable} from "@angular/core";
+import {InstantiationTemplatesRowModel} from "./instantiation.templates.row.model";
+
+@Injectable()
+export class InstantiationTemplatesModalService {
+  convertResponseToUI = (jobsResponse: any[]): InstantiationTemplatesRowModel[] => {
+    let tableRows: InstantiationTemplatesRowModel[] = [];
+
+    jobsResponse.forEach((job) => {
+      tableRows.push(new InstantiationTemplatesRowModel(job));
+    });
+
+    return tableRows;
+  };
+}
diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.row.model.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.row.model.ts
new file mode 100644 (file)
index 0000000..08982cc
--- /dev/null
@@ -0,0 +1,51 @@
+import * as moment from 'moment';
+import * as _ from 'lodash';
+
+export class InstantiationTemplatesRowModel {
+  readonly jobId: string;
+  readonly userId ?: string;
+  readonly createDate ?: string;
+  readonly instanceName ?: string;
+  readonly instantiationStatus?: string;
+  readonly summary?: string;
+  readonly region?: string;
+  readonly tenant?: string;
+  readonly aicZone?: string;
+
+  constructor(data) {
+    this.jobId = data.jobId;
+    this.userId = !_.isNil(data.created) ? data.userId : null;
+    this.createDate = !_.isNil(data.created) ? moment(data.created).format("YYYY-MM-DD HH:mm:ss") : null;
+    this.instanceName = this.getInstanceName(data.serviceInstanceName);
+    this.instantiationStatus = !_.isNil(data.jobStatus) ? data.jobStatus : null;
+    this.summary = null;
+    this.region = this.getRegion(data.regionId, data.owningEntityName);
+    this.tenant = !_.isNil(data.tenantName) ? data.tenantName : null;
+    this.aicZone = !_.isNil(data.aicZoneName) ? data.aicZoneName : null;
+
+  }
+
+
+  /**************************************************************************************************
+   return the LCP region and in brackets the cloud owner removing the “att-“ with capital letters.
+   **************************************************************************************************/
+  getCloudOwner = (owningEntityName: string): string => {
+    const splitByAtt: string[] = owningEntityName.split('att-');
+    let owning: string = splitByAtt[splitByAtt.length - 1];
+    return owning.toUpperCase();
+  };
+
+  getRegion = (regionId: string, owningEntityName: string): string => {
+    const convertOwning = !_.isNil(owningEntityName) ? `(${this.getCloudOwner(owningEntityName)})` : '';
+    return `${regionId} ${convertOwning}`.trim();
+  };
+
+
+  getInstanceName = (instanceName?: string): string => {
+    if (_.isNil(instanceName)) {
+      return '<Automatically generated>';
+    }
+    return instanceName;
+  }
+}
+
index cbf7324..2567cbf 100644 (file)
@@ -5,7 +5,7 @@ describe('Search filter pipe', () => {
 
   const items= [{'id':1, 'name': 'aaa'},
     {'id':12, 'name': 'bbb', 'children':{'first': 155, 'second': 2, 'third': 3}},
-    {'id':3, 'name': 'ccc', 'children':{'first': 1, 'BbB': '3', 'third': 3}},
+    {'id':3, 'name': 'ccc', 'children':{'first': 1, 'BbB': 'BbB', 'third': 3}},
     {'id':4, 'name': 'aad', 'children':{'first': 1, 'second': 2, 'third': 3}}];
 
   test('should return items contains substring bb', () => {
index 725eacb..6e5cfc6 100644 (file)
@@ -1,14 +1,43 @@
 import {Pipe, PipeTransform} from '@angular/core';
+import * as _ from 'lodash';
 
 @Pipe({
   name: 'searchFilter'
 })
 export class SearchFilterPipe implements PipeTransform {
   transform(items: Object[], searchText: string): any[] {
-    if(!items) return [];
-    if(!searchText) return items;
-    return items.filter( item => {
-      return JSON.stringify(item).toLowerCase().includes(searchText.toLowerCase());
+    if (!items) return [];
+    if (!searchText) return items;
+    return items.filter((item: object) => {
+
+      const deepFlatObject = this.flatten(item);
+
+      const values = _.values(deepFlatObject).map((item: string) => {
+        return item.toString().toLowerCase()
+      });
+
+      return _.some(values, _.method('includes', searchText.toLowerCase()));
     });
   }
+
+  flatten = object => {
+    return Object.assign(
+      {},
+      ...(function _flatten(objectBit, path = '') {
+        //spread the result into our return object
+        if(objectBit === null) return [];
+        return [].concat(
+          //concat everything into one level
+
+          ...Object.keys(objectBit).map(
+            //iterate over object
+            key =>
+              typeof objectBit[key] === 'object' //check if there is a nested object
+                ? _flatten(objectBit[key], `${path}/${key}`) //call itself if there is
+                : { [`${path}/${key}`]: objectBit[key] } //append object with it’s path as key
+          )
+        );
+      })(object)
+    );
+  };
 }
index a8b45c9..273dff4 100644 (file)
@@ -74,6 +74,8 @@ import {DynamicInputsComponent} from "./components/dynamic-inputs/dynamic-inputs
 import {DynamicInputLabelPipe} from "./pipes/dynamicInputLabel/dynamic-input-label.pipe";
 import {ModelInformationService} from "./components/model-information/model-information.service";
 import {MultiselectFormControlService} from "./components/formControls/component/multiselect/multiselect.formControl.service";
+import {InstantiationTemplatesModalComponent} from "./components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.component";
+import {InstantiationTemplatesModalService} from "./components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.service";
 import {SearchFilterPipe} from "./pipes/searchFilter/search-filter.pipe";
 import {RecreateResolver} from "./resolvers/recreate/recreate.resolver";
 import {InstantiationTemplatesService} from "./services/templateService/instantiationTemplates.service";
@@ -129,7 +131,8 @@ import {InstantiationTemplatesService} from "./services/templateService/instanti
     SvgComponent,
     ErrorMsgComponent,
     DynamicInputsComponent,
-    DynamicInputLabelPipe
+    DynamicInputLabelPipe,
+    InstantiationTemplatesModalComponent
   ],
   exports: [
     PopoverComponent,
@@ -165,7 +168,8 @@ import {InstantiationTemplatesService} from "./services/templateService/instanti
   ],
   entryComponents : [
     GenericFormPopupComponent,
-    SearchElementsModalComponent
+    SearchElementsModalComponent,
+    InstantiationTemplatesModalComponent
   ],
   providers: [
     ServiceInfoService,
@@ -205,7 +209,8 @@ import {InstantiationTemplatesService} from "./services/templateService/instanti
     DataFilterPipe,
     SearchFilterPipe,
     ModelInformationService,
-    MultiselectFormControlService
+    MultiselectFormControlService,
+    InstantiationTemplatesModalService
   ]
 })
 export class SharedModule {