Extend Modern UI for pnf usecase 73/114273/6
authorMateusz Gołuchowski <mateusz.goluchowski@nokia.com>
Thu, 5 Nov 2020 09:11:08 +0000 (10:11 +0100)
committerIkram Ikramullah <ikram@research.att.com>
Thu, 12 Nov 2020 14:21:25 +0000 (14:21 +0000)
Implemented functionalities to manage PNFs in modern UI:
   - Adding, removing, editing PNFs
   - PNF default generation based on 'min_instances' property
   - FE sends proper instantiation request to BE
This is still impossible to deploy service with PNFs as VID's BE logic must be adjusted
to generate proper request to SO as described in VID-695.

Issue-ID: VID-694
Signed-off-by: Mateusz Goluchowski <mateusz.goluchowski@nokia.com>
Change-Id: I5285ac2ab5e95665244ca29c6549249d9330b1ed

38 files changed:
.gitignore
vid-webpack-master/src/app/drawingBoard/service-planning/available-models-tree/available-models-tree.component.ts
vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-tree/drawing-board-tree.component.ts
vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/network/network.model.info.ts
vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/pnf/pnf.model.info.extended.spec.ts [new file with mode: 0644]
vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/pnf/pnf.model.info.extended.ts
vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/pnf/pnf.model.info.spec.ts
vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vfModule/vfModule.model.info.spec.ts
vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vfModule/vfModule.model.info.ts
vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vnf/vnf.model.info.ts
vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vnfGrouping/vnfGrouping.model.info.ts
vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vrf/vrfModal/vpnStep/vpn.step.service.ts
vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/objectToInstanceTree/objectToInstanceTree.service.spec.ts
vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/objectToInstanceTree/objectToInstanceTree.service.ts
vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/objectToModelTree/objectToModelTree.service.spec.ts
vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/objectToTree.service.ts
vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/shared.tree.service.spec.ts
vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/shared.tree.service.ts
vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/pnfGenerator/pnf.control.generator.spec.ts [new file with mode: 0644]
vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/pnfGenerator/pnf.control.generator.ts [new file with mode: 0644]
vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/pnf/pnf.popup.service.spec.ts [new file with mode: 0644]
vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/pnf/pnf.popup.service.ts
vid-webpack-master/src/app/shared/models/pnfInstance.ts
vid-webpack-master/src/app/shared/models/pnfModel.ts
vid-webpack-master/src/app/shared/models/serviceInstance.ts
vid-webpack-master/src/app/shared/services/aaiService/aai.service.spec.ts
vid-webpack-master/src/app/shared/services/aaiService/aai.service.ts
vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.spec.ts
vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.ts
vid-webpack-master/src/app/shared/shared.module.ts
vid-webpack-master/src/app/shared/storeUtil/utils/main.reducer.ts
vid-webpack-master/src/app/shared/storeUtil/utils/network/network.reducers.ts
vid-webpack-master/src/app/shared/storeUtil/utils/pnf/pnf.actions.ts [new file with mode: 0644]
vid-webpack-master/src/app/shared/storeUtil/utils/pnf/pnf.reducers.spec.ts [new file with mode: 0644]
vid-webpack-master/src/app/shared/storeUtil/utils/pnf/pnf.reducers.ts [new file with mode: 0644]
vid-webpack-master/src/app/shared/storeUtil/utils/reducersHelper.ts
vid-webpack-master/src/app/shared/storeUtil/utils/vnf/vnf.reducers.ts
vid-webpack-master/src/app/shared/storeUtil/utils/vrf/vrf.reducer.ts

index 348bcfd..63ec65c 100644 (file)
@@ -2,4 +2,8 @@ blackDuckHubProjectName.txt
 blackDuckHubProjectVersionName.txt
 vid-parent_bdio.jsonld
 *.iml
-.idea/
\ No newline at end of file
+.idea/
+target/
+vid-app-common/node/
+vid-app-common/package-lock.json
+vid-webpack-master/package-lock.json
\ No newline at end of file
index 8790afa..7e9b374 100644 (file)
@@ -129,7 +129,7 @@ export class AvailableModelsTreeComponent {
 
   selectNode(node: ITreeNode): void {
     node.expand();
-    this._sharedTreeService.setSelectedVNF(null);
+    this._sharedTreeService.setSelectedNF(null);
     this.highlightInstances.emit(node.data.modelUniqueId);
     if (FeatureFlagsService.getFlagState(Features.FLAG_1906_COMPONENT_INFO, this.store)) {
       const serviceHierarchy = this._store.getState().service.serviceHierarchy[this.serviceModelId];
@@ -179,8 +179,8 @@ export class AvailableModelsTreeComponent {
             positionOfNextInstance = this._defaultDataGeneratorService.calculatePositionOfVfmodule(this.serviceModelId);
           }
         }
-        if (this._sharedTreeService.selectedVNF) {
-          this.store.dispatch(createVFModuleInstance(vfModule, node.data.name, this.serviceModelId, positionOfNextInstance, this._sharedTreeService.selectedVNF));
+        if (this._sharedTreeService.selectedNF) {
+          this.store.dispatch(createVFModuleInstance(vfModule, node.data.name, this.serviceModelId, positionOfNextInstance, this._sharedTreeService.selectedNF));
           DrawingBoardTreeService.triggerCheckIsDirty.next(this.serviceModelId);
         } else if (this._availableModelsTreeService.getOptionalVNFs(this.serviceModelId, node.parent.data.modelUniqueId).length === 1) {
           let existVnf = this._store.getState().service.serviceInstance[this.serviceModelId].vnfs;
index 7526970..6998d6a 100644 (file)
@@ -231,7 +231,7 @@ export class DrawingBoardTreeComponent implements OnInit, AfterViewInit {
 
   public selectNode(node: ITreeNode): void {
     node.expand();
-    this._sharedTreeService.setSelectedVNF(node);
+    this._sharedTreeService.setSelectedNF(node);
     this.highlightNode.emit(node.data.modelUniqueId);
     if (FeatureFlagsService.getFlagState(Features.FLAG_1906_COMPONENT_INFO, this.store)) {
       const serviceHierarchy = this._store.getState().service.serviceHierarchy[this.serviceModelId];
index ce1af45..b01c796 100644 (file)
@@ -215,7 +215,7 @@ export class NetworkModelInfo implements ILevelNodeInfo {
           let storeKey: string = node.data.networkStoreKey;
           this._store.dispatch(removeInstance(node.data.networkStoreKey, serviceModelId, storeKey, node));
           this._store.dispatch(changeInstanceCounter(node.data.modelUniqueId, serviceModelId, -1, node));
-          this._sharedTreeService.selectedVNF = null;
+          this._sharedTreeService.selectedNF = null;
         },
         visible: (node) => node.data.parentType !== 'VRF' && this._sharedTreeService.shouldShowRemoveAndEdit(node),
         enable: (node) => node.data.parentType !== 'VRF' && this._sharedTreeService.shouldShowRemoveAndEdit(node),
diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/pnf/pnf.model.info.extended.spec.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/pnf/pnf.model.info.extended.spec.ts
new file mode 100644 (file)
index 0000000..7e7a6fc
--- /dev/null
@@ -0,0 +1,597 @@
+import {HttpClientTestingModule, HttpTestingController} from "@angular/common/http/testing";
+import {getTestBed, TestBed} from "@angular/core/testing";
+import {MockNgRedux, NgReduxTestingModule} from "@angular-redux/store/testing";
+import {PnfModelInfoExtended} from "./pnf.model.info.extended";
+import {DynamicInputsService} from "../../dynamicInputs.service";
+import {SharedTreeService} from "../../shared.tree.service";
+import {NgRedux} from "@angular-redux/store";
+import {AppState} from "../../../../../shared/store/reducers";
+import {DefaultDataGeneratorService} from "../../../../../shared/services/defaultDataServiceGenerator/default.data.generator.service";
+import {DialogService} from "ng2-bootstrap-modal";
+import {DuplicateService} from "../../../duplicate/duplicate.service";
+import {IframeService} from "../../../../../shared/utils/iframe.service";
+import {ComponentInfoService} from "../../../component-info/component-info.service";
+import {AaiService} from "../../../../../shared/services/aaiService/aai.service";
+import {HttpClient, HttpHandler} from "@angular/common/http";
+import {FeatureFlagsService} from "../../../../../shared/services/featureFlag/feature-flags.service";
+import {PnfPopupService} from "../../../../../shared/components/genericFormPopup/genericFormServices/pnf/pnf.popup.service";
+import {DrawingBoardModes} from "../../../drawing-board.modes";
+import {ServiceInstanceActions} from "../../../../../shared/models/serviceInstanceActions";
+import {PNFModel} from "../../../../../shared/models/pnfModel";
+import {AvailableNodeIcons} from "../../../available-models-tree/available-models-tree.service";
+
+class MockFeatureFlagsService extends FeatureFlagsService {
+  getAllFlags(): { [p: string]: boolean } {
+    return {};
+  }
+}
+
+class NodeBuilder {
+  static getPnfNode() {
+    return this.getPnfNodeWithAction(ServiceInstanceActions.None);
+  }
+
+  static getPnfNodeWithAction(action: ServiceInstanceActions) {
+    return {
+      data: {
+        "action": action,
+        "pnfStoreKey": "PNF_KEY",
+        "children": null,
+        "name": "pnfName",
+        "modelUniqueId": "modelCustomizationId",
+        "menuActions": {
+          "delete": "",
+          "undoDelete": ""
+        }
+      },
+      children: null,
+      type: 'PNF'
+    };
+  }
+}
+
+describe('Pnf Model Info Extended', () => {
+  let injector;
+  let _dynamicInputsService: DynamicInputsService;
+  let _sharedTreeService: SharedTreeService;
+  let _dialogService: DialogService;
+  let _pnfPopupService: PnfPopupService;
+  let _duplicateService: DuplicateService;
+  let _iframeService: IframeService;
+  let _featureFlagsService: FeatureFlagsService;
+  let _store: NgRedux<AppState>;
+  let pnfModelExtended: PnfModelInfoExtended;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      imports: [HttpClientTestingModule, NgReduxTestingModule],
+      providers: [
+        MockNgRedux,
+        DynamicInputsService,
+        DialogService,
+        PnfPopupService,
+        DefaultDataGeneratorService,
+        SharedTreeService,
+        DuplicateService,
+        AaiService,
+        HttpClient,
+        HttpHandler,
+        {provide: FeatureFlagsService, useClass: MockFeatureFlagsService},
+        ComponentInfoService,
+        IframeService]
+    }).compileComponents();
+
+    injector = getTestBed();
+    _sharedTreeService = injector.get(SharedTreeService);
+    _store = injector.get(NgRedux);
+    _featureFlagsService = injector.get(FeatureFlagsService);
+
+    pnfModelExtended = new PnfModelInfoExtended(
+      _store,
+      _sharedTreeService,
+      _dialogService,
+      _pnfPopupService,
+      _iframeService,
+      _duplicateService,
+      null,
+      _dynamicInputsService);
+  });
+
+  test('pnfModelExtended should be defined', () => {
+    expect(pnfModelExtended).toBeDefined();
+  });
+
+  test('getMenuAction: edit should not be visible when mode is null and actions is "None"', () => {
+    jest.spyOn(MockNgRedux.getInstance(), 'getState').mockReturnValue({
+      global: {}
+    });
+
+    let node = NodeBuilder.getPnfNodeWithAction(ServiceInstanceActions.None);
+    let serviceModelId = "d6557200-ecf2-4641-8094-5393ae3aae60";
+    let menuActions = pnfModelExtended.getMenuAction(<any>node, serviceModelId);
+
+    expect(menuActions['edit']).toBeDefined();
+    expect(menuActions['edit'].visible(node)).toBeFalsy();
+    expect(menuActions['edit'].enable(node)).toBeFalsy();
+  });
+
+  test('getMenuAction: edit should be visible when mode is null and action is "Create"', () => {
+    jest.spyOn(MockNgRedux.getInstance(), 'getState').mockReturnValue({
+      global: {}
+    });
+
+    let node = NodeBuilder.getPnfNodeWithAction(ServiceInstanceActions.Create);
+    let serviceModelId = "d6557200-ecf2-4641-8094-5393ae3aae60";
+    let menuActions = pnfModelExtended.getMenuAction(<any>node, serviceModelId);
+
+    expect(menuActions['edit']).toBeDefined();
+    expect(menuActions['edit'].visible(node)).toBeTruthy();
+    expect(menuActions['edit'].enable(node)).toBeTruthy();
+  });
+
+  test('getMenuAction: edit should be visible when mode is "RETRY" and action is "Create"', () => {
+    jest.spyOn(MockNgRedux.getInstance(), 'getState').mockReturnValue({
+      global: {
+        drawingBoardStatus: DrawingBoardModes.RETRY
+      }
+    });
+
+    let node = NodeBuilder.getPnfNodeWithAction(ServiceInstanceActions.Create);
+    let serviceModelId = "d6557200-ecf2-4641-8094-5393ae3aae60";
+    let menuActions = pnfModelExtended.getMenuAction(<any>node, serviceModelId);
+
+    expect(menuActions['edit']).toBeDefined();
+    expect(menuActions['edit'].visible(node)).toBeFalsy();
+    expect(menuActions['edit'].enable(node)).toBeFalsy();
+  });
+
+  test('getMenuAction: showAuditInfo should be visible when mode is "RETRY"', () => {
+    jest.spyOn(MockNgRedux.getInstance(), 'getState').mockReturnValue({
+      global: {
+        drawingBoardStatus: DrawingBoardModes.RETRY
+      }
+    });
+
+    let node = NodeBuilder.getPnfNodeWithAction(ServiceInstanceActions.Create);
+    let serviceModelId = "d6557200-ecf2-4641-8094-5393ae3aae60";
+    let menuActions = pnfModelExtended.getMenuAction(<any>node, serviceModelId);
+
+    expect(menuActions['showAuditInfo']).toBeDefined();
+    expect(menuActions['showAuditInfo'].visible(node)).toBeTruthy();
+    expect(menuActions['showAuditInfo'].enable(node)).toBeTruthy();
+  });
+
+  test('getMenuAction: showAuditInfo should not be visible when mode is not "RETRY" and action is "CREATE"', () => {
+    jest.spyOn(MockNgRedux.getInstance(), 'getState').mockReturnValue({
+      global: {
+        drawingBoardStatus: DrawingBoardModes.EDIT
+      }
+    });
+
+    let node = NodeBuilder.getPnfNodeWithAction(ServiceInstanceActions.Create);
+    let serviceModelId = "d6557200-ecf2-4641-8094-5393ae3aae60";
+    let menuActions = pnfModelExtended.getMenuAction(<any>node, serviceModelId);
+
+    expect(menuActions['showAuditInfo']).toBeDefined();
+    expect(menuActions['showAuditInfo'].visible(node)).toBeFalsy();
+    expect(menuActions['showAuditInfo'].enable(node)).toBeFalsy();
+  });
+
+  test('getMenuAction: remove should dispatch 2 actions with proper data', () => {
+    let node = NodeBuilder.getPnfNodeWithAction(ServiceInstanceActions.None);
+    let serviceModelId = "d6557200-ecf2-4641-8094-5393ae3aae60";
+    let menuActions = pnfModelExtended.getMenuAction(<any>node, serviceModelId);
+
+    spyOn(_store, 'dispatch');
+    menuActions['remove'].method(node, serviceModelId);
+
+    expect(_store.dispatch).toHaveBeenCalledWith(jasmine.objectContaining({
+      type: "REMOVE_INSTANCE",
+      storeKey: "PNF_KEY"
+    }));
+    expect(_store.dispatch).toHaveBeenCalledWith(jasmine.objectContaining({
+      type: "CHANGE_INSTANCE_COUNTER",
+      changeBy: -1
+    }));
+  });
+
+  test('getMenuAction: remove should not dispatch actions when node has children', () => {
+    let node = NodeBuilder.getPnfNodeWithAction(ServiceInstanceActions.None);
+    let serviceModelId = "d6557200-ecf2-4641-8094-5393ae3aae60";
+    let menuActions = pnfModelExtended.getMenuAction(<any>node, serviceModelId);
+    node.data.children = [
+      {"not": "empty"}
+    ];
+
+    spyOn(_store, 'dispatch');
+    menuActions['remove'].method(node, serviceModelId);
+
+    expect(_store.dispatch).not.toHaveBeenCalledWith(jasmine.objectContaining({
+      type: "REMOVE_INSTANCE",
+      storeKey: "PNF_KEY"
+    }));
+    expect(_store.dispatch).not.toHaveBeenCalledWith(jasmine.objectContaining({
+      type: "CHANGE_INSTANCE_COUNTER",
+      changeBy: -1
+    }));
+  });
+
+  test('getMenuAction: delete should dispatch delete action', () => {
+    let node = NodeBuilder.getPnfNodeWithAction(ServiceInstanceActions.None);
+    let serviceModelId = "d6557200-ecf2-4641-8094-5393ae3aae60";
+    let menuActions = pnfModelExtended.getMenuAction(<any>node, serviceModelId);
+
+    spyOn(_store, 'dispatch');
+    menuActions['delete'].method(node, serviceModelId);
+
+    expect(_store.dispatch).toHaveBeenCalledWith(jasmine.objectContaining({
+      type: "DELETE_PNF_INSTANCE",
+      storeKey: "PNF_KEY"
+    }));
+  });
+
+  test('getMenuAction: delete should show modal when node has children', () => {
+    let node = NodeBuilder.getPnfNodeWithAction(ServiceInstanceActions.None);
+    let serviceModelId = "d6557200-ecf2-4641-8094-5393ae3aae60";
+    let menuActions = pnfModelExtended.getMenuAction(<any>node, serviceModelId);
+    node.data.children = [
+      {"not": "empty"}
+    ];
+
+    spyOn(_sharedTreeService, 'shouldShowDeleteInstanceWithChildrenModal');
+    menuActions['delete'].method(node, serviceModelId);
+
+    expect(_sharedTreeService.shouldShowDeleteInstanceWithChildrenModal).toHaveBeenCalledWith(jasmine.objectContaining({
+      type: "PNF"
+    }), jasmine.anything(), jasmine.anything());
+  });
+
+  test('getMenuAction: delete should not be visible when service isMacro', () => {
+    jest.spyOn(MockNgRedux.getInstance(), 'getState').mockReturnValue({
+      global: {
+        drawingBoardStatus: DrawingBoardModes.EDIT
+      },
+      service: {
+        serviceInstance: {
+          "d6557200-ecf2-4641-8094-5393ae3aae60": {
+            isALaCarte: false
+          }
+        }
+      }
+    });
+
+    let node = NodeBuilder.getPnfNodeWithAction(ServiceInstanceActions.None);
+    let serviceModelId = "d6557200-ecf2-4641-8094-5393ae3aae60";
+    let menuActions = pnfModelExtended.getMenuAction(<any>node, serviceModelId);
+
+    expect(menuActions['delete']).toBeDefined();
+    expect(menuActions['delete'].visible(node)).toBeFalsy();
+    expect(menuActions['delete'].enable(node)).toBeFalsy();
+  });
+
+  test('getMenuAction: delete should not be visible when service is aLaCarte and Action is Create', () => {
+    jest.spyOn(MockNgRedux.getInstance(), 'getState').mockReturnValue({
+      global: {
+        drawingBoardStatus: DrawingBoardModes.EDIT
+      },
+      service: {
+        serviceInstance: {
+          "d6557200-ecf2-4641-8094-5393ae3aae60": {
+            isALaCarte: true
+          }
+        }
+      }
+    });
+
+    let node = NodeBuilder.getPnfNodeWithAction(ServiceInstanceActions.Create);
+    let serviceModelId = "d6557200-ecf2-4641-8094-5393ae3aae60";
+    let menuActions = pnfModelExtended.getMenuAction(<any>node, serviceModelId);
+
+    expect(menuActions['delete']).toBeDefined();
+    expect(menuActions['delete'].visible(node)).toBeFalsy();
+    expect(menuActions['delete'].enable(node)).toBeFalsy();
+  });
+
+  test('getMenuAction: delete should be visible when service is aLaCarte and Action is None', () => {
+    jest.spyOn(MockNgRedux.getInstance(), 'getState').mockReturnValue({
+      global: {
+        drawingBoardStatus: DrawingBoardModes.EDIT
+      },
+      service: {
+        serviceInstance: {
+          "d6557200-ecf2-4641-8094-5393ae3aae60": {
+            isALaCarte: true
+          }
+        }
+      }
+    });
+
+    let node = NodeBuilder.getPnfNodeWithAction(ServiceInstanceActions.None);
+    let serviceModelId = "d6557200-ecf2-4641-8094-5393ae3aae60";
+    let menuActions = pnfModelExtended.getMenuAction(<any>node, serviceModelId);
+
+    expect(menuActions['delete']).toBeDefined();
+    expect(menuActions['delete'].visible(node)).toBeTruthy();
+    expect(menuActions['delete'].enable(node)).toBeTruthy();
+  });
+
+  test('getMenuAction: undo delete should dispatch undo delete action when no children', () => {
+    let node = NodeBuilder.getPnfNodeWithAction(ServiceInstanceActions.None);
+    let serviceModelId = "d6557200-ecf2-4641-8094-5393ae3aae60";
+    let menuActions = pnfModelExtended.getMenuAction(<any>node, serviceModelId);
+
+    spyOn(_store, 'dispatch');
+    menuActions['undoDelete'].method(node, serviceModelId);
+
+    expect(_store.dispatch).toHaveBeenCalledWith(jasmine.objectContaining({
+      type: "UNDO_DELETE_PNF_INSTANCE",
+      storeKey: "PNF_KEY"
+    }));
+  });
+
+  test('getMenuAction: undo delete should iterate over children when they exist', () => {
+    let node = NodeBuilder.getPnfNodeWithAction(ServiceInstanceActions.None);
+    node.data.children = [
+      {"not": "empty"}
+    ];
+    node.children = node.data.children;
+    let serviceModelId = "d6557200-ecf2-4641-8094-5393ae3aae60";
+    let menuActions = pnfModelExtended.getMenuAction(<any>node, serviceModelId);
+
+    spyOn(_store, 'dispatch');
+    menuActions['undoDelete'].method(node, serviceModelId);
+
+    expect(_store.dispatch).toHaveBeenCalledWith(jasmine.objectContaining({
+      type: "UNDO_DELETE_PNF_INSTANCE",
+      storeKey: "PNF_KEY"
+    }));
+  });
+
+  test('getMenuAction: undo delete should not be visible when action is Create or Delete', () => {
+    jest.spyOn(MockNgRedux.getInstance(), 'getState').mockReturnValue({
+      global: {
+        drawingBoardStatus: DrawingBoardModes.EDIT
+      },
+      service: {
+        serviceInstance: {
+          "d6557200-ecf2-4641-8094-5393ae3aae60": {}
+        }
+      }
+    });
+
+    let node = NodeBuilder.getPnfNodeWithAction(ServiceInstanceActions.Create);
+    let serviceModelId = "d6557200-ecf2-4641-8094-5393ae3aae60";
+    let menuActions = pnfModelExtended.getMenuAction(<any>node, serviceModelId);
+
+    expect(menuActions['undoDelete']).toBeDefined();
+    expect(menuActions['undoDelete'].visible(node)).toBeFalsy();
+    expect(menuActions['undoDelete'].enable(node)).toBeFalsy();
+  });
+
+  test('getMenuAction: undo delete should be visible when action is contains "*_Delete"', () => {
+    jest.spyOn(MockNgRedux.getInstance(), 'getState').mockReturnValue({
+      global: {
+        drawingBoardStatus: DrawingBoardModes.EDIT
+      },
+      service: {
+        serviceInstance: {
+          "d6557200-ecf2-4641-8094-5393ae3aae60": {
+            action: ServiceInstanceActions.None
+          }
+        }
+      }
+    });
+
+    let node = NodeBuilder.getPnfNodeWithAction(ServiceInstanceActions.None_Delete);
+    let serviceModelId = "d6557200-ecf2-4641-8094-5393ae3aae60";
+    let menuActions = pnfModelExtended.getMenuAction(<any>node, serviceModelId);
+
+    expect(menuActions['undoDelete']).toBeDefined();
+    expect(menuActions['undoDelete'].visible(node)).toBeTruthy();
+    expect(menuActions['undoDelete'].enable(node, serviceModelId)).toBeTruthy();
+  });
+
+  test('getModel should return PNF model', () => {
+    expect(pnfModelExtended.getModel({})).toBeInstanceOf(PNFModel);
+  });
+
+  test('getNextLevelObject should return null as there are no childs expected in PNF for now', () => {
+    expect(pnfModelExtended.getNextLevelObject()).toBeNull();
+  });
+
+  test('getNodeCount should return counter of 0 when existingPNFCounterMap doesnt exist', () => {
+    jest.spyOn(MockNgRedux.getInstance(), 'getState').mockReturnValue({
+      global: {},
+      service: {
+        serviceInstance: {
+          'd6557200-ecf2-4641-8094-5393ae3aae60': {}
+        }
+      }
+    });
+
+    let serviceId: string = 'd6557200-ecf2-4641-8094-5393ae3aae60';
+    let node = NodeBuilder.getPnfNode();
+    let result = pnfModelExtended.getNodeCount(<any>node, serviceId);
+    jest.spyOn(_sharedTreeService, 'getExistingInstancesWithDeleteMode').mockReturnValue(0);
+
+    expect(result).toEqual(0);
+  });
+
+  test('getNodeCount should return counter of 1 when one node exists and no nodes in delete mode', () => {
+    jest.spyOn(MockNgRedux.getInstance(), 'getState').mockReturnValue({
+      global: {},
+      service: {
+        serviceInstance: {
+          'd6557200-ecf2-4641-8094-5393ae3aae60': {
+            'existingPNFCounterMap': {
+              'modelCustomizationId': 1
+            }
+          }
+        }
+      }
+    });
+
+    let serviceId: string = 'd6557200-ecf2-4641-8094-5393ae3aae60';
+    let node = NodeBuilder.getPnfNode();
+    let result = pnfModelExtended.getNodeCount(<any>node, serviceId);
+    jest.spyOn(_sharedTreeService, 'getExistingInstancesWithDeleteMode').mockReturnValue(0);
+
+    expect(result).toEqual(1);
+  });
+
+  test('getNodeCount should return counter of 2 when three nodes exist and one node is in delete mode', () => {
+    jest.spyOn(MockNgRedux.getInstance(), 'getState').mockReturnValue({
+      global: {},
+      service: {
+        serviceInstance: {
+          'd6557200-ecf2-4641-8094-5393ae3aae60': {
+            'existingPNFCounterMap': {
+              'modelCustomizationId': 3
+            }
+          }
+        }
+      }
+    });
+
+    jest.spyOn(_sharedTreeService, 'getExistingInstancesWithDeleteMode').mockReturnValue(1);
+    let serviceId: string = 'd6557200-ecf2-4641-8094-5393ae3aae60';
+    let node = NodeBuilder.getPnfNode();
+    let result = pnfModelExtended.getNodeCount(<any>node, serviceId);
+
+    expect(result).toEqual(2);
+  });
+
+  test('getTooltip should return "PNF"', () => {
+    expect(pnfModelExtended.getTooltip()).toEqual('PNF');
+  });
+
+  test('getType should return "PNF"', () => {
+    expect(pnfModelExtended.getType()).toEqual('PNF');
+  });
+
+  test('isEcompGeneratedNaming should return true if isEcompGeneratedNaming is "true" ', () => {
+    let isEcompGeneratedNaming: boolean = pnfModelExtended.isEcompGeneratedNaming(<any>{
+      properties: {
+        ecomp_generated_naming: 'true'
+      }
+    });
+    expect(isEcompGeneratedNaming).toBeTruthy();
+  });
+
+  test('isEcompGeneratedNaming should return false if isEcompGeneratedNaming is not defined', () => {
+    let isEcompGeneratedNaming: boolean = pnfModelExtended.isEcompGeneratedNaming({
+      properties: {}
+    });
+    expect(isEcompGeneratedNaming).toBeFalsy();
+  });
+
+  test('showNodeIcons should return false if reached limit of instances', () => {
+    let serviceId: string = 'servicedId';
+    let node = NodeBuilder.getPnfNode();
+    jest.spyOn(MockNgRedux.getInstance(), 'getState').mockReturnValue({
+      global: {},
+      service: {
+        serviceHierarchy: {
+          'servicedId': {
+            'pnfs': {
+              modelCustomizationId: "modelCustomizationId",
+              'modelInfo': {
+                modelCustomizationId: "modelCustomizationId"
+              },
+              'pnfName': {
+                'properties': {
+                  'max_instances': 1
+                }
+              }
+            }
+          }
+        },
+        serviceInstance: {
+          'servicedId': {
+            'existingPNFCounterMap': {
+              'modelCustomizationId': 1
+            },
+            'pnfs': {
+              'pnfName': {}
+            }
+          }
+        }
+      }
+    });
+
+    let result = pnfModelExtended.showNodeIcons(<any>node, serviceId);
+    expect(result).toEqual(new AvailableNodeIcons(false, true));
+  });
+
+  test('showNodeIcons should return true if limit of instances is not reached', () => {
+    let serviceId: string = 'servicedId';
+    let node = NodeBuilder.getPnfNode();
+    jest.spyOn(MockNgRedux.getInstance(), 'getState').mockReturnValue({
+      global: {
+        drawingBoardStatus: DrawingBoardModes.EDIT
+      },
+      service: {
+        serviceHierarchy: {
+          'servicedId': {
+            'pnfs': {
+              modelCustomizationId: "modelCustomizationId",
+              'modelInfo': {
+                modelCustomizationId: "modelCustomizationId"
+              },
+              'pnfName': {
+                'properties': {
+                  'max_instances': 2
+                }
+              }
+            }
+          }
+        },
+        serviceInstance: {
+          'servicedId': {
+            'existingPNFCounterMap': {
+              'modelCustomizationId': 1
+            },
+            'pnfs': {
+              'pnfName': {}
+            }
+          }
+        }
+      }
+    });
+
+    let result = pnfModelExtended.showNodeIcons(<any>node, serviceId);
+    expect(result).toEqual(new AvailableNodeIcons(true, false));
+  });
+
+  test('hasMissingData returns true instanceName is empty and ecompNameGenerating is off ', () => {
+    let instance = {
+      instanceName: "",
+      platformName: "platformName"
+    }
+
+    let result = pnfModelExtended.hasMissingData(instance, [], false);
+
+    expect(result).toBeTruthy();
+  });
+
+  test('hasMissingData returns true when at least one required field is empty ', () => {
+    let instance = {
+      platformName: ""
+    }
+
+    let result = pnfModelExtended.hasMissingData(instance, [], true);
+
+    expect(result).toBeTruthy();
+  });
+
+  test('hasMissingData returns false when there is no missing data', () => {
+    let instance = {
+      platformName: "platformName"
+    }
+
+    let result = pnfModelExtended.hasMissingData(instance, [], true);
+
+    expect(result).toBeFalsy();
+  });
+
+});
index e2e31e9..3c3f09b 100644 (file)
@@ -3,7 +3,6 @@ import {ComponentInfoType} from "../../../component-info/component-info-model";
 import {ModelInformationItem} from "../../../../../shared/components/model-information/model-information.component";
 import {ITreeNode} from "angular-tree-component/dist/defs/api";
 import {AvailableNodeIcons} from "../../../available-models-tree/available-models-tree.service";
-
 import {PNFModel} from "../../../../../shared/models/pnfModel";
 import {SharedTreeService} from "../../shared.tree.service";
 import * as _ from "lodash";
@@ -17,19 +16,35 @@ import {
 import {DialogService} from "ng2-bootstrap-modal";
 import {PnfPopupService} from "../../../../../shared/components/genericFormPopup/genericFormServices/pnf/pnf.popup.service";
 import {PnfTreeNode} from "../../../../../shared/models/pnfTreeNode";
+import {changeInstanceCounter, removeInstance} from "../../../../../shared/storeUtil/utils/general/general.actions";
+import {MessageBoxData} from "../../../../../shared/components/messageBox/messageBox.data";
+import {MessageBoxService} from "../../../../../shared/components/messageBox/messageBox.service";
+import {IframeService} from "../../../../../shared/utils/iframe.service";
+import {DuplicateService} from "../../../duplicate/duplicate.service";
+import {ModalService} from "../../../../../shared/components/customModal/services/modal.service";
+import {
+  deleteActionPnfInstance,
+  undoDeleteActionPnfInstance,
+  updatePnfPosition
+} from "../../../../../shared/storeUtil/utils/pnf/pnf.actions";
+import {DynamicInputsService} from "../../dynamicInputs.service";
+import {InputType} from "../../../../../shared/models/inputTypes";
 
-
-export class PnfModelInfoExtended implements ILevelNodeInfo{
+export class PnfModelInfoExtended implements ILevelNodeInfo {
 
   constructor(
     private _store: NgRedux<AppState>,
     private _sharedTreeService: SharedTreeService,
     private _dialogService: DialogService,
-    private _pnfPopupService: PnfPopupService
-  ){}
+    private _pnfPopupService: PnfPopupService,
+    private _iframeService: IframeService,
+    private _duplicateService: DuplicateService,
+    private modalService: ModalService,
+    private _dynamicInputsService: DynamicInputsService
+  ) {}
 
   name: string = 'pnfs';
-  type: string ='PNF';
+  type: string = 'PNF';
   typeName: string = 'PNF';
   childNames: string[];
   componentInfoType = ComponentInfoType.PNF;
@@ -60,15 +75,109 @@ export class PnfModelInfoExtended implements ILevelNodeInfo{
     return _.uniq(_.flatten(result));
   }
 
-  getMenuAction(node: ITreeNode, serviceModelId: string): { [methodName: string]: { method: Function; visible: Function; enable: Function } } {
-    return {};
+  getMenuAction(node: ITreeNode, serviceModelId: string): { [methodName: string]: { method: Function, visible: Function, enable: Function } } {
+    return <any>{
+      edit: {
+        method: (node, serviceModelId) => {
+          this._iframeService.addClassOpenModal('content');
+          this._dialogService.addDialog(GenericFormPopupComponent, {
+            type: PopupType.PNF,
+            uuidData: <any>{
+              serviceId: serviceModelId,
+              modelName: node.data.modelName,
+              pnfStoreKey: node.data.pnfStoreKey,
+              modelId: node.data.modelId,
+              type: node.data.type,
+              popupService: this._pnfPopupService
+            },
+            node: node,
+            isUpdateMode: true
+          });
+        },
+        visible: (node) => this._sharedTreeService.shouldShowRemoveAndEdit(node),
+        enable: (node) => this._sharedTreeService.shouldShowRemoveAndEdit(node),
+      },
+      showAuditInfo: {
+        method: (node, serviceModelId) => {
+          const instance = this._store.getState().service.serviceInstance[serviceModelId].pnfs[node.data.pnfStoreKey];
+          this._sharedTreeService.openAuditInfoModal(node, serviceModelId, instance, 'PNF', this);
+        },
+        visible: (node) => this._sharedTreeService.shouldShowAuditInfo(node),
+        enable: (node) => this._sharedTreeService.shouldShowAuditInfo(node)
+      },
+      remove: {
+        method: (node, serviceModelId) => {
+          if ((!_.isNil(node.data.children) && node.data.children.length === 0) || _.isNil(node.data.children)) {
+            let storeKey: string = node.data.pnfStoreKey;
+            this._store.dispatch(removeInstance(node.data.pnfStoreKey, serviceModelId, storeKey, node));
+            this._store.dispatch(changeInstanceCounter(node.data.modelUniqueId, serviceModelId, -1, node));
+            this._sharedTreeService.selectedNF = null;
+          } else {
+            let messageBoxData: MessageBoxData = new MessageBoxData(
+              "Remove PNF",  // modal title
+              "You are about to remove this PNF from this service. Are you sure you want to remove it?",
+              <any>"warning",
+              <any>"md",
+              [
+                {
+                  text: "Remove PNF",
+                  size: "large",
+                  callback: this.removePnf.bind(this, node, serviceModelId),
+                  closeModal: true
+                },
+                {text: "Don’t Remove", size: "medium", closeModal: true}
+              ]);
+
+            MessageBoxService.openModal.next(messageBoxData);
+          }
+        },
+        visible: (node) => this._sharedTreeService.shouldShowRemoveAndEdit(node),
+        enable: (node) => this._sharedTreeService.shouldShowRemoveAndEdit(node),
+      },
+      delete: {
+        method: (node, serviceModelId) => {
+          if ((!_.isNil(node.data.children) && node.data.children.length === 0) || _.isNil(node.data.children)) {
+            this._store.dispatch(deleteActionPnfInstance(node.data.pnfStoreKey, serviceModelId));
+          } else {
+            this._sharedTreeService.shouldShowDeleteInstanceWithChildrenModal(node, serviceModelId, (node, serviceModelId) => {
+              this._sharedTreeService.removeDeleteAllChild(node, serviceModelId, (node, serviceModelId) => {
+                this._store.dispatch(deleteActionPnfInstance(node.data.pnfStoreKey, serviceModelId));
+              });
+            });
+          }
+        },
+        visible: (node) => this._sharedTreeService.shouldShowDelete(node, serviceModelId),
+        enable: (node) => this._sharedTreeService.shouldShowDelete(node, serviceModelId)
+      },
+      undoDelete: {
+        method: (node, serviceModelId) => {
+          if ((!_.isNil(node.data.children) && node.data.children.length === 0) || _.isNil(node.data.children)) {
+            this._store.dispatch(undoDeleteActionPnfInstance(node.data.pnfStoreKey, serviceModelId));
+          } else {
+            this._sharedTreeService.undoDeleteAllChild(node, serviceModelId, (node, serviceModelId) => {
+              this._store.dispatch(undoDeleteActionPnfInstance(node.data.pnfStoreKey, serviceModelId));
+            });
+          }
+        },
+        visible: (node) => this._sharedTreeService.shouldShowUndoDelete(node),
+        enable: (node, serviceModelId) => this._sharedTreeService.shouldShowUndoDelete(node) && !this._sharedTreeService.isServiceOnDeleteMode(serviceModelId)
+      }
+    }
+  }
+
+  removePnf(this, node, serviceModelId) {
+    this._store.dispatch(removeInstance(node.data.modelName, serviceModelId, node.data.pnfStoreKey, node));
+    this._store.dispatch(changeInstanceCounter(node.data.modelUniqueId, serviceModelId, -1, node));
+    this._sharedTreeService.selectedNF = null;
   }
 
   getModel = (instanceModel: any): PNFModel => {
     return new PNFModel(instanceModel);
   };
 
-  getNextLevelObject(): any { return null;  }
+  getNextLevelObject(): any {
+    return null;
+  }
 
   getNodeCount(node: ITreeNode, serviceModelId: string): number {
     let map = null;
@@ -90,17 +199,20 @@ export class PnfModelInfoExtended implements ILevelNodeInfo{
 
   getTooltip = (): string => 'PNF';
 
-
   getType = (): string => 'PNF';
 
-
   hasMissingData(instance, dynamicInputs: any, isEcompGeneratedNaming: boolean): boolean {
-    return false;
+    return this._sharedTreeService.hasMissingData(instance, dynamicInputs, isEcompGeneratedNaming, [InputType.PLATFORM]);
   }
 
-  isEcompGeneratedNaming(currentModel): boolean {
-    return false;
-  }
+  /***********************************************************
+   * return if user should provide instance name or not.
+   * @param currentModel - current Model object
+   ************************************************************/
+  isEcompGeneratedNaming = (currentModel): boolean => {
+    const ecompGeneratedNaming = currentModel.properties.ecomp_generated_naming;
+    return ecompGeneratedNaming === "true";
+  };
 
   onClickAdd(node, serviceModelId: string): void {
     this._dialogService.addDialog(GenericFormPopupComponent, {
@@ -130,9 +242,12 @@ export class PnfModelInfoExtended implements ILevelNodeInfo{
     return new AvailableNodeIcons(showAddIcon, isReachedLimit)
   }
 
-  updateDynamicInputsDataFromModel = (currentModel): any => [];
+  updateDynamicInputsDataFromModel = (currentModel): any => {
+    let displayInputs = _.isNil(currentModel) ? [] : currentModel.inputs;
+    return _.isEmpty(displayInputs) ? [] : this._dynamicInputsService.getArbitraryInputs(displayInputs);
+  };
 
   updatePosition(that, node, instanceId): void {
+    that.store.dispatch(updatePnfPosition(node, instanceId, node.vnfStoreKey));
   }
-
 }
index 8675d36..150069d 100644 (file)
@@ -12,9 +12,7 @@ import {IframeService} from "../../../../../shared/utils/iframe.service";
 import {HttpClientTestingModule} from "@angular/common/http/testing";
 import {MockNgRedux, NgReduxTestingModule} from "@angular-redux/store/testing";
 import {HttpClient, HttpHandler} from "@angular/common/http";
-import {CollectionResourceModel} from "../../../../../shared/models/collectionResourceModel";
 import {ComponentInfoType} from "../../../component-info/component-info-model";
-import {VNFModel} from "../../../../../shared/models/vnfModel";
 import {PNFModel} from "../../../../../shared/models/pnfModel";
 
 
index 42ee238..dd6bb50 100644 (file)
@@ -197,7 +197,7 @@ describe('VFModule Model Info', () => {
         name : 'vnfName'
       }
     };
-    _sharedTreeService.setSelectedVNF({
+    _sharedTreeService.setSelectedNF({
       data : {
         id : 'vfModuleId',
         name : 'VfModuleName'
@@ -250,7 +250,7 @@ describe('VFModule Model Info', () => {
         }
       }
     };
-    jest.spyOn(_sharedTreeService, 'getSelectedVNF').mockReturnValue('vnfName');
+    jest.spyOn(_sharedTreeService, 'getSelectedNF').mockReturnValue('vnfName');
     jest.spyOn(_sharedTreeService, 'modelUniqueId').mockReturnValue('vnfCustomizationId');
     jest.spyOn(vfModuleModel, 'getOptionalVNFs').mockReturnValue([{vnfStoreKey: 'vnfName'}]);
     jest.spyOn(MockNgRedux.getInstance(), 'getState').mockReturnValue({
@@ -535,7 +535,7 @@ describe('VFModule Model Info', () => {
         }
       }
     });
-    jest.spyOn(_sharedTreeService, 'getSelectedVNF').mockReturnValue('vnfName_1');
+    jest.spyOn(_sharedTreeService, 'getSelectedNF').mockReturnValue('vnfName_1');
 
     let node = {
       data : {
@@ -557,7 +557,7 @@ describe('VFModule Model Info', () => {
 
 
   test('onClickAdd should open message box if no vnfStoreKey', ()=>{
-    jest.spyOn(_sharedTreeService, 'getSelectedVNF').mockReturnValue(null);
+    jest.spyOn(_sharedTreeService, 'getSelectedNF').mockReturnValue(null);
     jest.spyOn(vfModuleModel, 'getDefaultVNF').mockReturnValue(null);
     jest.spyOn(MessageBoxService.openModal, 'next');
     vfModuleModel.onClickAdd(<any>{}, 'serviceId');
index 72b3c39..a6d4e98 100644 (file)
@@ -146,7 +146,7 @@ export class VFModuleModelInfo implements ILevelNodeInfo {
    * @param serviceModelId - current service id
    ************************************************************/
   onClickAdd(node: ITreeNode, serviceModelId: string): void {
-    const vnfStoreKey = this._sharedTreeService.getSelectedVNF() || this.getDefaultVNF(node.parent, serviceModelId);
+    const vnfStoreKey = this._sharedTreeService.getSelectedNF() || this.getDefaultVNF(node.parent, serviceModelId);
     if (vnfStoreKey) {
       this._dialogService.addDialog(GenericFormPopupComponent, {
         type: PopupType.VF_MODULE,
@@ -191,7 +191,7 @@ export class VFModuleModelInfo implements ILevelNodeInfo {
     const vnfs = this._store.getState().service.serviceInstance[serviceModelId].vnfs;
     let count: number = 0;
     if (!_.isNil(this._store.getState().service.serviceInstance) && !_.isNil(this._store.getState().service.serviceInstance[serviceModelId])) {
-      const selectedVNF: string = this._sharedTreeService.getSelectedVNF();
+      const selectedVNF: string = this._sharedTreeService.getSelectedNF();
       if (selectedVNF) {
         count += this.countNumberOfVFModule(vnfs[selectedVNF], node);
       }else {
@@ -235,7 +235,7 @@ export class VFModuleModelInfo implements ILevelNodeInfo {
    * @param serviceModelId - service id
    ************************************************************/
   showNodeIcons(node: ITreeNode, serviceModelId: string): AvailableNodeIcons {
-    const selectedVNF: string = this._sharedTreeService.getSelectedVNF();
+    const selectedVNF: string = this._sharedTreeService.getSelectedNF();
     if (selectedVNF) {
       return this.showVFModuleOnSelectedVNF(node, selectedVNF, serviceModelId);
     } else {
index d391f4a..56a5391 100644 (file)
@@ -235,7 +235,7 @@ export class VnfModelInfo implements ILevelNodeInfo {
             let storeKey: string = node.data.vnfStoreKey;
             this._store.dispatch(removeInstance(node.data.vnfStoreKey, serviceModelId, storeKey, node));
             this._store.dispatch(changeInstanceCounter(node.data.modelUniqueId, serviceModelId, -1, node));
-            this._sharedTreeService.selectedVNF = null;
+            this._sharedTreeService.selectedNF = null;
           } else {
             let messageBoxData: MessageBoxData = new MessageBoxData(
               "Remove VNF",  // modal title
@@ -306,7 +306,7 @@ export class VnfModelInfo implements ILevelNodeInfo {
   removeVnf(this, node, serviceModelId) {
     this._store.dispatch(removeInstance(node.data.modelName, serviceModelId, node.data.vnfStoreKey, node));
     this._store.dispatch(changeInstanceCounter(node.data.modelUniqueId, serviceModelId, -1, node));
-    this._sharedTreeService.selectedVNF = null;
+    this._sharedTreeService.selectedNF = null;
   }
 
   /***********************************************************
index 2787c61..64b35a5 100644 (file)
@@ -170,7 +170,7 @@ export class VnfGroupingModelInfo implements ILevelNodeInfo {
             let storeKey: string = node.data.vnfGroupStoreKey;
             this._store.dispatch(removeInstance(node.data.vnfGroupStoreKey, serviceModelId, storeKey, node));
             this._store.dispatch(changeInstanceCounter(node.data.modelUniqueId, serviceModelId, -1, node));
-            this._sharedTreeService.selectedVNF = null;
+            this._sharedTreeService.selectedNF = null;
           } else {
             let messageBoxData: MessageBoxData = new MessageBoxData(
               "Remove VNFGroup",  // modal title
@@ -386,7 +386,7 @@ export class VnfGroupingModelInfo implements ILevelNodeInfo {
   removeGroup(this, node, serviceModelId) {
     this._store.dispatch(removeInstance(node.data.modelName, serviceModelId, node.data.vnfGroupStoreKey, node));
     this._store.dispatch(changeInstanceCounter(node.data.modelUniqueId, serviceModelId, -1, node));
-    this._sharedTreeService.selectedVNF = null;
+    this._sharedTreeService.selectedNF = null;
   }
 
   updatePosition(that, node, instanceId): void {
index 5523cf1..c706f24 100644 (file)
@@ -22,7 +22,7 @@ import {
   clearAssociateVRFMemberInstance,
   createVrfInstance
 } from "../../../../../../../shared/storeUtil/utils/vrf/vrf.actions";
-import {calculateNextUniqueModelName} from "../../../../../../../shared/storeUtil/utils/vnf/vnf.reducers";
+import {calculateNextUniqueModelName} from "../../../../../../../shared/storeUtil/utils/reducersHelper";
 
 @Injectable()
 export class VpnStepService {
index 32b8d6b..25d52b4 100644 (file)
@@ -37,6 +37,7 @@ import {SharedControllersService} from "../../../../shared/components/genericFor
 import {ModalService} from "../../../../shared/components/customModal/services/modal.service";
 import {CreateDynamicComponentService} from "../../../../shared/components/customModal/services/create-dynamic-component.service";
 import {PnfPopupService} from "../../../../shared/components/genericFormPopup/genericFormServices/pnf/pnf.popup.service";
+import {PnfControlGenerator} from "../../../../shared/components/genericForm/formControlsServices/pnfGenerator/pnf.control.generator";
 
 class MockAppStore<T> {
   getState() {
@@ -86,6 +87,7 @@ describe('Model Tree Generator service', () => {
         DialogService,
         FeatureFlagsService,
         VnfControlGenerator,
+        PnfControlGenerator,
         AaiService,
         DialogService,
         ErrorMsgService,
index cabf806..ca54f5f 100644 (file)
@@ -37,7 +37,7 @@ export class ObjectToInstanceTreeService {
     this.numberOfElements = 0;
     let _this = this;
     const serviceModelId:string = serviceInstance.modelInfo.modelVersionId;
-    const firstLevelOptions: ILevelNodeInfo[] = _this._objectToTreeService.getFirstLevelOptions(serviceInstance.isAlaCarte);
+    const firstLevelOptions: ILevelNodeInfo[] = _this._objectToTreeService.getFirstLevelOptions(serviceInstance.isALaCarte);
     for (let option of firstLevelOptions) {
       _.forOwn(serviceInstance[option.name], function (instance, modelName) {
         nodes.push(_this.getNodeInstance(modelName, null, instance, serviceHierarchy, option, serviceModelId));
index 8d4dbc4..64fdae1 100644 (file)
@@ -40,6 +40,7 @@ import {SharedControllersService} from "../../../../shared/components/genericFor
 import {ModalService} from "../../../../shared/components/customModal/services/modal.service";
 import {CreateDynamicComponentService} from "../../../../shared/components/customModal/services/create-dynamic-component.service";
 import {PnfPopupService} from "../../../../shared/components/genericFormPopup/genericFormServices/pnf/pnf.popup.service";
+import {PnfControlGenerator} from "../../../../shared/components/genericForm/formControlsServices/pnfGenerator/pnf.control.generator";
 
 class MockAppStore<T> {
   getState() {
@@ -110,6 +111,7 @@ describe('Model Tree Generator service', () => {
         DialogService,
         FeatureFlagsService,
         VnfControlGenerator,
+        PnfControlGenerator,
         AaiService,
         DialogService,
         DuplicateService,
index 67a962e..a1a6659 100644 (file)
@@ -60,7 +60,7 @@ export class ObjectToTreeService {
     if (FeatureFlagsService.getFlagState(Features.FLAG_EXTENDED_MACRO_PNF_CONFIG, this._store) === true && isALaCarte === false) {
       return [new VnfModelInfo(this._dynamicInputsService, this._sharedTreeService, this._defaultDataGeneratorService, this._dialogService, this._vnfPopupService, this._vfModulePopupService, this._vfModuleUpgradePopupService, this._duplicateService, this._modalService, this._iframeService, this._componentInfoService, this._featureFlagsService, this._store)
         , new NetworkModelInfo(this._dynamicInputsService, this._sharedTreeService, this._dialogService, this._networkPopupService, this._duplicateService, this._modalService, this._iframeService, this._featureFlagsService, this._store),
-        new PnfModelInfoExtended(this._store, this._sharedTreeService, this._dialogService, this._pnfPopupService),
+        new PnfModelInfoExtended(this._store, this._sharedTreeService, this._dialogService, this._pnfPopupService, this._iframeService, this._duplicateService, this._modalService, this._dynamicInputsService),
         new VrfModelInfo(this._store, this._sharedTreeService, this._dialogService, this._iframeService, this._featureFlagsService, this._networkStepService, this._vpnStepService),
         new CollectionResourceModelInfo(this._store, this._sharedTreeService),
         new ConfigurationModelInfo(this._dynamicInputsService, this._sharedTreeService),
index c82850e..ef11d6a 100644 (file)
@@ -45,6 +45,7 @@ import {SharedControllersService} from "../../../shared/components/genericForm/f
 import {ModalService} from "../../../shared/components/customModal/services/modal.service";
 import {CreateDynamicComponentService} from "../../../shared/components/customModal/services/create-dynamic-component.service";
 import { PnfPopupService } from "../../../shared/components/genericFormPopup/genericFormServices/pnf/pnf.popup.service";
+import { PnfControlGenerator } from "../../../shared/components/genericForm/formControlsServices/pnfGenerator/pnf.control.generator";
 
 class MockAppStore<T> {
   getState() {
@@ -110,6 +111,7 @@ describe('Shared Tree Service', () => {
         DialogService,
         FeatureFlagsService,
         VnfControlGenerator,
+        PnfControlGenerator,
         AaiService,
         DialogService,
         GenericFormService,
index 0ba90c2..26776d0 100644 (file)
@@ -28,19 +28,21 @@ export class SharedTreeService {
    * @param dynamicInputs - from the instance
    * @param isEcompGeneratedNaming
    ************************************************************/
-  selectedVNF: string = null;
+  selectedNF: string = null;
 
-
-  getSelectedVNF(): string {
-    return this.selectedVNF;
+  getSelectedNF(): string {
+    return this.selectedNF;
   }
 
-  setSelectedVNF(node): void {
+  setSelectedNF(node): void {
     if (_.isNil(node) || node.data.type !== 'VF') {
-      this.selectedVNF = null;
-    } else {
-      this.selectedVNF = node.data.vnfStoreKey;
+      this.selectedNF = null;
+    } else if (node.data.type === 'VF'){
+      this.selectedNF = node.data.vnfStoreKey;
+    } else if (node.data.type === 'PNF'){
+      this.selectedNF = node.data.pnfStoreKey;
     }
+
   }
 
   /**
diff --git a/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/pnfGenerator/pnf.control.generator.spec.ts b/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/pnfGenerator/pnf.control.generator.spec.ts
new file mode 100644 (file)
index 0000000..e510eaa
--- /dev/null
@@ -0,0 +1,175 @@
+import {getTestBed, TestBed} from '@angular/core/testing';
+import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
+import {NgRedux} from '@angular-redux/store';
+import {FormControlNames} from "../service.control.generator";
+import {ControlGeneratorUtil} from "../control.generator.util.service";
+import {AaiService} from "../../../../services/aaiService/aai.service";
+import {GenericFormService} from "../../generic-form.service";
+import {FormBuilder} from "@angular/forms";
+import {FormControlModel} from "../../../../models/formControlModels/formControl.model";
+import {LogService} from "../../../../utils/log/log.service";
+import {FeatureFlagsService} from "../../../../services/featureFlag/feature-flags.service";
+import {SharedControllersService} from "../sharedControlles/shared.controllers.service";
+import {PnfControlGenerator} from "./pnf.control.generator";
+import {MockNgRedux} from "@angular-redux/store/testing";
+
+class MockFeatureFlagsService {
+}
+
+class MockAppStore<T> {
+  getState() {
+    return {
+      global: {
+        flags: {
+          "FLAG_NETWORK_TO_ASYNC_INSTANTIATION": false,
+          "FLAG_SHOW_ASSIGNMENTS": true,
+          "FLAG_FABRIC_CONFIGURATION_ASSIGNMENTS": true,
+          "FLAG_SHOW_VERIFY_SERVICE": false,
+          "FLAG_SERVICE_MODEL_CACHE": true,
+          "FLAG_ADD_MSO_TESTAPI_FIELD": true
+        }
+      },
+      service: {
+        serviceHierarchy: {
+          'serviceId': {
+            'pnfs': {
+              'pnfName': {}
+            }
+          }
+        },
+        serviceInstance: {
+          'serviceId': {
+            'pnfs': {
+              'pnfName': {}
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+describe('PNF Control Generator', () => {
+  let injector;
+  let service: PnfControlGenerator;
+  let httpMock: HttpTestingController;
+
+  beforeAll(done => (async () => {
+    TestBed.configureTestingModule({
+      imports: [HttpClientTestingModule],
+      providers: [
+        MockNgRedux,
+        PnfControlGenerator,
+        GenericFormService,
+        ControlGeneratorUtil,
+        SharedControllersService,
+        AaiService,
+        FormBuilder,
+        LogService,
+        {provide: FeatureFlagsService, useClass: MockFeatureFlagsService},
+        {provide: NgRedux, useClass: MockAppStore}]
+    });
+    await TestBed.compileComponents();
+
+    injector = getTestBed();
+    service = injector.get(PnfControlGenerator);
+    httpMock = injector.get(HttpTestingController);
+
+  })().then(done).catch(done.fail));
+
+  test('getMacroFormControls check for mandatory controls', () => {
+    const serviceId = "serviceId";
+    const pnfName = "pnfName";
+    const pnfStoreKey = "pnfStoreKey";
+    const mandatoryControls: string[] = [
+      FormControlNames.INSTANCE_NAME,
+      FormControlNames.PRODUCT_FAMILY_ID
+    ];
+
+    const controls: FormControlModel[] = service.getMacroFormControls(serviceId, pnfStoreKey, pnfName, []);
+
+    for (let i = 0; i < mandatoryControls.length; i++) {
+      let requiredExist = controls.find(ctrl => ctrl.controlName === mandatoryControls[i]).validations.find(item => item.validatorName === 'required');
+      expect(requiredExist).toBeDefined();
+    }
+  });
+
+  test('should provide empty array on getMacroFormControls when serviceId, pnfName and pnfStoreKey equals to null', () => {
+    let pnfStoreKey = null;
+    const serviceId = null;
+    const pnfName: string = null;
+
+    const controls: FormControlModel[] = service.getMacroFormControls(serviceId, pnfStoreKey, pnfName, []);
+
+    expect(controls).toEqual([]);
+  });
+
+  test('getAlacartFormControls check for mandatory controls', () => {
+    const serviceId = "serviceId";
+    const pnfName = "pnfName";
+    const pnfStoreKey = "pnfStoreKey";
+    const mandatoryControls: string[] = [
+      FormControlNames.INSTANCE_NAME,
+      'platformName',
+      'lineOfBusiness',
+      'rollbackOnFailure'
+    ];
+
+    const controls: FormControlModel[] = service.getAlaCarteFormControls(serviceId, pnfStoreKey, pnfName, []);
+
+    for (let i = 0; i < mandatoryControls.length; i++) {
+      let requiredExist = controls.find(ctrl => ctrl.controlName === mandatoryControls[i]).validations.find(item => item.validatorName === 'required');
+      expect(requiredExist).toBeDefined();
+    }
+  });
+
+  test('should provide empty array on getAlaCarteFormControls when serviceId, pnfName and pnfStoreKey equals to null', () => {
+    let pnfStoreKey = null;
+    const serviceId = null;
+    const pnfName: string = null;
+
+    const result: FormControlModel[] = service.getAlaCarteFormControls(serviceId, pnfStoreKey, pnfName, []);
+
+    expect(result).toEqual([]);
+  });
+
+
+  test('getMacroFormControls should return the correct order of controls', () => {
+    const serviceId = "serviceId";
+    const pnfName = "pnfName";
+    const pnfStoreKey = "pnfStoreKey";
+    const controls: FormControlModel[] = service.getMacroFormControls(serviceId, pnfStoreKey, pnfName, []);
+
+    const controlsOrderNames = [
+      FormControlNames.INSTANCE_NAME,
+      FormControlNames.PRODUCT_FAMILY_ID,
+      'platformName',
+      'lineOfBusiness'];
+
+    expect(controls.length).toEqual(4);
+    for (let i = 0; i < controls.length; i++) {
+      expect(controls[i].controlName).toEqual(controlsOrderNames[i]);
+    }
+  });
+
+  test('getAlacarteFormControls should return the correct order of controls', () => {
+    const serviceId = "serviceId";
+    const pnfName = "pnfName";
+    const pnfStoreKey = "pnfStoreKey";
+    const controls: FormControlModel[] = service.getAlaCarteFormControls(serviceId, pnfStoreKey, pnfName, []);
+
+    const controlsOrderNames = [
+      FormControlNames.INSTANCE_NAME,
+      FormControlNames.PRODUCT_FAMILY_ID,
+      'platformName',
+      'lineOfBusiness',
+      'rollbackOnFailure'
+    ];
+
+    expect(controls.length).toEqual(5);
+    for (let i = 0; i < controls.length; i++) {
+      expect(controls[i].controlName).toEqual(controlsOrderNames[i]);
+    }
+  });
+});
+
diff --git a/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/pnfGenerator/pnf.control.generator.ts b/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/pnfGenerator/pnf.control.generator.ts
new file mode 100644 (file)
index 0000000..7e33817
--- /dev/null
@@ -0,0 +1,87 @@
+import {Injectable} from "@angular/core";
+import {GenericFormService} from "../../generic-form.service";
+import {AaiService} from "../../../../services/aaiService/aai.service";
+import {NgRedux} from "@angular-redux/store";
+import {HttpClient} from "@angular/common/http";
+import {ControlGeneratorUtil} from "../control.generator.util.service";
+import {FormControlModel} from "../../../../models/formControlModels/formControl.model";
+import {LogService} from "../../../../utils/log/log.service";
+import {PNFModel} from "../../../../models/pnfModel";
+import {AppState} from "../../../../store/reducers";
+import * as _ from 'lodash';
+import {SharedControllersService} from "../sharedControlles/shared.controllers.service";
+
+@Injectable()
+export class PnfControlGenerator {
+  aaiService: AaiService;
+  constructor(private genericFormService: GenericFormService,
+              private _basicControlGenerator: ControlGeneratorUtil,
+              private _sharedControllersService : SharedControllersService,
+              private store: NgRedux<AppState>,
+              private http: HttpClient,
+              private _aaiService: AaiService,
+              private _logService: LogService) {
+    this.aaiService = _aaiService;
+  }
+
+  getPnfInstance = (serviceId: string, pnfStoreKey: string): any => {
+    let pnfInstance = null;
+    if (this.store.getState().service.serviceInstance[serviceId] && _.has(this.store.getState().service.serviceInstance[serviceId].pnfs, pnfStoreKey)) {
+      pnfInstance = Object.assign({}, this.store.getState().service.serviceInstance[serviceId].pnfs[pnfStoreKey]);
+    }
+    return pnfInstance;
+  };
+
+  getMacroFormControls(serviceId: string, pnfStoreKey: string, pnfName: string, dynamicInputs?: any[]): FormControlModel[] {
+    pnfStoreKey = _.isNil(pnfStoreKey) ? pnfName : pnfStoreKey;
+
+    if (_.isNil(serviceId) || _.isNil(pnfStoreKey) || _.isNil(pnfName)) {
+      this._logService.error('should provide serviceId, pnfName, pnfStoreKey', serviceId);
+      return [];
+    }
+    const pnfInstance = this._basicControlGenerator.retrieveInstanceIfUpdateMode(this.store,this.getPnfInstance(serviceId, pnfStoreKey));
+    const pnfModel = new PNFModel(this.store.getState().service.serviceHierarchy[serviceId].pnfs[pnfName]);
+    let result: FormControlModel[] = [];
+    const flags = this.store.getState().global.flags;
+
+    if (!_.isNil(pnfModel)) {
+      const isPlatformMultiSelected = flags['FLAG_2002_PNF_PLATFORM_MULTI_SELECT'];
+      const isLobMultiSelected = flags['FLAG_2006_PNF_LOB_MULTI_SELECT'];
+
+      result.push(this.getInstanceName(pnfInstance, serviceId, pnfName, pnfModel.isEcompGeneratedNaming));
+      result.push(this._sharedControllersService.getProductFamilyControl(pnfInstance, result, true));
+      result.push(this._sharedControllersService.getPlatformMultiselectControl(pnfInstance, result, isPlatformMultiSelected));
+      result.push(this._sharedControllersService.getLobMultiselectControl(pnfInstance, isLobMultiSelected));
+    }
+    return result;
+  }
+
+  getAlaCarteFormControls(serviceId: string, pnfStoreKey: string, pnfName: string, dynamicInputs?: any[]): FormControlModel[] {
+    pnfStoreKey = _.isNil(pnfStoreKey) ? pnfName : pnfStoreKey;
+    if (_.isNil(serviceId) || _.isNil(pnfStoreKey) || _.isNil(pnfName)) {
+      this._logService.error('should provide serviceId, pnfName, pnfStoreKey', serviceId);
+      return [];
+    }
+
+    let result: FormControlModel[] = [];
+    const pnfInstance = this._basicControlGenerator.retrieveInstanceIfUpdateMode(this.store,this.getPnfInstance(serviceId, pnfStoreKey));
+    const pnfModel = new PNFModel(this.store.getState().service.serviceHierarchy[serviceId].pnfs[pnfName]);
+    const flags = this.store.getState().global.flags;
+
+    if (!_.isNil(pnfModel)) {
+      const isPlatformMultiSelected = flags['FLAG_2002_VNF_PLATFORM_MULTI_SELECT'];
+      const isLobMultiSelected = flags['FLAG_2006_VNF_LOB_MULTI_SELECT'];
+      result.push(this.getInstanceName(pnfInstance, serviceId, pnfName, pnfModel.isEcompGeneratedNaming));
+      result.push(this._sharedControllersService.getProductFamilyControl(pnfInstance, result, true));
+      result.push(this._sharedControllersService.getPlatformMultiselectControl(pnfInstance, result, isPlatformMultiSelected));
+      result.push(this._sharedControllersService.getLobMultiselectControl(pnfInstance,isLobMultiSelected));
+      result.push(this._sharedControllersService.getRollbackOnFailureControl(pnfInstance));
+    }
+    return result;
+  }
+
+  getInstanceName(instance : any, serviceId : string, pnfName : string, isEcompGeneratedNaming: boolean): FormControlModel {
+    const pnfModel : PNFModel = this.store.getState().service.serviceHierarchy[serviceId].pnfs[pnfName];
+    return this._sharedControllersService.getInstanceNameController(instance, serviceId, isEcompGeneratedNaming, pnfModel);
+  }
+}
diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/pnf/pnf.popup.service.spec.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/pnf/pnf.popup.service.spec.ts
new file mode 100644 (file)
index 0000000..bae5aee
--- /dev/null
@@ -0,0 +1,351 @@
+import {DefaultDataGeneratorService} from "../../../../services/defaultDataServiceGenerator/default.data.generator.service";
+import {NgRedux} from "@angular-redux/store";
+import {IframeService} from "../../../../utils/iframe.service";
+import {VfModulePopupService} from "../vfModule/vfModule.popup.service";
+import {FormBuilder} from "@angular/forms";
+import {GenericFormService} from "../../../genericForm/generic-form.service";
+import {BasicPopupService} from "../basic.popup.service";
+import {AaiService} from "../../../../services/aaiService/aai.service";
+import {LogService} from "../../../../utils/log/log.service";
+import {HttpClient} from "@angular/common/http";
+import {ControlGeneratorUtil} from "../../../genericForm/formControlsServices/control.generator.util.service";
+import {PnfControlGenerator} from "../../../genericForm/formControlsServices/pnfGenerator/pnf.control.generator";
+import {UUIDData} from "../../generic-form-popup.component";
+import {FeatureFlagsService} from "../../../../services/featureFlag/feature-flags.service";
+import {getTestBed, TestBed} from "@angular/core/testing";
+import {VfModuleUpgradePopupService} from "../vfModuleUpgrade/vfModule.upgrade.popuop.service";
+import {SharedControllersService} from "../../../genericForm/formControlsServices/sharedControlles/shared.controllers.service";
+import {PnfPopupService} from "../pnf/pnf.popup.service";
+import {AppState} from "../../../../store/reducers";
+
+class MockAppStore<T> {
+}
+
+class MockReduxStore<T> {
+  dispatch() {}
+  getState() {
+    return {
+      "global": {
+        "flags": {
+          "FLAG_NETWORK_TO_ASYNC_INSTANTIATION": false,
+          "FLAG_SHOW_ASSIGNMENTS": true,
+          "FLAG_FABRIC_CONFIGURATION_ASSIGNMENTS": true,
+          "FLAG_SHOW_VERIFY_SERVICE": false,
+          "FLAG_SERVICE_MODEL_CACHE": true,
+          "FLAG_ADD_MSO_TESTAPI_FIELD": true
+        }
+      },
+      "service": {
+        "serviceHierarchy": {
+          "serviceId": {
+            "service": {
+              "uuid": "6e59c5de-f052-46fa-aa7e-2fca9d674c44",
+              "invariantUuid": "e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0",
+              "name": "action-data",
+              "version": "1.0",
+              "toscaModelURL": null,
+              "category": "Emanuel",
+              "serviceType": "",
+              "serviceRole": "",
+              "description": "action-data",
+              "serviceEcompNaming": "false",
+              "instantiationType": "Macro",
+              "vidNotions": {
+                "instantiationType": "Macro"
+              },
+            },
+            "globalSubscriberId": "subscriberId",
+            "pnfs": {
+              "pnfInstanceV1": {
+                "name": "pnfName",
+                "pnfStoreKey": "pnfInstanceV1",
+                "version": "1.0",
+                "description": "PNF description",
+                "uuid": "0903e1c0-8e03-4936-b5c2-260653b96413",
+                "invariantUuid": "00beb8f9-6d39-452f-816d-c709b9cbb87d"
+              },
+              "pnfInstanceV2": {
+                "name": "pnfName2",
+                "pnfStoreKey": "pnfInstanceV2"
+              }
+            },
+            "modelInfo": {
+              "modelInvariantId": "e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0",
+              "modelVersionId": "6b528779-44a3-4472-bdff-9cd15ec93450",
+              "modelName": "action-data",
+              "modelVersion": "1.0",
+              "uuid": "6b528779-44a3-4472-bdff-9cd15ec93450"
+            },
+            "instanceName": "InstanceName",
+            "owningEntityId": "d61e6f2d-12fa-4cc2-91df-7c244011d6fc",
+            "productFamilyId": "17cc1042-527b-11e6-beb8-9e71128cae77",
+            "lcpCloudRegionId": "AAIAIC25",
+            "tenantId": "092eb9e8e4b7412e8787dd091bc58e86",
+            "aicZoneId": "JAG1",
+            "projectName": null,
+            "rollbackOnFailure": "true",
+            "aicZoneName": "YUDFJULP-JAG1",
+            "owningEntityName": "WayneHolland",
+            "testApi": "GR_API",
+            "tenantName": "USP-SIP-IC-24335-T-01",
+            "bulkSize": 1,
+            "isALaCarte": false,
+            "name": "action-data",
+            "version": "1.0",
+            "description": "",
+            "category": "",
+            "uuid": "6b528779-44a3-4472-bdff-9cd15ec93450",
+            "invariantUuid": "e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0",
+            "serviceType": "",
+            "serviceRole": "",
+            "isMultiStepDesign": false
+          }
+        },
+        "serviceInstance": {
+          "serviceId": {
+            "globalSubscriberId": "subscriberId",
+            "pnfs": {
+              "pnfInstanceV1": {
+                "name": "pnfName",
+                "pnfStoreKey": "pnfInstanceV1"
+              },
+              "pnfInstanceV2": {
+                "name": "pnfName2",
+                "pnfStoreKey": "pnfInstanceV2"
+              }
+            },
+            "modelInfo": {
+              "modelInvariantId": "e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0",
+              "modelVersionId": "6b528779-44a3-4472-bdff-9cd15ec93450",
+              "modelName": "action-data",
+              "modelVersion": "1.0",
+              "uuid": "6b528779-44a3-4472-bdff-9cd15ec93450"
+            },
+            "instanceName": "InstanceName",
+            "owningEntityId": "d61e6f2d-12fa-4cc2-91df-7c244011d6fc",
+            "productFamilyId": "17cc1042-527b-11e6-beb8-9e71128cae77",
+            "lcpCloudRegionId": "AAIAIC25",
+            "tenantId": "092eb9e8e4b7412e8787dd091bc58e86",
+            "aicZoneId": "JAG1",
+            "projectName": null,
+            "rollbackOnFailure": "true",
+            "aicZoneName": "YUDFJULP-JAG1",
+            "owningEntityName": "WayneHolland",
+            "testApi": "GR_API",
+            "tenantName": "USP-SIP-IC-24335-T-01",
+            "bulkSize": 1,
+            "isALaCarte": false,
+            "name": "action-data",
+            "version": "1.0",
+            "description": "",
+            "category": "",
+            "uuid": "6b528779-44a3-4472-bdff-9cd15ec93450",
+            "invariantUuid": "e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0",
+            "serviceType": "",
+            "serviceRole": "",
+            "isMultiStepDesign": false
+          }
+        },
+        "subscribers": [
+          {
+            "id": "someSubscriberId",
+            "name": "someSubscriberName",
+            "isPermitted": true
+          },
+          {
+            "id": "subscriberId",
+            "name": "subscriberName",
+            "isPermitted": true
+          },
+          {
+            "id": "subscriberId2",
+            "name": "subscriberName2",
+            "isPermitted": true
+          }
+        ]
+      }
+    }
+  }
+}
+
+class MockFeatureFlagsService {
+}
+
+describe('pnf new popup service', () => {
+  let injector;
+  let service: PnfPopupService;
+  let store: NgRedux<AppState>;
+
+
+  beforeAll(done => (async () => {
+    TestBed.configureTestingModule({
+      providers: [
+        PnfPopupService,
+        DefaultDataGeneratorService,
+        GenericFormService,
+        FormBuilder,
+        IframeService,
+        {provide: FeatureFlagsService, useClass: MockFeatureFlagsService},
+        AaiService,
+        LogService,
+        BasicPopupService,
+        VfModulePopupService,
+        VfModuleUpgradePopupService,
+        ControlGeneratorUtil,
+        SharedControllersService,
+        PnfControlGenerator,
+        {provide: NgRedux, useClass: MockReduxStore},
+        {provide: HttpClient, useClass: MockAppStore},
+      ]
+    });
+    await TestBed.compileComponents();
+
+    injector = getTestBed();
+    store = injector.get(NgRedux);
+    service = injector.get(PnfPopupService);
+
+  })().then(done).catch(done.fail));
+
+  test('getGenericFormPopupDetails returns the FormPopupDetails object', () => {
+      const serviceId: string = 'serviceId';
+      const pnfModelName: string = 'pnfInstanceV2';
+      const pnfStoreKey: string = 'pnfInstanceV2';
+      let uuidData: UUIDData = <any>{
+        serviceId: "serviceId",
+        modelName: "pnfInstanceV2",
+        pnfStoreKey: "pnfInstanceV2"
+      };
+
+      const formPopupDetailsObject = service.getGenericFormPopupDetails(serviceId, pnfModelName, pnfStoreKey, null, uuidData, true);
+
+      expect(formPopupDetailsObject).toBeDefined();
+    }
+  );
+
+  test('getInstance with empty storekey should be created', () => {
+    const serviceId: string = 'serviceId';
+    const pnfModelName: string = 'pnfInstanceV1';
+
+    const newInstance = service.getInstance(serviceId, pnfModelName, null);
+
+    expect(newInstance).toBeDefined();
+    expect(newInstance.pnfStoreKey).toBeNull();
+  });
+
+  test('getInstance with not empty storekey should return existing instance with the same model name as store key', () => {
+    const serviceId: string = 'serviceId';
+    const pnfModelName: string = 'pnfInstanceV1';
+    const pnfStoreKey: string = 'pnfInstanceV2';
+
+    const newInstance = service.getInstance(serviceId, pnfModelName, pnfStoreKey);
+
+    expect(newInstance).toBeDefined();
+    expect(newInstance.pnfStoreKey).toEqual('pnfInstanceV2');
+  });
+
+  test('getModelInformation  pnf should update modelInformations', () => {
+    const serviceId: string = 'serviceId';
+    const pnfModelName: string = 'pnfInstanceV1';
+
+    service.getModelInformation(serviceId, pnfModelName);
+
+    expect(service.modelInformations.length).toEqual(14);
+
+    expect(service.modelInformations[0].label).toEqual("Subscriber Name");
+    expect(service.modelInformations[0].values).toEqual(['subscriberName']);
+
+    expect(service.modelInformations[1].label).toEqual("Service Name");
+    expect(service.modelInformations[1].values).toEqual(['action-data']);
+
+    expect(service.modelInformations[2].label).toEqual("Service Instance Name");
+    expect(service.modelInformations[2].values).toEqual(['InstanceName']);
+
+    expect(service.modelInformations[3].label).toEqual("Model Name");
+    expect(service.modelInformations[3].values).toEqual(['pnfName']);
+
+    expect(service.modelInformations[4].label).toEqual("Model version");
+    expect(service.modelInformations[4].values).toEqual(['1.0']);
+
+    expect(service.modelInformations[5].label).toEqual("Description");
+    expect(service.modelInformations[5].values).toEqual(['PNF description']);
+
+    expect(service.modelInformations[6].label).toEqual("Category");
+    expect(service.modelInformations[6].values).toEqual([undefined]);
+
+    expect(service.modelInformations[7].label).toEqual("Sub Category");
+    expect(service.modelInformations[7].values).toEqual([undefined]);
+
+    expect(service.modelInformations[8].label).toEqual("UUID");
+    expect(service.modelInformations[8].values).toEqual(['0903e1c0-8e03-4936-b5c2-260653b96413']);
+
+    expect(service.modelInformations[9].label).toEqual("Invariant UUID");
+    expect(service.modelInformations[9].values).toEqual(['00beb8f9-6d39-452f-816d-c709b9cbb87d']);
+
+    expect(service.modelInformations[10].label).toEqual("Service type");
+    expect(service.modelInformations[10].values).toEqual(['']);
+
+    expect(service.modelInformations[11].label).toEqual("Service role");
+    expect(service.modelInformations[11].values).toEqual(['']);
+
+    expect(service.modelInformations[12].label).toEqual("Minimum to instantiate");
+    expect(service.modelInformations[12].values).toEqual(['0']);
+
+    expect(service.modelInformations[13].label).toEqual("Maximum to instantiate");
+    expect(service.modelInformations[13].values).toEqual(['1']);
+  });
+
+  test('getSubLeftTitle new pnf popup should return service model name', () => {
+    service.uuidData = {
+      serviceId: 'serviceId',
+      modelName: 'pnfInstanceV1'
+    };
+    expect(service.getSubLeftTitle()).toBe("PNF MODEL: pnfName");
+  });
+
+  test('storePNF should dispatch createPNFInstance action when isUpdateMode is false', () => {
+    let mockedPopupService = getMockedPopupService(false);
+
+    spyOn(store, 'dispatch');
+    service.storePNF(mockedPopupService, {});
+
+    expect(store.dispatch).toHaveBeenCalledWith(jasmine.objectContaining({
+      type: "CREATE_PNF_INSTANCE",
+    }));
+  });
+
+  test('storePNF should dispatch updatePNFInstance action when isUpdateMode is true', () => {
+    let mockedPopupService = getMockedPopupService(true);
+
+    spyOn(mockedPopupService._store, 'dispatch');
+    service.storePNF(mockedPopupService, {});
+
+    expect(mockedPopupService._store.dispatch).toHaveBeenCalledWith(jasmine.objectContaining({
+      type: "UPDATE_PNF_INSTANCE",
+    }));
+  });
+
+  function getMockedPopupService(isUpdateMode: boolean) {
+    return <any>{
+      model: {},
+      isUpdateMode: isUpdateMode,
+      _store: {
+        dispatch: () => {
+        }
+      },
+      uuidData: {
+        serviceId: "serviceId"
+      }
+    };
+  }
+
+  test('getTitle pnf should return the correct title for edit and create mode', () => {
+    expect(service.getTitle(false)).toBe('Set a new PNF');
+    expect(service.getTitle(true)).toBe('Edit PNF instance');
+  });
+
+  test('extractSubscriberNameBySubscriberId should extract proper subscriber by id', () => {
+    expect(service.extractSubscriberNameBySubscriberId("subscriberId", store))
+      .toBe('subscriberName');
+  });
+
+});
index c4f1b2e..70fcc16 100644 (file)
@@ -7,6 +7,7 @@ import {ModelInformationItem} from "../../../model-information/model-information
 import {ServiceModel} from "../../../../models/serviceModel";
 import {Subject} from "rxjs/Subject";
 import {ControlGeneratorUtil} from "../../../genericForm/formControlsServices/control.generator.util.service";
+import {PnfControlGenerator} from "../../../genericForm/formControlsServices/pnfGenerator/pnf.control.generator";
 import {IframeService} from "../../../../utils/iframe.service";
 import {DefaultDataGeneratorService} from "../../../../services/defaultDataServiceGenerator/default.data.generator.service";
 import {AaiService} from "../../../../services/aaiService/aai.service";
@@ -16,14 +17,17 @@ import {AppState} from "../../../../store/reducers";
 import {Subscriber} from "../../../../models/subscriber";
 import {Constants} from "../../../../utils/constants";
 import {PnfInstance} from "../../../../models/pnfInstance";
+import {ModelInfo} from "../../../../models/modelInfo";
+import {changeInstanceCounter} from "../../../../storeUtil/utils/general/general.actions";
+import {createPNFInstance, updatePNFInstance} from "../../../../storeUtil/utils/pnf/pnf.actions";
 import * as _ from 'lodash';
 
 @Injectable()
-export class PnfPopupService implements GenericPopupInterface{
+export class PnfPopupService implements GenericPopupInterface {
   dynamicInputs: any;
   instance: any;
-  model:any;
-  serviceModel:ServiceModel;
+  model: any;
+  serviceModel: ServiceModel;
   modelInformations: ModelInformationItem[] = [];
   uuidData: Object;
   closeDialogEvent: Subject<any> = new Subject<any>();
@@ -31,6 +35,7 @@ export class PnfPopupService implements GenericPopupInterface{
 
   constructor(
     private _basicControlGenerator: ControlGeneratorUtil,
+    private _pnfControlGenerator: PnfControlGenerator,
     private _iframeService: IframeService,
     private _defaultDataGeneratorService: DefaultDataGeneratorService,
     private _aaiService: AaiService,
@@ -53,17 +58,25 @@ export class PnfPopupService implements GenericPopupInterface{
       this.getControls(serviceId, modelName, pnfStoreKey),
       this._basicPopupService.getDynamicInputs(serviceId, modelName, pnfStoreKey, 'pnfs'),
       this.modelInformations,
-      (that, form: FormGroup) => {that.onSubmit(that, form);},
-      (that: any, form: FormGroup) => {that.onCancel(that, form); }
-      )
+      (that, form: FormGroup) => {
+        that.onSubmit(that, form);
+      },
+      (that: any, form: FormGroup) => {
+        that.onCancel(that, form);
+      }
+    )
   }
 
-  getControls(serviceId: string, modelName: string, pnfStoreKey: string){
-    return [];
+  getControls(serviceId: string, modelName: string, pnfStoreKey: string) {
+    if (this._store.getState().service.serviceHierarchy[serviceId].service.vidNotions.instantiationType === 'Macro') {
+      return this._pnfControlGenerator.getMacroFormControls(serviceId, pnfStoreKey, modelName);
+    } else {
+      return this._pnfControlGenerator.getAlaCarteFormControls(serviceId, pnfStoreKey, modelName);
+    }
   }
 
   getInstance(serviceId: string, modelName: string, pnfStoreKey: string): any {
-    if(_.isNil(pnfStoreKey)){
+    if (_.isNil(pnfStoreKey)) {
       return new PnfInstance();
     }
     return this._store.getState().service.serviceInstance[serviceId].pnfs[pnfStoreKey];
@@ -89,7 +102,7 @@ export class PnfPopupService implements GenericPopupInterface{
         new ModelInformationItem("Service role", "serviceRole", [this.serviceModel.serviceRole]),
         new ModelInformationItem("Minimum to instantiate", "min", [!_.isNil(this.model.min) ? this.model.min.toString() : '0'], "", false),
         this._basicPopupService.createMaximumToInstantiateModelInformationItem(this.model)
-        ];
+      ];
     })
   }
 
@@ -101,10 +114,20 @@ export class PnfPopupService implements GenericPopupInterface{
     return "PNF Instance Details";
   }
 
-  storePNF = (that, formValues: any): void => {};
+  storePNF = (that, formValues: any): void => {
+    formValues.modelInfo = new ModelInfo(that.model);
+    formValues.uuid = formValues.modelInfo.uuid;
+    formValues.isMissingData = false;
+    if (!that.isUpdateMode) {
+      that._store.dispatch(changeInstanceCounter(formValues.modelInfo.modelUniqueId, that.uuidData.serviceId, 1, <any>{data: {type: 'PNF'}}));
+      this._store.dispatch(createPNFInstance(formValues, that.uuidData['modelName'], that.uuidData['serviceId'], that.uuidData['modelName']));
+    } else {
+      that._store.dispatch(updatePNFInstance(formValues, that.uuidData.modelName, that.uuidData.serviceId, that.uuidData.pnfStoreKey))
+    }
+  };
 
   getTitle(isUpdateMode: boolean): string {
-    return isUpdateMode  ? "Edit PNF instance": "Set a new PNF" ;
+    return isUpdateMode ? "Edit PNF instance" : "Set a new PNF";
   }
 
   onCancel(that, form): void {
@@ -116,7 +139,7 @@ export class PnfPopupService implements GenericPopupInterface{
   onSubmit(that, form: FormGroup, ...args): void {
     form.value['instanceParams'] = form.value['instanceParams'] && [form.value['instanceParams']];
     that.storePNF(that, form.value);
-    window.parent.postMessage( {
+    window.parent.postMessage({
       eventId: 'submitIframe',
       data: {
         serviceModelId: that.uuidData.serviceId
index ba36796..ff855bc 100644 (file)
@@ -5,6 +5,7 @@ export class PnfInstance extends Level1Instance {
 
   pnfStoreKey : string;
   statusMessage?: string;
+  position: number;
 
   constructor() {
     super();
index 14e6588..8f9e0d1 100644 (file)
@@ -3,6 +3,8 @@ import {
   Level1ModelProperties,
   Level1ModelResponseInterface
 } from "./nodeModel";
+import {VNFModelResponseInterface} from "./vnfModel";
+import {Utils} from "../utils/utils";
 
 
 
@@ -18,10 +20,11 @@ export class PNFModel extends Level1Model{
   roles: string[] = [];
   properties: PnfProperties;
 
-  constructor(pnfJson?: PNFModelResponseInterface) {
+  constructor(pnfJson?: PNFModelResponseInterface, flags?: { [key: string]: boolean }) {
     super(pnfJson);
     if (pnfJson && pnfJson.properties) {
       this.properties = pnfJson.properties;
+      this.max = Utils.getMaxFirstLevel(this.properties, flags);
     }
   }
 
index 1f111d1..67b8806 100644 (file)
@@ -23,8 +23,8 @@ export class ServiceInstance extends NodeInstance{
   latestAvailableVersion: Number;
   pause: boolean;
   bulkSize: number;
-  pnfs: { [pnf_module_model_name: string]: PnfInstance; };
   vnfs: { [vnf_module_model_name: string]: VnfInstance; };
+  pnfs: { [pnf_module_model_name: string]: PnfInstance; };
   vrfs: { [vrf_model_name: string]: VrfInstance; };
   vnfGroups : {[vnf_module_model_name: string]: VnfGroupInstance; };
   networks: { [vnf_module_model_name: string]: NetworkInstance; };
@@ -36,8 +36,8 @@ export class ServiceInstance extends NodeInstance{
   validationCounter: number;
   existingNames:  {[key: string] : any};
   modelInavariantId?: string;
-  existingPNFCounterMap : { [pnf_module_model_name: string]: number; };
   existingVNFCounterMap : { [vnf_module_model_name: string]: number; };
+  existingPNFCounterMap : { [pnf_module_model_name: string]: number; };
   existingVRFCounterMap : { [vrf_module_model_name: string]: number; };
   existingVnfGroupCounterMap : { [vnf_group_module_model_name: string]: number; };
   existingNetworksCounterMap : { [network_module_model_name: string]: number; };
index f563cbc..ad934cf 100644 (file)
@@ -22,7 +22,7 @@ describe("AaiService", () => {
   let httpMock: HttpTestingController;
   let aaiService: AaiService;
   let mockFeatureFlagsService: FeatureFlagsService = mock(FeatureFlagsService);
-  let store : NgRedux<AppState>;
+  let store: NgRedux<AppState>;
 
   beforeAll(done => (async () => {
     TestBed.configureTestingModule({
@@ -42,8 +42,6 @@ describe("AaiService", () => {
 
   })().then(done).catch(done.fail));
 
-
-
   describe('#resolve tests', () => {
     test('aai service resolve should return the right object', () => {
       let serviceInstance = new ServiceInstance();
@@ -173,8 +171,37 @@ describe("AaiService", () => {
     });
   });
 
+  describe('#Pnf modelCustomizationName initialization tests', () => {
+
+    test('initializePnfModelCustomizationName should not reinitialize modelCustomizationName when it exists', () => {
+      let serviceHierarchy = {
+        "pnfs": {
+          "pnfInstance": {
+            "modelCustomizationName": "existingName"
+          }
+        }
+      }
+
+      aaiService.initializePnfModelCustomizationName(serviceHierarchy);
+
+      expect(serviceHierarchy.pnfs["pnfInstance"].modelCustomizationName).toBe("existingName");
+    });
+
+    test('initializePnfModelCustomizationName should initialize modelCustomizationName when it doesnt exist', () => {
+      let serviceHierarchy = {
+        "pnfs": {
+          "pnfInstance": {}
+        }
+      }
+
+      aaiService.initializePnfModelCustomizationName(serviceHierarchy);
+
+      expect((serviceHierarchy.pnfs["pnfInstance"] as any).modelCustomizationName).toBe("pnfInstance");
+    });
+  });
+
   function getTopology() {
-    return  {
+    return {
       "vnfs": {
         "2017-388_PASQUALE-vPE 0": {
           "vfModules": {},
@@ -398,7 +425,7 @@ describe("AaiService", () => {
     }
   }
 
-  function getMockActiveNetworks(){
+  function getMockActiveNetworks() {
     return [
       {
         networkInstanceName: "networkInstanceName",
index adb7017..2829a89 100644 (file)
@@ -58,6 +58,7 @@ export class AaiService {
 
   getServiceModelById = (serviceModelId: string): Observable<any> => {
     if (_.has(this.store.getState().service.serviceHierarchy, serviceModelId)) {
+      this.initializePnfModelCustomizationName(this.store.getState().service.serviceHierarchy[serviceModelId]);
       return of(<any> JSON.parse(JSON.stringify(this.store.getState().service.serviceHierarchy[serviceModelId])));
     }
     let pathQuery: string = Constants.Path.SERVICES_PATH + serviceModelId;
@@ -321,6 +322,15 @@ export class AaiService {
     return result;
   }
 
+  initializePnfModelCustomizationName(serviceHierarchy) : void {
+    let pnfs = serviceHierarchy.pnfs;
+    for (let pnf in pnfs) {
+      if (!pnfs[pnf].modelCustomizationName){
+        pnfs[pnf].modelCustomizationName = pnf;
+      }
+    }
+  }
+
   loadMockMembers(): any {
     return [
       {
index 56f49e7..4abffab 100644 (file)
@@ -4,25 +4,176 @@ import {NgRedux} from '@angular-redux/store';
 import {DefaultDataGeneratorService} from './default.data.generator.service';
 import {ServiceNodeTypes} from "../../models/ServiceNodeTypes";
 import {VNFModel} from "../../models/vnfModel";
+import {AppState} from "../../store/reducers";
 
-class MockAppStore<T> {}
+class MockAppStore<T> {
+  dispatch() {
+
+  }
+
+  getState() {
+    return {
+      "global": {
+        "flags": {
+          "FLAG_NETWORK_TO_ASYNC_INSTANTIATION": false,
+          "FLAG_SHOW_ASSIGNMENTS": true,
+          "FLAG_FABRIC_CONFIGURATION_ASSIGNMENTS": true,
+          "FLAG_SHOW_VERIFY_SERVICE": false,
+          "FLAG_SERVICE_MODEL_CACHE": true,
+          "FLAG_ADD_MSO_TESTAPI_FIELD": true
+        }
+      },
+      "service": {
+        "serviceHierarchy": {
+          "serviceId": {
+            "service": {
+              "uuid": "6e59c5de-f052-46fa-aa7e-2fca9d674c44",
+              "invariantUuid": "e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0",
+              "name": "action-data",
+              "version": "1.0",
+              "toscaModelURL": null,
+              "category": "Emanuel",
+              "serviceType": "",
+              "serviceRole": "",
+              "description": "action-data",
+              "serviceEcompNaming": "false",
+              "instantiationType": "Macro",
+              "vidNotions": {
+                "instantiationType": "Macro"
+              },
+            },
+            "globalSubscriberId": "subscriberId",
+            "pnfs": {
+              "pnfInstanceV1": {
+                "name": "pnfName",
+                "pnfStoreKey": "pnfInstanceV1",
+                "version": "1.0",
+                "description": "PNF description",
+                "uuid": "0903e1c0-8e03-4936-b5c2-260653b96413",
+                "invariantUuid": "00beb8f9-6d39-452f-816d-c709b9cbb87d",
+                "properties": {
+                  "min_instances": "1",
+                  "ecomp_generated_naming": "true"
+                }
+              }
+            },
+            "modelInfo": {
+              "modelInvariantId": "e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0",
+              "modelVersionId": "6b528779-44a3-4472-bdff-9cd15ec93450",
+              "modelName": "action-data",
+              "modelVersion": "1.0",
+              "uuid": "6b528779-44a3-4472-bdff-9cd15ec93450"
+            },
+            "instanceName": "InstanceName",
+            "owningEntityId": "d61e6f2d-12fa-4cc2-91df-7c244011d6fc",
+            "productFamilyId": "17cc1042-527b-11e6-beb8-9e71128cae77",
+            "lcpCloudRegionId": "AAIAIC25",
+            "tenantId": "092eb9e8e4b7412e8787dd091bc58e86",
+            "aicZoneId": "JAG1",
+            "projectName": null,
+            "rollbackOnFailure": "true",
+            "aicZoneName": "YUDFJULP-JAG1",
+            "owningEntityName": "WayneHolland",
+            "testApi": "GR_API",
+            "tenantName": "USP-SIP-IC-24335-T-01",
+            "bulkSize": 1,
+            "isALaCarte": false,
+            "name": "action-data",
+            "version": "1.0",
+            "description": "",
+            "category": "",
+            "uuid": "6b528779-44a3-4472-bdff-9cd15ec93450",
+            "invariantUuid": "e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0",
+            "serviceType": "",
+            "serviceRole": "",
+            "isMultiStepDesign": false
+          }
+        },
+        "serviceInstance": {
+          "serviceId": {
+            "globalSubscriberId": "subscriberId",
+            "pnfs": {
+              "pnfInstanceV1": {
+                "name": "pnfName",
+                "pnfStoreKey": "pnfInstanceV1"
+              },
+              "pnfInstanceV2": {
+                "name": "pnfName2",
+                "pnfStoreKey": "pnfInstanceV2"
+              }
+            },
+            "modelInfo": {
+              "modelInvariantId": "e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0",
+              "modelVersionId": "6b528779-44a3-4472-bdff-9cd15ec93450",
+              "modelName": "action-data",
+              "modelVersion": "1.0",
+              "uuid": "6b528779-44a3-4472-bdff-9cd15ec93450"
+            },
+            "instanceName": "InstanceName",
+            "owningEntityId": "d61e6f2d-12fa-4cc2-91df-7c244011d6fc",
+            "productFamilyId": "17cc1042-527b-11e6-beb8-9e71128cae77",
+            "lcpCloudRegionId": "AAIAIC25",
+            "tenantId": "092eb9e8e4b7412e8787dd091bc58e86",
+            "aicZoneId": "JAG1",
+            "projectName": null,
+            "rollbackOnFailure": "true",
+            "aicZoneName": "YUDFJULP-JAG1",
+            "owningEntityName": "WayneHolland",
+            "testApi": "GR_API",
+            "tenantName": "USP-SIP-IC-24335-T-01",
+            "bulkSize": 1,
+            "isALaCarte": false,
+            "name": "action-data",
+            "version": "1.0",
+            "description": "",
+            "category": "",
+            "uuid": "6b528779-44a3-4472-bdff-9cd15ec93450",
+            "invariantUuid": "e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0",
+            "serviceType": "",
+            "serviceRole": "",
+            "isMultiStepDesign": false
+          }
+        },
+        "subscribers": [
+          {
+            "id": "someSubscriberId",
+            "name": "someSubscriberName",
+            "isPermitted": true
+          },
+          {
+            "id": "subscriberId",
+            "name": "subscriberName",
+            "isPermitted": true
+          },
+          {
+            "id": "subscriberId2",
+            "name": "subscriberName2",
+            "isPermitted": true
+          }
+        ]
+      }
+    }
+  }
+}
 
 describe('Default Data Generator Service', () => {
   let injector;
   let service: DefaultDataGeneratorService;
+  let store: NgRedux<AppState>;
   let httpMock: HttpTestingController;
 
   beforeAll(done => (async () => {
     TestBed.configureTestingModule({
       imports: [HttpClientTestingModule],
-          providers: [DefaultDataGeneratorService,
-            {provide: NgRedux, useClass: MockAppStore}]
+      providers: [DefaultDataGeneratorService,
+        {provide: NgRedux, useClass: MockAppStore}]
     });
     await TestBed.compileComponents();
 
     injector = getTestBed();
     service = injector.get(DefaultDataGeneratorService);
     httpMock = injector.get(HttpTestingController);
+    store = injector.get(NgRedux);
   })().then(done).catch(done.fail));
 
   test('generateVFModule aLaCarte vf module object should missed data', () => {
@@ -124,7 +275,7 @@ describe('Default Data Generator Service', () => {
           "modelVersion": "1"
         },
         volumeGroupName: "",
-        isMissingData : false,
+        isMissingData: false,
         trackById: Math.random().toString()
       }, vfModuleModel, vnfModuleUUID, false, [], "");
       expect(newVfModule.name).toEqual('&lt;Automatically Assigned&gt;');
@@ -147,7 +298,7 @@ describe('Default Data Generator Service', () => {
           "modelVersion": "1"
         },
         volumeGroupName: "",
-        isMissingData : false,
+        isMissingData: false,
         trackById: Math.random().toString()
       }, vfModuleModel, vnfModuleUUID, true, [], "");
       expect(newVfModule.name).toEqual('&lt;Automatically Assigned&gt;');
@@ -159,7 +310,7 @@ describe('Default Data Generator Service', () => {
     test('createNewVnfTreeNode with isEcompGeneratedNaming instance name not filled - missing data true', () => {
       const vnfModel = generateServiceHierarchy().vnfs['VF_vGeraldine 0'];
       const newVnf = service.createNewTreeNode({
-        uuid : '',
+        uuid: '',
         instanceName: "",
         productFamilyId: "productFamilyId",
         lcpCloudRegionId: "lcpCloudRegionId",
@@ -168,7 +319,7 @@ describe('Default Data Generator Service', () => {
         platformName: "platformName",
         lineOfBusiness: "lineOfBusiness",
         rollbackOnFailure: "rollbackOnFailure",
-        originalName : null,
+        originalName: null,
         vfModules: {},
         modelInfo: {
           "modelCustomizationName": "VF_vGeraldine 0",
@@ -182,7 +333,7 @@ describe('Default Data Generator Service', () => {
         isMissingData: false,
         trackById: Math.random().toString(),
         vnfStoreKey: "abc"
-      }, new VNFModel(vnfModel),'VF_vGeraldine 0', 'vnfs');
+      }, new VNFModel(vnfModel), 'VF_vGeraldine 0', 'vnfs');
       expect(newVnf.name).toEqual('VF_vGeraldine 0');
       expect(newVnf.missingData).toEqual(true);
     });
@@ -190,7 +341,7 @@ describe('Default Data Generator Service', () => {
     test('createNewVnfTreeNode with isEcompGeneratedNaming instance name filled - missing data false', () => {
       const vnfModel = generateServiceHierarchy().vnfs['VF_vGeraldine 0'];
       const newVnf = service.createNewTreeNode({
-        uuid : '',
+        uuid: '',
         instanceName: "instanceName",
         productFamilyId: "productFamilyId",
         lcpCloudRegionId: "lcpCloudRegionId",
@@ -199,7 +350,7 @@ describe('Default Data Generator Service', () => {
         platformName: "platformName",
         lineOfBusiness: "lineOfBusiness",
         rollbackOnFailure: "rollbackOnFailure",
-        originalName : null,
+        originalName: null,
         vfModules: {},
         modelInfo: {
           "modelCustomizationName": "VF_vGeraldine 0",
@@ -213,12 +364,73 @@ describe('Default Data Generator Service', () => {
         isMissingData: false,
         trackById: Math.random().toString(),
         vnfStoreKey: "abc"
-      }, vnfModel,'VF_vGeraldine 0', 'vnfs');
+      }, vnfModel, 'VF_vGeraldine 0', 'vnfs');
       expect(newVnf.name).toEqual("instanceName");
       expect(newVnf.missingData).toEqual(false);
     });
   });
 
+  describe('#updatePnfsOnFirstSet tests', () => {
+
+    test('updatePnfsOnFirstSet should call createPNFInstanceReduxIfNotExist when pnfs exists and extended pnf flag is on', () => {
+      jest.spyOn(service, 'isExtendedMacroPnfConfigOn').mockReturnValue(true);
+      spyOn(service, 'createPNFInstanceReduxIfNotExist');
+
+      service.updatePnfsOnFirstSet("serviceId", {})
+
+      expect(service.createPNFInstanceReduxIfNotExist).toHaveBeenCalled();
+    });
+
+    test('updatePnfsOnFirstSet should not call createPNFInstanceReduxIfNotExist when pnfs exists and extended pnf flag is off', () => {
+      jest.spyOn(service, 'isExtendedMacroPnfConfigOn').mockReturnValue(false);
+      spyOn(service, 'createPNFInstanceReduxIfNotExist');
+
+      service.updatePnfsOnFirstSet("serviceId", {})
+
+      expect(service.createPNFInstanceReduxIfNotExist).not.toHaveBeenCalled();
+    });
+
+    test('updatePnfsOnFirstSet should not call createPNFInstanceReduxIfNotExist when min_instances == 0 and extended pnf flag is on', () => {
+      jest.spyOn(service, 'isExtendedMacroPnfConfigOn').mockReturnValue(true);
+      jest.spyOn(service, 'isMinInstancesGreaterThanZero').mockReturnValue(false);
+      spyOn(service, 'createPNFInstanceReduxIfNotExist');
+
+      service.updatePnfsOnFirstSet("serviceId", {})
+
+      expect(service.createPNFInstanceReduxIfNotExist).not.toHaveBeenCalled();
+    });
+
+    test('createPNFInstanceReduxIfNotExist should dispatch proper actions when pnf doesnt exist', () => {
+      let pnfData = {
+        modelInfo: {
+          modelCustomizationName: "pnfName"
+        }
+      };
+      spyOn(store, 'dispatch');
+
+      service.createPNFInstanceReduxIfNotExist("serviceId", pnfData);
+
+      expect(store.dispatch).toHaveBeenCalledWith(jasmine.objectContaining({
+        type: "CREATE_PNF_INSTANCE",
+      }));
+      expect(store.dispatch).toHaveBeenCalledWith(jasmine.objectContaining({
+        type: "CHANGE_INSTANCE_COUNTER",
+      }));
+    });
+
+    test('createPNFInstanceReduxIfNotExist should not dispatch anything when pnf exists', () => {
+      let pnfData = {
+        modelInfo: {
+          modelCustomizationName: "pnfInstanceV1"
+        }
+      };
+      spyOn(store, 'dispatch');
+
+      service.createPNFInstanceReduxIfNotExist("serviceId", pnfData);
+
+      expect(store.dispatch).not.toHaveBeenCalled();
+    });
+  });
 });
 
 
index e00b259..1bf6984 100644 (file)
@@ -1,6 +1,5 @@
 import {Injectable} from '@angular/core';
 import * as _ from 'lodash';
-
 import {NgRedux} from '@angular-redux/store';
 import {AppState} from '../../store/reducers';
 import {VnfTreeNode} from "../../models/vnfTreeNode";
@@ -21,12 +20,15 @@ import {VnfGroupTreeNode} from "../../models/vnfGroupTreeNode";
 import {ModelInfo} from "../../models/modelInfo";
 import {ServiceInstanceActions} from "../../models/serviceInstanceActions";
 import Parameter = Constants.Parameter;
+import {createPNFInstance} from "../../storeUtil/utils/pnf/pnf.actions";
+import {FeatureFlagsService, Features} from "../featureFlag/feature-flags.service";
 
 @Injectable()
 export class DefaultDataGeneratorService {
   static controlsFieldsStatus = {};
   public requiredFields = {
     VF: [InputType.LCP_REGION, InputType.TENANT, InputType.PLATFORM],
+    PNF: [InputType.PLATFORM],
     Network: [InputType.LCP_REGION, InputType.TENANT, InputType.PLATFORM],
     VL: [InputType.LCP_REGION, InputType.TENANT, InputType.PLATFORM],
     VFmodule: [],
@@ -65,11 +67,10 @@ export class DefaultDataGeneratorService {
           break;
       }
       if (Utils.hasContents(inputs[key][Parameter.CONSTRAINTS])
-        && ( inputs[key][Parameter.CONSTRAINTS].length > 0 )) {
+        && (inputs[key][Parameter.CONSTRAINTS].length > 0)) {
         let constraintsArray = inputs[key][Parameter.CONSTRAINTS];
         this.addConstraintParameters(parameterList, constraintsArray, key, inputs, parameter);
-      }
-      else {
+      } else {
 
         parameterList.push(parameter);
       }
@@ -97,8 +98,8 @@ export class DefaultDataGeneratorService {
                     name: constraintsArray[i][Parameter.VALID_VALUES][j],
                     isDefault: false
                   };
-                  if ((Utils.hasContents(inputs[key][Parameter.DEFAULT]) )
-                    && (inputs[key][Parameter.DEFAULT] === constraintsArray[i][Parameter.VALID_VALUES][j] )) {
+                  if ((Utils.hasContents(inputs[key][Parameter.DEFAULT]))
+                    && (inputs[key][Parameter.DEFAULT] === constraintsArray[i][Parameter.VALID_VALUES][j])) {
                     option = {
                       name: constraintsArray[i][Parameter.VALID_VALUES][j],
                       isDefault: true
@@ -180,14 +181,13 @@ export class DefaultDataGeneratorService {
     return _.isEmpty(displayInputs) ? [] : this.getArbitraryInputs(displayInputs);
   }
 
-  updateNetworksOnFirstSet(serviceId: string, formServiceValues: any){
+  updateNetworksOnFirstSet(serviceId: string, formServiceValues: any) {
     const serviceHierarchy = this.store.getState().service.serviceHierarchy[serviceId];
     if (serviceHierarchy && !_.isEmpty(serviceHierarchy.networks)) {
       for (let networkUUID in serviceHierarchy.networks) {
         const isEcompGeneratedNaming = this.getIsEcompGeneratedNaming(serviceHierarchy.networks[networkUUID]);
         let min_vnf_instances_greater_than_0 = serviceHierarchy.networks[networkUUID].properties['min_instances'] && serviceHierarchy.networks[networkUUID].properties['min_instances'] > 0;
-        if(min_vnf_instances_greater_than_0)
-        {
+        if (min_vnf_instances_greater_than_0) {
           this.createNetworkInstanceReduxIfNotExist(
             serviceId,
             this.generateNetworkData(serviceHierarchy, networkUUID, formServiceValues, isEcompGeneratedNaming)
@@ -197,14 +197,13 @@ export class DefaultDataGeneratorService {
     }
   }
 
-  updateVnfGroupsOnFirstSet(serviceId: string, formServiceValues: any){
+  updateVnfGroupsOnFirstSet(serviceId: string, formServiceValues: any) {
     const serviceHierarchy = this.store.getState().service.serviceHierarchy[serviceId];
     if (serviceHierarchy && !_.isEmpty(serviceHierarchy.vnfGroups)) {
       for (let vnfGroupUUID in serviceHierarchy.vnfGroups) {
         const isEcompGeneratedNaming = this.getIsEcompGeneratedNaming(serviceHierarchy.vnfGroups[vnfGroupUUID]);
         let min_vnf_group_instances_greater_than_0 = serviceHierarchy.vnfGroups[vnfGroupUUID].properties['min_instances'] && serviceHierarchy.vnfGroups[vnfGroupUUID].properties['min_instances'] > 0;
-        if(min_vnf_group_instances_greater_than_0)
-        {
+        if (min_vnf_group_instances_greater_than_0) {
           this.createVnfGroupInstanceReduxIfNotExist(
             serviceId,
             this.generateVnfGroupData(serviceHierarchy, vnfGroupUUID, formServiceValues, isEcompGeneratedNaming)
@@ -214,9 +213,38 @@ export class DefaultDataGeneratorService {
     }
   }
 
+  updatePnfsOnFirstSet(serviceId: string, formServiceValues: any) {
+    const serviceHierarchy = this.store.getState().service.serviceHierarchy[serviceId];
+    if (serviceHierarchy && !_.isEmpty(serviceHierarchy.pnfs)) {
+      for (let pnfUUID in serviceHierarchy.pnfs) {
+        if (this.isMinInstancesGreaterThanZero(serviceHierarchy, pnfUUID) && this.isExtendedMacroPnfConfigOn()) {
+          this.createPNFInstanceReduxIfNotExist(
+            serviceId,
+            this.generatePNFData(
+              serviceHierarchy,
+              pnfUUID,
+              formServiceValues,
+              this.getIsEcompGeneratedNaming(serviceHierarchy.pnfs[pnfUUID])
+            )
+          );
+        }
+      }
+    }
+  }
+
+  isExtendedMacroPnfConfigOn(): boolean {
+    return FeatureFlagsService.getFlagState(Features.FLAG_EXTENDED_MACRO_PNF_CONFIG, this.store)
+  }
+
+  isMinInstancesGreaterThanZero(serviceHierarchy, pnfUUID): boolean {
+    return serviceHierarchy.pnfs[pnfUUID].properties['min_instances']
+      && serviceHierarchy.pnfs[pnfUUID].properties['min_instances'] > 0;
+  }
+
   updateReduxOnFirstSet(serviceId: string, formServiceValues: any): void {
     this.updateNetworksOnFirstSet(serviceId, formServiceValues);
     this.updateVnfGroupsOnFirstSet(serviceId, formServiceValues);
+    this.updatePnfsOnFirstSet(serviceId, formServiceValues);
     const serviceHierarchy = this.store.getState().service.serviceHierarchy[serviceId];
     if (serviceHierarchy && !_.isEmpty(serviceHierarchy.vnfs)) {
       for (let vnfUUID in serviceHierarchy.vnfs) {
@@ -245,8 +273,7 @@ export class DefaultDataGeneratorService {
         }
 
         let min_vnf_instances_greater_than_0 = serviceHierarchy.vnfs[vnfUUID].properties['min_instances'] && serviceHierarchy.vnfs[vnfUUID].properties['min_instances'] > 0;
-        if(min_vnf_instances_greater_than_0)
-        {
+        if (min_vnf_instances_greater_than_0) {
           this.createVNFInstanceReduxIfNotExist(
             serviceId,
             this.generateVNFData(serviceHierarchy, vnfUUID, formServiceValues, isEcompGeneratedNaming)
@@ -256,7 +283,6 @@ export class DefaultDataGeneratorService {
     }
   }
 
-
   private getIsEcompGeneratedNaming(vnfJson) {
     const ecompGeneratedNaming = vnfJson.properties.ecomp_generated_naming;
     return ecompGeneratedNaming === "true";
@@ -269,6 +295,13 @@ export class DefaultDataGeneratorService {
     }
   }
 
+  createPNFInstanceReduxIfNotExist(serviceId: string, pnfData: any): void {
+    if(!this.store.getState().service.serviceInstance[serviceId].pnfs[pnfData.modelInfo.modelCustomizationName]){
+      this.store.dispatch(createPNFInstance(pnfData, pnfData.modelInfo.modelCustomizationName, serviceId));
+      this.store.dispatch(changeInstanceCounter(pnfData.modelInfo.modelUniqueId, serviceId, 1, <any> {data : {type : 'PNF'}}));
+    }
+  }
+
   createNetworkInstanceReduxIfNotExist(serviceId: string, networkData: any): void {
     if(!this.store.getState().service.serviceInstance[serviceId].vnfs[networkData.modelInfo.modelCustomizationName]){
       this.store.dispatch(createNetworkInstance(networkData, networkData.modelInfo.modelCustomizationName, serviceId));
@@ -378,6 +411,29 @@ export class DefaultDataGeneratorService {
     }
   }
 
+  generatePNFData(serviceHierarchy: any, pnfName: string, formValues: any, isEcompGeneratedNaming) {
+    return {
+      'uuid' : serviceHierarchy.pnfs[pnfName].uuid,
+      'isMissingData' :this.setIsMissingData(ServiceNodeTypes.PNF, [], isEcompGeneratedNaming),
+      'productFamilyId': formValues.productFamilyId,
+      'lcpCloudRegionId': null,
+      'tenantId': null,
+      'lineOfBusiness': null,
+      'platformName': null,
+      'modelInfo': {
+        'modelType': 'PNF',
+        'modelInvariantId': serviceHierarchy.pnfs[pnfName].invariantUuid,
+        'modelVersionId': serviceHierarchy.pnfs[pnfName].uuid,
+        'modelName': serviceHierarchy.pnfs[pnfName].name,
+        'modelVersion': serviceHierarchy.pnfs[pnfName].version,
+        'modelCustomizationId': serviceHierarchy.pnfs[pnfName].customizationUuid,
+        'modelCustomizationName': serviceHierarchy.pnfs[pnfName].modelCustomizationName,
+        'modelUniqueId' : serviceHierarchy.pnfs[pnfName].customizationUuid || serviceHierarchy.pnfs[pnfName].uuid,
+      },
+      'trackById': DefaultDataGeneratorService.createRandomTrackById(),
+    }
+  }
+
   generateNetworkData(serviceHierarchy: any, networkName: string, formValues: any, isEcompGeneratedNaming) {
       return {
         'uuid' : serviceHierarchy.network[networkName].uuid,
index f228389..e4238e0 100644 (file)
@@ -99,6 +99,9 @@ import {MessageModal} from "./components/messageModal/message-modal.service";
 import {SpaceToUnderscorePipe} from "./pipes/spaceToUnderscore/space-to-underscore.pipe";
 import {ResizableModule} from 'angular-resizable-element';
 import {PnfPopupService} from "./components/genericFormPopup/genericFormServices/pnf/pnf.popup.service";
+import {PnfControlGenerator} from "./components/genericForm/formControlsServices/pnfGenerator/pnf.control.generator";
+
+
 
 @NgModule({
   imports: [
@@ -235,6 +238,7 @@ import {PnfPopupService} from "./components/genericFormPopup/genericFormServices
     ServiceControlGenerator,
     ServicePopupService,
     VnfControlGenerator,
+    PnfControlGenerator,
     VfModuleControlGenerator,
     ControlGeneratorUtil,
     SharedControllersService,
index a135563..faf4aae 100644 (file)
@@ -26,6 +26,8 @@ import {CrActions} from "./cr/cr.actions";
 import {crReducer} from "./cr/cr.reducer";
 import {NcfActions} from "./ncf/ncf.actions";
 import {ncfReducer} from "./ncf/ncf.reducer";
+import {PNFActions} from "./pnf/pnf.actions";
+import {pnfReducer} from "./pnf/pnf.reducers";
 
 export let initialState: ServiceState = {
   serviceHierarchy: {},
@@ -64,6 +66,8 @@ export const MainReducer = function (state: ServiceState = initialState, action:
     return vfModuleReducer(state, action);
   }else if (Object.values(VNFActions).includes(action.type)){
     return vnfReducer(state, action);
+  }else if (Object.values(PNFActions).includes(action.type)){
+    return pnfReducer(state, action);
   }else if (Object.values(VnfGroupActions).includes(action.type)){
     return vnfGroupReducer(state, action);
   }else if(Object.values(RelatedVnfActions).includes(action.type)){
index a8154e8..9c44213 100644 (file)
@@ -8,7 +8,7 @@ import {
   UpdateNetworkInstanceAction
 } from "./network.actions";
 import {ServiceInstance} from "../../../models/serviceInstance";
-import {calculateNextUniqueModelName} from "../vnf/vnf.reducers";
+import {calculateNextUniqueModelName} from "../reducersHelper";
 import {ServiceState} from "../main.reducer";
 import {ServiceInstanceActions} from "../../../models/serviceInstanceActions";
 import {deleteFirstLevel, updateServiceValidationCounter} from "../reducersHelper";
diff --git a/vid-webpack-master/src/app/shared/storeUtil/utils/pnf/pnf.actions.ts b/vid-webpack-master/src/app/shared/storeUtil/utils/pnf/pnf.actions.ts
new file mode 100644 (file)
index 0000000..82ab59f
--- /dev/null
@@ -0,0 +1,88 @@
+import {Action, ActionCreator} from "redux";
+import {PnfInstance} from "../../../models/pnfInstance";
+import {ActionOnFirstLevel} from "../firstLevel/firstLevel.actions";
+
+export enum PNFActions {
+  CREATE_PNF_INSTANCE = "CREATE_PNF_INSTANCE",
+  UPDATE_PNF_INSTANCE = "UPDATE_PNF_INSTANCE",
+  REMOVE_PNF_INSTANCE = "REMOVE_PNF_INSTANCE",
+  DELETE_ACTION_PNF_INSTANCE = "DELETE_PNF_INSTANCE",
+  UNDO_DELETE_ACTION_PNF_INSTANCE = "UNDO_DELETE_PNF_INSTANCE",
+  UPDATE_PNF_POSITION = "UPDATE_PNF_POISTION"
+}
+
+export enum PNFMethods{
+  UPGRADE = "upgrade",
+  UNDO_UPGRADE = "undoUpgrade"
+}
+
+
+export interface CreatePnfInstanceAction extends Action {
+  pnfInstance?: PnfInstance;
+  pnfModelName?: string;
+  serviceUuid?: string;
+  pnfStoreKey?:string;
+}
+
+export interface UpdatePnfPosition extends Action {
+  node: any,
+  instanceId : string,
+  pnfStoreKey?: string;
+}
+
+export interface UpdatePnfInstanceAction extends Action {
+  pnfInstance?: PnfInstance;
+  pnfModelName?: string;
+  serviceUuid?: string;
+  pnfStoreKey?:string;
+}
+
+export interface RemovePnfInstanceAction extends Action {
+  pnfStoreKey: string;
+  serviceId?: string;
+}
+
+export const createPNFInstance: ActionCreator<CreatePnfInstanceAction> = (pnfInstance, pnfModelName, serviceUuid, pnfStoreKey) => ({
+  type: PNFActions.CREATE_PNF_INSTANCE,
+  pnfInstance: pnfInstance,
+  pnfModelName: pnfModelName,
+  serviceUuid: serviceUuid,
+  pnfStoreKey : pnfStoreKey
+});
+
+
+export const updatePNFInstance: ActionCreator<UpdatePnfInstanceAction> = (pnfInstance, pnfModelName, serviceUuid, pnfStoreKey) => ({
+  type: PNFActions.UPDATE_PNF_INSTANCE,
+  pnfInstance: pnfInstance,
+  pnfModelName: pnfModelName,
+  serviceUuid: serviceUuid,
+  pnfStoreKey : pnfStoreKey
+});
+
+
+export const deleteActionPnfInstance: ActionCreator<ActionOnFirstLevel> = (pnfStoreKey, serviceId) => ({
+  type: PNFActions.DELETE_ACTION_PNF_INSTANCE,
+  firstLevelName: 'pnfs',
+  storeKey: pnfStoreKey,
+  serviceId: serviceId
+});
+
+export const undoDeleteActionPnfInstance: ActionCreator<ActionOnFirstLevel> = (pnfStoreKey, serviceId) => ({
+  type: PNFActions.UNDO_DELETE_ACTION_PNF_INSTANCE,
+  firstLevelName: 'pnfs',
+  storeKey: pnfStoreKey,
+  serviceId: serviceId
+});
+
+export const removePnfInstance: ActionCreator<RemovePnfInstanceAction> = (pnfStoreKey, serviceId) => ({
+  type: PNFActions.REMOVE_PNF_INSTANCE,
+  pnfStoreKey: pnfStoreKey,
+  serviceId: serviceId
+});
+
+export const updatePnfPosition: ActionCreator<UpdatePnfPosition> = (node, instanceId, pnfStoreKey) => ({
+  type: PNFActions.UPDATE_PNF_POSITION,
+  node: node,
+  instanceId: instanceId,
+  pnfStoreKey : pnfStoreKey
+});
diff --git a/vid-webpack-master/src/app/shared/storeUtil/utils/pnf/pnf.reducers.spec.ts b/vid-webpack-master/src/app/shared/storeUtil/utils/pnf/pnf.reducers.spec.ts
new file mode 100644 (file)
index 0000000..b924957
--- /dev/null
@@ -0,0 +1,172 @@
+import {
+  CreatePnfInstanceAction,
+  RemovePnfInstanceAction,
+  UpdatePnfPosition,
+  UpdatePnfInstanceAction,
+  PNFActions
+} from "./pnf.actions";
+import {pnfReducer} from "./pnf.reducers";
+import {ServiceInstanceActions} from "../../../models/serviceInstanceActions";
+import {ActionOnFirstLevel} from "../firstLevel/firstLevel.actions";
+import {PnfInstance} from "../../../models/pnfInstance";
+
+describe('pnfReducer', () => {
+
+  test('#CREATE_PNF_INSTANCE', () => {
+    let pnfInstance: PnfInstance = new PnfInstance();
+    pnfInstance.isMissingData = false;
+    pnfInstance.instanceName = 'instanceName';
+    let pnfState = pnfReducer(<any>{
+        serviceInstance: {
+          'serviceModelId': {
+            pnfs: {}
+          }
+        }
+      },
+      <CreatePnfInstanceAction>{
+        type: PNFActions.CREATE_PNF_INSTANCE,
+        pnfInstance: pnfInstance,
+        pnfStoreKey: null,
+        pnfModelName: 'pnfModelName',
+        serviceUuid: 'serviceModelId'
+      }).serviceInstance['serviceModelId'].pnfs['pnfModelName'];
+
+    expect(pnfState).toBeDefined();
+    expect(pnfState.isMissingData).toBeFalsy();
+  });
+
+  test('#UPDATE_PNF_INSTANCE_NAME', () => {
+    let newInstanceName: string = "newInstanceName"
+    let pnfInstance: PnfInstance = new PnfInstance();
+    pnfInstance.isMissingData = false;
+    pnfInstance.instanceName = newInstanceName;
+
+    let pnfState = pnfReducer(<any>{
+        serviceInstance: {
+          'serviceModelId': {
+            pnfs: {
+              "pnfStoreKey": {
+                instanceName: 'oldInstanceName'
+              }
+            },
+            existingNames: {
+              'oldinstancename': {}
+            }
+          }
+        }
+      },
+      <UpdatePnfInstanceAction>{
+        type: PNFActions.UPDATE_PNF_INSTANCE,
+        pnfInstance: pnfInstance,
+        pnfStoreKey: 'pnfStoreKey',
+        pnfModelName: 'pnfModelName',
+        serviceUuid: 'serviceModelId'
+      }).serviceInstance['serviceModelId'];
+
+    expect(pnfState).toBeDefined();
+    expect(pnfState.pnfs['pnfStoreKey'].instanceName).toEqual(newInstanceName);
+    expect(newInstanceName.toLowerCase() in pnfState.existingNames).toBeTruthy();
+  });
+
+  test('#DELETE_ACTION_PNF_INSTANCE', () => {
+    let pnfState = pnfReducer(<any>{
+        serviceInstance: {
+          'serviceModelId': {
+            pnfs: {
+              'pnfStoreKey': {
+                isMissingData: true,
+                action: 'None'
+              }
+            }
+          }
+        }
+      },
+      <ActionOnFirstLevel>{
+        type: PNFActions.DELETE_ACTION_PNF_INSTANCE,
+        firstLevelName: 'pnfs',
+        storeKey: 'pnfStoreKey',
+        serviceId: 'serviceModelId'
+      }).serviceInstance['serviceModelId'].pnfs['pnfStoreKey'];
+
+    expect(pnfState).toBeDefined();
+    expect(pnfState.action).toEqual(ServiceInstanceActions.None_Delete);
+  });
+
+  test('#UNDO_DELETE_ACTION_PNF_INSTANCE', () => {
+    let pnfState = pnfReducer(<any>{
+        serviceInstance: {
+          'serviceModelId': {
+            pnfs: {
+              'pnfStoreKey': {
+                isMissingData: true,
+                action: 'Update_Delete'
+              }
+            }
+          }
+        }
+      },
+      <ActionOnFirstLevel>{
+        type: PNFActions.UNDO_DELETE_ACTION_PNF_INSTANCE,
+        storeKey: 'pnfStoreKey',
+        firstLevelName: 'pnfs',
+        serviceId: 'serviceModelId'
+      }).serviceInstance['serviceModelId'].pnfs['pnfStoreKey'];
+
+    expect(pnfState).toBeDefined();
+    expect(pnfState.action).toEqual(ServiceInstanceActions.Update);
+  });
+
+  test('#REMOVE_PNF_INSTANCE', () => {
+    let pnfs = pnfReducer(<any>{
+        serviceInstance: {
+          'serviceModelId': {
+            pnfs: {
+              'pnfStoreKey': {
+                isMissingData: true,
+                action: 'Update_Delete'
+              },
+              'pnfStoreKey_1': {
+                isMissingData: true,
+                action: 'Update_Delete'
+              }
+            }
+          }
+        }
+      },
+      <RemovePnfInstanceAction>{
+        type: PNFActions.REMOVE_PNF_INSTANCE,
+        pnfStoreKey: 'pnfStoreKey',
+        serviceId: 'serviceModelId'
+      }).serviceInstance['serviceModelId'].pnfs;
+
+    expect(pnfs).toBeDefined();
+    expect(pnfs['pnfStoreKey']).toBeUndefined();
+    expect(pnfs['pnfStoreKey_1']).toBeDefined();
+  });
+
+  test('#UPDATE_PNF_POSITION', () => {
+    let pnfInstance: PnfInstance = new PnfInstance();
+    pnfInstance.isMissingData = false;
+    pnfInstance.instanceName = 'instanceName';
+    let pnfState = pnfReducer(<any>{
+        serviceInstance: {
+          'serviceModelId': {
+            pnfs: {
+              "pnfStoreKey": {}
+            }
+          }
+        }
+      },
+      <UpdatePnfPosition>{
+        type: PNFActions.UPDATE_PNF_POSITION,
+        node: <any>{
+          position: 2
+        },
+        pnfStoreKey: 'pnfStoreKey',
+        instanceId: 'serviceModelId'
+      }).serviceInstance['serviceModelId'].pnfs['pnfStoreKey'];
+
+    expect(pnfState).toBeDefined();
+    expect(pnfState.position).toEqual(2);
+  });
+});
diff --git a/vid-webpack-master/src/app/shared/storeUtil/utils/pnf/pnf.reducers.ts b/vid-webpack-master/src/app/shared/storeUtil/utils/pnf/pnf.reducers.ts
new file mode 100644 (file)
index 0000000..aeeae4d
--- /dev/null
@@ -0,0 +1,87 @@
+import {Action} from "redux";
+import {PnfInstance} from "../../../models/pnfInstance";
+import {
+  CreatePnfInstanceAction,
+  RemovePnfInstanceAction,
+  UpdatePnfInstanceAction, UpdatePnfPosition,
+  PNFActions
+} from "./pnf.actions";
+import * as _ from "lodash";
+import {ServiceState} from "../main.reducer";
+import {ServiceInstanceActions} from "../../../models/serviceInstanceActions";
+import {deleteFirstLevel, updateServiceValidationCounter, calculateNextUniqueModelName, updateUniqueNames} from "../reducersHelper";
+import {ActionOnFirstLevel} from "../firstLevel/firstLevel.actions";
+
+export function pnfReducer(state: ServiceState, action: Action): ServiceState {
+  switch (action.type) {
+    case PNFActions.CREATE_PNF_INSTANCE: {
+      const updatePnfInstanceAction = <CreatePnfInstanceAction>action;
+      const serviceUuid = updatePnfInstanceAction.serviceUuid;
+      let pnfModelName = updatePnfInstanceAction.pnfModelName;
+      let newState = _.cloneDeep(state);
+
+      updatePnfInstanceAction.pnfInstance.originalName = pnfModelName;
+      updatePnfInstanceAction.pnfModelName = calculateNextUniqueModelName(pnfModelName, serviceUuid, newState, 'pnfs');
+
+      let pnfInstance: PnfInstance = newState.serviceInstance[serviceUuid].pnfs[pnfModelName];
+      pnfInstance = new PnfInstance();
+      updatePnfInstanceAction.pnfInstance.pnfStoreKey = updatePnfInstanceAction.pnfModelName;
+      updatePnfInstanceAction.pnfInstance.originalName = pnfModelName;
+      pnfInstance.originalName = updatePnfInstanceAction.pnfInstance.originalName;
+      pnfInstance.pnfStoreKey = updatePnfInstanceAction.pnfInstance.pnfStoreKey;
+      updateServiceValidationCounter(newState, pnfInstance['isMissingData'], updatePnfInstanceAction.pnfInstance['isMissingData'], serviceUuid);
+
+      newState.serviceInstance[serviceUuid].pnfs[updatePnfInstanceAction.pnfModelName] = Object.assign(pnfInstance, updatePnfInstanceAction.pnfInstance);
+      return newState;
+    }
+
+    case PNFActions.UPDATE_PNF_INSTANCE: {
+      const updatePnfInstanceAction = <UpdatePnfInstanceAction>action;
+      const serviceUuid = updatePnfInstanceAction.serviceUuid;
+      let pnfStoreKey = updatePnfInstanceAction.pnfStoreKey;
+
+      let newState = _.cloneDeep(state);
+      let pnfInstance: PnfInstance = newState.serviceInstance[serviceUuid].pnfs[pnfStoreKey];
+      let oldInstanceName = pnfInstance ? pnfInstance.instanceName : null;
+      updateUniqueNames(oldInstanceName, updatePnfInstanceAction.pnfInstance.instanceName, newState.serviceInstance[serviceUuid]);
+
+      pnfInstance = pnfInstance || new PnfInstance();
+      updateServiceValidationCounter(newState, pnfInstance['isMissingData'], updatePnfInstanceAction.pnfInstance['isMissingData'], serviceUuid);
+
+      newState.serviceInstance[serviceUuid].pnfs[pnfStoreKey] = Object.assign(pnfInstance, updatePnfInstanceAction.pnfInstance);
+      return newState;
+    }
+
+    case PNFActions.DELETE_ACTION_PNF_INSTANCE : {
+      return deleteFirstLevel(state, <ActionOnFirstLevel>action,true);
+    }
+
+    case PNFActions.UNDO_DELETE_ACTION_PNF_INSTANCE : {
+      let newState = _.cloneDeep(state);
+      let pnf = newState.serviceInstance[(<ActionOnFirstLevel>action).serviceId].pnfs[(<ActionOnFirstLevel>action).storeKey];
+      let oldState = pnf.action;
+      newState.serviceInstance[(<ActionOnFirstLevel>action).serviceId].pnfs[(<ActionOnFirstLevel>action).storeKey].action = (oldState.split('_')[0]) as ServiceInstanceActions;
+      updateServiceValidationCounter(newState, pnf['isMissingData'], false,  (<ActionOnFirstLevel>action).serviceId);
+      return newState;
+    }
+
+    case PNFActions.REMOVE_PNF_INSTANCE : {
+      let newState = _.cloneDeep(state);
+      let pnfInstance = newState.serviceInstance[(<RemovePnfInstanceAction>action).serviceId].pnfs[(<RemovePnfInstanceAction>action).pnfStoreKey];
+      updateServiceValidationCounter(newState, pnfInstance['isMissingData'], false,  (<RemovePnfInstanceAction>action).serviceId);
+      delete newState.serviceInstance[(<RemovePnfInstanceAction>action).serviceId].pnfs[(<RemovePnfInstanceAction>action).pnfStoreKey];
+      return newState;
+    }
+
+    case PNFActions.UPDATE_PNF_POSITION : {
+      let newState = _.cloneDeep(state);
+      newState.serviceInstance[(<UpdatePnfPosition>action).instanceId]
+        .pnfs[(<UpdatePnfPosition>action).pnfStoreKey]
+        .position = (<UpdatePnfPosition>action).node.position;
+      return newState;
+    }
+  }
+}
+
+
+
index c192ece..0291a3b 100644 (file)
@@ -2,6 +2,7 @@ import * as _ from "lodash";
 import {ActionOnFirstLevel} from "./firstLevel/firstLevel.actions";
 import {ServiceInstanceActions} from "../../models/serviceInstanceActions";
 import {ServiceState} from "./main.reducer";
+import {ServiceInstance} from "../../models/serviceInstance";
 
 export function deleteFirstLevel(state: ServiceState, action: ActionOnFirstLevel,shouldUpdateServiceValidationCounter: boolean){
   let newState = _.cloneDeep(state);
@@ -28,3 +29,26 @@ function resetUpgradeStatus(newState: any, serviceUuid: string){
   newState.serviceInstance[serviceUuid].upgradedVFMSonsCounter = 0;
   newState.serviceInstance[serviceUuid].isUpgraded = false;
 }
+
+export const updateUniqueNames = (oldName: string, newName: string, serviceInstance: ServiceInstance): void => {
+  let existingNames = serviceInstance.existingNames;
+  if (!_.isNil(oldName) && oldName.toLowerCase() in existingNames) {
+    delete existingNames[oldName.toLowerCase()];
+  }
+  if (!_.isNil(newName)) {
+    existingNames[newName.toLowerCase()] = "";
+  }
+};
+
+
+export const calculateNextUniqueModelName = (nfModelName: string, serviceId: string, state: any, levelName: string): string => {
+  let counter: number = null;
+  while (true) {
+    let pattern = !_.isNil(counter) ? ("_" + counter) : "";
+    if (!_.isNil(state.serviceInstance[serviceId][levelName][nfModelName + pattern])) {
+      counter = counter ? (counter + 1) : 1;
+    } else {
+      return nfModelName + pattern;
+    }
+  }
+};
index c5cd88a..b5bcec9 100644 (file)
@@ -7,10 +7,9 @@ import {
   VNFActions
 } from "./vnf.actions";
 import * as _ from "lodash";
-import {ServiceInstance} from "../../../models/serviceInstance";
 import {ServiceState} from "../main.reducer";
 import {ServiceInstanceActions} from "../../../models/serviceInstanceActions";
-import {deleteFirstLevel, updateServiceValidationCounter} from "../reducersHelper";
+import {deleteFirstLevel, updateServiceValidationCounter, calculateNextUniqueModelName, updateUniqueNames} from "../reducersHelper";
 import {ActionOnFirstLevel} from "../firstLevel/firstLevel.actions";
 
 export function vnfReducer(state: ServiceState, action: Action): ServiceState {
@@ -126,27 +125,3 @@ export function vnfReducer(state: ServiceState, action: Action): ServiceState {
 
   }
 }
-
-
-const updateUniqueNames = (oldName: string, newName: string, serviceInstance: ServiceInstance): void => {
-  let existingNames = serviceInstance.existingNames;
-  if (!_.isNil(oldName) && oldName.toLowerCase() in existingNames) {
-    delete existingNames[oldName.toLowerCase()];
-  }
-  if (!_.isNil(newName)) {
-    existingNames[newName.toLowerCase()] = "";
-  }
-};
-
-
-export const calculateNextUniqueModelName = (vnfModelName: string, serviceId: string, state: any, levelName: string): string => {
-  let counter: number = null;
-  while (true) {
-    let pattern = !_.isNil(counter) ? ("_" + counter) : "";
-    if (!_.isNil(state.serviceInstance[serviceId][levelName][vnfModelName + pattern])) {
-      counter = counter ? (counter + 1) : 1;
-    } else {
-      return vnfModelName + pattern;
-    }
-  }
-};
index f3d8120..33992dd 100644 (file)
@@ -7,7 +7,7 @@ import {
   CreateVRFInstanceAction, DeleteActionVrfInstanceAction, UndoDeleteActionVrfInstanceAction,
   VrfActions
 } from "./vrf.actions";
-import {calculateNextUniqueModelName} from "../vnf/vnf.reducers";
+import {calculateNextUniqueModelName} from "../reducersHelper";
 import {ServiceInstanceActions} from "../../../models/serviceInstanceActions";
 
 export function vrfReducer(state: ServiceState, action: Action): ServiceState {